Compare commits

..

104 Commits

Author SHA1 Message Date
durch e8d8bfc55f DJB lib25519 Rust wrapper 2023-06-20 17:19:09 +02:00
benedettadavico 72b92784cc Merge remote-tracking branch 'origin/develop' into develop 2023-06-20 13:45:29 +02:00
benedettadavico 38b95c2673 nym connect version fix 2023-06-20 13:45:04 +02:00
Mark Sinclair f0f9899f82 Update package.json to fix build:dev target 2023-06-20 12:12:03 +01:00
benedettadavico 09c46e3403 Merge remote-tracking branch 'origin/master' into develop 2023-06-20 10:36:06 +02:00
benedettadavico 9fbab5aaad updating versions for release v1.1.22 2023-06-20 10:24:54 +02:00
benedettadavico 2032b3bdae updating versions for release v1.1.22 2023-06-20 09:51:28 +02:00
Jędrzej Stuczyński 8f57919571 optional id argument for NymNodeTester (#3555) 2023-06-16 11:19:40 +01:00
Jon Häggblad 3748ab77a1 Statically link openssl (#3504)
* Statically link openssl

* Fix wasm client build

* Enable openssl for nyxd-client feature
2023-06-16 11:27:25 +02:00
pierre 3cdca0ad8d copy change 2023-06-16 11:20:20 +02:00
pierre 9cbb3dfa0e copy change 2023-06-16 11:19:44 +02:00
Pierre Dommerc 4071d30f3c feat(nc-desktop): add error reporting and monitoring setting (#3553) 2023-06-16 11:09:29 +02:00
pierre 4c13d91bfb fix(nyms5-android): add check for worker and proxy states desync 2023-06-16 10:08:57 +02:00
Pierre Dommerc 8355e6ce5e feat(nc-desktop): add error reporting and monitoring setting (#3553) 2023-06-15 19:23:23 +02:00
Tommy Verrall dd33052f0e Merge pull request #3520 from nymtech/bugfix/incorrect-client-versions
bugfix #3434
2023-06-14 17:23:11 +01:00
Jędrzej Stuczyński a76a51e823 fixed config version of binaries depending on socks5 core configs 2023-06-14 17:16:07 +01:00
Jon Häggblad b31d3c003f Cargo.lock 2023-06-14 15:04:37 +02:00
Jon Häggblad 21e11c9221 Merge remote-tracking branch 'origin/master' into release/v1.1.22 2023-06-14 14:55:30 +02:00
Jon Häggblad c9dcde0ee0 getrandom on workspace level 2023-06-14 13:26:09 +02:00
Jon Häggblad 3fcc16090a Update nym-outfox Cargo.toml in prep for publish to crates.io 2023-06-14 13:15:40 +02:00
Jędrzej Stuczyński bbb1e5e15a Feature/node tester disconnect (#3552)
* Ability to disconnect and reconnect GatewayClient

* usage of ibid. inside NodeTester

* example

* wasm-compatible `wait_for_shutdown` (for the future)
2023-06-13 17:25:42 +01:00
Drazen Urch 6d30e7ea8e Adjustments to cover traffic and ack handling (#3548) 2023-06-13 13:07:02 +02:00
farbanas 9288f71c5f Merge branch 'master' into develop 2023-06-13 11:16:17 +02:00
farbanas 79f5983c76 updating lock files 2023-06-13 11:04:36 +02:00
farbanas 335453b63c update versions and changelog for release v1.1.21 2023-06-13 10:54:01 +02:00
Jędrzej Stuczyński 4292a55614 explicitly setting up fresh gateway in native socks5 (#3547) 2023-06-12 17:01:05 +01:00
mx f97f0475e9 Merge pull request #3545 from nymtech/feature/docs-1-1-21
version bump for variables
2023-06-12 12:04:16 +00:00
mx 67a945a15f version bump for variables 2023-06-12 13:45:49 +02:00
Tommy Verrall d1a28826d5 Merge pull request #3535 from nymtech/feature/socks5-message-ordering
Feature/socks5 message ordering
2023-06-12 11:47:35 +01:00
Tommy Verrall 8f026ab6c6 Update build-and-upload-binaries-ci.yml
temp use rust version 1.69.0 for ci build uploads, will need to switch and use: cosmwasm/rust-optimizer in the near future
2023-06-12 11:07:22 +01:00
Jędrzej Stuczyński bdcdcf7f8b clippy 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 182e147a86 fixed and updated related units tests 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 687b437ea0 cargo fmt 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński f2c5dbb696 deadcode 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński a8bf690c17 tests 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 1328ba35be removed original Ordered sender 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 8b046d4139 split up functions a bit more 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński f4cd372808 simplified keeping track of message sequence 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 7228331db6 removed OrderedMessage 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 45f3f3ec01 cleanup 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński 5f9e54c83c buggy, but backwards compatible, explicit sequencing 2023-06-12 10:20:13 +01:00
Jędrzej Stuczyński f8c2f90502 initial work on putting data sequence explicitly inside socks5 request 2023-06-12 10:20:13 +01:00
Jon Häggblad 96e8bdfea4 Reduce number of surbs in nym-nr-query ping (#3540) 2023-06-12 09:53:47 +02:00
Jon Häggblad 0fa1961a04 nym-nr-query CLI tool for query network-requester (#3538)
* Initial version

* Add open_proxy request

* Start adding cli commands

* use commands

* Common response type

* json output

* Tidy

* Remove the All case

* Remove empty file

* Remove todo

* Add ping command

* Ping 4 times by default

* Logic for controlling number of loops

* Some print tweaks
2023-06-12 09:30:02 +02:00
pierre 88002969b7 ci(nyms5-android): add debug apk build 2023-06-09 22:46:04 +02:00
pierre b90dedf3ae build(android): fix repro build wip 2023-06-09 17:11:15 +02:00
pierre 582cb32223 build(android): fix repro build wip 2023-06-09 16:34:50 +02:00
Jędrzej Stuczyński 31b7921c68 bugfix #3434 2023-06-09 15:12:56 +01:00
Jędrzej Stuczyński 42a43a3709 Feature/extract gateway config (#3517)
* wip client core

* hashing shared key in persisted details

* native client using on-disk gateway details

* ibid for socks5

* ibid for NR

* nym sdk

* non-wasm fixes

* wasm

* missed cargo fmt

* fixed nym-connect build

* changed serialization of the key hash to be more human readable

* allowing some dead code

* fixed gateway details deserializtion

* removed needless borrow in wasm client

* removed deadcode

* exhaustive match on GatewaySetup after having loaded the keys
2023-06-09 15:12:29 +01:00
Jędrzej Stuczyński c5ad4006ae simplified construction of 'BaseClient' (#3513) 2023-06-09 15:11:45 +01:00
Tommy Verrall 71fb6a1ba1 Merge pull request #3516 from nymtech/feature/explorer-sp-extra-detail
Explorer - Extra detail for Service Providers
2023-06-09 10:01:59 +01:00
Jon Häggblad 68a37bc692 Merge remote-tracking branch 'origin/release/v1.1.21' into develop 2023-06-09 09:54:53 +02:00
Jon Häggblad 542fd92a46 Allow query network-requestor for open_proxy (#3531)
* wip: playing around

* WIP

* wip

* Add query request

* Create new example

* Restore control_requests example

* Remove ControlRequest::OpenProxy

* Remove Socks5RequestContent::OpenProxy

* Remove bunch of debug logging

* Remove more old leftovers

* Another few lines of leftovers to remove

* Disable logging in example

* Return error if incoming request if future version

* Serialization unit tests

* Network requester in example

* Cargo.lock
2023-06-09 09:54:21 +02:00
pierre 527c71d3df fix(nyms5-android): clean code 2023-06-08 18:45:02 +02:00
pierre 7ee47bdb4c fix(nyms5-android): fix notif channel creation on android prior to 12 2023-06-08 18:44:18 +02:00
pierre 9fa36b6393 add a log on channel creation 2023-06-08 11:06:14 +02:00
pierre 2b40b96aa1 wip 2023-06-08 11:06:14 +02:00
Jędrzej Stuczyński 9a68702d4d Feature/config refactor (#3498)
* revamping mixnode connfig

* wip

* native client config revamping

* wip

* building socks5

* using const for mixnnode config template

* compiling updated gateway

* nym-api

* nym-sdk

* everything compiling once more

but definitely not compatible with CI and older versions (yet)

* creating full directory structure on init

* renamed paths to storage_paths and fixed mixnode template

* mixnode config migration

* gateway config migration

* nym-api config migration

* native client config migration

* socks5 client config migration

* NR config migration

* removed deprecations (that will be resolved in the following PRs) + fixed clippy

* nym-connect clippy

* nym-connect config updates

* outfox fixes

* defined socks5 lib config

* clippy

* fixed wasm client build

* removed explicit packet_type argument when starting base client

it's known implicitly from the previously passed config struct

* Empty commit

* fixed re-using gateway information when client configs are re-initialised

* fixed borrowing id value in nym-connect

* post-rebase fixes

* updated 'old_config' versions

---------

Co-authored-by: Tommy Verrall <tommy@nymtech.net>
2023-06-07 17:06:35 +01:00
Drazen Urch bc5198768e Push traces to jaeger (#3522)
* Push traces to jaeger

* Remove default feature

* Update toolchain
2023-06-07 17:32:26 +02:00
Jon Häggblad 892653cd96 Remove gateway from detailed service provider type 2023-06-07 14:09:13 +02:00
Jon Häggblad a7471ef324 Replace fold with collect 2023-06-07 14:06:12 +02:00
fmtabbara 403141c1f5 add tooltip to sp routing score 2023-06-07 12:22:59 +01:00
fmtabbara d8c82bf6d0 use default sort order 2023-06-07 12:05:32 +01:00
fmtabbara dd86ba36dd use default success ping time - 120 mins 2023-06-07 11:56:44 +01:00
Tommy Verrall 42acbfe806 Update build-and-upload-binaries-ci.yml
temp use rust version 1.69.0 for ci build uploads, will need to switch and use: cosmwasm/rust-optimizer in the near future
2023-06-07 11:24:53 +02:00
farbanas 7c55483585 Merge branch 'master' into develop 2023-06-07 10:43:11 +02:00
fmtabbara 0320220219 refactor 2023-06-06 22:56:13 +01:00
fmtabbara e32ee2ccf3 add service-type and routing-scores to sps + allow sorting on routing scores 2023-06-06 22:45:09 +01:00
fmtabbara b8ca1762c2 allow sp routing score to be undefined 2023-06-06 22:45:07 +01:00
pierre d1e9fcf03a make routing score optional 2023-06-06 16:44:17 +02:00
pierre 303a774378 add some logs 2023-06-06 15:25:29 +02:00
pierre c9ca71f47b wip 2023-06-06 14:50:25 +02:00
pierre 92faf1e3d5 wip 2023-06-06 14:22:52 +02:00
fmtabbara e509989ac3 wip 2023-06-06 10:04:50 +01:00
farbanas c04cc9a4cf Merge branch 'release/v1.1.20' 2023-06-06 10:54:17 +02:00
farbanas 17258d1445 updated locks and versions bumped 2023-06-06 10:35:42 +02:00
farbanas 8f3d7606f5 bump crate versions 2023-06-06 10:07:06 +02:00
farbanas fd97f0e8ca update changelogs and version for release 2023-06-06 10:00:11 +02:00
Jędrzej Stuczyński d4ce1635a8 Fixed incorrect assertion when sending replies (#3515) 2023-06-05 14:36:44 +01:00
Jędrzej Stuczyński 2bc564ad01 updating managed keys after gateway registration (#3514) 2023-06-05 14:09:28 +01:00
Tommy Verrall 5910bcbc02 Update build-and-upload-binaries-ci.yml
add the service provider and name service contract to build output
2023-06-05 14:49:49 +02:00
Tommy Verrall 8c63fe9d0d Merge pull request #3507 from nymtech/jon/feat/reduce-shutdown-timeout-in-socks5
Reduce SHUTDOWN_TIMEOUT to 3 sec
2023-06-05 10:30:34 +01:00
Pierre Dommerc 6e5a1973da fix(wallet): fix bonding data refresh (#3499) 2023-06-05 11:07:23 +02:00
Pierre Dommerc 1aa11887aa fix(wallet): fix bonding data refresh (#3499) 2023-06-05 11:05:25 +02:00
Jon Häggblad 07740cbf08 Reduce SHUTDOWN_TIMEOUT to 3 sec
Can't think of a scenario where we don't want to close one at the same
time as the other, but let's be conservative and keep it a very low
number for now.
2023-06-05 10:42:38 +02:00
Jon Häggblad 87cb8a6b20 Sign when announcing service providers to the directory contract (#3459)
* create_payload and call from nym-cli

* Remove some commented out code

* wip

* Service announce now compiles

* Fix other compilation issues

* Move ServiceDetails into Service

* Move service_id inside Service type

* wip: start sorting out tests

* wip: sorting out testing

* wip: first announce test now works

* wip: more work on announce test

* Move nonce

* Add check for nonce

* Extract out some helpers to separate files

* reenable state::services tests

* wip: start going through the integration tests

* All integration tests reenabled

* Remove some unused stuff

* Iterate on integration tests

* More iteration on test setup

* Rename to test_setup.rs

* Add more tests specific to signing

* Tweak

* Another nonce test and reorg

* Rename to announce.rs and delete.rs

* Tidy

* Make some inner modules private

* Use IdentityKey alias

* Update nym-api contract cache

* Fix that nym-cli was asking for signing nonce from wrong contract

* Add sign comment to network-requester

* Uploaded updated service provider contract to qwerty

* Allow large enum variant

* lock files

* Remove dbg

* Move error.rs to service-provider common

* Update code for moving errors.rs to common crate

* Rename to SpContractError

* constants module not pub

* lock file

* rustfmt

* Move IdentityKey type to contract-common

* clippy
2023-06-05 10:32:58 +02:00
Jon Häggblad 2977b8f25f Fix clippy for 1.70 (#3505) 2023-06-05 08:54:18 +01:00
mx 019b3299f2 Merge pull request #3435 from Pawnflake/release/v1.1.19
mixnode documentation update
2023-06-02 09:40:50 +00:00
mx d684957423 Update documentation/docs/src/nodes/mix-node-setup.md
Co-authored-by: ️2FakTor️ <twofaktor@protonmail.com>
2023-06-02 09:20:56 +00:00
mx cb4eda4c62 Update documentation/docs/src/nodes/mix-node-setup.md
Co-authored-by: ️2FakTor️ <twofaktor@protonmail.com>
2023-06-02 09:20:42 +00:00
mx ac5f380ee2 Merge pull request #3485 from nymtech/feature/docs-1-1-20
removing hardcoded version numbers
2023-06-02 09:20:10 +00:00
mx 4e278ca07d reintroduced hardcoding for links for moment 2023-06-02 10:52:28 +02:00
mx cd6a725875 Merge pull request #3493 from twofaktor/patch-1
[BUG] network requester documentation update
2023-06-02 08:42:20 +00:00
⚡️2FakTor⚡️ d5514a060c Update network-requester-setup.md 2023-05-31 15:22:11 +02:00
mx 958b6d37ee Merge pull request #3481 from twofaktor/patch-1
[BUG] network requester documentation update
2023-05-30 14:40:33 +00:00
mx 5e36bb014c removing hardcoded versoin numbers 2023-05-30 16:10:15 +02:00
⚡️2FakTor⚡️ 8d821881ae Update network-requester-setup.md 2023-05-30 14:48:51 +02:00
mx fca9761145 Merge pull request #3141 from nymtech/chore/update-community-links-in-readme
updated readme with new developer chat links + new docs links
2023-05-30 12:05:14 +00:00
mx 11481e4d13 Merge branch 'release/v1.1.20' into chore/update-community-links-in-readme 2023-05-30 12:04:45 +00:00
Pawnflake c8b82a9553 removed comment http and https 2023-05-20 07:23:13 +08:00
mx 00c2f5359c Merge pull request #3428 from nymtech/feature/updated-sandbox-docs
added sandbox sync docs
2023-05-16 14:53:51 +00:00
mx 1a4e0f4e08 added sandbox sync docs 2023-05-16 16:44:39 +02:00
Pawnflake 69230a10cb Added ufw restart after adding ports and removed port 80 and 443 from the command 2023-05-16 10:10:41 +08:00
mx 3a0c8f3f4e updated readme with new developer chat links + new docs links 2023-03-03 13:32:44 +01:00
1942 changed files with 345273 additions and 3441 deletions
@@ -107,6 +107,8 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
+13 -5
View File
@@ -49,7 +49,7 @@ jobs:
"build-tools;$SDK_BUILDTOOLS_VERSION" "build-tools;$SDK_BUILDTOOLS_VERSION"
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@1.70.0
- name: Install rust android targets - name: Install rust android targets
run: | run: |
@@ -60,6 +60,7 @@ jobs:
working-directory: sdk/lib/socks5-listener/ working-directory: sdk/lib/socks5-listener/
env: env:
RELEASE: true RELEASE: true
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
# build for arm64 and x86_64 # build for arm64 and x86_64
run: ./build-android.sh aarch64 x86_64 run: ./build-android.sh aarch64 x86_64
@@ -68,19 +69,24 @@ jobs:
env: env:
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }} ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
# build for arm64 and x86_64 # build for arm64 and x86_64
run: ./gradlew :app:assembleArch64Release run: |
./gradlew :app:assembleArch64Debug
./gradlew :app:assembleArch64Release
- name: Prepare APKs - name: Prepare APKs
run: | run: |
mkdir apk mkdir apk
mv nym-connect/native/android/app/build/outputs/apk/arch64/debug/app-arch64-debug.apk \
apk/nyms5-arch64-debug.apk
mv nym-connect/native/android/app/build/outputs/apk/arch64/release/app-arch64-release-unsigned.apk \ mv nym-connect/native/android/app/build/outputs/apk/arch64/release/app-arch64-release-unsigned.apk \
apk/nyms5-arch64-release.apk apk/nyms5-arch64-release.apk
- name: Upload APKs - name: Upload APKs
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: nyms5-apk-arch64-release name: nyms5-apk-arch64
path: | path: |
apk/nyms5-arch64-debug.apk
apk/nyms5-arch64-release.apk apk/nyms5-arch64-release.apk
gh-release: gh-release:
@@ -93,10 +99,12 @@ jobs:
- name: Download binary artifact - name: Download binary artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: nyms5-apk-arch64-release name: nyms5-apk-arch64
path: apk path: apk
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: apk/nyms5-arch64-release.apk files: |
apk/nyms5-arch64-debug.apk
apk/nyms5-arch64-release.apk
+63
View File
@@ -4,6 +4,69 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [v1.1.22] (2023-06-20)
- CLI tool for querying network-requesters ([#3539])
- Statically link OpenSSL ([#3510])
- NymConnect - add sentry.io reporting ([#3421])
- init command does not change version number in config.toml ([#3336])
- [Bug] Config version does not correspond to binary version ([#3434])
[#3539]: https://github.com/nymtech/nym/issues/3539
[#3510]: https://github.com/nymtech/nym/issues/3510
[#3421]: https://github.com/nymtech/nym/issues/3421
[#3336]: https://github.com/nymtech/nym/issues/3336
[#3434]: https://github.com/nymtech/nym/issues/3434
## [v1.1.21] (2023-06-13)
- mixFetch: Change socks5 `SendRequest` to include OrderedMessage index as a field rather than making it serialized inside the `data` field
([#3534])
- Explorer - add more data columns to the Service Provider section: ([#3474])
- network-requester: support report if they run an open proxy using `ControlRequest` API ([#3461])
- Refactor client configs (London discussion) ([#3444])
- Increase `DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE` to 2000 to improve reliability ([#3433])
- socks5: sender waits for lanes to clear even though the connection is closed ([#3366])
- version bump for variables ([#3545])
[#3534]: https://github.com/nymtech/nym/issues/3534
[#3474]: https://github.com/nymtech/nym/issues/3474
[#3461]: https://github.com/nymtech/nym/issues/3461
[#3444]: https://github.com/nymtech/nym/issues/3444
[#3433]: https://github.com/nymtech/nym/issues/3433
[#3366]: https://github.com/nymtech/nym/issues/3366
[#3545]: https://github.com/nymtech/nym/pull/3545
## [v1.1.20] (2023-06-06)
- Explorer - Fix SP supported apps list ([#3458])
- Investigate if we need to lower `SHUTDOWN_TIMEOUT` in socks5 to zero (or almost zero) ([#3438])
- Explorer - show all gateways in the default view regardless of the version number ([#3427])
- service-provider-directory: add signature check when announcing ([#3360])
- Support functionality for nym-name-service (nym-api, nym-cli, etc) ([#3355])
- Edit the nym-network-requester to support the enabled-credentials-mode flag ([#3101])
- [BUG] network requester documentation update ([#3493])
- removing hardcoded version numbers ([#3485])
- [BUG] network requester documentation update ([#3481])
- [BUG] network requester documentation update ([#3469])
- Sign when announcing service providers to the directory contract ([#3459])
- mixnode documentation update ([#3435])
- updated readme with new developer chat links + new docs links ([#3141])
[#3458]: https://github.com/nymtech/nym/issues/3458
[#3438]: https://github.com/nymtech/nym/issues/3438
[#3427]: https://github.com/nymtech/nym/issues/3427
[#3360]: https://github.com/nymtech/nym/issues/3360
[#3355]: https://github.com/nymtech/nym/issues/3355
[#3101]: https://github.com/nymtech/nym/issues/3101
[#3493]: https://github.com/nymtech/nym/pull/3493
[#3485]: https://github.com/nymtech/nym/pull/3485
[#3481]: https://github.com/nymtech/nym/pull/3481
[#3469]: https://github.com/nymtech/nym/pull/3469
[#3459]: https://github.com/nymtech/nym/pull/3459
[#3435]: https://github.com/nymtech/nym/pull/3435
[#3141]: https://github.com/nymtech/nym/pull/3141
## [v1.1.19] (2023-05-16) ## [v1.1.19] (2023-05-16)
- nym-name-service endpoint in nym-api ([#3403]) - nym-name-service endpoint in nym-api ([#3403])
Generated
+336 -46
View File
@@ -68,7 +68,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
"once_cell", "once_cell",
"version_check", "version_check",
] ]
@@ -204,6 +204,17 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "async-channel"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]] [[package]]
name = "async-file-watcher" name = "async-file-watcher"
version = "0.1.0" version = "0.1.0"
@@ -362,9 +373,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.0" version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
@@ -378,6 +389,15 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bip32" name = "bip32"
version = "0.3.0" version = "0.3.0"
@@ -561,6 +581,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "castaway"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.79"
@@ -810,6 +836,15 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "concurrent-queue"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "console-api" name = "console-api"
version = "0.5.0" version = "0.5.0"
@@ -911,7 +946,7 @@ dependencies = [
"cosmos-sdk-proto", "cosmos-sdk-proto",
"ecdsa", "ecdsa",
"eyre", "eyre",
"getrandom 0.2.9", "getrandom 0.2.10",
"k256", "k256",
"prost 0.10.4", "prost 0.10.4",
"prost-types 0.10.1", "prost-types 0.10.1",
@@ -1229,6 +1264,36 @@ dependencies = [
"rustc_version 0.2.3", "rustc_version 0.2.3",
] ]
[[package]]
name = "curl"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
dependencies = [
"curl-sys",
"libc",
"openssl-probe",
"openssl-sys",
"schannel",
"socket2",
"winapi",
]
[[package]]
name = "curl-sys"
version = "0.4.63+curl-8.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
"winapi",
]
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "3.2.0" version = "3.2.0"
@@ -1243,6 +1308,20 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "cw-controllers"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
dependencies = [
"cosmwasm-std",
"cw-storage-plus",
"cw-utils",
"schemars",
"serde",
"thiserror",
]
[[package]] [[package]]
name = "cw-storage-plus" name = "cw-storage-plus"
version = "0.13.4" version = "0.13.4"
@@ -1653,7 +1732,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]] [[package]]
name = "explorer-api" name = "explorer-api"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap 4.2.7", "clap 4.2.7",
@@ -1927,6 +2006,21 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.28" version = "0.3.28"
@@ -2023,9 +2117,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.9" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@@ -2630,6 +2724,12 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "integer-encoding"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
[[package]] [[package]]
name = "inventory" name = "inventory"
version = "0.1.11" version = "0.1.11"
@@ -2699,6 +2799,31 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "isahc"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
dependencies = [
"async-channel",
"castaway",
"crossbeam-utils",
"curl",
"curl-sys",
"event-listener",
"futures-lite",
"http",
"log",
"once_cell",
"polling",
"slab",
"sluice",
"tracing",
"tracing-futures",
"url",
"waker-fn",
]
[[package]] [[package]]
name = "isocountry" name = "isocountry"
version = "0.3.2" version = "0.3.2"
@@ -3197,7 +3322,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-api" name = "nym-api"
version = "1.1.20" version = "1.1.23"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3298,18 +3423,20 @@ dependencies = [
[[package]] [[package]]
name = "nym-bin-common" name = "nym-bin-common"
version = "0.5.0" version = "0.6.0"
dependencies = [ dependencies = [
"atty", "atty",
"clap 4.2.7", "clap 4.2.7",
"clap_complete", "clap_complete",
"clap_complete_fig", "clap_complete_fig",
"log", "log",
"opentelemetry",
"opentelemetry-jaeger",
"pretty_env_logger", "pretty_env_logger",
"semver 0.11.0", "semver 0.11.0",
"serde", "serde",
"serde_json", "serde_json",
"tracing-appender", "tracing-opentelemetry",
"tracing-subscriber", "tracing-subscriber",
"tracing-tree", "tracing-tree",
"vergen", "vergen",
@@ -3332,7 +3459,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-cli" name = "nym-cli"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.13.1", "base64 0.13.1",
@@ -3381,6 +3508,7 @@ dependencies = [
"nym-name-service-common", "nym-name-service-common",
"nym-network-defaults", "nym-network-defaults",
"nym-service-provider-directory-common", "nym-service-provider-directory-common",
"nym-sphinx",
"nym-validator-client", "nym-validator-client",
"nym-vesting-contract-common", "nym-vesting-contract-common",
"rand 0.6.5", "rand 0.6.5",
@@ -3395,7 +3523,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-client" name = "nym-client"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"clap 4.2.7", "clap 4.2.7",
"dirs 4.0.0", "dirs 4.0.0",
@@ -3431,9 +3559,10 @@ dependencies = [
[[package]] [[package]]
name = "nym-client-core" name = "nym-client-core"
version = "1.1.14" version = "1.1.15"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64 0.21.2",
"dashmap 5.4.0", "dashmap 5.4.0",
"dirs 4.0.0", "dirs 4.0.0",
"futures", "futures",
@@ -3456,6 +3585,7 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.6",
"sqlx 0.6.3", "sqlx 0.6.3",
"tap", "tap",
"tempfile", "tempfile",
@@ -3492,7 +3622,7 @@ dependencies = [
"digest 0.9.0", "digest 0.9.0",
"doc-comment", "doc-comment",
"ff 0.11.1", "ff 0.11.1",
"getrandom 0.2.9", "getrandom 0.2.10",
"group 0.11.0", "group 0.11.0",
"itertools", "itertools",
"nym-dkg", "nym-dkg",
@@ -3553,7 +3683,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-contracts-common" name = "nym-contracts-common"
version = "0.4.0" version = "0.5.0"
dependencies = [ dependencies = [
"bs58", "bs58",
"cosmwasm-std", "cosmwasm-std",
@@ -3610,7 +3740,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-crypto" name = "nym-crypto"
version = "0.3.0" version = "0.4.0"
dependencies = [ dependencies = [
"aes 0.8.2", "aes 0.8.2",
"blake3", "blake3",
@@ -3667,7 +3797,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-gateway" name = "nym-gateway"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3720,7 +3850,7 @@ name = "nym-gateway-client"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"getrandom 0.2.9", "getrandom 0.2.10",
"log", "log",
"nym-bandwidth-controller", "nym-bandwidth-controller",
"nym-coconut-interface", "nym-coconut-interface",
@@ -3799,7 +3929,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-mixnet-contract-common" name = "nym-mixnet-contract-common"
version = "0.5.0" version = "0.6.0"
dependencies = [ dependencies = [
"bs58", "bs58",
"cosmwasm-std", "cosmwasm-std",
@@ -3818,7 +3948,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-mixnode" name = "nym-mixnode"
version = "1.1.20" version = "1.1.23"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bs58", "bs58",
@@ -3928,10 +4058,12 @@ dependencies = [
[[package]] [[package]]
name = "nym-network-requester" name = "nym-network-requester"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"anyhow",
"async-file-watcher", "async-file-watcher",
"async-trait", "async-trait",
"bs58",
"clap 4.2.7", "clap 4.2.7",
"dirs 4.0.0", "dirs 4.0.0",
"futures", "futures",
@@ -3954,11 +4086,13 @@ dependencies = [
"nym-sphinx", "nym-sphinx",
"nym-statistics-common", "nym-statistics-common",
"nym-task", "nym-task",
"nym-types",
"pretty_env_logger", "pretty_env_logger",
"publicsuffix", "publicsuffix",
"rand 0.7.3", "rand 0.7.3",
"reqwest", "reqwest",
"serde", "serde",
"serde_json",
"sqlx 0.6.3", "sqlx 0.6.3",
"tap", "tap",
"tempfile", "tempfile",
@@ -3970,7 +4104,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-network-statistics" name = "nym-network-statistics"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"dirs 4.0.0", "dirs 4.0.0",
"log", "log",
@@ -4016,6 +4150,22 @@ dependencies = [
"wasm-timer", "wasm-timer",
] ]
[[package]]
name = "nym-nr-query"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.2.7",
"log",
"nym-bin-common",
"nym-network-defaults",
"nym-sdk",
"nym-service-providers-common",
"nym-socks5-requests",
"serde",
"tokio",
]
[[package]] [[package]]
name = "nym-ordered-buffer" name = "nym-ordered-buffer"
version = "0.1.0" version = "0.1.0"
@@ -4034,7 +4184,7 @@ dependencies = [
"criterion", "criterion",
"curve25519-dalek", "curve25519-dalek",
"fastrand", "fastrand",
"getrandom 0.2.9", "getrandom 0.2.10",
"log", "log",
"rand 0.7.3", "rand 0.7.3",
"rayon", "rayon",
@@ -4045,7 +4195,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-pemstore" name = "nym-pemstore"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"pem", "pem",
] ]
@@ -4086,8 +4236,12 @@ name = "nym-service-provider-directory-common"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cosmwasm-std", "cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars", "schemars",
"serde", "serde",
"thiserror",
] ]
[[package]] [[package]]
@@ -4099,6 +4253,7 @@ dependencies = [
"log", "log",
"nym-bin-common", "nym-bin-common",
"nym-sdk", "nym-sdk",
"nym-socks5-requests",
"nym-sphinx-anonymous-replies", "nym-sphinx-anonymous-replies",
"serde", "serde",
"serde_json", "serde_json",
@@ -4108,7 +4263,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.19" version = "1.1.22"
dependencies = [ dependencies = [
"clap 4.2.7", "clap 4.2.7",
"lazy_static", "lazy_static",
@@ -4204,10 +4359,13 @@ dependencies = [
name = "nym-socks5-requests" name = "nym-socks5-requests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bincode",
"log",
"nym-service-providers-common", "nym-service-providers-common",
"nym-sphinx-addressing", "nym-sphinx-addressing",
"serde", "serde",
"serde_json", "serde_json",
"tap",
"thiserror", "thiserror",
] ]
@@ -4379,7 +4537,7 @@ dependencies = [
"aes-gcm", "aes-gcm",
"argon2", "argon2",
"generic-array 0.14.7", "generic-array 0.14.7",
"getrandom 0.2.9", "getrandom 0.2.10",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"serde_json", "serde_json",
@@ -4473,6 +4631,7 @@ dependencies = [
"nym-service-provider-directory-common", "nym-service-provider-directory-common",
"nym-vesting-contract", "nym-vesting-contract",
"nym-vesting-contract-common", "nym-vesting-contract-common",
"openssl",
"prost 0.10.4", "prost 0.10.4",
"reqwest", "reqwest",
"serde", "serde",
@@ -4505,7 +4664,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-vesting-contract-common" name = "nym-vesting-contract-common"
version = "0.6.0" version = "0.7.0"
dependencies = [ dependencies = [
"cosmwasm-std", "cosmwasm-std",
"nym-contracts-common", "nym-contracts-common",
@@ -4635,12 +4794,55 @@ dependencies = [
"opentelemetry_sdk", "opentelemetry_sdk",
] ]
[[package]]
name = "opentelemetry-http"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a819b71d6530c4297b49b3cae2939ab3a8cc1b9f382826a1bc29dd0ca3864906"
dependencies = [
"async-trait",
"bytes",
"http",
"isahc",
"opentelemetry_api",
]
[[package]]
name = "opentelemetry-jaeger"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08e028dc9f4f304e9320ce38c80e7cf74067415b1ad5a8750a38bae54a4d450d"
dependencies = [
"async-trait",
"futures",
"futures-executor",
"http",
"isahc",
"once_cell",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-semantic-conventions",
"thiserror",
"thrift",
"tokio",
]
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e33428e6bf08c6f7fcea4ddb8e358fab0fe48ab877a87c70c6ebe20f673ce5"
dependencies = [
"opentelemetry",
]
[[package]] [[package]]
name = "opentelemetry_api" name = "opentelemetry_api"
version = "0.19.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2" checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2"
dependencies = [ dependencies = [
"fnv",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"indexmap", "indexmap",
@@ -4658,6 +4860,8 @@ checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"crossbeam-channel", "crossbeam-channel",
"dashmap 5.4.0",
"fnv",
"futures-channel", "futures-channel",
"futures-executor", "futures-executor",
"futures-util", "futures-util",
@@ -4666,6 +4870,8 @@ dependencies = [
"percent-encoding", "percent-encoding",
"rand 0.8.5", "rand 0.8.5",
"thiserror", "thiserror",
"tokio",
"tokio-stream",
] ]
[[package]] [[package]]
@@ -4674,6 +4880,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-float"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.5.0" version = "6.5.0"
@@ -4704,6 +4919,12 @@ dependencies = [
"group 0.11.0", "group 0.11.0",
] ]
[[package]]
name = "parking"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.11.2" version = "0.11.2"
@@ -4966,6 +5187,22 @@ dependencies = [
"plotters-backend", "plotters-backend",
] ]
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg 1.1.0",
"bitflags",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "poly1305" name = "poly1305"
version = "0.8.0" version = "0.8.0"
@@ -5280,7 +5517,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
] ]
[[package]] [[package]]
@@ -5437,7 +5674,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
"redox_syscall 0.2.16", "redox_syscall 0.2.16",
"thiserror", "thiserror",
] ]
@@ -5500,7 +5737,7 @@ version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.21.2",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@@ -5770,7 +6007,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.21.2",
] ]
[[package]] [[package]]
@@ -6197,6 +6434,17 @@ dependencies = [
"autocfg 1.1.0", "autocfg 1.1.0",
] ]
[[package]]
name = "sluice"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
dependencies = [
"async-channel",
"futures-core",
"futures-io",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.10.0" version = "1.10.0"
@@ -6716,7 +6964,7 @@ dependencies = [
"bytes", "bytes",
"flex-error", "flex-error",
"futures", "futures",
"getrandom 0.2.9", "getrandom 0.2.10",
"http", "http",
"hyper", "hyper",
"hyper-proxy", "hyper-proxy",
@@ -6784,6 +7032,28 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "threadpool"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
dependencies = [
"num_cpus",
]
[[package]]
name = "thrift"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09"
dependencies = [
"byteorder",
"integer-encoding",
"log",
"ordered-float",
"threadpool",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.45" version = "0.1.45"
@@ -7049,7 +7319,7 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
"base64 0.21.0", "base64 0.21.2",
"bytes", "bytes",
"futures-core", "futures-core",
"futures-util", "futures-util",
@@ -7108,22 +7378,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"log",
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-appender"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
dependencies = [
"crossbeam-channel",
"time 0.3.21",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.24" version = "0.1.24"
@@ -7145,6 +7405,16 @@ dependencies = [
"valuable", "valuable",
] ]
[[package]]
name = "tracing-futures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"pin-project",
"tracing",
]
[[package]] [[package]]
name = "tracing-log" name = "tracing-log"
version = "0.1.3" version = "0.1.3"
@@ -7156,6 +7426,20 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-opentelemetry"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00a39dcf9bfc1742fa4d6215253b33a6e474be78275884c216fc2a06267b3600"
dependencies = [
"once_cell",
"opentelemetry",
"tracing",
"tracing-core",
"tracing-log",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.3.17" version = "0.3.17"
@@ -7450,7 +7734,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -7489,6 +7773,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.3.3" version = "2.3.3"
@@ -7612,7 +7902,7 @@ name = "wasm-utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"getrandom 0.2.9", "getrandom 0.2.10",
"indexed_db_futures", "indexed_db_futures",
"js-sys", "js-sys",
"nym-store-cipher", "nym-store-cipher",
+6 -3
View File
@@ -87,6 +87,7 @@ members = [
"nym-api/nym-api-requests", "nym-api/nym-api-requests",
"nym-outfox", "nym-outfox",
"tools/nym-cli", "tools/nym-cli",
"tools/nym-nr-query",
"tools/ts-rs-cli" "tools/ts-rs-cli"
] ]
@@ -101,7 +102,7 @@ default-members = [
"explorer-api", "explorer-api",
] ]
exclude = ["explorer", "contracts", "clients/webassembly", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "cpu-cycles"] exclude = ["explorer", "contracts", "clients/webassembly", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "cpu-cycles", "lib-25519"]
[workspace.package] [workspace.package]
authors = ["Nym Technologies SA"] authors = ["Nym Technologies SA"]
@@ -112,22 +113,24 @@ edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
[workspace.dependencies] [workspace.dependencies]
async-trait = "0.1.64"
anyhow = "1.0.71" anyhow = "1.0.71"
async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0" cfg-if = "1.0.0"
cosmwasm-derive = "=1.0.0" cosmwasm-derive = "=1.0.0"
cosmwasm-schema = "=1.0.0" cosmwasm-schema = "=1.0.0"
cosmwasm-std = "=1.0.0" cosmwasm-std = "=1.0.0"
cosmwasm-storage = "=1.0.0" cosmwasm-storage = "=1.0.0"
cw-utils = "=0.13.4" cw-controllers = "=0.13.4"
cw-storage-plus = "=0.13.4" cw-storage-plus = "=0.13.4"
cw-utils = "=0.13.4"
cw2 = { version = "=0.13.4" } cw2 = { version = "=0.13.4" }
cw3 = { version = "=0.13.4" } cw3 = { version = "=0.13.4" }
cw3-fixed-multisig = { version = "=0.13.4" } cw3-fixed-multisig = { version = "=0.13.4" }
cw4 = { version = "=0.13.4" } cw4 = { version = "=0.13.4" }
dotenvy = "0.15.6" dotenvy = "0.15.6"
generic-array = "0.14.7" generic-array = "0.14.7"
getrandom = "0.2.10"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4" log = "0.4"
once_cell = "1.7.2" once_cell = "1.7.2"
+7 -3
View File
@@ -21,8 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building ### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/building-nym.html). Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/build-nym.html).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers). Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
### Developing ### Developing
@@ -32,7 +32,11 @@ For Typescript components, please see [ts-packages](./ts-packages).
### Developer chat ### Developer chat
You can chat with us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes place in the **#dev** channel. Node operators should be in the **#node-operators** channel. > We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://discord.gg/nym)
### Rewards ### Rewards
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.19" version = "1.1.22"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
+16 -15
View File
@@ -20,7 +20,8 @@ pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig}; pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
pub mod old_config_v1_1_13; pub mod old_config_v1_1_13;
pub mod old_config_v1_1_19; pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
mod persistence; mod persistence;
mod template; mod template;
@@ -52,19 +53,6 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
.join(DEFAULT_DATA_DIR) .join(DEFAULT_DATA_DIR)
} }
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
WebSocket,
None,
}
impl SocketType {
pub fn is_websocket(&self) -> bool {
matches!(self, SocketType::WebSocket)
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Config { pub struct Config {
#[serde(flatten)] #[serde(flatten)]
@@ -87,7 +75,7 @@ impl NymConfigTemplate for Config {
impl Config { impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self { pub fn new<S: AsRef<str>>(id: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id.as_ref()), base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())), storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())),
logging: Default::default(), logging: Default::default(),
socket: Default::default(), socket: Default::default(),
@@ -187,6 +175,19 @@ impl Config {
// define_optional_set_inner!(Config, base, BaseClientConfig); // define_optional_set_inner!(Config, base, BaseClientConfig);
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
WebSocket,
None,
}
impl SocketType {
pub fn is_websocket(&self) -> bool {
matches!(self, SocketType::WebSocket)
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct Socket { pub struct Socket {
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_19::{ConfigV1_1_19, SocketV1_1_19}; use crate::client::config::old_config_v1_1_20::{ConfigV1_1_20, SocketV1_1_20};
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13; use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -13,7 +13,7 @@ pub struct OldConfigV1_1_13 {
#[serde(flatten)] #[serde(flatten)]
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>, pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub socket: SocketV1_1_19, pub socket: SocketV1_1_20,
} }
impl MigrationNymConfig for OldConfigV1_1_13 { impl MigrationNymConfig for OldConfigV1_1_13 {
@@ -25,9 +25,9 @@ impl MigrationNymConfig for OldConfigV1_1_13 {
} }
} }
impl From<OldConfigV1_1_13> for ConfigV1_1_19 { impl From<OldConfigV1_1_13> for ConfigV1_1_20 {
fn from(value: OldConfigV1_1_13) -> Self { fn from(value: OldConfigV1_1_13) -> Self {
ConfigV1_1_19 { ConfigV1_1_20 {
base: value.base.into(), base: value.base.into(),
socket: value.socket, socket: value.socket,
} }
@@ -1,13 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths; use crate::client::config::old_config_v1_1_20_2::{
use crate::client::config::{Config, Socket, SocketType}; ClientPathsV1_1_20_2, ConfigV1_1_20_2, SocketTypeV1_1_20_2, SocketV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings; use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths; use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths; use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19; use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::{Client, Config as BaseConfig}; use nym_client_core::config::old_config_v1_1_20_2::{
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseConfigV1_1_20_2,
};
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT; use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -17,34 +20,34 @@ use std::path::PathBuf;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)] #[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub enum SocketTypeV1_1_19 { pub enum SocketTypeV1_1_20 {
WebSocket, WebSocket,
None, None,
} }
impl From<SocketTypeV1_1_19> for SocketType { impl From<SocketTypeV1_1_20> for SocketTypeV1_1_20_2 {
fn from(value: SocketTypeV1_1_19) -> Self { fn from(value: SocketTypeV1_1_20) -> Self {
match value { match value {
SocketTypeV1_1_19::WebSocket => SocketType::WebSocket, SocketTypeV1_1_20::WebSocket => SocketTypeV1_1_20_2::WebSocket,
SocketTypeV1_1_19::None => SocketType::None, SocketTypeV1_1_20::None => SocketTypeV1_1_20_2::None,
} }
} }
} }
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19 { pub struct ConfigV1_1_20 {
#[serde(flatten)] #[serde(flatten)]
pub base: BaseConfigV1_1_19<ConfigV1_1_19>, pub base: BaseConfigV1_1_20<ConfigV1_1_20>,
pub socket: SocketV1_1_19, pub socket: SocketV1_1_20,
} }
impl From<ConfigV1_1_19> for Config { impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
fn from(value: ConfigV1_1_19) -> Self { fn from(value: ConfigV1_1_20) -> Self {
Config { ConfigV1_1_20_2 {
base: BaseConfig { base: BaseConfigV1_1_20_2 {
client: Client { client: ClientV1_1_20_2 {
version: value.base.client.version, version: value.base.client.version,
id: value.base.client.id, id: value.base.client.id,
disabled_credentials_mode: value.base.client.disabled_credentials_mode, disabled_credentials_mode: value.base.client.disabled_credentials_mode,
@@ -55,8 +58,8 @@ impl From<ConfigV1_1_19> for Config {
debug: value.base.debug.into(), debug: value.base.debug.into(),
}, },
socket: value.socket.into(), socket: value.socket.into(),
storage_paths: ClientPaths { storage_paths: ClientPathsV1_1_20_2 {
common_paths: CommonClientPaths { common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths { keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_identity_key_file, private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file, public_identity_key_file: value.base.client.public_identity_key_file,
@@ -74,7 +77,7 @@ impl From<ConfigV1_1_19> for Config {
} }
} }
impl MigrationNymConfig for ConfigV1_1_19 { impl MigrationNymConfig for ConfigV1_1_20 {
fn default_root_directory() -> PathBuf { fn default_root_directory() -> PathBuf {
dirs::home_dir() dirs::home_dir()
.expect("Failed to evaluate $HOME value") .expect("Failed to evaluate $HOME value")
@@ -85,15 +88,15 @@ impl MigrationNymConfig for ConfigV1_1_19 {
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct SocketV1_1_19 { pub struct SocketV1_1_20 {
socket_type: SocketTypeV1_1_19, socket_type: SocketTypeV1_1_20,
host: IpAddr, host: IpAddr,
listening_port: u16, listening_port: u16,
} }
impl From<SocketV1_1_19> for Socket { impl From<SocketV1_1_20> for SocketV1_1_20_2 {
fn from(value: SocketV1_1_19) -> Self { fn from(value: SocketV1_1_20) -> Self {
Socket { SocketV1_1_20_2 {
socket_type: value.socket_type.into(), socket_type: value.socket_type.into(),
host: value.host, host: value.host,
listening_port: value.listening_port, listening_port: value.listening_port,
@@ -101,10 +104,10 @@ impl From<SocketV1_1_19> for Socket {
} }
} }
impl Default for SocketV1_1_19 { impl Default for SocketV1_1_20 {
fn default() -> Self { fn default() -> Self {
SocketV1_1_19 { SocketV1_1_20 {
socket_type: SocketTypeV1_1_19::WebSocket, socket_type: SocketTypeV1_1_20::WebSocket,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT, listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
} }
@@ -0,0 +1,103 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
use nym_config::read_config_from_toml_file;
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use serde::{Deserialize, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr};
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct ClientPathsV1_1_20_2 {
#[serde(flatten)]
pub common_paths: CommonClientPathsV1_1_20_2,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct ConfigV1_1_20_2 {
#[serde(flatten)]
pub base: BaseConfigV1_1_20_2,
pub socket: SocketV1_1_20_2,
pub storage_paths: ClientPathsV1_1_20_2,
pub logging: LoggingSettings,
}
impl ConfigV1_1_20_2 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
let gateway_details = self.base.client.gateway_endpoint.clone().into();
let config = Config {
base: self.base.into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default(),
},
logging: self.logging,
};
(config, gateway_details)
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketTypeV1_1_20_2 {
WebSocket,
None,
}
impl From<SocketTypeV1_1_20_2> for SocketType {
fn from(value: SocketTypeV1_1_20_2) -> Self {
match value {
SocketTypeV1_1_20_2::WebSocket => SocketType::WebSocket,
SocketTypeV1_1_20_2::None => SocketType::None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct SocketV1_1_20_2 {
pub socket_type: SocketTypeV1_1_20_2,
pub host: IpAddr,
pub listening_port: u16,
}
impl From<SocketV1_1_20_2> for Socket {
fn from(value: SocketV1_1_20_2) -> Self {
Socket {
socket_type: value.socket_type.into(),
host: value.host,
listening_port: value.listening_port,
}
}
}
impl Default for SocketV1_1_20_2 {
fn default() -> Self {
SocketV1_1_20_2 {
socket_type: SocketTypeV1_1_20_2::WebSocket,
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
}
}
}
+3 -12
View File
@@ -64,18 +64,9 @@ credentials_database = '{{ storage_paths.credentials_database }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. # Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}' reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# DEPRECATED # Path to the file containing information about gateway used by this client,
[client.gateway_endpoint] # i.e. details such as its public key, owner address or the network information.
# ID of the gateway from which the client should be fetching messages. gateway_details = '{{ storage_paths.gateway_details }}'
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
##### socket config options ##### ##### socket config options #####
+14 -31
View File
@@ -6,18 +6,15 @@ use crate::error::ClientError;
use crate::websocket; use crate::websocket;
use futures::channel::mpsc; use futures::channel::mpsc;
use log::*; use log::*;
use nym_bandwidth_controller::BandwidthController; use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::non_wasm_helpers::create_bandwidth_controller;
use nym_client_core::client::base_client::storage::OnDiskPersistent; use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
use nym_client_core::client::inbound_messages::InputMessage; use nym_client_core::client::inbound_messages::InputMessage;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::received_buffer::{ use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver, ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
}; };
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
@@ -45,17 +42,6 @@ impl SocketClient {
SocketClient { config } SocketClient { config }
} }
async fn create_bandwidth_controller(
config: &Config,
) -> BandwidthController<Client<QueryNyxdClient>, PersistentStorage> {
let storage = nym_credential_storage::initialise_persistent_storage(
&config.storage_paths.common_paths.credentials_database,
)
.await;
create_bandwidth_controller(&config.base, storage)
}
fn start_websocket_listener( fn start_websocket_listener(
config: &Config, config: &Config,
client_input: ClientInput, client_input: ClientInput,
@@ -105,29 +91,26 @@ impl SocketClient {
res res
} }
fn key_store(&self) -> OnDiskKeys { async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> {
OnDiskKeys::new(self.config.storage_paths.common_paths.keys.clone()) Ok(OnDiskPersistent::from_paths(
self.config.storage_paths.common_paths.clone(),
&self.config.base.debug,
)
.await?)
} }
// TODO: see if this could also be shared with socks5 client / nym-sdk maybe // TODO: see if this could also be shared with socks5 client / nym-sdk maybe
async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> { async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> {
// don't create bandwidth controller if credentials are disabled // don't create dkg client for the bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.base.client.disabled_credentials_mode { let dkg_query_client = if self.config.base.client.disabled_credentials_mode {
None None
} else { } else {
Some(Self::create_bandwidth_controller(&self.config).await) Some(default_query_dkg_client_from_config(&self.config.base))
}; };
let base_client = BaseClientBuilder::new_from_base_config( let storage = self.initialise_storage().await?;
&self.config.base,
self.key_store(), let base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
bandwidth_controller,
non_wasm_helpers::setup_fs_reply_surb_backend(
&self.config.storage_paths.common_paths.reply_surb_database,
&self.config.base.debug.reply_surbs,
)
.await?,
);
Ok(base_client) Ok(base_client)
} }
+18 -14
View File
@@ -12,7 +12,10 @@ use crate::{
}; };
use clap::Args; use clap::Args;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys; use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize; use serde::Serialize;
@@ -105,9 +108,9 @@ pub struct InitResults {
} }
impl InitResults { impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self { fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self { Self {
client_core: nym_client_core::init::InitResults::new(&config.base, address), client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
client_listening_port: config.socket.listening_port, client_listening_port: config.socket.listening_port,
client_address: address.to_string(), client_address: address.to_string(),
} }
@@ -157,25 +160,29 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
// Attempt to use a user-provided gateway, if possible // Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway; let user_chosen_gateway_id = args.gateway;
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
);
// Load and potentially override config // Load and potentially override config
let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone())); let config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
// Setup gateway by either registering a new one, or creating a new config from the selected // 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. // one but with keys kept, or reusing the gateway configuration.
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let gateway = nym_client_core::init::setup_gateway_from_config::<_>( let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let init_details = nym_client_core::init::setup_gateway(
&gateway_setup,
&key_store, &key_store,
&details_store,
register_gateway, register_gateway,
user_chosen_gateway_id, Some(&config.base.client.nym_api_urls),
&config.base,
args.latency_based_selection,
) )
.await .await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
config.base.set_gateway_endpoint(gateway);
let config_save_location = config.default_location(); let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| { config.save_to_default_location().tap_err(|_| {
log::error!("Failed to save the config file"); log::error!("Failed to save the config file");
@@ -185,14 +192,11 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
config_save_location.display() config_save_location.display()
); );
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys( let address = init_details.client_address()?;
&config.storage_paths.common_paths.keys,
&config.base.client.gateway_endpoint,
)?;
eprintln!("Client configuration completed.\n"); eprintln!("Client configuration completed.\n");
let init_results = InitResults::new(&config, &address); let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
println!("{}", args.output.format(&init_results)); println!("{}", args.output.format(&init_results));
Ok(()) Ok(())
+77 -11
View File
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13; use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::old_config_v1_1_19::ConfigV1_1_19; use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::{BaseClientConfig, Config}; use crate::client::config::{BaseClientConfig, Config};
use crate::error::ClientError; use crate::error::ClientError;
use clap::CommandFactory; use clap::CommandFactory;
@@ -11,6 +12,12 @@ use lazy_static::lazy_static;
use log::{error, info}; use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation; use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet; use nym_config::OptionalSet;
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
@@ -109,6 +116,28 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
) )
} }
fn persist_gateway_details(
config: &Config,
details: GatewayEndpointConfig,
) -> Result<(), ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
})
}
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> { fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
@@ -121,40 +150,77 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
info!("It seems the client is using <= v1.1.13 config template."); info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification."); info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into(); let updated_step1: ConfigV1_1_20 = old_config.into();
let updated: Config = updated_step1.into(); let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated, gateway_config) = updated_step2.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?; updated.save_to_default_location()?;
Ok(true) Ok(true)
} }
fn try_upgrade_v1_1_19_config(id: &str) -> Result<bool, ClientError> { fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20) // explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_19::load_from_file(id) else { let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade // if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day // or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false); return Ok(false);
}; };
info!("It seems the client is using <= v1.1.19 config template."); info!("It seems the client is using <= v1.1.20 config template.");
info!("It is going to get updated to the current specification."); info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into(); let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated, gateway_config) = updated_step1.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated, gateway_config) = old_config.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?; updated.save_to_default_location()?;
Ok(true) Ok(true)
} }
fn try_upgrade_config(id: &str) -> Result<(), ClientError> { fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
let upgraded = try_upgrade_v1_1_13_config(id)?; if try_upgrade_v1_1_13_config(id)? {
if !upgraded { return Ok(());
try_upgrade_v1_1_19_config(id)?; }
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
} }
Ok(()) Ok(())
} }
fn try_load_current_config(id: &str) -> Result<Config, ClientError> { fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
// try to load the config as is
if let Ok(cfg) = Config::read_from_default_path(id) {
return if !cfg.validate() {
Err(ClientError::ConfigValidationFailure)
} else {
Ok(cfg)
};
}
// we couldn't load it - try upgrading it from older revisions
try_upgrade_config(id)?; try_upgrade_config(id)?;
let config = match Config::read_from_default_path(id) { let config = match Config::read_from_default_path(id) {
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.19" version = "1.1.22"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] 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" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
+23 -14
View File
@@ -11,7 +11,10 @@ use crate::{
}; };
use clap::Args; use clap::Args;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys; use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize; use serde::Serialize;
@@ -107,9 +110,13 @@ pub struct InitResults {
} }
impl InitResults { impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self { fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self { Self {
client_core: nym_client_core::init::InitResults::new(&config.core.base, address), client_core: nym_client_core::init::InitResults::new(
&config.core.base,
address,
gateway,
),
socks5_listening_port: config.core.socks5.listening_port, socks5_listening_port: config.core.socks5.listening_port,
client_address: address.to_string(), client_address: address.to_string(),
} }
@@ -160,9 +167,13 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
// Attempt to use a user-provided gateway, if possible // Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway; let user_chosen_gateway_id = args.gateway;
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
);
// Load and potentially override config // Load and potentially override config
let mut config = override_config( let config = override_config(
Config::new(id, &provider_address.to_string()), Config::new(id, &provider_address.to_string()),
OverrideConfig::from(args.clone()), OverrideConfig::from(args.clone()),
); );
@@ -170,18 +181,18 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
// Setup gateway by either registering a new one, or creating a new config from the selected // 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. // one but with keys kept, or reusing the gateway configuration.
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let gateway = nym_client_core::init::setup_gateway_from_config::<_>( let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let init_details = nym_client_core::init::setup_gateway(
&gateway_setup,
&key_store, &key_store,
&details_store,
register_gateway, register_gateway,
user_chosen_gateway_id, Some(&config.core.base.client.nym_api_urls),
&config.core.base,
args.latency_based_selection,
) )
.await .await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
config.core.base.set_gateway_endpoint(gateway);
// TODO: ask the service provider we specified for its interface version and set it in the config // TODO: ask the service provider we specified for its interface version and set it in the config
let config_save_location = config.default_location(); let config_save_location = config.default_location();
@@ -193,11 +204,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
config_save_location.display() config_save_location.display()
); );
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys( let address = init_details.client_address()?;
&config.storage_paths.common_paths.keys,
&config.core.base.client.gateway_endpoint, let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
)?;
let init_results = InitResults::new(&config, &address);
println!("{}", args.output.format(&init_results)); println!("{}", args.output.format(&init_results));
Ok(()) Ok(())
+77 -11
View File
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_13::OldConfigV1_1_13; use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_19::ConfigV1_1_19; use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::{BaseClientConfig, Config}; use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError; use crate::error::Socks5ClientError;
use clap::CommandFactory; use clap::CommandFactory;
@@ -11,6 +12,12 @@ use lazy_static::lazy_static;
use log::{error, info}; use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation; use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet; use nym_config::OptionalSet;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use std::error::Error; use std::error::Error;
@@ -116,6 +123,28 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
) )
} }
fn persist_gateway_details(
config: &Config,
details: GatewayEndpointConfig,
) -> Result<(), Socks5ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
})
}
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> { fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
@@ -128,40 +157,77 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It seems the client is using <= v1.1.13 config template."); info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification."); info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into(); let updated_step1: ConfigV1_1_20 = old_config.into();
let updated: Config = updated_step1.into(); let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated, gateway_config) = updated_step2.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?; updated.save_to_default_location()?;
Ok(true) Ok(true)
} }
fn try_upgrade_v1_1_19_config(id: &str) -> Result<bool, Socks5ClientError> { fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20) // explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_19::load_from_file(id) else { let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade // if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day // or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false); return Ok(false);
}; };
info!("It seems the client is using <= v1.1.19 config template."); info!("It seems the client is using <= v1.1.20 config template.");
info!("It is going to get updated to the current specification."); info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into(); let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated, gateway_config) = updated_step1.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated, gateway_config) = old_config.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?; updated.save_to_default_location()?;
Ok(true) Ok(true)
} }
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> { fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
let upgraded = try_upgrade_v1_1_13_config(id)?; if try_upgrade_v1_1_13_config(id)? {
if !upgraded { return Ok(());
try_upgrade_v1_1_19_config(id)?; }
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
} }
Ok(()) Ok(())
} }
fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> { fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
// try to load the config as is
if let Ok(cfg) = Config::read_from_default_path(id) {
return if !cfg.validate() {
Err(Socks5ClientError::ConfigValidationFailure)
} else {
Ok(cfg)
};
}
// we couldn't load it - try upgrading it from older revisions
try_upgrade_config(id)?; try_upgrade_config(id)?;
let config = match Config::read_from_default_path(id) { let config = match Config::read_from_default_path(id) {
+7 -2
View File
@@ -18,7 +18,8 @@ pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_socks5_client_core::config::Config as CoreConfig; pub use nym_socks5_client_core::config::Config as CoreConfig;
pub mod old_config_v1_1_13; pub mod old_config_v1_1_13;
pub mod old_config_v1_1_19; pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
mod persistence; mod persistence;
mod template; mod template;
@@ -69,7 +70,11 @@ impl NymConfigTemplate for Config {
impl Config { impl Config {
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self { pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
Config { Config {
core: CoreConfig::new(id.as_ref(), provider_mix_address.as_ref()), core: CoreConfig::new(
id.as_ref(),
env!("CARGO_PKG_VERSION"),
provider_mix_address.as_ref(),
),
storage_paths: SocksClientPaths::new_default(default_data_directory(id.as_ref())), storage_paths: SocksClientPaths::new_default(default_data_directory(id.as_ref())),
logging: Default::default(), logging: Default::default(),
} }
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_19::{ConfigV1_1_19, Socks5V1_1_19}; use crate::config::old_config_v1_1_20::{ConfigV1_1_20, Socks5V1_1_20};
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13; use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home; use nym_config::must_get_home;
@@ -14,7 +14,7 @@ pub struct OldConfigV1_1_13 {
#[serde(flatten)] #[serde(flatten)]
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>, pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub socks5: Socks5V1_1_19, pub socks5: Socks5V1_1_20,
} }
impl MigrationNymConfig for OldConfigV1_1_13 { impl MigrationNymConfig for OldConfigV1_1_13 {
@@ -28,9 +28,9 @@ impl MigrationNymConfig for OldConfigV1_1_13 {
} }
} }
impl From<OldConfigV1_1_13> for ConfigV1_1_19 { impl From<OldConfigV1_1_13> for ConfigV1_1_20 {
fn from(value: OldConfigV1_1_13) -> Self { fn from(value: OldConfigV1_1_13) -> Self {
ConfigV1_1_19 { ConfigV1_1_20 {
base: value.base.into(), base: value.base.into(),
socks5: value.socks5, socks5: value.socks5,
} }
@@ -1,18 +1,20 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths; use crate::config::old_config_v1_1_20_2::{
use crate::config::{BaseClientConfig, Config, CoreConfig}; ConfigV1_1_20_2, CoreConfigV1_1_20_2, SocksClientPathsV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings; use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths; use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths; use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19; use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::Client; use nym_client_core::config::old_config_v1_1_20_2::ClientV1_1_20_2;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home; use nym_config::must_get_home;
use nym_socks5_client_core::config::{ use nym_socks5_client_core::config::old_config_v1_1_20_2::{
ProviderInterfaceVersion, Socks5, Socks5Debug, Socks5ProtocolVersion, BaseClientConfigV1_1_20_2, Socks5DebugV1_1_20_2, Socks5V1_1_20_2,
}; };
use nym_socks5_client_core::config::{ProviderInterfaceVersion, Socks5ProtocolVersion};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::path::PathBuf; use std::path::PathBuf;
@@ -22,19 +24,19 @@ const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19 { pub struct ConfigV1_1_20 {
#[serde(flatten)] #[serde(flatten)]
pub base: BaseConfigV1_1_19<ConfigV1_1_19>, pub base: BaseConfigV1_1_20<ConfigV1_1_20>,
pub socks5: Socks5V1_1_19, pub socks5: Socks5V1_1_20,
} }
impl From<ConfigV1_1_19> for Config { impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
fn from(value: ConfigV1_1_19) -> Self { fn from(value: ConfigV1_1_20) -> Self {
Config { ConfigV1_1_20_2 {
core: CoreConfig { core: CoreConfigV1_1_20_2 {
base: BaseClientConfig { base: BaseClientConfigV1_1_20_2 {
client: Client { client: ClientV1_1_20_2 {
version: value.base.client.version, version: value.base.client.version,
id: value.base.client.id, id: value.base.client.id,
disabled_credentials_mode: value.base.client.disabled_credentials_mode, disabled_credentials_mode: value.base.client.disabled_credentials_mode,
@@ -46,8 +48,8 @@ impl From<ConfigV1_1_19> for Config {
}, },
socks5: value.socks5.into(), socks5: value.socks5.into(),
}, },
storage_paths: SocksClientPaths { storage_paths: SocksClientPathsV1_1_20_2 {
common_paths: CommonClientPaths { common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths { keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_identity_key_file, private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file, public_identity_key_file: value.base.client.public_identity_key_file,
@@ -65,7 +67,7 @@ impl From<ConfigV1_1_19> for Config {
} }
} }
impl MigrationNymConfig for ConfigV1_1_19 { impl MigrationNymConfig for ConfigV1_1_20 {
fn default_root_directory() -> PathBuf { fn default_root_directory() -> PathBuf {
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
let base_dir = must_get_home(); let base_dir = must_get_home();
@@ -78,7 +80,7 @@ impl MigrationNymConfig for ConfigV1_1_19 {
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Socks5V1_1_19 { pub struct Socks5V1_1_20 {
pub listening_port: u16, pub listening_port: u16,
pub provider_mix_address: String, pub provider_mix_address: String,
@@ -93,12 +95,12 @@ pub struct Socks5V1_1_19 {
pub send_anonymously: bool, pub send_anonymously: bool,
#[serde(default)] #[serde(default)]
pub socks5_debug: Socks5DebugV1_1_19, pub socks5_debug: Socks5DebugV1_1_20,
} }
impl From<Socks5V1_1_19> for Socks5 { impl From<Socks5V1_1_20> for Socks5V1_1_20_2 {
fn from(value: Socks5V1_1_19) -> Self { fn from(value: Socks5V1_1_20) -> Self {
Socks5 { Socks5V1_1_20_2 {
listening_port: value.listening_port, listening_port: value.listening_port,
provider_mix_address: value.provider_mix_address, provider_mix_address: value.provider_mix_address,
provider_interface_version: value.provider_interface_version, provider_interface_version: value.provider_interface_version,
@@ -111,23 +113,23 @@ impl From<Socks5V1_1_19> for Socks5 {
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Socks5DebugV1_1_19 { pub struct Socks5DebugV1_1_20 {
connection_start_surbs: u32, connection_start_surbs: u32,
per_request_surbs: u32, per_request_surbs: u32,
} }
impl From<Socks5DebugV1_1_19> for Socks5Debug { impl From<Socks5DebugV1_1_20> for Socks5DebugV1_1_20_2 {
fn from(value: Socks5DebugV1_1_19) -> Self { fn from(value: Socks5DebugV1_1_20) -> Self {
Socks5Debug { Socks5DebugV1_1_20_2 {
connection_start_surbs: value.connection_start_surbs, connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs, per_request_surbs: value.per_request_surbs,
} }
} }
} }
impl Default for Socks5DebugV1_1_19 { impl Default for Socks5DebugV1_1_20 {
fn default() -> Self { fn default() -> Self {
Socks5DebugV1_1_19 { Socks5DebugV1_1_20 {
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS, connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
per_request_surbs: DEFAULT_PER_REQUEST_SURBS, per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
} }
@@ -0,0 +1,54 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{default_config_filepath, Config};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
use nym_config::read_config_from_toml_file;
pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2;
use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct SocksClientPathsV1_1_20_2 {
#[serde(flatten)]
pub common_paths: CommonClientPathsV1_1_20_2,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20_2 {
pub core: CoreConfigV1_1_20_2,
pub storage_paths: SocksClientPathsV1_1_20_2,
pub logging: LoggingSettings,
}
impl ConfigV1_1_20_2 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
let config = Config {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default(),
},
logging: self.logging,
};
(config, gateway_details)
}
}
+3 -11
View File
@@ -64,17 +64,9 @@ credentials_database = '{{ storage_paths.credentials_database }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. # Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}' reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# DEPRECATED # Path to the file containing information about gateway used by this client,
[core.client.gateway_endpoint] # i.e. details such as its public key, owner address or the network information.
# ID of the gateway from which the client should be fetching messages. gateway_details = '{{ storage_paths.gateway_details }}'
gateway_id = '{{ core.client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ core.client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ core.client.gateway_endpoint.gateway_listener }}'
##### socket config options ##### ##### socket config options #####
+42 -22
View File
@@ -62,7 +62,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
"once_cell", "once_cell",
"version_check", "version_check",
] ]
@@ -225,9 +225,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.0" version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
@@ -622,7 +622,7 @@ dependencies = [
"cosmos-sdk-proto", "cosmos-sdk-proto",
"ecdsa", "ecdsa",
"eyre", "eyre",
"getrandom 0.2.9", "getrandom 0.2.10",
"k256", "k256",
"prost", "prost",
"prost-types", "prost-types",
@@ -865,6 +865,20 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "cw-controllers"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
dependencies = [
"cosmwasm-std",
"cw-storage-plus",
"cw-utils",
"schemars",
"serde",
"thiserror",
]
[[package]] [[package]]
name = "cw-storage-plus" name = "cw-storage-plus"
version = "0.13.4" version = "0.13.4"
@@ -1428,9 +1442,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.9" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"js-sys", "js-sys",
@@ -2244,6 +2258,7 @@ name = "nym-client-core"
version = "1.1.14" version = "1.1.14"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64 0.21.2",
"dashmap", "dashmap",
"dirs 4.0.0", "dirs 4.0.0",
"futures", "futures",
@@ -2266,6 +2281,7 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.6",
"sqlx 0.6.3", "sqlx 0.6.3",
"tap", "tap",
"thiserror", "thiserror",
@@ -2329,7 +2345,7 @@ dependencies = [
"bs58", "bs58",
"digest 0.9.0", "digest 0.9.0",
"ff 0.11.1", "ff 0.11.1",
"getrandom 0.2.9", "getrandom 0.2.10",
"group 0.11.0", "group 0.11.0",
"itertools", "itertools",
"nym-dkg", "nym-dkg",
@@ -2389,7 +2405,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-contracts-common" name = "nym-contracts-common"
version = "0.4.0" version = "0.5.0"
dependencies = [ dependencies = [
"bs58", "bs58",
"cosmwasm-std", "cosmwasm-std",
@@ -2424,7 +2440,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-crypto" name = "nym-crypto"
version = "0.3.0" version = "0.4.0"
dependencies = [ dependencies = [
"aes 0.8.2", "aes 0.8.2",
"blake3", "blake3",
@@ -2473,7 +2489,7 @@ name = "nym-gateway-client"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"getrandom 0.2.9", "getrandom 0.2.10",
"log", "log",
"nym-bandwidth-controller", "nym-bandwidth-controller",
"nym-coconut-interface", "nym-coconut-interface",
@@ -2531,7 +2547,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-mixnet-contract-common" name = "nym-mixnet-contract-common"
version = "0.5.0" version = "0.6.0"
dependencies = [ dependencies = [
"bs58", "bs58",
"cosmwasm-std", "cosmwasm-std",
@@ -2620,7 +2636,7 @@ dependencies = [
"chacha20", "chacha20",
"chacha20poly1305", "chacha20poly1305",
"curve25519-dalek", "curve25519-dalek",
"getrandom 0.2.9", "getrandom 0.2.10",
"log", "log",
"rand 0.7.3", "rand 0.7.3",
"rayon", "rayon",
@@ -2631,7 +2647,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-pemstore" name = "nym-pemstore"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"pem", "pem",
] ]
@@ -2641,8 +2657,12 @@ name = "nym-service-provider-directory-common"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cosmwasm-std", "cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars", "schemars",
"serde", "serde",
"thiserror",
] ]
[[package]] [[package]]
@@ -2796,7 +2816,7 @@ dependencies = [
"aes-gcm", "aes-gcm",
"argon2", "argon2",
"generic-array 0.14.7", "generic-array 0.14.7",
"getrandom 0.2.9", "getrandom 0.2.10",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"serde_json", "serde_json",
@@ -2895,7 +2915,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-vesting-contract-common" name = "nym-vesting-contract-common"
version = "0.6.0" version = "0.7.0"
dependencies = [ dependencies = [
"cosmwasm-std", "cosmwasm-std",
"nym-contracts-common", "nym-contracts-common",
@@ -3382,7 +3402,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
] ]
[[package]] [[package]]
@@ -3450,7 +3470,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
"redox_syscall 0.2.16", "redox_syscall 0.2.16",
"thiserror", "thiserror",
] ]
@@ -3478,7 +3498,7 @@ version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.21.2",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@@ -3612,7 +3632,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.21.2",
] ]
[[package]] [[package]]
@@ -4368,7 +4388,7 @@ dependencies = [
"bytes", "bytes",
"flex-error", "flex-error",
"futures", "futures",
"getrandom 0.2.9", "getrandom 0.2.10",
"http", "http",
"hyper", "hyper",
"hyper-proxy", "hyper-proxy",
@@ -4768,7 +4788,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [ dependencies = [
"getrandom 0.2.9", "getrandom 0.2.10",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -4944,7 +4964,7 @@ name = "wasm-utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"getrandom 0.2.9", "getrandom 0.2.10",
"indexed_db_futures", "indexed_db_futures",
"js-sys", "js-sys",
"nym-store-cipher", "nym-store-cipher",
+1 -1
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "nym-client-wasm" name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.1.0" version = "1.1.1"
edition = "2021" edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0" license = "Apache-2.0"
+41 -4
View File
@@ -116,11 +116,11 @@ async function testWithTester() {
// B) first get topology directly from nym-api // B) first get topology directly from nym-api
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const topology = await current_network_topology(validator) // const topology = await current_network_topology(validator)
// const nodeTester = await new NymNodeTester(topology, preferredGateway); // const nodeTester = await new NymNodeTester(topology, undefined, preferredGateway);
// //
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers) // C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway) // const nodeTester = await NymNodeTester.new_with_api(validator, undefined, preferredGateway)
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used // D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
// const topology = dummyTopology() // const topology = dummyTopology()
@@ -139,6 +139,40 @@ async function testWithTester() {
} }
} }
}; };
}
async function testerReconnection() {
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
const nodeTester = await NymNodeTester.new_with_api(validator);
self.onmessage = async event => {
if (event.data && event.data.kind) {
switch (event.data.kind) {
case 'TestPacket': {
const {mixnodeIdentity} = event.data.args;
console.log("starting node test...");
let result1 = await nodeTester.test_node(mixnodeIdentity);
console.log("sleeping for 5s");
await new Promise(r => setTimeout(r, 5000));
await nodeTester.disconnect_from_gateway();
console.log("sleeping for 5s");
await new Promise(r => setTimeout(r, 5000));
await nodeTester.reconnect_to_gateway();
let result2 = await nodeTester.test_node(mixnodeIdentity);
printAndDisplayTestResult(result1)
printAndDisplayTestResult(result2)
}
}
}
};
} }
async function testWithNymClient() { async function testWithNymClient() {
@@ -330,14 +364,17 @@ async function main() {
// sets up better stack traces in case of in-rust panics // sets up better stack traces in case of in-rust panics
set_panic_hook(); set_panic_hook();
// show reconnection capabilities
// await testerReconnection()
// run test on simplified and dedicated tester: // run test on simplified and dedicated tester:
// await testWithTester() await testWithTester()
// hook-up the whole client for testing // hook-up the whole client for testing
// await testWithNymClient() // await testWithNymClient()
// 'Normal' client setup (to send 'normal' messages) // 'Normal' client setup (to send 'normal' messages)
await normalNymClientUsage() // await normalNymClientUsage()
} }
// Let's get started! // Let's get started!
+2 -2
View File
@@ -29,7 +29,7 @@ impl Config {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self { pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
Config { Config {
base: BaseClientConfig::new(id) base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string())
.with_custom_nyxd(vec![validator_server .with_custom_nyxd(vec![validator_server
.parse() .parse()
.expect("provided url was malformed")]) .expect("provided url was malformed")])
@@ -39,7 +39,7 @@ impl Config {
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self { pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id) base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
.with_disabled_credentials(true) .with_disabled_credentials(true)
.with_disabled_cover_traffic(true) .with_disabled_cover_traffic(true)
.with_disabled_topology_refresh(true), .with_disabled_topology_refresh(true),
+26 -53
View File
@@ -7,7 +7,7 @@ use crate::client::response_pusher::ResponsePusher;
use crate::constants::NODE_TESTER_CLIENT_ID; use crate::constants::NODE_TESTER_CLIENT_ID;
use crate::error::WasmClientError; use crate::error::WasmClientError;
use crate::helpers::{ use crate::helpers::{
choose_gateway, gateway_from_topology, parse_recipient, parse_sender_tag, parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api,
setup_reply_surb_storage_backend, setup_reply_surb_storage_backend,
}; };
use crate::storage::traits::FullWasmClientStorage; use crate::storage::traits::FullWasmClientStorage;
@@ -15,13 +15,11 @@ use crate::storage::ClientStorage;
use crate::topology::WasmNymTopology; use crate::topology::WasmNymTopology;
use js_sys::Promise; use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState, CredentialsToggle, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
use nym_client_core::client::inbound_messages::InputMessage; use nym_client_core::client::inbound_messages::InputMessage;
use nym_client_core::client::replies::reply_storage::browser_backend; use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::TaskManager; use nym_task::TaskManager;
@@ -29,7 +27,7 @@ use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::{thread_rng, RngCore}; use rand::RngCore;
use std::sync::Arc; use std::sync::Arc;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::future_to_promise;
@@ -63,14 +61,7 @@ pub struct NymClientBuilder {
preferred_gateway: Option<IdentityKey>, preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>, storage_passphrase: Option<String>,
reply_surb_storage_backend: browser_backend::Backend,
on_message: js_sys::Function, on_message: js_sys::Function,
// unimplemented:
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
} }
#[wasm_bindgen] #[wasm_bindgen]
@@ -83,15 +74,10 @@ impl NymClientBuilder {
storage_passphrase: Option<String>, storage_passphrase: Option<String>,
) -> Self { ) -> Self {
NymClientBuilder { NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(
config.base.debug.reply_surbs,
),
config, config,
custom_topology: None, custom_topology: None,
storage_passphrase, storage_passphrase,
on_message, on_message,
bandwidth_controller: None,
disabled_credentials: true,
preferred_gateway, preferred_gateway,
} }
} }
@@ -114,14 +100,9 @@ impl NymClientBuilder {
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID); let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID);
NymClientBuilder { NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(
full_config.base.debug.reply_surbs,
),
config: full_config, config: full_config,
custom_topology: Some(topology.into()), custom_topology: Some(topology.into()),
on_message, on_message,
bandwidth_controller: None,
disabled_credentials: true,
storage_passphrase: None, storage_passphrase: None,
preferred_gateway: gateway, preferred_gateway: gateway,
} }
@@ -139,15 +120,17 @@ impl NymClientBuilder {
} }
} }
fn initialise_storage(config: &Config, base_storage: ClientStorage) -> FullWasmClientStorage {
FullWasmClientStorage {
keys_and_gateway_store: base_storage,
reply_storage: setup_reply_surb_storage_backend(config.base.debug.reply_surbs),
credential_storage: EphemeralCredentialStorage::default(),
}
}
async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> { async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> {
console_log!("Starting the wasm client"); console_log!("Starting the wasm client");
let disabled_credentials = if self.disabled_credentials {
CredentialsToggle::Disabled
} else {
CredentialsToggle::Enabled
};
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone(); let nym_api_endpoints = self.config.base.client.nym_api_urls.clone();
// TODO: this will have to be re-used for surbs. but this is a problem for another PR. // TODO: this will have to be re-used for surbs. but this is a problem for another PR.
@@ -155,35 +138,25 @@ impl NymClientBuilder {
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take()) ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
.await?; .await?;
let user_chosen = self.preferred_gateway.clone();
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way // if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
let gateway_endpoint = if let Some(topology) = &self.custom_topology { if let Some(topology) = &self.custom_topology {
gateway_from_topology( setup_from_topology(user_chosen, topology, &client_store).await?
&mut thread_rng(),
self.preferred_gateway.as_deref(),
topology,
&client_store,
)
.await?
} else { } else {
choose_gateway( setup_gateway_from_api(&client_store, user_chosen, &nym_api_endpoints).await?
&client_store,
self.preferred_gateway.clone(),
&nym_api_endpoints,
)
.await?
}; };
let packet_type = self.config.base.debug.traffic.packet_type;
let storage = Self::initialise_storage(&self.config, client_store);
let maybe_topology_provider = self.topology_provider(); let maybe_topology_provider = self.topology_provider();
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = BaseClientBuilder::new( let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> =
&gateway_endpoint, BaseClientBuilder::<FakeClient<DirectSigningNyxdClient>, _>::new(
&self.config.base.debug, &self.config.base,
client_store, storage,
self.bandwidth_controller, None,
self.reply_surb_storage_backend, );
disabled_credentials,
nym_api_endpoints,
);
if let Some(topology_provider) = maybe_topology_provider { if let Some(topology_provider) = maybe_topology_provider {
base_builder = base_builder.with_topology_provider(topology_provider); base_builder = base_builder.with_topology_provider(topology_provider);
} }
@@ -202,7 +175,7 @@ impl NymClientBuilder {
client_state: Arc::new(started_client.client_state), client_state: Arc::new(started_client.client_state),
_full_topology: None, _full_topology: None,
_task_manager: started_client.task_manager, _task_manager: started_client.task_manager,
packet_type: self.config.base.debug.traffic.packet_type, packet_type,
}) })
} }
+32 -83
View File
@@ -7,19 +7,18 @@ use crate::topology::WasmNymTopology;
use js_sys::Promise; use js_sys::Promise;
use nym_client_core::client::replies::reply_storage::browser_backend; use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config; use nym_client_core::config;
use nym_client_core::config::GatewayEndpointConfig; use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::GatewaySetup; use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails};
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::NymTopology; use nym_topology::{gateway, NymTopology};
use nym_validator_client::client::{IdentityKey, IdentityKeyRef}; use nym_validator_client::client::IdentityKey;
use nym_validator_client::NymApiClient; use nym_validator_client::NymApiClient;
use rand::{CryptoRng, Rng}; use rand::thread_rng;
use url::Url; use url::Url;
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, PromisableResult}; use wasm_utils::PromisableResult;
// don't get too excited about the name, under the hood it's just a big fat placeholder // don't get too excited about the name, under the hood it's just a big fat placeholder
// with no disk_persistence // with no disk_persistence
@@ -79,87 +78,37 @@ pub fn current_network_topology(nym_api_url: String) -> Promise {
}) })
} }
pub(crate) async fn choose_gateway( async fn setup_gateway(
client_store: &ClientStorage,
chosen_gateway: Option<IdentityKey>,
gateways: &[gateway::Node],
) -> Result<InitialisationDetails, WasmClientError> {
let setup = if client_store.has_full_gateway_info().await? {
GatewaySetup::MustLoad
} else {
GatewaySetup::new_fresh(chosen_gateway.clone(), None)
};
setup_gateway_from(&setup, client_store, client_store, false, Some(gateways))
.await
.map_err(Into::into)
}
pub(crate) async fn setup_gateway_from_api(
client_store: &ClientStorage, client_store: &ClientStorage,
chosen_gateway: Option<IdentityKey>, chosen_gateway: Option<IdentityKey>,
nym_apis: &[Url], nym_apis: &[Url],
) -> Result<GatewayEndpointConfig, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
let existing_gateway_config = client_store.read_gateway_config().await?; let mut rng = thread_rng();
let gateways = current_gateways(&mut rng, nym_apis).await?;
console_log!("loaded: {:?}", existing_gateway_config); setup_gateway(client_store, chosen_gateway, &gateways).await
if let Some(existing) = existing_gateway_config {
if let Some(provided) = &chosen_gateway {
if provided != &existing.gateway_id {
return Err(WasmClientError::AlreadyRegistered {
gateway_config: existing,
});
}
}
return Ok(existing);
};
// if NOTHING is specified nor available, choose gateway randomly.
let setup = GatewaySetup::new(None, chosen_gateway, None);
let config = setup.try_get_gateway_details(nym_apis).await?;
// perform registration + persist the new gateway info
// TODO: this is actually quite bad. we shouldn't be persisting gateway info here since we did not have persisted
// the shared key yet. this will only happen when we start the base client itself.
// but unfortunately, we can't do much more until we do a bit more refactoring.
client_store.store_gateway_config(&config).await?;
console_log!("stored: {:?}", config);
Ok(config)
} }
pub(crate) async fn gateway_from_topology<R: Rng + CryptoRng>( pub(crate) async fn setup_from_topology(
rng: &mut R, explicit_gateway: Option<IdentityKey>,
explicit_gateway: Option<IdentityKeyRef<'_>>,
topology: &NymTopology, topology: &NymTopology,
client_store: &ClientStorage, client_store: &ClientStorage,
) -> Result<GatewayEndpointConfig, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
let existing_gateway_config = client_store.read_gateway_config().await?; let gateways = topology.gateways();
console_log!("loaded: {:?}", existing_gateway_config); setup_gateway(client_store, explicit_gateway, gateways).await
let new_gateway: GatewayEndpointConfig = if let Some(provided) = explicit_gateway {
if let Some(existing) = existing_gateway_config {
// we have stored gateway info and explicitly provided identity key
//
// check if they match, otherwise return an error
return if provided != existing.gateway_id {
Err(WasmClientError::AlreadyRegistered {
gateway_config: existing,
})
} else {
Ok(existing)
};
} else {
// we have explicitly provided identity key and didn't have any prior stored data
//
// try to grab details from the topology
let gateway_identity = identity::PublicKey::from_base58_string(provided)
.map_err(|source| WasmClientError::InvalidGatewayIdentity { source })?;
if let Some(gateway) = topology.get_gateway(&gateway_identity) {
gateway.clone().into()
} else {
return Err(WasmClientError::NonExistentGateway {
gateway_identity: gateway_identity.to_base58_string(),
});
}
}
} else if let Some(existing) = existing_gateway_config {
// we have stored data and didn't provide anything separately - use what's stored!
return Ok(existing);
} else {
// we don't have anything stored nor we have provided anything
//
// just grab random gateway from our topology
topology.random_gateway(rng)?.clone().into()
};
console_log!("storing: {:?}", new_gateway);
client_store.store_gateway_config(&new_gateway).await?;
Ok(new_gateway)
} }
@@ -16,6 +16,9 @@ pub enum ClientStorageError {
#[error("{typ} cryptographic key is not available in storage")] #[error("{typ} cryptographic key is not available in storage")]
CryptoKeyNotInStorage { typ: String }, CryptoKeyNotInStorage { typ: String },
#[error("the prior gateway details are not available in the storage")]
GatewayDetailsNotInStorage,
} }
impl From<ClientStorageError> for JsValue { impl From<ClientStorageError> for JsValue {
+44 -10
View File
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::storage::errors::ClientStorageError; use crate::storage::errors::ClientStorageError;
use js_sys::Promise; use js_sys::Promise;
use nym_client_core::config::GatewayEndpointConfig; use nym_client_core::client::base_client::storage::gateway_details::PersistedGatewayDetails;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::SharedKeys; use nym_gateway_client::SharedKeys;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
@@ -27,8 +28,8 @@ mod v1 {
pub const CORE_STORE: &str = "core"; pub const CORE_STORE: &str = "core";
// keys // keys
// TODO: to replace with FULL config pub const CONFIG: &str = "config";
pub const GATEWAY_CONFIG: &str = "gateway_config"; pub const GATEWAY_DETAILS: &str = "gateway_details";
pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair"; pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair";
pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair"; pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair";
@@ -110,15 +111,32 @@ impl ClientStorage {
}) })
} }
pub(crate) async fn read_gateway_config( // TODO: persist client's config
&self, #[allow(dead_code)]
) -> Result<Option<GatewayEndpointConfig>, ClientStorageError> { pub(crate) async fn read_config(&self) -> Result<Option<Config>, ClientStorageError> {
self.inner self.inner
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_CONFIG)) .read_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG))
.await .await
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) async fn may_read_gateway_details(
&self,
) -> Result<Option<PersistedGatewayDetails>, ClientStorageError> {
self.inner
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_DETAILS))
.await
.map_err(Into::into)
}
pub(crate) async fn must_read_gateway_details(
&self,
) -> Result<PersistedGatewayDetails, ClientStorageError> {
self.may_read_gateway_details()
.await?
.ok_or(ClientStorageError::GatewayDetailsNotInStorage)
}
async fn may_read_identity_keypair( async fn may_read_identity_keypair(
&self, &self,
) -> Result<Option<identity::KeyPair>, ClientStorageError> { ) -> Result<Option<identity::KeyPair>, ClientStorageError> {
@@ -244,17 +262,33 @@ impl ClientStorage {
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) async fn store_gateway_config( pub(crate) async fn store_gateway_details(
&self, &self,
gateway_endpoint: &GatewayEndpointConfig, gateway_endpoint: &PersistedGatewayDetails,
) -> Result<(), ClientStorageError> { ) -> Result<(), ClientStorageError> {
self.inner self.inner
.store_value( .store_value(
v1::CORE_STORE, v1::CORE_STORE,
JsValue::from_str(v1::GATEWAY_CONFIG), JsValue::from_str(v1::GATEWAY_DETAILS),
gateway_endpoint, gateway_endpoint,
) )
.await .await
.map_err(Into::into) .map_err(Into::into)
} }
// TODO: persist client's config
#[allow(dead_code)]
pub(crate) async fn store_config(&self, config: &Config) -> Result<(), ClientStorageError> {
self.inner
.store_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG), config)
.await
.map_err(Into::into)
}
pub(crate) async fn has_full_gateway_info(&self) -> Result<bool, ClientStorageError> {
let has_keys = self.may_read_gateway_shared_key().await?.is_some();
let has_details = self.may_read_gateway_details().await?.is_some();
Ok(has_keys && has_details)
}
} }
+31 -6
View File
@@ -4,6 +4,9 @@
use crate::storage::errors::ClientStorageError; use crate::storage::errors::ClientStorageError;
use crate::storage::ClientStorage; use crate::storage::ClientStorage;
use async_trait::async_trait; use async_trait::async_trait;
use nym_client_core::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use nym_client_core::client::base_client::storage::MixnetClientStorage; use nym_client_core::client::base_client::storage::MixnetClientStorage;
use nym_client_core::client::key_manager::persistence::KeyStore; use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::key_manager::KeyManager; use nym_client_core::client::key_manager::KeyManager;
@@ -14,9 +17,9 @@ use wasm_utils::console_log;
// temporary until other variants are properly implemented (probably it should get changed into `ClientStorage` // temporary until other variants are properly implemented (probably it should get changed into `ClientStorage`
// implementing all traits and everything getting combined // implementing all traits and everything getting combined
pub struct FullWasmClientStorage { pub struct FullWasmClientStorage {
key_store: ClientStorage, pub(crate) keys_and_gateway_store: ClientStorage,
reply_storage: browser_backend::Backend, pub(crate) reply_storage: browser_backend::Backend,
credential_storage: EphemeralCredentialStorage, pub(crate) credential_storage: EphemeralCredentialStorage,
} }
impl MixnetClientStorage for FullWasmClientStorage { impl MixnetClientStorage for FullWasmClientStorage {
@@ -24,12 +27,14 @@ impl MixnetClientStorage for FullWasmClientStorage {
type ReplyStore = browser_backend::Backend; type ReplyStore = browser_backend::Backend;
type CredentialStore = EphemeralCredentialStorage; type CredentialStore = EphemeralCredentialStorage;
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) { type GatewayDetailsStore = ClientStorage;
(self.key_store, self.reply_storage, self.credential_storage)
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_storage, self.credential_storage)
} }
fn key_store(&self) -> &Self::KeyStore { fn key_store(&self) -> &Self::KeyStore {
&self.key_store &self.keys_and_gateway_store
} }
fn reply_store(&self) -> &Self::ReplyStore { fn reply_store(&self) -> &Self::ReplyStore {
@@ -39,6 +44,10 @@ impl MixnetClientStorage for FullWasmClientStorage {
fn credential_store(&self) -> &Self::CredentialStore { fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_storage &self.credential_storage
} }
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.keys_and_gateway_store
}
} }
#[async_trait(?Send)] #[async_trait(?Send)]
@@ -74,3 +83,19 @@ impl KeyStore for ClientStorage {
.await .await
} }
} }
#[async_trait(?Send)]
impl GatewayDetailsStore for ClientStorage {
type StorageError = ClientStorageError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.must_read_gateway_details().await
}
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
self.store_gateway_details(details).await
}
}
+38
View File
@@ -4,6 +4,9 @@
// due to expansion of #[wasm_bindgen] macro on NodeTestResult // due to expansion of #[wasm_bindgen] macro on NodeTestResult
#![allow(clippy::drop_non_drop)] #![allow(clippy::drop_non_drop)]
use crate::error::WasmClientError;
use crate::tester::LockedGatewayClient;
use js_sys::Promise;
use nym_node_tester_utils::processor::Received; use nym_node_tester_utils::processor::Received;
use nym_node_tester_utils::receiver::ReceivedReceiver; use nym_node_tester_utils::receiver::ReceivedReceiver;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -12,6 +15,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, console_warn}; use wasm_utils::{console_log, console_warn};
#[derive(Clone)] #[derive(Clone)]
@@ -107,3 +111,37 @@ impl Drop for TestMarker {
self.value.store(false, Ordering::SeqCst) self.value.store(false, Ordering::SeqCst)
} }
} }
pub(crate) trait GatewayReconnection {
fn disconnect_from_gateway(&self) -> Promise;
fn reconnect_to_gateway(&self) -> Promise;
}
impl GatewayReconnection for LockedGatewayClient {
fn disconnect_from_gateway(&self) -> Promise {
let this = self.clone();
future_to_promise(async move {
let mut guard = this.lock().await;
guard
.disconnect()
.await
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
Ok(JsValue::undefined())
})
}
fn reconnect_to_gateway(&self) -> Promise {
let this = self.clone();
future_to_promise(async move {
let mut guard = this.lock().await;
guard
.try_reconnect()
.await
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
Ok(JsValue::undefined())
})
}
}
+56 -32
View File
@@ -3,11 +3,11 @@
use crate::constants::NODE_TESTER_ID; use crate::constants::NODE_TESTER_ID;
use crate::error::WasmClientError; use crate::error::WasmClientError;
use crate::helpers::{current_network_topology_async, gateway_from_topology}; use crate::helpers::{current_network_topology_async, setup_from_topology};
use crate::storage::ClientStorage; use crate::storage::ClientStorage;
use crate::tester::ephemeral_receiver::EphemeralTestReceiver; use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
use crate::tester::helpers::{ use crate::tester::helpers::{
NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt, GatewayReconnection, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
}; };
use crate::topology::WasmNymTopology; use crate::topology::WasmNymTopology;
use futures::channel::mpsc; use futures::channel::mpsc;
@@ -15,7 +15,7 @@ use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::key_manager::ManagedKeys; use nym_client_core::client::key_manager::ManagedKeys;
use nym_client_core::config::GatewayEndpointConfig; use nym_client_core::init::InitialisationDetails;
use nym_credential_storage::ephemeral_storage::EphemeralStorage; use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_node_tester_utils::receiver::SimpleMessageReceiver; use nym_node_tester_utils::receiver::SimpleMessageReceiver;
@@ -28,7 +28,6 @@ use nym_task::TaskManager;
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::{CryptoRng, Rng};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex as SyncMutex}; use std::sync::{Arc, Mutex as SyncMutex};
@@ -74,6 +73,7 @@ pub struct NymNodeTester {
#[wasm_bindgen] #[wasm_bindgen]
pub struct NymNodeTesterBuilder { pub struct NymNodeTesterBuilder {
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
id: Option<String>,
base_topology: NymTopology, base_topology: NymTopology,
@@ -95,9 +95,11 @@ impl NymNodeTesterBuilder {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new( pub fn new(
base_topology: WasmNymTopology, base_topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
) -> NymNodeTesterBuilder { ) -> NymNodeTesterBuilder {
NymNodeTesterBuilder { NymNodeTesterBuilder {
id,
gateway, gateway,
base_topology: base_topology.into(), base_topology: base_topology.into(),
bandwidth_controller: None, bandwidth_controller: None,
@@ -106,43 +108,51 @@ impl NymNodeTesterBuilder {
async fn _new_with_api( async fn _new_with_api(
api_url: String, api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> { ) -> Result<Self, WasmClientError> {
let topology = current_network_topology_async(api_url).await?; let topology = current_network_topology_async(api_url).await?;
Ok(NymNodeTesterBuilder::new(topology, gateway)) Ok(NymNodeTesterBuilder::new(topology, id, gateway))
} }
pub fn new_with_api(gateway: Option<IdentityKey>, api_url: String) -> Promise { pub fn new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
future_to_promise(async move { future_to_promise(async move {
Self::_new_with_api(api_url, gateway) Self::_new_with_api(api_url, id, gateway)
.await .await
.into_promise_result() .into_promise_result()
}) })
} }
async fn gateway_info<R: Rng + CryptoRng>( async fn gateway_info(
&self, &self,
rng: &mut R,
client_store: &ClientStorage, client_store: &ClientStorage,
) -> Result<GatewayEndpointConfig, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
gateway_from_topology( if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await {
rng, Ok(loaded)
self.gateway.as_deref(), } else {
&self.base_topology, setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await
client_store, }
)
.await
} }
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> { async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
let mut rng = OsRng;
let task_manager = TaskManager::default(); let task_manager = TaskManager::default();
let client_store = ClientStorage::new_async(NODE_TESTER_ID, None).await?; let storage_id = if let Some(client_id) = &self.id {
format!("{NODE_TESTER_ID}-{client_id}")
} else {
NODE_TESTER_ID.to_owned()
};
let gateway_endpoint = self.gateway_info(&mut rng, &client_store).await?; let client_store = ClientStorage::new_async(&storage_id, None).await?;
let init_details = self.gateway_info(&client_store).await?;
let gateway_endpoint = init_details.gateway_details;
let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?; let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?;
let mut managed_keys = ManagedKeys::load_or_generate(&mut rng, &client_store).await; let managed_keys = init_details.managed_keys;
let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded(); let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
let (ack_sender, ack_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded();
@@ -151,7 +161,7 @@ impl NymNodeTesterBuilder {
gateway_endpoint.gateway_listener, gateway_endpoint.gateway_listener,
managed_keys.identity_keypair(), managed_keys.identity_keypair(),
gateway_identity, gateway_identity,
managed_keys.gateway_shared_key(), Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender, mixnet_message_sender,
ack_sender, ack_sender,
Duration::from_secs(10), Duration::from_secs(10),
@@ -160,14 +170,11 @@ impl NymNodeTesterBuilder {
); );
gateway_client.set_disabled_credentials_mode(true); gateway_client.set_disabled_credentials_mode(true);
let shared_keys = gateway_client.authenticate_and_start().await?; gateway_client.authenticate_and_start().await?;
managed_keys
.deal_with_gateway_key(shared_keys, &client_store)
.await?;
// TODO: make those values configurable later // TODO: make those values configurable later
let tester = NodeTester::new( let tester = NodeTester::new(
rng, OsRng,
self.base_topology, self.base_topology,
Some(address(&managed_keys, gateway_identity)), Some(address(&managed_keys, gateway_identity)),
PacketSize::default(), PacketSize::default(),
@@ -241,29 +248,46 @@ async fn test_mixnode(
impl NymNodeTester { impl NymNodeTester {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new(topology: WasmNymTopology, gateway: Option<IdentityKey>) -> Promise { pub fn new(
topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
console_log!("constructing node tester!"); console_log!("constructing node tester!");
NymNodeTesterBuilder::new(topology, gateway).setup_client() NymNodeTesterBuilder::new(topology, id, gateway).setup_client()
} }
async fn _new_with_api( async fn _new_with_api(
api_url: String, api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> { ) -> Result<Self, WasmClientError> {
NymNodeTesterBuilder::_new_with_api(api_url, gateway) NymNodeTesterBuilder::_new_with_api(api_url, id, gateway)
.await? .await?
._setup_client() ._setup_client()
.await .await
} }
pub fn new_with_api(api_url: String, gateway: Option<IdentityKey>) -> Promise { pub fn new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
future_to_promise(async move { future_to_promise(async move {
Self::_new_with_api(api_url, gateway) Self::_new_with_api(api_url, id, gateway)
.await .await
.into_promise_result() .into_promise_result()
}) })
} }
pub fn disconnect_from_gateway(&self) -> Promise {
self.gateway_client.disconnect_from_gateway()
}
pub fn reconnect_to_gateway(&self) -> Promise {
self.gateway_client.reconnect_to_gateway()
}
fn prepare_test_packets( fn prepare_test_packets(
&self, &self,
mixnode_identity: String, mixnode_identity: String,
+15 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-bin-common" name = "nym-bin-common"
version = "0.5.0" version = "0.6.0"
description = "Common code for nym binaries" description = "Common code for nym binaries"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -19,11 +19,17 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true } serde_json = { workspace = true, optional = true }
## tracing ## tracing
tracing-appender = { version = "0.2.2", optional = true }
tracing-subscriber = { version = "0.3.16", features = [ tracing-subscriber = { version = "0.3.16", features = [
"env-filter", "env-filter",
], optional = true } ], optional = true }
tracing-tree = { version = "0.2.2", optional = true } tracing-tree = { version = "0.2.2", optional = true }
opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
"rt-tokio",
"collector_client",
"isahc_collector_client",
] }
tracing-opentelemetry = { version = "0.19.0", optional = true }
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
[build-dependencies] [build-dependencies]
@@ -37,4 +43,10 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features] [features]
default = [] default = []
output_format = ["serde_json"] output_format = ["serde_json"]
tracing = ["tracing-appender", "tracing-subscriber", "tracing-tree"] tracing = [
"tracing-subscriber",
"tracing-tree",
"opentelemetry-jaeger",
"tracing-opentelemetry",
"opentelemetry",
]
@@ -101,7 +101,7 @@ impl BinaryBuildInformation {
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BinaryBuildInformationOwned { pub struct BinaryBuildInformationOwned {
// VERGEN_BUILD_TIMESTAMP // VERGEN_BUILD_TIMESTAMP
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
+25 -13
View File
@@ -4,7 +4,11 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
pub use tracing_appender; pub use opentelemetry;
#[cfg(feature = "tracing")]
pub use opentelemetry_jaeger;
#[cfg(feature = "tracing")]
pub use tracing_opentelemetry;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
pub use tracing_subscriber; pub use tracing_subscriber;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@@ -43,27 +47,35 @@ pub fn setup_logging() {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
#[macro_export] #[macro_export]
macro_rules! setup_tracing { macro_rules! setup_tracing {
($file_name: expr) => { ($service_name: expr) => {
use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt; use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt;
use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt; use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt;
let file_appender = let registry = nym_bin_common::logging::tracing_subscriber::Registry::default()
nym_bin_common::logging::tracing_appender::rolling::hourly($file_name, "log");
let (non_blocking, _guard) =
nym_bin_common::logging::tracing_appender::non_blocking(file_appender);
let appender_layer = nym_bin_common::logging::tracing_subscriber::fmt::Layer::new()
.with_ansi(false)
.with_writer(non_blocking);
nym_bin_common::logging::tracing_subscriber::Registry::default()
.with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env()) .with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env())
.with(appender_layer)
.with( .with(
nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4) nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4)
.with_targets(true) .with_targets(true)
.with_bracketed_fields(true), .with_bracketed_fields(true),
);
let tracer = nym_bin_common::logging::opentelemetry_jaeger::new_collector_pipeline()
.with_endpoint("http://44.199.230.10:14268/api/traces")
.with_service_name($service_name)
.with_isahc()
.with_trace_config(
nym_bin_common::logging::opentelemetry::sdk::trace::config().with_sampler(
nym_bin_common::logging::opentelemetry::sdk::trace::Sampler::TraceIdRatioBased(
0.1,
),
),
) )
.init(); .install_batch(nym_bin_common::logging::opentelemetry::runtime::Tokio)
.expect("Could not init tracer");
let telemetry = nym_bin_common::logging::tracing_opentelemetry::layer().with_tracer(tracer);
registry.with(telemetry).init();
}; };
} }
+3 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client-core" name = "nym-client-core"
version = "1.1.14" version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.66" rust-version = "1.66"
@@ -9,6 +9,7 @@ rust-version = "1.66"
[dependencies] [dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
base64 = "0.21.2"
dirs = "4.0" dirs = "4.0"
dashmap = "5.4.0" dashmap = "5.4.0"
futures = "0.3" futures = "0.3"
@@ -17,6 +18,7 @@ log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
sha2 = "0.10.6"
tap = "1.0.1" tap = "1.0.1"
thiserror = "1.0.34" thiserror = "1.0.34"
url = { version ="2.2", features = ["serde"] } url = { version ="2.2", features = ["serde"] }
@@ -42,7 +42,6 @@ use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskManager}; use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider; use nym_topology::provider_trait::TopologyProvider;
use rand::rngs::OsRng;
use std::sync::Arc; use std::sync::Arc;
use tap::TapFallible; use tap::TapFallible;
use url::Url; use url::Url;
@@ -50,6 +49,8 @@ use url::Url;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::traits::DkgQueryClient; use nym_validator_client::nyxd::traits::DkgQueryClient;
@@ -157,17 +158,11 @@ impl From<bool> for CredentialsToggle {
} }
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> { pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
// due to wasm limitations I had to split it like this : ( config: &'a Config,
gateway_config: &'a GatewayEndpointConfig, client_store: S,
debug_config: &'a DebugConfig, dkg_query_client: Option<C>,
disabled_credentials: bool,
nym_api_endpoints: Vec<Url>,
reply_storage_backend: S::ReplyStore,
key_store: S::KeyStore,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>, custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>, setup_method: GatewaySetup,
managed_keys: ManagedKeys,
} }
impl<'a, C, S> BaseClientBuilder<'a, C, S> impl<'a, C, S> BaseClientBuilder<'a, C, S>
@@ -175,47 +170,23 @@ where
S: MixnetClientStorage + 'static, S: MixnetClientStorage + 'static,
C: DkgQueryClient + Send + Sync + 'static, C: DkgQueryClient + Send + Sync + 'static,
{ {
// TODO: combine all storages pub fn new(
pub fn new_from_base_config(
base_config: &'a Config, base_config: &'a Config,
key_store: S::KeyStore, client_store: S,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>, dkg_query_client: Option<C>,
reply_storage_backend: S::ReplyStore,
) -> BaseClientBuilder<'a, C, S> { ) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder { BaseClientBuilder {
gateway_config: base_config.get_gateway_endpoint_config(), config: base_config,
debug_config: &base_config.debug, client_store,
disabled_credentials: base_config.get_disabled_credentials_mode(), dkg_query_client,
nym_api_endpoints: base_config.get_nym_api_endpoints(),
bandwidth_controller,
reply_storage_backend,
key_store,
managed_keys: ManagedKeys::Invalidated,
custom_topology_provider: None, custom_topology_provider: None,
setup_method: GatewaySetup::MustLoad,
} }
} }
// TODO: combine all storages pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
pub fn new( self.setup_method = setup;
gateway_config: &'a GatewayEndpointConfig, self
debug_config: &'a DebugConfig,
key_store: S::KeyStore,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
reply_storage_backend: S::ReplyStore,
credentials_toggle: CredentialsToggle,
nym_api_endpoints: Vec<Url>,
) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder {
gateway_config,
debug_config,
disabled_credentials: credentials_toggle.is_disabled(),
nym_api_endpoints,
reply_storage_backend,
custom_topology_provider: None,
bandwidth_controller,
key_store,
managed_keys: ManagedKeys::Invalidated,
}
} }
pub fn with_topology_provider( pub fn with_topology_provider(
@@ -228,13 +199,16 @@ where
// note: do **NOT** make this method public as its only valid usage is from within `start_base` // note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded // because it relies on the crypto keys being already loaded
fn as_mix_recipient(&self) -> Recipient { fn mix_address(
managed_keys: &ManagedKeys,
gateway_config: &GatewayEndpointConfig,
) -> Recipient {
Recipient::new( Recipient::new(
*self.managed_keys.identity_public_key(), *managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(), *managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id // TODO: below only works under assumption that gateway address == gateway id
// (which currently is true) // (which currently is true)
NodeIdentity::from_base58_string(&self.gateway_config.gateway_id).unwrap(), NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
) )
} }
@@ -318,7 +292,10 @@ where
} }
async fn start_gateway_client( async fn start_gateway_client(
&mut self, config: &Config,
gateway_config: GatewayEndpointConfig,
managed_keys: &ManagedKeys,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
shutdown: TaskClient, shutdown: TaskClient,
@@ -327,33 +304,26 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static, <S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{ {
let gateway_id = self.gateway_config.gateway_id.clone(); let gateway_address = gateway_config.gateway_listener.clone();
if gateway_id.is_empty() { let gateway_id = gateway_config.gateway_id;
return Err(ClientCoreError::GatewayIdUnknown);
}
let gateway_address = self.gateway_config.gateway_listener.clone();
if gateway_address.is_empty() {
return Err(ClientCoreError::GatewayAddressUnknown);
}
// TODO: in theory, at this point, this should be infallible
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let mut gateway_client = GatewayClient::new( let mut gateway_client = GatewayClient::new(
gateway_address, gateway_address,
self.managed_keys.identity_keypair(), managed_keys.identity_keypair(),
gateway_identity, gateway_identity,
self.managed_keys.gateway_shared_key(), Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender, mixnet_message_sender,
ack_sender, ack_sender,
self.debug_config config.debug.gateway_connection.gateway_response_timeout,
.gateway_connection bandwidth_controller,
.gateway_response_timeout,
self.bandwidth_controller.take(),
shutdown, shutdown,
); );
gateway_client.set_disabled_credentials_mode(self.disabled_credentials); gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
let shared_key = gateway_client let shared_key = gateway_client
.authenticate_and_start() .authenticate_and_start()
@@ -362,12 +332,7 @@ where
log::error!("Could not authenticate and start up the gateway connection - {err}") log::error!("Could not authenticate and start up the gateway connection - {err}")
})?; })?;
self.managed_keys managed_keys.ensure_gateway_key(shared_key);
.deal_with_gateway_key(shared_key, &self.key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
Ok(gateway_client) Ok(gateway_client)
} }
@@ -473,21 +438,41 @@ where
Ok(mem_store) Ok(mem_store)
} }
async fn initial_key_setup(&mut self) { async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError>
assert!(!self.managed_keys.is_valid()); where
let mut rng = OsRng; <S::KeyStore as KeyStore>::StorageError: Sync + Send,
self.managed_keys = ManagedKeys::load_or_generate(&mut rng, &self.key_store).await; <S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{
setup_gateway(
&self.setup_method,
self.client_store.key_store(),
self.client_store.gateway_details_store(),
false,
Some(&self.config.client.nym_api_urls),
)
.await
} }
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError> pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
where where
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
S::ReplyStore: Send + Sync, S::ReplyStore: Send + Sync,
<S::KeyStore as KeyStore>::StorageError: Send + Sync, <S::KeyStore as KeyStore>::StorageError: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{ {
info!("Starting nym client"); info!("Starting nym client");
self.initial_key_setup().await;
// derive (or load) client keys and gateway configuration
let details = self.initialise_keys_and_gateway().await?;
let gateway_config = details.gateway_details;
let managed_keys = details.managed_keys;
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
let bandwidth_controller = self
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
// channels for inter-component communication // channels for inter-component communication
// TODO: make the channels be internally created by the relevant components // TODO: make the channels be internally created by the relevant components
@@ -515,34 +500,39 @@ where
let (reply_controller_sender, reply_controller_receiver) = let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels(); reply_controller::requests::new_control_channels();
let self_address = self.as_mix_recipient(); let self_address = Self::mix_address(&managed_keys, &gateway_config);
// the components are started in very specific order. Unless you know what you are doing, // the components are started in very specific order. Unless you know what you are doing,
// do not change that. // do not change that.
let gateway_client = self let gateway_client = Self::start_gateway_client(
.start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe()) self.config,
.await?; gateway_config,
&managed_keys,
let reply_storage = Self::setup_persistent_reply_storage( bandwidth_controller,
self.reply_storage_backend, mixnet_messages_sender,
ack_sender,
task_manager.subscribe(), task_manager.subscribe(),
) )
.await?; .await?;
let reply_storage =
Self::setup_persistent_reply_storage(reply_storage_backend, task_manager.subscribe())
.await?;
let topology_provider = Self::setup_topology_provider( let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(), self.custom_topology_provider.take(),
self.nym_api_endpoints, self.config.get_nym_api_endpoints(),
); );
Self::start_topology_refresher( Self::start_topology_refresher(
topology_provider, topology_provider,
self.debug_config.topology, self.config.debug.topology,
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
task_manager.subscribe(), task_manager.subscribe(),
) )
.await?; .await?;
Self::start_received_messages_buffer_controller( Self::start_received_messages_buffer_controller(
self.managed_keys.encryption_keypair(), managed_keys.encryption_keypair(),
received_buffer_request_receiver, received_buffer_request_receiver,
mixnet_messages_receiver, mixnet_messages_receiver,
reply_storage.key_storage(), reply_storage.key_storage(),
@@ -566,8 +556,8 @@ where
let shared_lane_queue_lengths = LaneQueueLengths::new(); let shared_lane_queue_lengths = LaneQueueLengths::new();
let controller_config = real_messages_control::Config::new( let controller_config = real_messages_control::Config::new(
self.debug_config, &self.config.debug,
self.managed_keys.ack_key(), managed_keys.ack_key(),
self_address, self_address,
); );
@@ -583,17 +573,18 @@ where
shared_lane_queue_lengths.clone(), shared_lane_queue_lengths.clone(),
client_connection_rx, client_connection_rx,
task_manager.subscribe(), task_manager.subscribe(),
self.debug_config.traffic.packet_type, self.config.debug.traffic.packet_type,
); );
if !self if !self
.debug_config .config
.debug
.cover_traffic .cover_traffic
.disable_loop_cover_traffic_stream .disable_loop_cover_traffic_stream
{ {
Self::start_cover_traffic_stream( Self::start_cover_traffic_stream(
self.debug_config, &self.config.debug,
self.managed_keys.ack_key(), managed_keys.ack_key(),
self_address, self_address,
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
message_sender, message_sender,
@@ -122,13 +122,30 @@ pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>(
nym_api_url: Url, nym_api_url: Url,
storage: St, storage: St,
) -> BandwidthController<Client<QueryNyxdClient>, St> { ) -> BandwidthController<Client<QueryNyxdClient>, St> {
let client = default_query_dkg_client(nyxd_url, nym_api_url);
BandwidthController::new(storage, client)
}
pub fn default_query_dkg_client_from_config(config: &Config) -> Client<QueryNyxdClient> {
let nyxd_url = config
.get_validator_endpoints()
.pop()
.expect("No nyxd validator endpoint provided");
let api_url = config
.get_nym_api_endpoints()
.pop()
.expect("No validator api endpoint provided");
default_query_dkg_client(nyxd_url, api_url)
}
pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client<QueryNyxdClient> {
let details = nym_network_defaults::NymNetworkDetails::new_from_env(); let details = nym_network_defaults::NymNetworkDetails::new_from_env();
let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details) let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config"); .expect("failed to construct validator client config");
// overwrite env configuration with config URLs // overwrite env configuration with config URLs
client_config = client_config.with_urls(nyxd_url, nym_api_url); client_config = client_config.with_urls(nyxd_url, nym_api_url);
let client = nym_validator_client::Client::new_query(client_config) nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query client"); .expect("Could not construct query client")
BandwidthController::new(storage, client)
} }
@@ -0,0 +1,201 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use async_trait::async_trait;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
use std::ops::Deref;
use tokio::sync::Mutex;
use zeroize::Zeroizing;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewayDetailsStore {
type StorageError: Error;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedGatewayDetails {
// TODO: should we also verify correctness of the details themselves?
// i.e. we could include a checksum or tag (via the shared keys)
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
// in order to actually use it, we'd have to recompute the whole checksum which would be a huge pain.
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
#[serde(with = "base64")]
key_hash: Vec<u8>,
/// Actual gateway details being persisted.
pub(crate) details: GatewayEndpointConfig,
}
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
fn from(value: PersistedGatewayDetails) -> Self {
value.details
}
}
impl PersistedGatewayDetails {
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize().to_vec();
PersistedGatewayDetails { key_hash, details }
}
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize();
self.key_hash == key_hash.deref()
}
}
// helper to make Vec<u8> serialization use base64 representation to make it human readable
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
mod base64 {
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&STANDARD.encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
let s = <String>::deserialize(deserializer)?;
STANDARD.decode(s).map_err(serde::de::Error::custom)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, thiserror::Error)]
pub enum OnDiskGatewayDetailsError {
#[error("JSON failure: {0}")]
SerializationFailure(#[from] serde_json::Error),
#[error("failed to store gateway details to {path}: {err}")]
StoreFailure {
path: String,
#[source]
err: std::io::Error,
},
#[error("failed to load gateway details from {path}: {err}")]
LoadFailure {
path: String,
#[source]
err: std::io::Error,
},
}
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskGatewayDetails {
file_location: std::path::PathBuf,
}
#[cfg(not(target_arch = "wasm32"))]
impl OnDiskGatewayDetails {
pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
OnDiskGatewayDetails {
file_location: path.as_ref().to_owned(),
}
}
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
let file = std::fs::File::open(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::LoadFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::from_reader(file)?)
}
pub fn store_to_disk(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), OnDiskGatewayDetailsError> {
// ensure the whole directory structure exists
if let Some(parent_dir) = &self.file_location.parent() {
std::fs::create_dir_all(parent_dir).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?
}
let file = std::fs::File::create(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::to_writer_pretty(file, details)?)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for OnDiskGatewayDetails {
type StorageError = OnDiskGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.load_from_disk()
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
self.store_to_disk(gateway_details)
}
}
#[derive(Default)]
pub struct InMemGatewayDetails {
details: Mutex<Option<PersistedGatewayDetails>>,
}
#[derive(Debug, thiserror::Error)]
#[error("old ephemeral gateway details can't be loaded from storage")]
pub struct EphemeralGatewayDetailsError;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for InMemGatewayDetails {
type StorageError = EphemeralGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.details
.lock()
.await
.clone()
.ok_or(EphemeralGatewayDetailsError)
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
*self.details.lock().await = Some(gateway_details.clone());
Ok(())
}
}
@@ -4,17 +4,20 @@
// TODO: combine those more closely. Perhaps into a single underlying store. // 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? // Like for persistent, on-disk, storage, what's the point of having 3 different databases?
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, InMemGatewayDetails,
};
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore}; use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage; use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend; use crate::client::replies::reply_storage::ReplyStorageBackend;
use nym_credential_storage::ephemeral_storage::{ use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
EphemeralStorage as EphemeralCredentialStorage, EphemeralStorage,
};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::non_wasm_helpers; use crate::client::base_client::non_wasm_helpers;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::key_manager::persistence::OnDiskKeys; use crate::client::key_manager::persistence::OnDiskKeys;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend; use crate::client::replies::reply_storage::fs_backend;
@@ -25,24 +28,33 @@ use crate::error::ClientCoreError;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage; use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
pub mod gateway_details;
// TODO: ideally this should be changed into
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
pub trait MixnetClientStorage { pub trait MixnetClientStorage {
type KeyStore: KeyStore; type KeyStore: KeyStore;
type ReplyStore: ReplyStorageBackend; type ReplyStore: ReplyStorageBackend;
type CredentialStore: CredentialStorage; type CredentialStore: CredentialStorage;
type GatewayDetailsStore: GatewayDetailsStore;
// this is a TERRIBLE name... // this is a TERRIBLE name...
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore); // fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore, Self::GatewayDetailsStore);
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
fn key_store(&self) -> &Self::KeyStore; fn key_store(&self) -> &Self::KeyStore;
fn reply_store(&self) -> &Self::ReplyStore; fn reply_store(&self) -> &Self::ReplyStore;
fn credential_store(&self) -> &Self::CredentialStore; fn credential_store(&self) -> &Self::CredentialStore;
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore;
} }
#[derive(Default)] #[derive(Default)]
pub struct Ephemeral { pub struct Ephemeral {
key_store: InMemEphemeralKeys, key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty, reply_store: reply_storage::Empty,
credential_store: EphemeralStorage, credential_store: EphemeralCredentialStorage,
gateway_details_store: InMemGatewayDetails,
} }
impl Ephemeral { impl Ephemeral {
@@ -55,9 +67,10 @@ impl MixnetClientStorage for Ephemeral {
type KeyStore = InMemEphemeralKeys; type KeyStore = InMemEphemeralKeys;
type ReplyStore = reply_storage::Empty; type ReplyStore = reply_storage::Empty;
type CredentialStore = EphemeralCredentialStorage; type CredentialStore = EphemeralCredentialStorage;
type GatewayDetailsStore = InMemGatewayDetails;
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) { fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.key_store, self.reply_store, self.credential_store) (self.reply_store, self.credential_store)
} }
fn key_store(&self) -> &Self::KeyStore { fn key_store(&self) -> &Self::KeyStore {
@@ -71,6 +84,10 @@ impl MixnetClientStorage for Ephemeral {
fn credential_store(&self) -> &Self::CredentialStore { fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_store &self.credential_store
} }
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.gateway_details_store
}
} }
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -78,6 +95,7 @@ pub struct OnDiskPersistent {
pub(crate) key_store: OnDiskKeys, pub(crate) key_store: OnDiskKeys,
pub(crate) reply_store: fs_backend::Backend, pub(crate) reply_store: fs_backend::Backend,
pub(crate) credential_store: PersistentCredentialStorage, pub(crate) credential_store: PersistentCredentialStorage,
pub(crate) gateway_details_store: OnDiskGatewayDetails,
} }
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -86,11 +104,13 @@ impl OnDiskPersistent {
key_store: OnDiskKeys, key_store: OnDiskKeys,
reply_store: fs_backend::Backend, reply_store: fs_backend::Backend,
credential_store: PersistentCredentialStorage, credential_store: PersistentCredentialStorage,
gateway_details_store: OnDiskGatewayDetails,
) -> Self { ) -> Self {
Self { Self {
key_store, key_store,
reply_store, reply_store,
credential_store, credential_store,
gateway_details_store,
} }
} }
@@ -109,10 +129,13 @@ impl OnDiskPersistent {
let credential_store = let credential_store =
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await; nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await;
let gateway_details_store = OnDiskGatewayDetails::new(paths.gateway_details);
Ok(OnDiskPersistent { Ok(OnDiskPersistent {
key_store, key_store,
reply_store, reply_store,
credential_store, credential_store,
gateway_details_store,
}) })
} }
} }
@@ -122,9 +145,10 @@ impl MixnetClientStorage for OnDiskPersistent {
type KeyStore = OnDiskKeys; type KeyStore = OnDiskKeys;
type ReplyStore = fs_backend::Backend; type ReplyStore = fs_backend::Backend;
type CredentialStore = PersistentCredentialStorage; type CredentialStore = PersistentCredentialStorage;
type GatewayDetailsStore = OnDiskGatewayDetails;
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) { fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.key_store, self.reply_store, self.credential_store) (self.reply_store, self.credential_store)
} }
fn key_store(&self) -> &Self::KeyStore { fn key_store(&self) -> &Self::KeyStore {
@@ -138,4 +162,8 @@ impl MixnetClientStorage for OnDiskPersistent {
fn credential_store(&self) -> &Self::CredentialStore { fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_store &self.credential_store
} }
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.gateway_details_store
}
} }
@@ -10,7 +10,7 @@ use log::*;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::cover::generate_loop_cover_packet; use nym_sphinx::cover::generate_loop_cover_packet;
use nym_sphinx::params::PacketSize; use nym_sphinx::params::{PacketSize, PacketType};
use nym_sphinx::utils::sample_poisson_duration; use nym_sphinx::utils::sample_poisson_duration;
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin; use std::pin::Pin;
@@ -63,6 +63,8 @@ where
/// Optional secondary predefined packet size used for the loop cover messages. /// Optional secondary predefined packet size used for the loop cover messages.
secondary_packet_size: Option<PacketSize>, secondary_packet_size: Option<PacketSize>,
packet_type: PacketType,
} }
impl<R> Stream for LoopCoverTrafficStream<R> impl<R> Stream for LoopCoverTrafficStream<R>
@@ -135,6 +137,7 @@ impl LoopCoverTrafficStream<OsRng> {
topology_access, topology_access,
primary_packet_size: traffic_config.primary_packet_size, primary_packet_size: traffic_config.primary_packet_size,
secondary_packet_size: traffic_config.secondary_packet_size, secondary_packet_size: traffic_config.secondary_packet_size,
packet_type: traffic_config.packet_type,
} }
} }
@@ -194,7 +197,7 @@ impl LoopCoverTrafficStream<OsRng> {
self.average_ack_delay, self.average_ack_delay,
self.cover_traffic.loop_cover_traffic_average_delay, self.cover_traffic.loop_cover_traffic_average_delay,
cover_traffic_packet_size, cover_traffic_packet_size,
nym_sphinx::params::PacketType::Mix, self.packet_type,
) )
.expect("Somehow failed to generate a loop cover message with a valid topology"); .expect("Somehow failed to generate a loop cover message with a valid topology");
@@ -6,6 +6,7 @@ use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::registration::handshake::SharedKeys; use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use std::fmt::{Debug, Formatter};
use std::sync::Arc; use std::sync::Arc;
use zeroize::ZeroizeOnDrop; use zeroize::ZeroizeOnDrop;
@@ -20,6 +21,16 @@ pub enum ManagedKeys {
Invalidated, Invalidated,
} }
impl Debug for ManagedKeys {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ManagedKeys::Initial(_) => write!(f, "initial"),
ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
ManagedKeys::Invalidated => write!(f, "invalidated"),
}
}
}
impl From<KeyManagerBuilder> for ManagedKeys { impl From<KeyManagerBuilder> for ManagedKeys {
fn from(value: KeyManagerBuilder) -> Self { fn from(value: KeyManagerBuilder) -> Self {
ManagedKeys::Initial(value) ManagedKeys::Initial(value)
@@ -84,6 +95,11 @@ impl ManagedKeys {
} }
} }
pub fn must_get_gateway_shared_key(&self) -> Arc<SharedKeys> {
self.gateway_shared_key()
.expect("failed to extract gateway shared key")
}
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> { pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
match self { match self {
ManagedKeys::Initial(_) => None, ManagedKeys::Initial(_) => None,
@@ -108,6 +124,17 @@ impl ManagedKeys {
} }
} }
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
if let ManagedKeys::FullyDerived(key_manager) = &self {
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|| key_manager.gateway_shared_key != gateway_shared_key
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
}
}
}
pub async fn deal_with_gateway_key<S: KeyStore>( pub async fn deal_with_gateway_key<S: KeyStore>(
&mut self, &mut self,
gateway_shared_key: Arc<SharedKeys>, gateway_shared_key: Arc<SharedKeys>,
@@ -120,12 +147,7 @@ impl ManagedKeys {
key_manager key_manager
} }
ManagedKeys::FullyDerived(key_manager) => { ManagedKeys::FullyDerived(key_manager) => {
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key) self.ensure_gateway_key(gateway_shared_key);
|| key_manager.gateway_shared_key != gateway_shared_key
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
}
key_manager key_manager
} }
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"), ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
@@ -4,6 +4,7 @@
use crate::client::key_manager::KeyManager; use crate::client::key_manager::KeyManager;
use async_trait::async_trait; use async_trait::async_trait;
use std::error::Error; use std::error::Error;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::config::disk_persistence::keys_paths::ClientKeysPaths; use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
@@ -36,6 +37,7 @@ pub enum OnDiskKeysError {
KeyPairLoadFailure { KeyPairLoadFailure {
keys: String, keys: String,
paths: nym_pemstore::KeyPairPath, paths: nym_pemstore::KeyPairPath,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
@@ -43,6 +45,7 @@ pub enum OnDiskKeysError {
KeyPairStoreFailure { KeyPairStoreFailure {
keys: String, keys: String,
paths: nym_pemstore::KeyPairPath, paths: nym_pemstore::KeyPairPath,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
@@ -50,6 +53,7 @@ pub enum OnDiskKeysError {
KeyLoadFailure { KeyLoadFailure {
key: String, key: String,
path: String, path: String,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
@@ -57,6 +61,7 @@ pub enum OnDiskKeysError {
KeyStoreFailure { KeyStoreFailure {
key: String, key: String,
path: String, path: String,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
} }
@@ -79,6 +84,26 @@ impl OnDiskKeys {
OnDiskKeys { paths } OnDiskKeys { paths }
} }
#[doc(hidden)]
pub fn ephemeral_load_gateway_keys(
&self,
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.map(zeroize::Zeroizing::new)
}
#[doc(hidden)]
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
let encryption_paths = self.paths.encryption_key_pair_path();
self.load_keypair(encryption_paths, "encryption keys")
}
#[doc(hidden)]
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
self.load_keypair(identity_paths, "identity keys")
}
fn load_key<T: PemStorableKey>( fn load_key<T: PemStorableKey>(
&self, &self,
path: &std::path::Path, path: &std::path::Path,
@@ -132,13 +157,8 @@ impl OnDiskKeys {
} }
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> { fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path(); let identity_keypair = self.load_identity_keypair()?;
let encryption_paths = self.paths.encryption_key_pair_path(); let encryption_keypair = self.load_encryption_keypair()?;
let identity_keypair: identity::KeyPair =
self.load_keypair(identity_paths, "identity keys")?;
let encryption_keypair: encryption::KeyPair =
self.load_keypair(encryption_paths, "encryption keys")?;
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?; let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
let gateway_shared_key: SharedKeys = let gateway_shared_key: SharedKeys =
@@ -193,10 +213,12 @@ impl KeyStore for OnDiskKeys {
} }
#[derive(Default)] #[derive(Default)]
pub struct InMemEphemeralKeys; pub struct InMemEphemeralKeys {
keys: Mutex<Option<KeyManager>>,
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[error("ephemeral keys can't be loaded from storage")] #[error("old ephemeral keys can't be loaded from storage")]
pub struct EphemeralKeysError; pub struct EphemeralKeysError;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -205,10 +227,11 @@ impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError; type StorageError = EphemeralKeysError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> { async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
Err(EphemeralKeysError) self.keys.lock().await.clone().ok_or(EphemeralKeysError)
} }
async fn store_keys(&self, _keys: &KeyManager) -> Result<(), Self::StorageError> { async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
*self.keys.lock().await = Some(keys.clone());
Ok(()) Ok(())
} }
} }
@@ -565,7 +565,7 @@ where
fragments: Vec<Fragment>, fragments: Vec<Fragment>,
reply_surbs: Vec<ReplySurb>, reply_surbs: Vec<ReplySurb>,
) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> { ) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> {
debug_assert_ne!( debug_assert_eq!(
fragments.len(), fragments.len(),
reply_surbs.len(), reply_surbs.len(),
"attempted to send {} fragments with {} reply surbs", "attempted to send {} fragments with {} reply surbs",
@@ -6,16 +6,21 @@ use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub mod keys_paths; pub mod keys_paths;
pub mod old_v1_1_20_2;
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite"; pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db"; pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths { pub struct CommonClientPaths {
pub keys: ClientKeysPaths, pub keys: ClientKeysPaths,
// TODO: /// Path to the file containing information about gateway used by this client,
// pub gateway_config_pathfinder: (), /// i.e. details such as its public key, owner address or the network information.
pub gateway_details: PathBuf,
/// Path to the database containing bandwidth credentials of this client. /// Path to the database containing bandwidth credentials of this client.
pub credentials_database: PathBuf, pub credentials_database: PathBuf,
@@ -30,6 +35,7 @@ impl CommonClientPaths {
CommonClientPaths { CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME), credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME), reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
keys: ClientKeysPaths::new_default(base_data_directory), keys: ClientKeysPaths::new_default(base_data_directory),
} }
} }
@@ -0,0 +1,30 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAY_DETAILS_FILENAME};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV1_1_20_2 {
pub keys: ClientKeysPaths,
pub credentials_database: PathBuf,
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV1_1_20_2 {
pub fn upgrade_default(self) -> CommonClientPaths {
let data_dir = self
.reply_surb_database
.parent()
.expect("client paths upgrade failure");
CommonClientPaths {
keys: self.keys,
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
}
}
}
+27 -45
View File
@@ -14,7 +14,8 @@ use wasm_bindgen::prelude::*;
pub mod disk_persistence; pub mod disk_persistence;
pub mod old_config_v1_1_13; pub mod old_config_v1_1_13;
pub mod old_config_v1_1_19; pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
// 'DEBUG' // 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5; const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -65,15 +66,19 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn new<S: Into<String>>(id: S) -> Self { pub fn new<S: Into<String>>(id: S, version: S) -> Self {
Config { Config {
client: Client::new_default(id), client: Client::new_default(id, version),
debug: Default::default(), debug: Default::default(),
} }
} }
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self {
Config { client, debug }
}
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
self.client.validate() && self.debug.validate() self.debug.validate()
} }
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self { pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
@@ -86,19 +91,6 @@ impl Config {
self self
} }
pub fn set_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
self.client.gateway_endpoint = gateway_endpoint;
}
pub fn with_gateway_endpoint(mut self, gateway_endpoint: GatewayEndpointConfig) -> Self {
self.client.gateway_endpoint = gateway_endpoint;
self
}
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_endpoint.gateway_id = id.into();
}
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self { pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
self.client.nyxd_urls = urls; self.client.nyxd_urls = urls;
self self
@@ -174,22 +166,6 @@ impl Config {
pub fn get_nym_api_endpoints(&self) -> Vec<Url> { pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone() self.client.nym_api_urls.clone()
} }
pub fn get_gateway_id(&self) -> String {
self.client.gateway_endpoint.gateway_id.clone()
}
pub fn get_gateway_owner(&self) -> String {
self.client.gateway_endpoint.gateway_owner.clone()
}
pub fn get_gateway_listener(&self) -> String {
self.client.gateway_endpoint.gateway_listener.clone()
}
pub fn get_gateway_endpoint_config(&self) -> &GatewayEndpointConfig {
&self.client.gateway_endpoint
}
} }
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
@@ -242,6 +218,8 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
} }
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct Client { pub struct Client {
/// Version of the client for which this configuration was created. /// Version of the client for which this configuration was created.
pub version: String, pub version: String,
@@ -251,6 +229,7 @@ pub struct Client {
/// Indicates whether this client is running in a disabled credentials mode, thus attempting /// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials. /// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)] #[serde(default)]
pub disabled_credentials_mode: bool, pub disabled_credentials_mode: bool,
@@ -261,15 +240,10 @@ pub struct Client {
/// Addresses to APIs running on validator from which the client gets the view of the network. /// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")] #[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>, pub nym_api_urls: Vec<Url>,
/// Information regarding how the client should send data to gateway.
// #[deprecated(note = "this shall be moved to separate file because it doesn't belong here...")]
// TODO: this should be removed from config files and be moved to separate file instead
pub gateway_endpoint: GatewayEndpointConfig,
} }
impl Client { impl Client {
pub fn new_default<S: Into<String>>(id: S) -> Self { pub fn new_default<S: Into<String>>(id: S, version: S) -> Self {
let network = NymNetworkDetails::new_mainnet(); let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network let nyxd_urls = network
.endpoints .endpoints
@@ -283,19 +257,27 @@ impl Client {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Client { Client {
version: env!("CARGO_PKG_VERSION").to_string(), version: version.into(),
id: id.into(), id: id.into(),
disabled_credentials_mode: true, disabled_credentials_mode: true,
nyxd_urls, nyxd_urls,
nym_api_urls, nym_api_urls,
gateway_endpoint: Default::default(),
} }
} }
pub fn validate(&self) -> bool { pub fn new<S: Into<String>>(
!self.gateway_endpoint.gateway_id.is_empty() id: S,
&& !self.gateway_endpoint.gateway_owner.is_empty() disabled_credentials_mode: bool,
&& !self.gateway_endpoint.gateway_owner.is_empty() nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
) -> Self {
Client {
version: env!("CARGO_PKG_VERSION").to_string(),
id: id.into(),
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
}
} }
} }
@@ -1,9 +1,9 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_19::{ use crate::config::old_config_v1_1_20::{
AcknowledgementsV1_1_19, ClientV1_1_19, ConfigV1_1_19, CoverTrafficV1_1_19, DebugConfigV1_1_19, AcknowledgementsV1_1_20, ClientV1_1_20, ConfigV1_1_20, CoverTrafficV1_1_20, DebugConfigV1_1_20,
GatewayConnectionV1_1_19, LoggingV1_1_19, ReplySurbsV1_1_19, TopologyV1_1_19, TrafficV1_1_19, GatewayConnectionV1_1_20, LoggingV1_1_20, ReplySurbsV1_1_20, TopologyV1_1_20, TrafficV1_1_20,
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE,
@@ -39,7 +39,7 @@ impl From<ExtendedPacketSize> for PacketSize {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> { pub struct OldConfigV1_1_13<T> {
pub client: ClientV1_1_19<T>, pub client: ClientV1_1_20<T>,
#[serde(default)] #[serde(default)]
pub logging: OldLoggingV1_1_13, pub logging: OldLoggingV1_1_13,
@@ -51,9 +51,9 @@ pub struct OldConfigV1_1_13<T> {
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct OldLoggingV1_1_13 {} pub struct OldLoggingV1_1_13 {}
impl From<OldLoggingV1_1_13> for LoggingV1_1_19 { impl From<OldLoggingV1_1_13> for LoggingV1_1_20 {
fn from(_value: OldLoggingV1_1_13) -> Self { fn from(_value: OldLoggingV1_1_13) -> Self {
LoggingV1_1_19 {} LoggingV1_1_20 {}
} }
} }
@@ -114,10 +114,10 @@ pub struct OldDebugConfigV1_1_13 {
pub maximum_reply_key_age: Duration, pub maximum_reply_key_age: Duration,
} }
impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_19 { impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
fn from(value: OldDebugConfigV1_1_13) -> Self { fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfigV1_1_19 { DebugConfigV1_1_20 {
traffic: TrafficV1_1_19 { traffic: TrafficV1_1_20 {
average_packet_delay: value.average_packet_delay, average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay, message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value disable_main_poisson_packet_distribution: value
@@ -125,25 +125,25 @@ impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_19 {
primary_packet_size: PacketSize::RegularPacket, primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: value.use_extended_packet_size.map(Into::into), secondary_packet_size: value.use_extended_packet_size.map(Into::into),
}, },
cover_traffic: CoverTrafficV1_1_19 { cover_traffic: CoverTrafficV1_1_20 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay, loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream, disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
..CoverTrafficV1_1_19::default() ..CoverTrafficV1_1_20::default()
}, },
gateway_connection: GatewayConnectionV1_1_19 { gateway_connection: GatewayConnectionV1_1_20 {
gateway_response_timeout: value.gateway_response_timeout, gateway_response_timeout: value.gateway_response_timeout,
}, },
acknowledgements: AcknowledgementsV1_1_19 { acknowledgements: AcknowledgementsV1_1_20 {
average_ack_delay: value.average_ack_delay, average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier, ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition, ack_wait_addition: value.ack_wait_addition,
}, },
topology: TopologyV1_1_19 { topology: TopologyV1_1_20 {
topology_refresh_rate: value.topology_refresh_rate, topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout, topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: false, disable_refreshing: false,
}, },
reply_surbs: ReplySurbsV1_1_19 { reply_surbs: ReplySurbsV1_1_20 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold, minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold, maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size, minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -190,10 +190,10 @@ impl Default for OldDebugConfigV1_1_13 {
} }
} }
impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_19<U> { impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self { fn from(value: OldConfigV1_1_13<T>) -> Self {
ConfigV1_1_19 { ConfigV1_1_20 {
client: ClientV1_1_19 { client: ClientV1_1_20 {
version: value.client.version, version: value.client.version,
id: value.client.id, id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode, disabled_credentials_mode: value.client.disabled_credentials_mode,
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::{ use crate::config::old_config_v1_1_20_2::{
Acknowledgements, CoverTraffic, DebugConfig, GatewayConnection, GatewayEndpointConfig, AcknowledgementsV1_1_20_2, CoverTrafficV1_1_20_2, DebugConfigV1_1_20_2,
ReplySurbs, Topology, Traffic, GatewayConnectionV1_1_20_2, GatewayEndpointConfigV1_1_20_2, ReplySurbsV1_1_20_2,
TopologyV1_1_20_2, TrafficV1_1_20_2,
}; };
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -55,25 +56,25 @@ pub(crate) const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(2
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19<T> { pub struct ConfigV1_1_20<T> {
pub client: ClientV1_1_19<T>, pub client: ClientV1_1_20<T>,
#[serde(default)] #[serde(default)]
pub logging: LoggingV1_1_19, pub logging: LoggingV1_1_20,
#[serde(default)] #[serde(default)]
pub debug: DebugConfigV1_1_19, pub debug: DebugConfigV1_1_20,
} }
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_19 { pub struct GatewayEndpointConfigV1_1_20 {
pub gateway_id: String, pub gateway_id: String,
pub gateway_owner: String, pub gateway_owner: String,
pub gateway_listener: String, pub gateway_listener: String,
} }
impl From<GatewayEndpointConfigV1_1_19> for GatewayEndpointConfig { impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
fn from(value: GatewayEndpointConfigV1_1_19) -> Self { fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
GatewayEndpointConfig { GatewayEndpointConfigV1_1_20_2 {
gateway_id: value.gateway_id, gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner, gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener, gateway_listener: value.gateway_listener,
@@ -82,7 +83,7 @@ impl From<GatewayEndpointConfigV1_1_19> for GatewayEndpointConfig {
} }
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_19<T> { pub struct ClientV1_1_20<T> {
pub version: String, pub version: String,
pub id: String, pub id: String,
#[serde(default)] #[serde(default)]
@@ -97,7 +98,7 @@ pub struct ClientV1_1_19<T> {
pub public_encryption_key_file: PathBuf, pub public_encryption_key_file: PathBuf,
pub gateway_shared_key_file: PathBuf, pub gateway_shared_key_file: PathBuf,
pub ack_key_file: PathBuf, pub ack_key_file: PathBuf,
pub gateway_endpoint: GatewayEndpointConfigV1_1_19, pub gateway_endpoint: GatewayEndpointConfigV1_1_20,
pub database_path: PathBuf, pub database_path: PathBuf,
#[serde(default)] #[serde(default)]
pub reply_surb_database_path: PathBuf, pub reply_surb_database_path: PathBuf,
@@ -109,11 +110,11 @@ pub struct ClientV1_1_19<T> {
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct LoggingV1_1_19 {} pub struct LoggingV1_1_20 {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)] #[serde(default)]
pub struct TrafficV1_1_19 { pub struct TrafficV1_1_20 {
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub average_packet_delay: Duration, pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
@@ -123,9 +124,9 @@ pub struct TrafficV1_1_19 {
pub secondary_packet_size: Option<PacketSize>, pub secondary_packet_size: Option<PacketSize>,
} }
impl From<TrafficV1_1_19> for Traffic { impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
fn from(value: TrafficV1_1_19) -> Self { fn from(value: TrafficV1_1_20) -> Self {
Traffic { TrafficV1_1_20_2 {
average_packet_delay: value.average_packet_delay, average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay, message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value disable_main_poisson_packet_distribution: value
@@ -137,9 +138,9 @@ impl From<TrafficV1_1_19> for Traffic {
} }
} }
impl Default for TrafficV1_1_19 { impl Default for TrafficV1_1_20 {
fn default() -> Self { fn default() -> Self {
TrafficV1_1_19 { TrafficV1_1_20 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY, average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false, disable_main_poisson_packet_distribution: false,
@@ -151,16 +152,16 @@ impl Default for TrafficV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_19 { pub struct CoverTrafficV1_1_20 {
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration, pub loop_cover_traffic_average_delay: Duration,
pub cover_traffic_primary_size_ratio: f64, pub cover_traffic_primary_size_ratio: f64,
pub disable_loop_cover_traffic_stream: bool, pub disable_loop_cover_traffic_stream: bool,
} }
impl From<CoverTrafficV1_1_19> for CoverTraffic { impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
fn from(value: CoverTrafficV1_1_19) -> Self { fn from(value: CoverTrafficV1_1_20) -> Self {
CoverTraffic { CoverTrafficV1_1_20_2 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay, loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio, cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream, disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
@@ -168,9 +169,9 @@ impl From<CoverTrafficV1_1_19> for CoverTraffic {
} }
} }
impl Default for CoverTrafficV1_1_19 { impl Default for CoverTrafficV1_1_20 {
fn default() -> Self { fn default() -> Self {
CoverTrafficV1_1_19 { CoverTrafficV1_1_20 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO, cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false, disable_loop_cover_traffic_stream: false,
@@ -180,22 +181,22 @@ impl Default for CoverTrafficV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_19 { pub struct GatewayConnectionV1_1_20 {
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration, pub gateway_response_timeout: Duration,
} }
impl From<GatewayConnectionV1_1_19> for GatewayConnection { impl From<GatewayConnectionV1_1_20> for GatewayConnectionV1_1_20_2 {
fn from(value: GatewayConnectionV1_1_19) -> Self { fn from(value: GatewayConnectionV1_1_20) -> Self {
GatewayConnection { GatewayConnectionV1_1_20_2 {
gateway_response_timeout: value.gateway_response_timeout, gateway_response_timeout: value.gateway_response_timeout,
} }
} }
} }
impl Default for GatewayConnectionV1_1_19 { impl Default for GatewayConnectionV1_1_20 {
fn default() -> Self { fn default() -> Self {
GatewayConnectionV1_1_19 { GatewayConnectionV1_1_20 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT, gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
} }
} }
@@ -203,7 +204,7 @@ impl Default for GatewayConnectionV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_19 { pub struct AcknowledgementsV1_1_20 {
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub average_ack_delay: Duration, pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64, pub ack_wait_multiplier: f64,
@@ -211,9 +212,9 @@ pub struct AcknowledgementsV1_1_19 {
pub ack_wait_addition: Duration, pub ack_wait_addition: Duration,
} }
impl From<AcknowledgementsV1_1_19> for Acknowledgements { impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
fn from(value: AcknowledgementsV1_1_19) -> Self { fn from(value: AcknowledgementsV1_1_20) -> Self {
Acknowledgements { AcknowledgementsV1_1_20_2 {
average_ack_delay: value.average_ack_delay, average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier, ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition, ack_wait_addition: value.ack_wait_addition,
@@ -221,9 +222,9 @@ impl From<AcknowledgementsV1_1_19> for Acknowledgements {
} }
} }
impl Default for AcknowledgementsV1_1_19 { impl Default for AcknowledgementsV1_1_20 {
fn default() -> Self { fn default() -> Self {
AcknowledgementsV1_1_19 { AcknowledgementsV1_1_20 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY, average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER, ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION, ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -233,7 +234,7 @@ impl Default for AcknowledgementsV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_19 { pub struct TopologyV1_1_20 {
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration, pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
@@ -241,9 +242,9 @@ pub struct TopologyV1_1_19 {
pub disable_refreshing: bool, pub disable_refreshing: bool,
} }
impl From<TopologyV1_1_19> for Topology { impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
fn from(value: TopologyV1_1_19) -> Self { fn from(value: TopologyV1_1_20) -> Self {
Topology { TopologyV1_1_20_2 {
topology_refresh_rate: value.topology_refresh_rate, topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout, topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing, disable_refreshing: value.disable_refreshing,
@@ -251,9 +252,9 @@ impl From<TopologyV1_1_19> for Topology {
} }
} }
impl Default for TopologyV1_1_19 { impl Default for TopologyV1_1_20 {
fn default() -> Self { fn default() -> Self {
TopologyV1_1_19 { TopologyV1_1_20 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false, disable_refreshing: false,
@@ -263,7 +264,7 @@ impl Default for TopologyV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_19 { pub struct ReplySurbsV1_1_20 {
pub minimum_reply_surb_storage_threshold: usize, pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize, pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32, pub minimum_reply_surb_request_size: u32,
@@ -279,9 +280,9 @@ pub struct ReplySurbsV1_1_19 {
pub maximum_reply_key_age: Duration, pub maximum_reply_key_age: Duration,
} }
impl From<ReplySurbsV1_1_19> for ReplySurbs { impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
fn from(value: ReplySurbsV1_1_19) -> Self { fn from(value: ReplySurbsV1_1_20) -> Self {
ReplySurbs { ReplySurbsV1_1_20_2 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold, minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold, maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size, minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -296,9 +297,9 @@ impl From<ReplySurbsV1_1_19> for ReplySurbs {
} }
} }
impl Default for ReplySurbsV1_1_19 { impl Default for ReplySurbsV1_1_20 {
fn default() -> Self { fn default() -> Self {
ReplySurbsV1_1_19 { ReplySurbsV1_1_20 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD, minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -313,20 +314,20 @@ impl Default for ReplySurbsV1_1_19 {
} }
} }
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_19 { pub struct DebugConfigV1_1_20 {
pub traffic: TrafficV1_1_19, pub traffic: TrafficV1_1_20,
pub cover_traffic: CoverTrafficV1_1_19, pub cover_traffic: CoverTrafficV1_1_20,
pub gateway_connection: GatewayConnectionV1_1_19, pub gateway_connection: GatewayConnectionV1_1_20,
pub acknowledgements: AcknowledgementsV1_1_19, pub acknowledgements: AcknowledgementsV1_1_20,
pub topology: TopologyV1_1_19, pub topology: TopologyV1_1_20,
pub reply_surbs: ReplySurbsV1_1_19, pub reply_surbs: ReplySurbsV1_1_20,
} }
impl From<DebugConfigV1_1_19> for DebugConfig { impl From<DebugConfigV1_1_20> for DebugConfigV1_1_20_2 {
fn from(value: DebugConfigV1_1_19) -> Self { fn from(value: DebugConfigV1_1_20) -> Self {
DebugConfig { DebugConfigV1_1_20_2 {
traffic: value.traffic.into(), traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(), cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(), gateway_connection: value.gateway_connection.into(),
@@ -336,19 +337,3 @@ impl From<DebugConfigV1_1_19> for DebugConfig {
} }
} }
} }
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfigV1_1_19 {
fn default() -> Self {
DebugConfigV1_1_19 {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
@@ -0,0 +1,348 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection,
GatewayEndpointConfig, ReplySurbs, Topology, Traffic,
};
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20_2 {
pub client: ClientV1_1_20_2,
#[serde(default)]
pub debug: DebugConfigV1_1_20_2,
}
impl From<ConfigV1_1_20_2> for Config {
fn from(value: ConfigV1_1_20_2) -> Self {
Config {
client: value.client.into(),
debug: value.debug.into(),
}
}
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_20_2 {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
/// Address of the gateway owner to which the client should send messages.
pub gateway_owner: String,
/// Address of the gateway listener to which all client requests should be sent.
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
GatewayEndpointConfig {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_20_2 {
pub version: String,
pub id: String,
#[serde(default)]
pub disabled_credentials_mode: bool,
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
}
impl From<ClientV1_1_20_2> for Client {
fn from(value: ClientV1_1_20_2) -> Self {
Client {
version: value.version,
id: value.id,
disabled_credentials_mode: value.disabled_credentials_mode,
nyxd_urls: value.nyxd_urls,
nym_api_urls: value.nym_api_urls,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
pub disable_main_poisson_packet_distribution: bool,
pub primary_packet_size: PacketSize,
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl From<TrafficV1_1_20_2> for Traffic {
fn from(value: TrafficV1_1_20_2) -> Self {
Traffic {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.disable_main_poisson_packet_distribution,
primary_packet_size: value.primary_packet_size,
secondary_packet_size: value.secondary_packet_size,
packet_type: PacketType::Mix,
}
}
}
impl Default for TrafficV1_1_20_2 {
fn default() -> Self {
TrafficV1_1_20_2 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
pub cover_traffic_primary_size_ratio: f64,
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTrafficV1_1_20_2> for CoverTraffic {
fn from(value: CoverTrafficV1_1_20_2) -> Self {
CoverTraffic {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
}
}
}
impl Default for CoverTrafficV1_1_20_2 {
fn default() -> Self {
CoverTrafficV1_1_20_2 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_20_2> for GatewayConnection {
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV1_1_20_2 {
fn default() -> Self {
GatewayConnectionV1_1_20_2 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV1_1_20_2> for Acknowledgements {
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
Acknowledgements {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
}
}
}
impl Default for AcknowledgementsV1_1_20_2 {
fn default() -> Self {
AcknowledgementsV1_1_20_2 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
pub disable_refreshing: bool,
}
impl Default for TopologyV1_1_20_2 {
fn default() -> Self {
TopologyV1_1_20_2 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
}
}
}
impl From<TopologyV1_1_20_2> for Topology {
fn from(value: TopologyV1_1_20_2) -> Self {
Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_20_2 {
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
pub maximum_reply_surb_request_size: u32,
pub maximum_allowed_reply_surb_request_size: u32,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV1_1_20_2 {
fn default() -> Self {
ReplySurbsV1_1_20_2 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
}
}
}
impl From<ReplySurbsV1_1_20_2> for ReplySurbs {
fn from(value: ReplySurbsV1_1_20_2) -> Self {
ReplySurbs {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.maximum_reply_surb_age,
maximum_reply_key_age: value.maximum_reply_key_age,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_20_2 {
pub traffic: TrafficV1_1_20_2,
pub cover_traffic: CoverTrafficV1_1_20_2,
pub gateway_connection: GatewayConnectionV1_1_20_2,
pub acknowledgements: AcknowledgementsV1_1_20_2,
pub topology: TopologyV1_1_20_2,
pub reply_surbs: ReplySurbsV1_1_20_2,
}
impl From<DebugConfigV1_1_20_2> for DebugConfig {
fn from(value: DebugConfigV1_1_20_2) -> Self {
DebugConfig {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
acknowledgements: value.acknowledgements.into(),
topology: value.topology.into(),
reply_surbs: value.reply_surbs.into(),
}
}
}
+19
View File
@@ -50,6 +50,11 @@ pub enum ClientCoreError {
source: Box<dyn Error + Send + Sync>, source: Box<dyn Error + Send + Sync>,
}, },
#[error("experienced a failure with our gateway details storage: {source}")]
GatewayDetailsStoreError {
source: Box<dyn Error + Send + Sync>,
},
#[error("The gateway id is invalid - {0}")] #[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError), UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
@@ -97,6 +102,20 @@ pub enum ClientCoreError {
"This operation would have resulted in clients keys being overwritten without permission" "This operation would have resulted in clients keys being overwritten without permission"
)] )]
ForbiddenKeyOverwrite, ForbiddenKeyOverwrite,
#[error("gateway details are unavailable")]
UnavailableGatewayDetails {
source: Box<dyn Error + Send + Sync>,
},
#[error("gateway shared key is unavailable whilst we have full node information")]
UnavailableSharedKey,
#[error("attempted to obtain fresh gateway details whilst already knowing about one")]
UnexpectedGatewayDetails,
#[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")]
MismatchedGatewayDetails { gateway_id: String },
} }
/// Set of messages that the client can send to listeners via the task manager /// Set of messages that the client can send to listeners via the task manager
+9 -9
View File
@@ -44,18 +44,18 @@ const MEASUREMENTS: usize = 3;
const CONN_TIMEOUT: Duration = Duration::from_millis(1500); const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
const PING_TIMEOUT: Duration = Duration::from_millis(1000); const PING_TIMEOUT: Duration = Duration::from_millis(1000);
struct GatewayWithLatency { struct GatewayWithLatency<'a> {
gateway: gateway::Node, gateway: &'a gateway::Node,
latency: Duration, latency: Duration,
} }
impl GatewayWithLatency { impl<'a> GatewayWithLatency<'a> {
fn new(gateway: gateway::Node, latency: Duration) -> Self { fn new(gateway: &'a gateway::Node, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency } GatewayWithLatency { gateway, latency }
} }
} }
pub(super) async fn current_gateways<R: Rng>( pub async fn current_gateways<R: Rng>(
rng: &mut R, rng: &mut R,
nym_apis: &[Url], nym_apis: &[Url],
) -> Result<Vec<gateway::Node>, ClientCoreError> { ) -> Result<Vec<gateway::Node>, ClientCoreError> {
@@ -64,7 +64,7 @@ pub(super) async fn current_gateways<R: Rng>(
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone()); let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of gateways from: {}", nym_api); log::trace!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_cached_gateways().await?; let gateways = client.get_cached_gateways().await?;
let valid_gateways = gateways let valid_gateways = gateways
@@ -91,7 +91,7 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure) JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
} }
async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> { async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
let addr = gateway.clients_address(); let addr = gateway.clients_address();
trace!( trace!(
"establishing connection to {} ({addr})...", "establishing connection to {} ({addr})...",
@@ -156,7 +156,7 @@ async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, C
pub(super) async fn choose_gateway_by_latency<R: Rng>( pub(super) async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: Vec<gateway::Node>, gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> { ) -> Result<gateway::Node, ClientCoreError> {
info!("choosing gateway by latency..."); info!("choosing gateway by latency...");
@@ -189,7 +189,7 @@ pub(super) async fn choose_gateway_by_latency<R: Rng>(
pub(super) fn uniformly_random_gateway<R: Rng>( pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: Vec<gateway::Node>, gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> { ) -> Result<gateway::Node, ClientCoreError> {
gateways gateways
.choose(rng) .choose(rng)
+273 -145
View File
@@ -3,26 +3,82 @@
//! Collection of initialization steps used by client implementations //! Collection of initialization steps used by client implementations
use crate::client::base_client::storage::MixnetClientStorage; use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use crate::client::key_manager::persistence::KeyStore; use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::{KeyManager, ManagedKeys}; use crate::client::key_manager::ManagedKeys;
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway}; use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
use crate::{ use crate::{
config::{disk_persistence::keys_paths::ClientKeysPaths, Config, GatewayEndpointConfig}, config::{Config, GatewayEndpointConfig},
error::ClientCoreError, error::ClientCoreError,
}; };
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity}; use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use serde::Serialize; use serde::Serialize;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use url::Url; use url::Url;
mod helpers; pub mod helpers;
#[derive(Clone)] // TODO: rename to something better...
#[derive(Debug)]
pub struct InitialisationDetails {
pub gateway_details: GatewayEndpointConfig,
pub managed_keys: ManagedKeys,
}
impl InitialisationDetails {
pub fn new(gateway_details: GatewayEndpointConfig, managed_keys: ManagedKeys) -> Self {
InitialisationDetails {
gateway_details,
managed_keys,
}
}
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
if !loaded_details.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_details.details.gateway_id,
});
}
Ok(InitialisationDetails {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
})
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&self.gateway_details.gateway_id)?,
);
Ok(client_recipient)
}
}
#[derive(Debug, Clone)]
pub enum GatewaySetup { pub enum GatewaySetup {
/// The gateway specification MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new, random, gateway. /// Specifies usage of a new, random, gateway.
New { New {
/// Should the new gateway be selected based on latency. /// Should the new gateway be selected based on latency.
@@ -34,13 +90,13 @@ pub enum GatewaySetup {
}, },
Predefined { Predefined {
/// Full gateway configuration /// Full gateway configuration
config: GatewayEndpointConfig, details: PersistedGatewayDetails,
}, },
} }
impl From<GatewayEndpointConfig> for GatewaySetup { impl From<PersistedGatewayDetails> for GatewaySetup {
fn from(config: GatewayEndpointConfig) -> Self { fn from(details: PersistedGatewayDetails) -> Self {
GatewaySetup::Predefined { config } GatewaySetup::Predefined { details }
} }
} }
@@ -57,14 +113,11 @@ impl Default for GatewaySetup {
} }
impl GatewaySetup { impl GatewaySetup {
pub fn new( pub fn new_fresh(
full_config: Option<GatewayEndpointConfig>, gateway_identity: Option<String>,
gateway_identity: Option<IdentityKey>,
latency_based_selection: Option<bool>, latency_based_selection: Option<bool>,
) -> Self { ) -> Self {
if let Some(config) = full_config { if let Some(gateway_identity) = gateway_identity {
GatewaySetup::Predefined { config }
} else if let Some(gateway_identity) = gateway_identity {
GatewaySetup::Specified { gateway_identity } GatewaySetup::Specified { gateway_identity }
} else { } else {
GatewaySetup::New { GatewaySetup::New {
@@ -73,15 +126,22 @@ impl GatewaySetup {
} }
} }
pub async fn try_get_gateway_details( pub fn is_must_load(&self) -> bool {
self, matches!(self, GatewaySetup::MustLoad)
validator_servers: &[Url], }
pub fn has_full_details(&self) -> bool {
matches!(self, GatewaySetup::Predefined { .. }) || self.is_must_load()
}
pub async fn choose_gateway(
&self,
gateways: &[gateway::Node],
) -> Result<GatewayEndpointConfig, ClientCoreError> { ) -> Result<GatewayEndpointConfig, ClientCoreError> {
match self { match self {
GatewaySetup::New { by_latency } => { GatewaySetup::New { by_latency } => {
let mut rng = OsRng; let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?; if *by_latency {
if by_latency {
choose_gateway_by_latency(&mut rng, gateways).await choose_gateway_by_latency(&mut rng, gateways).await
} else { } else {
uniformly_random_gateway(&mut rng, gateways) uniformly_random_gateway(&mut rng, gateways)
@@ -89,20 +149,28 @@ impl GatewaySetup {
} }
.map(Into::into), .map(Into::into),
GatewaySetup::Specified { gateway_identity } => { GatewaySetup::Specified { gateway_identity } => {
let user_gateway = identity::PublicKey::from_base58_string(&gateway_identity) let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
gateways gateways
.into_iter() .iter()
.find(|gateway| gateway.identity_key == user_gateway) .find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string())) .ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
} }
.map(Into::into), .map(Into::into),
GatewaySetup::Predefined { config } => Ok(config), _ => Err(ClientCoreError::UnexpectedGatewayDetails),
} }
} }
pub async fn try_get_new_gateway_details(
&self,
validator_servers: &[Url],
) -> Result<GatewayEndpointConfig, ClientCoreError> {
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
self.choose_gateway(&gateways).await
}
} }
/// Struct describing the results of the client initialization procedure. /// Struct describing the results of the client initialization procedure.
@@ -117,14 +185,14 @@ pub struct InitResults {
} }
impl InitResults { impl InitResults {
pub fn new(config: &Config, address: &Recipient) -> Self { pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self { Self {
version: config.client.version.clone(), version: config.client.version.clone(),
id: config.client.id.clone(), id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(), identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(), encryption_key: address.encryption_key().to_base58_string(),
gateway_id: config.get_gateway_id(), gateway_id: gateway.gateway_id.clone(),
gateway_listener: config.get_gateway_listener(), gateway_listener: gateway.gateway_listener.clone(),
} }
} }
} }
@@ -140,38 +208,164 @@ impl Display for InitResults {
} }
} }
/// Recovers the already present gateway information or attempts to register with new gateway // helpers for error wrapping
/// and stores the newly obtained key async fn _store_gateway_details<D>(
pub async fn get_registered_gateway<S>( details_store: &D,
validator_servers: Vec<Url>, details: &PersistedGatewayDetails,
key_store: &S::KeyStore, ) -> Result<(), ClientCoreError>
setup: GatewaySetup,
overwrite_keys: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError>
where where
S: MixnetClientStorage, D: GatewayDetailsStore,
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static, D::StorageError: Send + Sync + 'static,
{
details_store
.store_gateway_details(details)
.await
.map_err(|source| ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
}
async fn _load_gateway_details<D>(
details_store: &D,
) -> Result<PersistedGatewayDetails, ClientCoreError>
where
D: GatewayDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.load_gateway_details()
.await
.map_err(|source| ClientCoreError::UnavailableGatewayDetails {
source: Box::new(source),
})
}
async fn _load_managed_keys<K>(key_store: &K) -> Result<ManagedKeys, ClientCoreError>
where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
ManagedKeys::try_load(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})
}
fn ensure_valid_details(
details: &PersistedGatewayDetails,
loaded_keys: &ManagedKeys,
) -> Result<(), ClientCoreError> {
if !details.verify(&loaded_keys.must_get_gateway_shared_key()) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
pub async fn setup_gateway_from<K, D>(
setup: &GatewaySetup,
key_store: &K,
details_store: &D,
overwrite_data: bool,
gateways: Option<&[gateway::Node]>,
) -> Result<InitialisationDetails, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{ {
let mut rng = OsRng; let mut rng = OsRng;
// try load keys // try load gateway details
let loaded_details = _load_gateway_details(details_store).await;
// try load keys and decide what to do based on the GatewaySetup
let mut managed_keys = match ManagedKeys::try_load(key_store).await { let mut managed_keys = match ManagedKeys::try_load(key_store).await {
Ok(_) => { Ok(loaded_keys) => {
// if we loaded something and we don't have full gateway details, check if we can overwrite the data match setup {
if let GatewaySetup::Predefined { config } = setup { GatewaySetup::MustLoad => {
// we already have defined gateway details AND a shared key, so nothing more for us to do // get EVERYTHING from the storage
return Ok(config); let details = loaded_details?;
} else if overwrite_keys { ensure_valid_details(&details, &loaded_keys)?;
ManagedKeys::generate_new(&mut rng)
} else { // no need to persist anything as we got everything from the storage
return Err(ClientCoreError::ForbiddenKeyOverwrite); return Ok(InitialisationDetails::new(details.into(), loaded_keys));
}
GatewaySetup::Predefined { details } => {
// we already have defined gateway details AND a shared key
ensure_valid_details(details, &loaded_keys)?;
// if nothing was stored or we're allowed to overwrite what's there, just persist the passed data
if overwrite_data || loaded_details.is_err() {
_store_gateway_details(details_store, details).await?;
}
return Ok(InitialisationDetails::new(
details.clone().into(),
loaded_keys,
));
}
GatewaySetup::Specified { gateway_identity } => {
// if that data was already stored...
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
if &existing_gateway.details.gateway_id != gateway_identity
&& !overwrite_data
{
// if our loaded details don't match requested value and we CANT overwrite it...
return Err(ClientCoreError::UnexpectedGatewayDetails);
} else if &existing_gateway.details.gateway_id == gateway_identity {
// if they do match up, just return it
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
));
}
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
GatewaySetup::New { .. } => {
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
));
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
} }
} }
Err(_) => ManagedKeys::generate_new(&mut rng), Err(_) => {
// if we failed to load the keys, ensure we didn't provide gateway details in some form
// (in that case we CAN'T generate new keys
if setup.has_full_details() {
return Err(ClientCoreError::UnavailableSharedKey);
}
ManagedKeys::generate_new(&mut rng)
}
}; };
// choose gateway // choose gateway
let gateway_details = setup.try_get_gateway_details(&validator_servers).await?; let gateway_details = setup.choose_gateway(gateways.unwrap_or_default()).await?;
// get our identity key // get our identity key
let our_identity = managed_keys.identity_keypair(); let our_identity = managed_keys.identity_keypair();
@@ -179,6 +373,9 @@ where
// Establish connection, authenticate and generate keys for talking with the gateway // Establish connection, authenticate and generate keys for talking with the gateway
let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?; let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?;
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
// persist gateway keys
managed_keys managed_keys
.deal_with_gateway_key(shared_keys, key_store) .deal_with_gateway_key(shared_keys, key_store)
.await .await
@@ -186,108 +383,39 @@ where
source: Box::new(source), source: Box::new(source),
})?; })?;
// TODO: here we should be probably persisting gateway details as opposed to returning them // persist gateway config
_store_gateway_details(details_store, &persisted_details).await?;
Ok(gateway_details) Ok(InitialisationDetails::new(
persisted_details.into(),
managed_keys,
))
} }
/// Convenience function for setting up the gateway for a client given a `Config`. Depending on the pub async fn setup_gateway<K, D>(
/// arguments given it will do the sensible thing. Either it will setup: &GatewaySetup,
/// key_store: &K,
/// a. Reuse existing gateway configuration from storage. details_store: &D,
/// b. Create a new gateway configuration but keep existing keys. This assumes that the caller overwrite_data: bool,
/// knows what they are doing and that the keys match the requested gateway. validator_servers: Option<&[Url]>,
/// c. Create a new gateway configuration with a newly registered gateway and keys. ) -> Result<InitialisationDetails, ClientCoreError>
pub async fn setup_gateway_from_config<KSt>(
key_store: &KSt,
register_gateway: bool,
user_chosen_gateway_id: Option<identity::PublicKey>,
config: &Config,
by_latency: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError>
where where
KSt: KeyStore, K: KeyStore,
<KSt as KeyStore>::StorageError: Send + Sync + 'static, D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{ {
// If we are not going to register gateway, and an explicitly chosen gateway is not passed in,
// load the existing configuration file
if !register_gateway && user_chosen_gateway_id.is_none() {
eprintln!("Not registering gateway, will reuse existing config and keys");
return Ok(config.client.gateway_endpoint.clone());
}
let gateway_setup = GatewaySetup::new(
None,
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(by_latency),
);
// Else, we proceed by querying the nym-api
let gateway = gateway_setup
.try_get_gateway_details(&config.get_nym_api_endpoints())
.await?;
log::debug!("Querying gateway gives: {:?}", gateway);
// If we are not registering, just return this and assume the caller has the keys already and
// wants to keep the,
if !register_gateway && user_chosen_gateway_id.is_some() {
eprintln!("Using gateway provided by user, keeping existing keys");
return Ok(gateway);
}
let mut rng = OsRng; let mut rng = OsRng;
let mut managed_keys = let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
crate::client::key_manager::ManagedKeys::load_or_generate(&mut rng, key_store).await;
// Create new keys and derive our identity setup_gateway_from(
let our_identity = managed_keys.identity_keypair(); setup,
key_store,
// Establish connection, authenticate and generate keys for talking with the gateway details_store,
eprintln!("Registering with new gateway"); overwrite_data,
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?; Some(&gateways),
managed_keys
.deal_with_gateway_key(shared_keys, key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
Ok(gateway)
}
/// Get the full client address from the client keys and the gateway identity
pub fn get_client_address(
key_manager: &KeyManager,
gateway_config: &GatewayEndpointConfig,
) -> Recipient {
Recipient::new(
*key_manager.identity_keypair().public_key(),
*key_manager.encryption_keypair().public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
) )
} .await
/// Get the client address by loading the keys from stored files.
// TODO: rethink that sucker
pub fn get_client_address_from_stored_ondisk_keys(
keys_paths: &ClientKeysPaths,
gateway_config: &GatewayEndpointConfig,
) -> Result<Recipient, ClientCoreError> {
let public_identity: identity::PublicKey =
nym_pemstore::load_key(&keys_paths.public_identity_key_file)?;
let public_encryption: encryption::PublicKey =
nym_pemstore::load_key(&keys_paths.public_encryption_key_file)?;
let client_recipient = Recipient::new(
public_identity,
public_encryption,
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&gateway_config.gateway_id)?,
);
Ok(client_recipient)
} }
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) { pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
@@ -724,7 +724,7 @@ impl<C, St> GatewayClient<C, St> {
Ok(()) Ok(())
} }
async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> { pub async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() { if !self.connection.is_established() {
self.establish_connection().await?; self.establish_connection().await?;
} }
@@ -738,6 +738,12 @@ impl<C, St> GatewayClient<C, St> {
Ok(()) Ok(())
} }
pub async fn disconnect(&mut self) -> Result<(), GatewayClientError> {
self.recover_socket_connection().await?;
self.connection = SocketState::NotConnected;
Ok(())
}
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError> pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
where where
C: DkgQueryClient + Send + Sync, C: DkgQueryClient + Send + Sync,
@@ -48,17 +48,21 @@ impl PacketRouter {
// data he takes the SURB-ACK and first hop address. // data he takes the SURB-ACK and first hop address.
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true // currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN; let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let outfox_ack_overhead =
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets { for received_packet in unwrapped_packets {
if received_packet.len() == PacketSize::AckPacket.plaintext_size() if received_packet.len() == PacketSize::AckPacket.plaintext_size()
|| received_packet.len() == PacketSize::OutfoxAckPacket.plaintext_size() // we don't know the real size of the payload, it could be anything <= 48 bytes
|| received_packet.len() <= PacketSize::OutfoxAckPacket.plaintext_size()
{ {
received_acks.push(received_packet); received_acks.push(received_packet);
} else if received_packet.len() } else if received_packet.len()
== PacketSize::RegularPacket.plaintext_size() - ack_overhead == PacketSize::RegularPacket.plaintext_size() - ack_overhead
|| received_packet.len() || received_packet.len()
== PacketSize::OutfoxRegularPacket.plaintext_size() - ack_overhead == PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead
|| received_packet.len() == PacketSize::OutfoxRegularPacket.size() - 6 || received_packet.len()
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
{ {
trace!("routing regular packet"); trace!("routing regular packet");
received_messages.push(received_packet); received_messages.push(received_packet);
@@ -29,6 +29,7 @@ log = { workspace = true }
url = { version = "2.2", features = ["serde"] } url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["sync", "time"] } tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = "0.3" futures = "0.3"
openssl = { version = "0.10", features = ["vendored"], optional = true }
nym-coconut-interface = { path = "../../coconut-interface" } nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" } nym-network-defaults = { path = "../../network-defaults" }
@@ -77,13 +78,14 @@ required-features = ["nyxd-client"]
nyxd-client = [ nyxd-client = [
"async-trait", "async-trait",
"cosmrs", "cosmrs",
"cosmwasm-std",
"cw3", "cw3",
"cw4", "cw4",
"prost",
"flate2", "flate2",
"sha2",
"itertools", "itertools",
"cosmwasm-std", "openssl",
"prost",
"sha2",
"signing" "signing"
] ]
signing = [ signing = [
@@ -1,12 +1,12 @@
use async_trait::async_trait; use async_trait::async_trait;
use cosmrs::AccountId; use cosmrs::AccountId;
use nym_contracts_common::ContractBuildInformation; use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
use nym_service_provider_directory_common::{ use nym_service_provider_directory_common::{
msg::QueryMsg as SpQueryMsg, msg::QueryMsg as SpQueryMsg,
response::{ response::{
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse, ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
}, },
NymAddress, ServiceId, ServiceInfo, NymAddress, Service, ServiceId,
}; };
use serde::Deserialize; use serde::Deserialize;
@@ -63,17 +63,14 @@ pub trait SpDirectoryQueryClient {
.await .await
} }
async fn get_all_services(&self) -> Result<Vec<ServiceInfo>, NyxdError> { async fn get_all_services(&self) -> Result<Vec<Service>, NyxdError> {
let mut services = Vec::new(); let mut services = Vec::new();
let mut start_after = None; let mut start_after = None;
loop { loop {
let mut paged_response = self.get_services_paged(start_after.take(), None).await?; let mut paged_response = self.get_services_paged(start_after.take(), None).await?;
let last_id = paged_response.services.last().map(|serv| serv.service_id);
services.append(&mut paged_response.services); services.append(&mut paged_response.services);
if let Some(start_after_res) = last_id { if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res) start_after = Some(start_after_res)
} else { } else {
break; break;
@@ -82,6 +79,13 @@ pub trait SpDirectoryQueryClient {
Ok(services) Ok(services)
} }
async fn get_service_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::SigningNonce {
address: address.to_string(),
})
.await
}
} }
#[async_trait] #[async_trait]
@@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait; use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_service_provider_directory_common::{ use nym_service_provider_directory_common::{
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceId, ServiceType, msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
}; };
use crate::nyxd::{ use crate::nyxd::{
@@ -22,16 +23,16 @@ pub trait SpDirectorySigningClient {
async fn announce_service_provider( async fn announce_service_provider(
&self, &self,
nym_address: NymAddress, service: ServiceDetails,
service_type: ServiceType, owner_signature: MessageSignature,
deposit: Coin, deposit: Coin,
fee: Option<Fee>, fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> { ) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract( self.execute_service_provider_directory_contract(
fee, fee,
SpExecuteMsg::Announce { SpExecuteMsg::Announce {
nym_address, service,
service_type, owner_signature,
}, },
vec![deposit], vec![deposit],
) )
+1
View File
@@ -40,3 +40,4 @@ nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" } nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" } nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" } nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
nym-sphinx = { path = "../../common/nymsphinx" }
@@ -14,6 +14,7 @@ pub struct Mixnet {
pub command: MixnetCommands, pub command: MixnetCommands,
} }
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
pub enum MixnetCommands { pub enum MixnetCommands {
/// Query the mixnet directory /// Query the mixnet directory
@@ -15,6 +15,7 @@ pub struct MixnetOperators {
pub command: MixnetOperatorsCommands, pub command: MixnetOperatorsCommands,
} }
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
pub enum MixnetOperatorsCommands { pub enum MixnetOperatorsCommands {
/// Manage your mixnode /// Manage your mixnode
@@ -1,6 +1,7 @@
use clap::Parser; use clap::Parser;
use log::info; use log::info;
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceType}; use nym_contracts_common::signing::MessageSignature;
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceDetails, ServiceType};
use nym_validator_client::nyxd::traits::SpDirectorySigningClient; use nym_validator_client::nyxd::traits::SpDirectorySigningClient;
use crate::context::SigningClient; use crate::context::SigningClient;
@@ -10,9 +11,15 @@ pub struct Args {
#[clap(long)] #[clap(long)]
pub nym_address: String, pub nym_address: String,
#[clap(long)]
pub signature: MessageSignature,
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym') /// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
#[clap(long)] #[clap(long)]
pub deposit: u128, pub deposit: u128,
#[clap(long)]
pub identity_key: String,
} }
pub async fn announce(args: Args, client: SigningClient) { pub async fn announce(args: Args, client: SigningClient) {
@@ -20,12 +27,17 @@ pub async fn announce(args: Args, client: SigningClient) {
let nym_address = NymAddress::Address(args.nym_address); let nym_address = NymAddress::Address(args.nym_address);
let service_type = ServiceType::NetworkRequester; let service_type = ServiceType::NetworkRequester;
let service = ServiceDetails {
nym_address,
service_type,
identity_key: args.identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str(); let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.deposit, denom); let deposit = Coin::new(args.deposit, denom);
let res = client let res = client
.announce_service_provider(nym_address, service_type, deposit.into(), None) .announce_service_provider(service, args.signature, deposit.into(), None)
.await .await
.expect("Failed to announce service provider"); .expect("Failed to announce service provider");
@@ -0,0 +1,61 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
context::SigningClient,
utils::{account_id_to_cw_addr, DataWrapper},
};
use clap::Parser;
use cosmwasm_std::Coin;
use nym_bin_common::output_format::OutputFormat;
use nym_service_provider_directory_common::{
signing_types::construct_service_provider_announce_sign_payload, NymAddress,
ServiceType::NetworkRequester,
};
use nym_sphinx::addressing::clients::Recipient;
use nym_validator_client::nyxd::traits::SpDirectoryQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub nym_address: Recipient,
#[clap(long)]
pub amount: u128,
#[clap(long)]
pub identity_key: String,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
pub async fn create_payload(args: Args, client: SigningClient) {
let service = nym_service_provider_directory_common::ServiceDetails {
nym_address: NymAddress::new(&args.nym_address.to_string()),
service_type: NetworkRequester,
identity_key: args.identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.amount, denom);
let nonce = match client.get_service_signing_nonce(client.address()).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
client.address()
);
return;
}
};
let address = account_id_to_cw_addr(client.address());
let payload =
construct_service_provider_announce_sign_payload(nonce, address, deposit, service);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -1,6 +1,7 @@
use clap::{Args, Subcommand}; use clap::{Args, Subcommand};
pub mod announce; pub mod announce;
pub mod announce_sign_payload;
pub mod delete; pub mod delete;
#[derive(Debug, Args)] #[derive(Debug, Args)]
@@ -10,10 +11,13 @@ pub struct MixnetOperatorsService {
pub command: MixnetOperatorsServiceCommands, pub command: MixnetOperatorsServiceCommands,
} }
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
pub enum MixnetOperatorsServiceCommands { pub enum MixnetOperatorsServiceCommands {
/// Announce service provider to the world /// Announce service provider to the world
Announce(announce::Args), Announce(announce::Args),
/// Delete entry for service provider from the directory /// Delete entry for service provider from the directory
Delete(delete::Args), Delete(delete::Args),
/// Create base58-encoded payload required for producing valid announce signature.
CreateServiceAnnounceSignPayload(announce_sign_payload::Args),
} }
@@ -37,7 +37,7 @@ pub async fn query(args: Args, client: &QueryClientWithNyxd) {
for service in res.services { for service in res.services {
table.add_row(vec![ table.add_row(vec![
service.service_id.to_string(), service.service_id.to_string(),
service.service.announcer.to_string(), service.announcer.to_string(),
service.service.service_type.to_string(), service.service.service_type.to_string(),
service.service.nym_address.to_string(), service.service.nym_address.to_string(),
]); ]);
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-contracts-common" name = "nym-contracts-common"
version = "0.4.0" version = "0.5.0"
description = "Common library for Nym cosmwasm contracts" description = "Common library for Nym cosmwasm contracts"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -11,6 +11,9 @@ use std::ops::Mul;
use std::str::FromStr; use std::str::FromStr;
use thiserror::Error; use thiserror::Error;
pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str;
pub fn truncate_decimal(amount: Decimal) -> Uint128 { pub fn truncate_decimal(amount: Decimal) -> Uint128 {
amount * Uint128::new(1) amount * Uint128::new(1)
} }
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-mixnet-contract-common" name = "nym-mixnet-contract-common"
version = "0.5.0" version = "0.6.0"
description = "Common library for the Nym mixnet contract" description = "Common library for the Nym mixnet contract"
rust-version = "1.62" rust-version = "1.62"
edition = { workspace = true } edition = { workspace = true }
@@ -15,7 +15,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1" serde_repr = "0.1"
schemars = "0.8" schemars = "0.8"
thiserror = "1.0" thiserror = "1.0"
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" } contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0 # use 0.4.1 as that's the version used by cosmwasm-std 1.0.0
# (and ideally we don't want to pull the same dependency twice) # (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=0.4.1" serde-json-wasm = "=0.4.1"
@@ -4,6 +4,7 @@
use crate::error::MixnetContractError; use crate::error::MixnetContractError;
use crate::families::{Family, FamilyHead}; use crate::families::{Family, FamilyHead};
use crate::{Layer, RewardedSetNodeStatus}; use crate::{Layer, RewardedSetNodeStatus};
use contracts_common::IdentityKey;
use cosmwasm_std::Addr; use cosmwasm_std::Addr;
use cosmwasm_std::Coin; use cosmwasm_std::Coin;
use schemars::JsonSchema; use schemars::JsonSchema;
@@ -11,8 +12,6 @@ use serde::{Deserialize, Serialize};
use std::ops::Index; use std::ops::Index;
// type aliases for better reasoning about available data // type aliases for better reasoning about available data
pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str;
pub type SphinxKey = String; pub type SphinxKey = String;
pub type SphinxKeyRef<'a> = &'a str; pub type SphinxKeyRef<'a> = &'a str;
pub type EpochId = u32; pub type EpochId = u32;
@@ -7,5 +7,9 @@ edition = "2021"
[dependencies] [dependencies]
cosmwasm-std = { workspace = true } cosmwasm-std = { workspace = true }
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
schemars = "0.8" schemars = "0.8"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
cw-utils = { workspace = true }
cw-controllers = { workspace = true }
@@ -1,10 +1,12 @@
use cosmwasm_std::{Addr, StdError}; use cosmwasm_std::{Addr, StdError};
use cw_controllers::AdminError; use cw_controllers::AdminError;
use nym_service_provider_directory_common::{NymAddress, ServiceId}; use nym_contracts_common::signing::verifier::ApiVerifierError;
use thiserror::Error; use thiserror::Error;
use crate::{NymAddress, ServiceId};
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
pub enum ContractError { pub enum SpContractError {
#[error("{0}")] #[error("{0}")]
Std(#[from] StdError), Std(#[from] StdError),
@@ -46,6 +48,21 @@ pub enum ContractError {
value: String, value: String,
error_message: String, error_message: String,
}, },
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
MalformedEd25519IdentityKey(String),
#[error("Failed to recover ed25519 signature from its base58 representation - {0}")]
MalformedEd25519Signature(String),
#[error("Provided ed25519 signature did not verify correctly")]
InvalidEd25519Signature,
#[error("failed to verify message signature: {source}")]
SignatureVerificationFailure {
#[from]
source: ApiVerifierError,
},
} }
pub(crate) type Result<T, E = ContractError> = std::result::Result<T, E>; pub type Result<T, E = SpContractError> = std::result::Result<T, E>;
@@ -39,16 +39,16 @@ pub fn new_announce_event(service_id: ServiceId, service: Service) -> Event {
Event::new(ServiceProviderEventType::Announce) Event::new(ServiceProviderEventType::Announce)
.add_attribute(ACTION, ServiceProviderEventType::Announce) .add_attribute(ACTION, ServiceProviderEventType::Announce)
.add_attribute(SERVICE_ID, service_id.to_string()) .add_attribute(SERVICE_ID, service_id.to_string())
.add_attribute(SERVICE_TYPE, service.service_type.to_string()) .add_attribute(SERVICE_TYPE, service.service.service_type.to_string())
.add_attribute(NYM_ADDRESS, service.nym_address.to_string()) .add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
.add_attribute(OWNER, service.announcer.to_string()) .add_attribute(OWNER, service.announcer.to_string())
} }
pub fn new_delete_id_event(service_id: ServiceId, service: Service) -> Event { pub fn new_delete_id_event(service: Service) -> Event {
Event::new(ServiceProviderEventType::DeleteId) Event::new(ServiceProviderEventType::DeleteId)
.add_attribute(ACTION, ServiceProviderEventType::DeleteId) .add_attribute(ACTION, ServiceProviderEventType::DeleteId)
.add_attribute(SERVICE_ID, service_id.to_string()) .add_attribute(SERVICE_ID, service.service_id.to_string())
.add_attribute(NYM_ADDRESS, service.nym_address.to_string()) .add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
} }
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event { pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
@@ -1,6 +1,8 @@
pub mod error;
pub mod events; pub mod events;
pub mod msg; pub mod msg;
pub mod response; pub mod response;
pub mod signing_types;
pub mod types; pub mod types;
// Re-export all types at the top-level // Re-export all types at the top-level
@@ -1,5 +1,6 @@
use crate::{NymAddress, ServiceId, ServiceType}; use crate::{NymAddress, ServiceDetails, ServiceId};
use cosmwasm_std::Coin; use cosmwasm_std::Coin;
use nym_contracts_common::signing::MessageSignature;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -22,8 +23,8 @@ pub struct MigrateMsg {}
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ExecuteMsg { pub enum ExecuteMsg {
Announce { Announce {
nym_address: NymAddress, service: ServiceDetails,
service_type: ServiceType, owner_signature: MessageSignature,
}, },
DeleteId { DeleteId {
service_id: ServiceId, service_id: ServiceId,
@@ -44,9 +45,12 @@ impl ExecuteMsg {
pub fn default_memo(&self) -> String { pub fn default_memo(&self) -> String {
match self { match self {
ExecuteMsg::Announce { ExecuteMsg::Announce {
nym_address, service,
service_type, owner_signature: _,
} => format!("announcing {nym_address} as type {service_type}"), } => format!(
"announcing {} as type {}",
service.nym_address, service.service_type
),
ExecuteMsg::DeleteId { service_id } => { ExecuteMsg::DeleteId { service_id } => {
format!("deleting service with service id {service_id}") format!("deleting service with service id {service_id}")
} }
@@ -76,6 +80,9 @@ pub enum QueryMsg {
limit: Option<u32>, limit: Option<u32>,
start_after: Option<ServiceId>, start_after: Option<ServiceId>,
}, },
SigningNonce {
address: String,
},
Config {}, Config {},
GetContractVersion {}, GetContractVersion {},
#[serde(rename = "get_cw2_contract_version")] #[serde(rename = "get_cw2_contract_version")]
@@ -1,4 +1,4 @@
use crate::{msg::ExecuteMsg, Service, ServiceId, ServiceInfo}; use crate::{Service, ServiceId};
use cosmwasm_std::Coin; use cosmwasm_std::Coin;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -13,22 +13,17 @@ pub struct ServiceInfoResponse {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct ServicesListResponse { pub struct ServicesListResponse {
pub services: Vec<ServiceInfo>, pub services: Vec<Service>,
} }
impl ServicesListResponse { impl ServicesListResponse {
pub fn new(services: Vec<(ServiceId, Service)>) -> ServicesListResponse { pub fn new(services: Vec<Service>) -> ServicesListResponse {
ServicesListResponse { ServicesListResponse { services }
services: services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect(),
}
} }
} }
impl From<&[ServiceInfo]> for ServicesListResponse { impl From<&[Service]> for ServicesListResponse {
fn from(services: &[ServiceInfo]) -> Self { fn from(services: &[Service]) -> Self {
Self { Self {
services: services.to_vec(), services: services.to_vec(),
} }
@@ -38,21 +33,17 @@ impl From<&[ServiceInfo]> for ServicesListResponse {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct PagedServicesListResponse { pub struct PagedServicesListResponse {
pub services: Vec<ServiceInfo>, pub services: Vec<Service>,
pub per_page: usize, pub per_page: usize,
pub start_next_after: Option<ServiceId>, pub start_next_after: Option<ServiceId>,
} }
impl PagedServicesListResponse { impl PagedServicesListResponse {
pub fn new( pub fn new(
services: Vec<(ServiceId, Service)>, services: Vec<Service>,
per_page: usize, per_page: usize,
start_next_after: Option<ServiceId>, start_next_after: Option<ServiceId>,
) -> PagedServicesListResponse { ) -> PagedServicesListResponse {
let services = services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect();
PagedServicesListResponse { PagedServicesListResponse {
services, services,
per_page, per_page,
@@ -66,12 +57,3 @@ impl PagedServicesListResponse {
pub struct ConfigResponse { pub struct ConfigResponse {
pub deposit_required: Coin, pub deposit_required: Coin,
} }
impl From<Service> for ExecuteMsg {
fn from(service: Service) -> Self {
ExecuteMsg::Announce {
nym_address: service.nym_address,
service_type: service.service_type,
}
}
}
@@ -0,0 +1,33 @@
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::signing::{
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
use serde::Serialize;
use crate::ServiceDetails;
pub type SignableServiceProviderAnnounceMsg =
SignableMessage<ContractMessageContent<ServiceProviderAnnounce>>;
#[derive(Serialize)]
pub struct ServiceProviderAnnounce {
service: ServiceDetails,
}
impl SigningPurpose for ServiceProviderAnnounce {
fn message_type() -> MessageType {
MessageType::new("service-provider-announce")
}
}
pub fn construct_service_provider_announce_sign_payload(
nonce: Nonce,
sender: Addr,
deposit: Coin,
service: ServiceDetails,
) -> SignableServiceProviderAnnounceMsg {
let payload = ServiceProviderAnnounce { service };
let proxy = None;
let content = ContractMessageContent::new(sender, proxy, vec![deposit], payload);
SignableMessage::new(nonce, content)
}
@@ -1,6 +1,7 @@
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use cosmwasm_std::{Addr, Coin}; use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::IdentityKey;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -9,11 +10,11 @@ pub type ServiceId = u32;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)] #[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct Service { pub struct Service {
/// The address of the service. /// Unique id assigned to the anounced service.
pub nym_address: NymAddress, pub service_id: ServiceId,
/// The service type. /// The announced service.
pub service_type: ServiceType, pub service: ServiceDetails,
/// Service owner. /// Address of the service owner.
pub announcer: Addr, pub announcer: Addr,
/// Block height at which the service was added. /// Block height at which the service was added.
pub block_height: u64, pub block_height: u64,
@@ -21,6 +22,16 @@ pub struct Service {
pub deposit: Coin, pub deposit: Coin,
} }
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct ServiceDetails {
/// The address of the service.
pub nym_address: NymAddress,
/// The service type.
pub service_type: ServiceType,
/// The identity key of the service.
pub identity_key: IdentityKey,
}
/// The types of addresses supported. /// The types of addresses supported.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)] #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@@ -28,7 +39,7 @@ pub enum NymAddress {
/// String representation of a nym address, which is of the form /// String representation of a nym address, which is of the form
/// client_id.client_enc@gateway_id. /// client_id.client_enc@gateway_id.
Address(String), Address(String),
// For the future when we have a nym-dns contract // String name that can looked up in the nym-name-service contract (once it exists)
//Name(String), //Name(String),
} }
@@ -41,6 +52,7 @@ impl NymAddress {
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
match self { match self {
NymAddress::Address(address) => address, NymAddress::Address(address) => address,
//NymAddress::Name(name) => name,
} }
} }
} }
@@ -66,19 +78,3 @@ impl std::fmt::Display for ServiceType {
write!(f, "{service_type}") write!(f, "{service_type}")
} }
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct ServiceInfo {
pub service_id: ServiceId,
pub service: Service,
}
impl ServiceInfo {
pub fn new(service_id: ServiceId, service: Service) -> Self {
Self {
service_id,
service,
}
}
}
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-vesting-contract-common" name = "nym-vesting-contract-common"
version = "0.6.0" version = "0.7.0"
description = "Common library for the Nym vesting contract" description = "Common library for the Nym vesting contract"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -9,8 +9,8 @@ repository = { workspace = true }
[dependencies] [dependencies]
cosmwasm-std = { workspace = true } cosmwasm-std = { workspace = true }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.5.0" } mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" } contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
schemars = "0.8" schemars = "0.8"
ts-rs = {version = "6.1.2", optional = true} ts-rs = {version = "6.1.2", optional = true}
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-crypto" name = "nym-crypto"
version = "0.3.0" version = "0.4.0"
description = "Crypto library for the nym mixnet" description = "Crypto library for the nym mixnet"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -28,7 +28,7 @@ zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
# internal # internal
nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0" } nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0" }
nym-pemstore = { path = "../../common/pemstore", version = "0.2.0" } nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
[dev-dependencies] [dev-dependencies]
rand_chacha = "0.2" rand_chacha = "0.2"
+14 -14
View File
@@ -19,20 +19,20 @@ macro_rules! measure {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] { if #[cfg(feature = "cpucycles")] {
let start_cycles = $crate::cpu_cycles(); let start_cycles = $crate::cpu_cycles();
// if the block needs to return something, we can return it // if the block needs to return something, we can return it
let r = $x; let r = $x;
let end_cycles = $crate::cpu_cycles(); let end_cycles = $crate::cpu_cycles();
let name = if let Some(meta) = tracing::Span::current().metadata() { let name = if let Some(meta) = tracing::Span::current().metadata() {
meta.name() meta.name()
} else { } else {
"measure" "measure"
}; };
match (start_cycles, end_cycles) { match (start_cycles, end_cycles) {
(Ok(start), Ok(end)) => info!("{} cpucycles: {}", name, end - start), (Ok(start), Ok(end)) => log::trace!("{} cpucycles: {}", name, end - start),
(Err(e), _) => error!("{e}"), (Err(e), _) => error!("{e}"),
(_, Err(e)) => error!("{e}"), (_, Err(e)) => error!("{e}"),
} }
r r
} else { } else {
$x $x
} }
@@ -3,7 +3,7 @@
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError; use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError; use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
use nym_sphinx_types::{NymPacketError, SphinxError}; use nym_sphinx_types::{NymPacketError, OutfoxError, SphinxError};
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -25,4 +25,7 @@ pub enum MixProcessingError {
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")] #[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
ReceivedOldTypeVpnPacket, ReceivedOldTypeVpnPacket,
#[error("failed to process received outfox packet: {0}")]
OutfoxProcessingError(#[from] OutfoxError),
} }
@@ -20,12 +20,14 @@ use tracing::instrument;
type ForwardAck = MixPacket; type ForwardAck = MixPacket;
#[derive(Debug)]
pub struct ProcessedFinalHop { pub struct ProcessedFinalHop {
pub destination: DestinationAddressBytes, pub destination: DestinationAddressBytes,
pub forward_ack: Option<ForwardAck>, pub forward_ack: Option<ForwardAck>,
pub message: Vec<u8>, pub message: Vec<u8>,
} }
#[derive(Debug)]
pub enum MixProcessingResult { pub enum MixProcessingResult {
/// Contains unwrapped data that should first get delayed before being sent to next hop. /// Contains unwrapped data that should first get delayed before being sent to next hop.
ForwardHop(MixPacket, Option<SphinxDelay>), ForwardHop(MixPacket, Option<SphinxDelay>),
@@ -141,7 +143,7 @@ impl SphinxPacketProcessor {
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) { match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
Ok((first_hop, packet)) => (first_hop, packet), Ok((first_hop, packet)) => (first_hop, packet),
Err(err) => { Err(err) => {
debug!("Failed to recover first hop from ack data: {err}"); info!("Failed to recover first hop from ack data: {err}");
return Err(err.into()); return Err(err.into());
} }
}; };
@@ -205,7 +207,7 @@ impl SphinxPacketProcessor {
if packet.is_final_hop() { if packet.is_final_hop() {
self.process_final_hop( self.process_final_hop(
DestinationAddressBytes::from_bytes(next_address), DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext().to_vec(), packet.recover_plaintext()?.to_vec(),
packet_size, packet_size,
packet_type, packet_type,
) )
@@ -239,7 +241,14 @@ impl SphinxPacketProcessor {
// for forward packets, extract next hop and set delay (but do NOT delay here) // for forward packets, extract next hop and set delay (but do NOT delay here)
// for final packets, extract SURBAck // for final packets, extract SURBAck
self.perform_final_processing(processed_packet, packet_size, packet_type) let final_processing_result =
self.perform_final_processing(processed_packet, packet_size, packet_type);
if final_processing_result.is_err() {
error!("{:?}", final_processing_result)
}
final_processing_result
}) })
} }
} }
+2 -2
View File
@@ -25,12 +25,12 @@ nym-sphinx-types = { path = "types" }
# those dependencies are due to intriducing preparer and receiver. Perpaphs that indicates they should be moved # those dependencies are due to intriducing preparer and receiver. Perpaphs that indicates they should be moved
# to separate crate? # to separate crate?
nym-crypto = { path = "../crypto", version = "0.3.0" } nym-crypto = { path = "../crypto", version = "0.4.0" }
nym-topology = { path = "../topology" } nym-topology = { path = "../topology" }
[dev-dependencies] [dev-dependencies]
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-crypto = { path = "../crypto", version = "0.3.0", features = ["asymmetric"] } nym-crypto = { path = "../crypto", version = "0.4.0", features = ["asymmetric"] }
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require # do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2 # net2 via tokio-util -> tokio -> mio -> net2
+27 -10
View File
@@ -91,6 +91,7 @@ where
>(rng, full_address.encryption_key()); >(rng, full_address.encryption_key());
let public_key_bytes = ephemeral_keypair.public_key().to_bytes(); let public_key_bytes = ephemeral_keypair.public_key().to_bytes();
let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len(); let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len();
let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD
@@ -122,19 +123,35 @@ where
let delays = delays::generate_from_average_duration(route.len(), average_packet_delay); let delays = delays::generate_from_average_duration(route.len(), average_packet_delay);
let destination = full_address.as_sphinx_destination(); let destination = full_address.as_sphinx_destination();
// once merged, that's an easy rng injection point for sphinx packets : )
let packet = NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?;
let first_hop_address = let first_hop_address =
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap(); NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
Ok(MixPacket::new(first_hop_address, packet, PacketType::Mix)) // once merged, that's an easy rng injection point for sphinx packets : )
let packet = match packet_type {
PacketType::Mix => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
PacketType::Outfox => NymPacket::outfox_build(
packet_payload,
&route,
&destination,
Some(packet_size.plaintext_size()),
)?,
};
Ok(MixPacket::new(first_hop_address, packet, packet_type))
} }
/// Helper function used to determine if given message represents a loop cover message. /// Helper function used to determine if given message represents a loop cover message.
+1 -1
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "nym-pemstore" name = "nym-pemstore"
description = "Store private-public keypairs in PEM format" description = "Store private-public keypairs in PEM format"
version = "0.2.0" version = "0.3.0"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
+4 -2
View File
@@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::str::FromStr; use std::str::FromStr;
pub mod old_config_v1_1_20_2;
pub use nym_service_providers_common::interface::ProviderInterfaceVersion; pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion; pub use nym_socks5_requests::Socks5ProtocolVersion;
@@ -25,9 +27,9 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self { pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id), base: BaseClientConfig::new(id, version),
socks5: Socks5::new(provider_mix_address), socks5: Socks5::new(provider_mix_address),
} }
} }
@@ -0,0 +1,87 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseClientConfigV1_1_20_2;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use crate::config::{Config, Socks5, Socks5Debug};
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20_2 {
#[serde(flatten)]
pub base: BaseClientConfigV1_1_20_2,
pub socks5: Socks5V1_1_20_2,
}
impl From<ConfigV1_1_20_2> for Config {
fn from(value: ConfigV1_1_20_2) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5.into(),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5V1_1_20_2 {
pub listening_port: u16,
pub provider_mix_address: String,
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
pub provider_interface_version: ProviderInterfaceVersion,
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
pub socks5_protocol_version: Socks5ProtocolVersion,
#[serde(default)]
pub send_anonymously: bool,
#[serde(default)]
pub socks5_debug: Socks5DebugV1_1_20_2,
}
impl From<Socks5V1_1_20_2> for Socks5 {
fn from(value: Socks5V1_1_20_2) -> Self {
Socks5 {
listening_port: value.listening_port,
provider_mix_address: value.provider_mix_address,
provider_interface_version: value.provider_interface_version,
socks5_protocol_version: value.socks5_protocol_version,
send_anonymously: value.send_anonymously,
socks5_debug: value.socks5_debug.into(),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5DebugV1_1_20_2 {
/// Number of reply SURBs attached to each `Request::Connect` message.
pub connection_start_surbs: u32,
/// Number of reply SURBs attached to each `Request::Send` message.
pub per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_20_2> for Socks5Debug {
fn from(value: Socks5DebugV1_1_20_2) -> Self {
Socks5Debug {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
}
}
impl Default for Socks5DebugV1_1_20_2 {
fn default() -> Self {
Socks5DebugV1_1_20_2 {
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
}
}
}
+23 -16
View File
@@ -10,13 +10,16 @@ use crate::socks::{
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::storage::gateway_details::GatewayDetailsStore;
use nym_client_core::client::base_client::storage::MixnetClientStorage; use nym_client_core::client::base_client::storage::MixnetClientStorage;
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
use nym_client_core::client::key_manager::persistence::KeyStore; use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::replies::reply_storage::ReplyStorageBackend; use nym_client_core::client::replies::reply_storage::ReplyStorageBackend;
use nym_client_core::config::DebugConfig; use nym_client_core::config::DebugConfig;
use nym_client_core::init::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
@@ -51,6 +54,8 @@ pub struct NymClient<S> {
config: Config, config: Config,
storage: S, storage: S,
setup_method: GatewaySetup,
} }
impl<S> NymClient<S> impl<S> NymClient<S>
@@ -59,10 +64,20 @@ where
S::ReplyStore: Send + Sync, S::ReplyStore: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send, <S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync, <S::KeyStore as KeyStore>::StorageError: Send + Sync,
{ {
pub fn new(config: Config, storage: S) -> Self { pub fn new(config: Config, storage: S) -> Self {
NymClient { config, storage } NymClient {
config,
storage,
setup_method: GatewaySetup::MustLoad,
}
}
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
self
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -187,24 +202,16 @@ where
} }
pub async fn start(self) -> Result<StartedSocks5Client, Socks5ClientCoreError> { pub async fn start(self) -> Result<StartedSocks5Client, Socks5ClientCoreError> {
let (key_store, reply_storage_backend, credential_store) = self.storage.into_split(); // don't create dkg client for the bandwidth controller if credentials are disabled
let dkg_query_client = if self.config.base.client.disabled_credentials_mode {
// don't create bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.base.client.disabled_credentials_mode {
None None
} else { } else {
Some(non_wasm_helpers::create_bandwidth_controller( Some(default_query_dkg_client_from_config(&self.config.base))
&self.config.base,
credential_store,
))
}; };
let base_builder = BaseClientBuilder::<_, S>::new_from_base_config( let base_builder =
&self.config.base, BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client)
key_store, .with_gateway_setup(self.setup_method);
bandwidth_controller,
reply_storage_backend,
);
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 mut started_client = base_builder.start_base().await?;
@@ -428,18 +428,14 @@ impl SocksClient {
Some(self.lane_queue_lengths.clone()), Some(self.lane_queue_lengths.clone()),
self.shutdown_listener.clone(), self.shutdown_listener.clone(),
) )
.run(move |conn_id, read_data, socket_closed| { .run(move |socket_data| {
let provider_request = Socks5Request::new_send( let lane = TransmissionLane::ConnectionId(socket_data.header.connection_id);
request_version.provider_protocol, let provider_request =
conn_id, Socks5Request::new_send(request_version.provider_protocol, socket_data);
read_data,
socket_closed,
);
let provider_message = Socks5ProviderRequest::new_provider_data( let provider_message = Socks5ProviderRequest::new_provider_data(
request_version.provider_interface, request_version.provider_interface,
provider_request, provider_request,
); );
let lane = TransmissionLane::ConnectionId(conn_id);
if anonymous { if anonymous {
InputMessage::new_anonymous( InputMessage::new_anonymous(
recipient, recipient,
@@ -1,19 +1,20 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Socks5ClientCoreError;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_client_core::client::received_buffer::ReconstructedMessagesReceiver; use nym_client_core::client::received_buffer::ReconstructedMessagesReceiver;
use nym_client_core::client::received_buffer::{ use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReceivedBufferMessage, ReceivedBufferRequestSender,
}; };
use nym_service_providers_common::interface::{ControlResponse, ResponseContent}; use nym_service_providers_common::interface::{ControlResponse, ResponseContent};
use nym_socks5_proxy_helpers::connection_controller::ControllerSender; use nym_socks5_proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent}; use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
use nym_sphinx::receiver::ReconstructedMessage; use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::TaskClient; use nym_task::TaskClient;
use crate::error::Socks5ClientCoreError;
pub(crate) struct MixnetResponseListener { pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender, buffer_requester: ReceivedBufferRequestSender,
mix_response_receiver: ReconstructedMessagesReceiver, mix_response_receiver: ReconstructedMessagesReceiver,
@@ -79,12 +80,20 @@ impl MixnetResponseListener {
); );
Err(err_response.into()) Err(err_response.into())
} }
Socks5ResponseContent::NetworkData(response) => { Socks5ResponseContent::NetworkData { content } => {
self.controller_sender self.controller_sender
.unbounded_send(response.into()) .unbounded_send(ControllerCommand::new_send(content))
.unwrap(); .unwrap();
Ok(()) Ok(())
} }
Socks5ResponseContent::Query(response) => {
error!("received a query response which we don't know how to handle yet!");
error!("got: {:?}", response);
// I guess we'd need another channel here to forward those to where they need to go
Ok(())
}
} }
} }
+172 -142
View File
@@ -1,6 +1,21 @@
use crate::message::OrderedMessage; // Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use log::*; use log::*;
use std::collections::HashMap; use std::collections::BTreeMap;
use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum OrderedMessageError {
#[error("received message with sequence number {received}, which is way higher than our current {current}")]
MessageSequenceTooLarge { current: u64, received: u64 },
#[error("received message with sequence number {received}, while we're already at {current}!")]
MessageAlreadyReconstructed { current: u64, received: u64 },
#[error("attempted to overwrite message at sequence {received}")]
AttemptedToOverwriteSequence { received: u64 },
}
/// Stores messages and emits them in order. /// Stores messages and emits them in order.
/// ///
@@ -9,36 +24,58 @@ use std::collections::HashMap;
/// to fill up with the full sequence. /// to fill up with the full sequence.
#[derive(Debug)] #[derive(Debug)]
pub struct OrderedMessageBuffer { pub struct OrderedMessageBuffer {
next_index: u64, next_sequence: u64,
messages: HashMap<u64, OrderedMessage>, messages: BTreeMap<u64, Vec<u8>>,
} }
/// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data. /// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct ReadContiguousData { pub struct ReadContiguousData {
pub data: Vec<u8>, pub data: Vec<u8>,
pub last_index: u64, pub last_sequence: u64,
} }
const MAX_REASONABLE_OFFSET: u64 = 1000;
impl OrderedMessageBuffer { impl OrderedMessageBuffer {
pub fn new() -> OrderedMessageBuffer { pub fn new() -> OrderedMessageBuffer {
OrderedMessageBuffer { OrderedMessageBuffer {
next_index: 0, next_sequence: 0,
messages: HashMap::new(), messages: BTreeMap::new(),
} }
} }
/// Writes a message to the buffer. messages are sort on insertion, so /// Writes a message to the buffer. messages are sort on insertion, so
/// that later on multiple reads for incomplete sequences don't result in /// that later on multiple reads for incomplete sequences don't result in
/// useless sort work. /// useless sort work.
pub fn write(&mut self, message: OrderedMessage) { pub fn write(&mut self, sequence: u64, data: Vec<u8>) -> Result<(), OrderedMessageError> {
// reject messages that have clearly malformed sequence
if sequence > self.next_sequence + MAX_REASONABLE_OFFSET {
return Err(OrderedMessageError::MessageSequenceTooLarge {
current: self.next_sequence,
received: sequence,
});
}
if self.messages.contains_key(&sequence) {
return Err(OrderedMessageError::AttemptedToOverwriteSequence { received: sequence });
}
if sequence < self.next_sequence {
return Err(OrderedMessageError::MessageAlreadyReconstructed {
current: self.next_sequence,
received: sequence,
});
}
trace!( trace!(
"Writing message index: {} length {:?} to OrderedMessageBuffer.", "Writing message index: {} length {} to OrderedMessageBuffer.",
message.index, sequence,
message.data.len() data.len()
); );
self.messages.insert(message.index, message); self.messages.insert(sequence, data);
Ok(())
} }
/// Returns `Option<Vec<u8>>` where it's `Some(bytes)` if there is gapless /// Returns `Option<Vec<u8>>` where it's `Some(bytes)` if there is gapless
@@ -49,33 +86,31 @@ impl OrderedMessageBuffer {
/// a read will return the bytes of messages 0, 1, 2. Subsequent reads will /// a read will return the bytes of messages 0, 1, 2. Subsequent reads will
/// return `None` until message 3 comes in, at which point 3, 4, and any /// return `None` until message 3 comes in, at which point 3, 4, and any
/// further contiguous messages which have arrived will be returned. /// further contiguous messages which have arrived will be returned.
#[must_use]
pub fn read(&mut self) -> Option<ReadContiguousData> { pub fn read(&mut self) -> Option<ReadContiguousData> {
if !self.messages.contains_key(&self.next_index) { if !self.messages.contains_key(&self.next_sequence) {
return None; return None;
} }
let mut contiguous_messages = Vec::new(); let mut contiguous_messages = Vec::new();
let mut index = self.next_index; let mut seq = self.next_sequence;
while let Some(ordered_message) = self.messages.remove(&index) { while let Some(mut data) = self.messages.remove(&seq) {
contiguous_messages.push(ordered_message); contiguous_messages.append(&mut data);
index += 1; seq += 1;
} }
let high_water = index; let high_water = seq;
self.next_index = high_water; self.next_sequence = high_water;
trace!("Next high water mark is: {}", high_water); trace!("Next high water mark is: {high_water}");
// dig out the bytes from inside the struct trace!(
let data: Vec<u8> = contiguous_messages "Returning {} bytes from ordered message buffer",
.into_iter() contiguous_messages.len()
.flat_map(|message| message.data) );
.collect();
trace!("Returning {} bytes from ordered message buffer", data.len());
Some(ReadContiguousData { Some(ReadContiguousData {
data, data: contiguous_messages,
last_index: index, last_sequence: seq,
}) })
} }
} }
@@ -90,6 +125,64 @@ impl Default for OrderedMessageBuffer {
mod test_chunking_and_reassembling { mod test_chunking_and_reassembling {
use super::*; use super::*;
#[test]
fn trying_to_write_unreasonable_high_sequence() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
buffer.write(0, first_message).unwrap();
buffer.write(1, second_message).unwrap();
assert_eq!(
Err(OrderedMessageError::MessageSequenceTooLarge {
current: 0,
received: 12345678
}),
buffer.write(12345678, b"foomp".to_vec())
)
}
#[test]
fn trying_to_overwrite_sequence() {
let mut buffer = OrderedMessageBuffer::new();
let message = vec![1, 2, 3, 4];
buffer.write(0, message.clone()).unwrap();
buffer.write(1, message.clone()).unwrap();
buffer.write(2, message.clone()).unwrap();
buffer.write(3, message.clone()).unwrap();
for seq in 0..=3 {
assert_eq!(
Err(OrderedMessageError::AttemptedToOverwriteSequence { received: seq }),
buffer.write(seq, message.clone())
)
}
}
#[test]
fn writing_past_data() {
let mut buffer = OrderedMessageBuffer::new();
let message = vec![1, 2, 3, 4];
buffer.write(0, message.clone()).unwrap();
buffer.write(1, message.clone()).unwrap();
buffer.write(2, message.clone()).unwrap();
buffer.write(3, message.clone()).unwrap();
let _ = buffer.read().unwrap();
for seq in 0..=3 {
assert_eq!(
Err(OrderedMessageError::MessageAlreadyReconstructed {
current: 4,
received: seq
}),
buffer.write(seq, message.clone())
)
}
}
#[cfg(test)] #[cfg(test)]
mod reading_from_and_writing_to_the_buffer { mod reading_from_and_writing_to_the_buffer {
use super::*; use super::*;
@@ -102,20 +195,14 @@ mod test_chunking_and_reassembling {
fn read_returns_ordered_bytes_and_resets_buffer() { fn read_returns_ordered_bytes_and_resets_buffer() {
let mut buffer = OrderedMessageBuffer::new(); let mut buffer = OrderedMessageBuffer::new();
let first_message = OrderedMessage { let first_message = vec![1, 2, 3, 4];
data: vec![1, 2, 3, 4], let second_message = vec![5, 6, 7, 8];
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
buffer.write(first_message); buffer.write(0, first_message).unwrap();
let first_read = buffer.read().unwrap().data; let first_read = buffer.read().unwrap().data;
assert_eq!(vec![1, 2, 3, 4], first_read); assert_eq!(vec![1, 2, 3, 4], first_read);
buffer.write(second_message); buffer.write(1, second_message).unwrap();
let second_read = buffer.read().unwrap().data; let second_read = buffer.read().unwrap().data;
assert_eq!(vec![5, 6, 7, 8], second_read); assert_eq!(vec![5, 6, 7, 8], second_read);
@@ -126,17 +213,11 @@ mod test_chunking_and_reassembling {
fn test_multiple_adds_stacks_up_bytes_in_the_buffer() { fn test_multiple_adds_stacks_up_bytes_in_the_buffer() {
let mut buffer = OrderedMessageBuffer::new(); let mut buffer = OrderedMessageBuffer::new();
let first_message = OrderedMessage { let first_message = vec![1, 2, 3, 4];
data: vec![1, 2, 3, 4], let second_message = vec![5, 6, 7, 8];
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
buffer.write(first_message); buffer.write(0, first_message).unwrap();
buffer.write(second_message); buffer.write(1, second_message).unwrap();
let second_read = buffer.read(); let second_read = buffer.read();
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap().data); assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap().data);
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
@@ -146,17 +227,11 @@ mod test_chunking_and_reassembling {
fn out_of_order_adds_results_in_ordered_byte_vector() { fn out_of_order_adds_results_in_ordered_byte_vector() {
let mut buffer = OrderedMessageBuffer::new(); let mut buffer = OrderedMessageBuffer::new();
let first_message = OrderedMessage { let first_message = vec![1, 2, 3, 4];
data: vec![1, 2, 3, 4], let second_message = vec![5, 6, 7, 8];
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
buffer.write(second_message); buffer.write(1, second_message).unwrap();
buffer.write(first_message); buffer.write(0, first_message).unwrap();
let read = buffer.read().unwrap().data; let read = buffer.read().unwrap().data;
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read); assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read);
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
@@ -170,23 +245,13 @@ mod test_chunking_and_reassembling {
fn setup() -> OrderedMessageBuffer { fn setup() -> OrderedMessageBuffer {
let mut buffer = OrderedMessageBuffer::new(); let mut buffer = OrderedMessageBuffer::new();
let zero_message = OrderedMessage { let zero_message = vec![0, 0, 0, 0];
data: vec![0, 0, 0, 0], let one_message = vec![1, 1, 1, 1];
index: 0, let three_message = vec![3, 3, 3, 3];
};
let one_message = OrderedMessage {
data: vec![1, 1, 1, 1],
index: 1,
};
let three_message = OrderedMessage { buffer.write(0, zero_message).unwrap();
data: vec![3, 3, 3, 3], buffer.write(1, one_message).unwrap();
index: 3, buffer.write(3, three_message).unwrap();
};
buffer.write(zero_message);
buffer.write(one_message);
buffer.write(three_message);
buffer buffer
} }
#[test] #[test]
@@ -199,43 +264,31 @@ mod test_chunking_and_reassembling {
assert_eq!(None, buffer.read()); assert_eq!(None, buffer.read());
// let's add another message, leaving a gap in place at index 2 // let's add another message, leaving a gap in place at index 2
let five_message = OrderedMessage { let five_message = vec![5, 5, 5, 5];
data: vec![5, 5, 5, 5], buffer.write(5, five_message).unwrap();
index: 5,
};
buffer.write(five_message);
assert_eq!(None, buffer.read()); assert_eq!(None, buffer.read());
} }
#[test] #[test]
fn filling_the_gap_allows_us_to_get_everything() { fn filling_the_gap_allows_us_to_get_everything() {
let mut buffer = setup(); let mut buffer = setup();
buffer.read(); // that burns the first two. We still have a gap before the 3s. let _ = buffer.read(); // that burns the first two. We still have a gap before the 3s.
let two_message = OrderedMessage { let two_message = vec![2, 2, 2, 2];
data: vec![2, 2, 2, 2], buffer.write(2, two_message).unwrap();
index: 2,
};
buffer.write(two_message);
let more_ordered_bytes = buffer.read().unwrap().data; let more_ordered_bytes = buffer.read().unwrap().data;
assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes); assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes);
// let's add another message // let's add another message
let five_message = OrderedMessage { let five_message = vec![5, 5, 5, 5];
data: vec![5, 5, 5, 5], buffer.write(5, five_message).unwrap();
index: 5,
};
buffer.write(five_message);
assert_eq!(None, buffer.read()); assert_eq!(None, buffer.read());
// let's fill in the gap of 4s now and read again // let's fill in the gap of 4s now and read again
let four_message = OrderedMessage { let four_message = vec![4, 4, 4, 4];
data: vec![4, 4, 4, 4], buffer.write(4, four_message).unwrap();
index: 4,
};
buffer.write(four_message);
assert_eq!( assert_eq!(
[4, 4, 4, 4, 5, 5, 5, 5].to_vec(), [4, 4, 4, 4, 5, 5, 5, 5].to_vec(),
@@ -249,70 +302,47 @@ mod test_chunking_and_reassembling {
#[test] #[test]
fn filling_the_gap_allows_us_to_get_everything_when_last_element_is_empty() { fn filling_the_gap_allows_us_to_get_everything_when_last_element_is_empty() {
let mut buffer = OrderedMessageBuffer::new(); let mut buffer = OrderedMessageBuffer::new();
let zero_message = OrderedMessage { let zero_message = vec![0, 0, 0, 0];
data: vec![0, 0, 0, 0], let one_message = vec![2, 2, 2, 2];
index: 0, let two_message = vec![];
};
let one_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 1,
};
let two_message = OrderedMessage {
data: vec![],
index: 2,
};
buffer.write(zero_message); buffer.write(0, zero_message).unwrap();
assert!(buffer.read().is_some()); // burn the buffer assert!(buffer.read().is_some()); // burn the buffer
buffer.write(two_message); buffer.write(2, two_message).unwrap();
buffer.write(one_message); buffer.write(1, one_message).unwrap();
assert!(buffer.read().is_some()); assert!(buffer.read().is_some());
assert_eq!(buffer.next_index, 3); assert_eq!(buffer.next_sequence, 3);
} }
#[test] #[test]
fn works_with_gaps_bigger_than_one() { fn works_with_gaps_bigger_than_one() {
let mut buffer = OrderedMessageBuffer::new(); let mut buffer = OrderedMessageBuffer::new();
let zero_message = OrderedMessage { let zero_message = vec![0, 0, 0, 0];
data: vec![0, 0, 0, 0], let one_message = vec![2, 2, 2, 2];
index: 0, let two_message = vec![2, 2, 2, 2];
}; let three_message = vec![2, 2, 2, 2];
let one_message = OrderedMessage { let four_message = vec![2, 2, 2, 2];
data: vec![2, 2, 2, 2],
index: 1, buffer.write(0, zero_message).unwrap();
};
let two_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 2,
};
let three_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 3,
};
let four_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 4,
};
buffer.write(zero_message);
assert!(buffer.read().is_some()); assert!(buffer.read().is_some());
assert_eq!(buffer.next_index, 1); assert_eq!(buffer.next_sequence, 1);
buffer.write(four_message); buffer.write(4, four_message).unwrap();
assert!(buffer.read().is_none()); assert!(buffer.read().is_none());
assert_eq!(buffer.next_index, 1); assert_eq!(buffer.next_sequence, 1);
buffer.write(three_message); buffer.write(3, three_message).unwrap();
assert!(buffer.read().is_none()); assert!(buffer.read().is_none());
assert_eq!(buffer.next_index, 1); assert_eq!(buffer.next_sequence, 1);
buffer.write(two_message); buffer.write(2, two_message).unwrap();
assert!(buffer.read().is_none()); assert!(buffer.read().is_none());
assert_eq!(buffer.next_index, 1); assert_eq!(buffer.next_sequence, 1);
buffer.write(one_message); buffer.write(1, one_message).unwrap();
assert!(buffer.read().is_some()); assert!(buffer.read().is_some());
assert_eq!(buffer.next_index, 5) assert_eq!(buffer.next_sequence, 5)
} }
} }
} }
+1 -6
View File
@@ -1,8 +1,3 @@
mod buffer; mod buffer;
mod message;
mod sender;
pub use buffer::{OrderedMessageBuffer, ReadContiguousData}; pub use buffer::{OrderedMessageBuffer, OrderedMessageError, ReadContiguousData};
pub use message::MessageError;
pub use message::OrderedMessage;
pub use sender::OrderedMessageSender;
-143
View File
@@ -1,143 +0,0 @@
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::cmp::Ordering;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Eq)]
pub enum MessageError {
#[error("the received message was empty")]
NoData,
#[error("could not extract message index. Received {received} bytes, but expected {expected}")]
IndexTooShort { received: usize, expected: usize },
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OrderedMessage {
pub data: Vec<u8>,
pub index: u64,
}
impl OrderedMessage {
/// Serializes an `OrderedMessage` into bytes.
/// The output format is:
/// | 8 bytes index | data... |
pub fn into_bytes(self) -> Vec<u8> {
self.index
.to_be_bytes()
.iter()
.cloned()
.chain(self.data.into_iter())
.collect()
}
/// Attempts to deserialize an `OrderedMessage` from bytes.
pub fn try_from_bytes(data: Vec<u8>) -> Result<OrderedMessage, MessageError> {
if data.is_empty() {
return Err(MessageError::NoData);
}
if data.len() < 8 {
return Err(MessageError::IndexTooShort {
received: data.len(),
expected: 8,
});
}
let index = u64::from_be_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]);
Ok(OrderedMessage {
data: data[8..].to_vec(),
index,
})
}
}
/// Order messages by their index only, ignoring their data
impl PartialOrd for OrderedMessage {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some((self.index).cmp(&(other.index)))
}
}
#[cfg(test)]
mod ordered_message_to_bytes {
use super::*;
#[test]
fn works() {
let message = OrderedMessage {
data: vec![123],
index: 1,
};
let bytes = message.into_bytes();
let expected = vec![0, 0, 0, 0, 0, 0, 0, 1, 123];
assert_eq!(expected, bytes);
}
}
#[cfg(test)]
mod ordered_message_from_bytes {
use super::*;
#[test]
fn fails_when_there_is_no_data() {
let result = OrderedMessage::try_from_bytes(Vec::new());
assert_eq!(Err(MessageError::NoData), result);
}
#[test]
fn fails_when_data_is_too_short() {
let result = OrderedMessage::try_from_bytes(vec![1, 2, 3]);
assert_eq!(
Err(MessageError::IndexTooShort {
received: 3,
expected: 8
}),
result
);
}
#[test]
fn works_when_there_is_enough_to_make_a_sequence_number_but_no_message_data() {
let expected = OrderedMessage {
data: Vec::new(),
index: 1,
};
let result = OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1]).unwrap();
assert_eq!(expected, result);
}
#[test]
fn works_when_there_is_seq_number_and_data() {
let expected = OrderedMessage {
data: vec![255, 255, 255],
index: 1,
};
let result =
OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255]).unwrap();
assert_eq!(expected, result);
}
}
#[test]
fn empty_message_does_not_affect_ordering() {
let mut msg1 = OrderedMessage {
data: vec![255, 255, 255],
index: 1,
};
let mut msg2 = OrderedMessage {
data: vec![],
index: 2,
};
assert!(msg1 < msg2);
msg1.index = 2;
msg2.index = 1;
assert!(msg1 > msg2);
}
@@ -1,57 +0,0 @@
use crate::message::OrderedMessage;
/// Assigns sequence numbers to outbound byte vectors. These messages can then
/// be reassembled into an ordered sequence by the `OrderedMessageSender`.
#[derive(Debug)]
pub struct OrderedMessageSender {
next_index: u64,
}
impl OrderedMessageSender {
pub fn new() -> OrderedMessageSender {
OrderedMessageSender { next_index: 0 }
}
/// Turns raw bytes into an OrderedMessage containing the original bytes
/// and a sequence number;
pub fn wrap_message(&mut self, input: Vec<u8>) -> OrderedMessage {
let message = OrderedMessage {
data: input.to_vec(),
index: self.next_index,
};
self.next_index += 1;
message
}
}
impl Default for OrderedMessageSender {
fn default() -> Self {
OrderedMessageSender::new()
}
}
#[cfg(test)]
mod ordered_message_sender {
use super::*;
mod when_input_bytes_are_empty {}
#[cfg(test)]
mod sequence_index_numbers {
use super::*;
#[test]
fn increase_as_messages_are_sent() {
let mut sender = OrderedMessageSender::new();
let first_bytes = vec![1, 2, 3, 4];
let second_bytes = vec![5, 6, 7, 8];
let first_message = sender.wrap_message(first_bytes);
assert_eq!(first_message.index, 0);
let second_message = sender.wrap_message(second_bytes);
assert_eq!(second_message.index, 1);
}
}
}
@@ -1,11 +1,11 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData}; use nym_ordered_buffer::{OrderedMessageBuffer, ReadContiguousData};
use nym_socks5_requests::{ConnectionId, NetworkData, SendRequest}; use nym_socks5_requests::{ConnectionId, SocketData};
use nym_task::connections::{ConnectionCommand, ConnectionCommandSender}; use nym_task::connections::{ConnectionCommand, ConnectionCommandSender};
use nym_task::TaskClient; use nym_task::TaskClient;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@@ -40,29 +40,13 @@ pub enum ControllerCommand {
connection_id: ConnectionId, connection_id: ConnectionId,
}, },
Send { Send {
connection_id: ConnectionId, data: SocketData,
data: Vec<u8>,
is_closed: bool,
}, },
} }
impl From<NetworkData> for ControllerCommand { impl ControllerCommand {
fn from(value: NetworkData) -> Self { pub fn new_send(data: SocketData) -> Self {
ControllerCommand::Send { ControllerCommand::Send { data }
connection_id: value.connection_id,
data: value.data,
is_closed: value.is_closed,
}
}
}
impl From<SendRequest> for ControllerCommand {
fn from(value: SendRequest) -> Self {
ControllerCommand::Send {
connection_id: value.conn_id,
data: value.data,
is_closed: value.local_closed,
}
} }
} }
@@ -74,18 +58,13 @@ struct ActiveConnection {
} }
impl ActiveConnection { impl ActiveConnection {
fn write_to_buf(&mut self, payload: Vec<u8>, is_closed: bool) { fn write_to_buf(&mut self, seq: u64, payload: Vec<u8>, is_closed: bool) {
let ordered_message = match OrderedMessage::try_from_bytes(payload) {
Ok(msg) => msg,
Err(err) => {
error!("Malformed ordered message - {err}");
return;
}
};
if is_closed { if is_closed {
self.closed_at_index = Some(ordered_message.index); self.closed_at_index = Some(seq);
}
if let Err(err) = self.ordered_buffer.write(seq, payload) {
error!("failed to write to the buffer: {err}")
} }
self.ordered_buffer.write(ordered_message);
} }
fn read_from_buf(&mut self) -> Option<ReadContiguousData> { fn read_from_buf(&mut self) -> Option<ReadContiguousData> {
@@ -117,7 +96,7 @@ pub struct Controller {
// buffer for messages received before connection was established due to mixnet being able to // buffer for messages received before connection was established due to mixnet being able to
// un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here // un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here
pending_messages: HashMap<ConnectionId, Vec<(Vec<u8>, bool)>>, pending_messages: HashMap<ConnectionId, Vec<SocketData>>,
shutdown: TaskClient, shutdown: TaskClient,
} }
@@ -154,8 +133,8 @@ impl Controller {
// check if there were any pending messages // check if there were any pending messages
if let Some(pending) = self.pending_messages.remove(&conn_id) { if let Some(pending) = self.pending_messages.remove(&conn_id) {
debug!("There were some pending messages for {}", conn_id); debug!("There were some pending messages for {}", conn_id);
for (payload, is_closed) in pending { for data in pending {
self.send_to_connection(conn_id, payload, is_closed) self.send_to_connection(data)
} }
} }
} }
@@ -184,20 +163,25 @@ impl Controller {
} }
} }
fn send_to_connection(&mut self, conn_id: ConnectionId, payload: Vec<u8>, is_closed: bool) { fn send_to_connection(&mut self, message: SocketData) {
if let Some(active_connection) = self.active_connections.get_mut(&conn_id) { let hdr = message.header;
if !payload.is_empty() { if let Some(active_connection) = self.active_connections.get_mut(&hdr.connection_id) {
active_connection.write_to_buf(payload, is_closed); // always write to the buffer even if payload is empty (because it could have been the keep-alive message)
} else if !is_closed { active_connection.write_to_buf(hdr.seq, message.data, hdr.local_socket_closed);
error!("Tried to write an empty message to a not-closing connection. Please let us know if you see this message");
}
if let Some(payload) = active_connection.read_from_buf() { if let Some(payload) = active_connection.read_from_buf() {
if let Some(closed_at_index) = active_connection.closed_at_index { if let Some(closed_at_index) = active_connection.closed_at_index {
if payload.last_index > closed_at_index { if payload.last_sequence > closed_at_index {
active_connection.is_closed = true; active_connection.is_closed = true;
} }
} }
// however, don't send empty payload to the actual connection if it's not a close message
// TODO: or should we?
if payload.data.is_empty() && !active_connection.is_closed {
return;
}
if let Err(err) = active_connection if let Err(err) = active_connection
.connection_sender .connection_sender
.as_mut() .as_mut()
@@ -207,34 +191,26 @@ impl Controller {
socket_closed: active_connection.is_closed, socket_closed: active_connection.is_closed,
}) })
{ {
error!("WTF IS THIS: {err}"); error!("failed to send on the active connection channel: {err}");
} }
// TODO: ABOVE UNWRAP CAUSED A CRASH IN A NORMAL USE!!!!
// TODO:
// TODO: surprisingly it only happened on socks client, never on nSP
// TODO:
// TODO:
// TODO:
// TODO:
} }
} else if !self.recently_closed.contains(&conn_id) { } else if !self.recently_closed.contains(&hdr.connection_id) {
debug!("Received a 'Send' before 'Connect' - going to buffer the data"); debug!("Received a 'Send' before 'Connect' - going to buffer the data");
let pending = self let pending = self
.pending_messages .pending_messages
.entry(conn_id) .entry(hdr.connection_id)
.or_insert_with(Vec::new); .or_insert_with(Vec::new);
pending.push((payload, is_closed)); pending.push(message);
} else if !is_closed { } else if !hdr.local_socket_closed {
error!( error!(
"Tried to write to closed connection {} ({} bytes were 'lost)", "Tried to write to closed connection {} ({} bytes were 'lost)",
conn_id, hdr.connection_id,
payload.len() message.data.len()
); );
} else { } else {
debug!( debug!(
"Tried to write to closed connection {}, but remote is already closed", "Tried to write to closed connection {}, but remote is already closed",
conn_id hdr.connection_id
) )
} }
} }
@@ -243,8 +219,8 @@ impl Controller {
loop { loop {
tokio::select! { tokio::select! {
command = self.receiver.next() => match command { command = self.receiver.next() => match command {
Some(ControllerCommand::Send{connection_id, data, is_closed}) => { Some(ControllerCommand::Send{data}) => {
self.send_to_connection(connection_id, data, is_closed) self.send_to_connection(data)
} }
Some(ControllerCommand::Insert{connection_id, connection_sender}) => { Some(ControllerCommand::Insert{connection_id, connection_sender}) => {
self.insert_connection(connection_id, connection_sender) self.insert_connection(connection_id, connection_sender)
+1
View File
@@ -3,4 +3,5 @@
pub mod available_reader; pub mod available_reader;
pub mod connection_controller; pub mod connection_controller;
pub mod ordered_sender;
pub mod proxy_runner; pub mod proxy_runner;
@@ -0,0 +1,116 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::proxy_runner::MixProxySender;
use bytes::Bytes;
use log::{debug, error};
use nym_socks5_requests::{ConnectionId, SocketData};
use std::io;
pub(crate) struct OrderedMessageSender<F, S> {
connection_id: ConnectionId,
// addresses are provided for better logging
local_destination_address: String,
remote_source_address: String,
mixnet_sender: MixProxySender<S>,
next_message_seq: u64,
mix_message_adapter: F,
}
impl<F, S> OrderedMessageSender<F, S>
where
F: Fn(SocketData) -> S,
{
pub(crate) fn new(
local_destination_address: String,
remote_source_address: String,
connection_id: ConnectionId,
mixnet_sender: MixProxySender<S>,
mix_message_adapter: F,
) -> Self {
OrderedMessageSender {
local_destination_address,
remote_source_address,
connection_id,
mixnet_sender,
next_message_seq: 0,
mix_message_adapter,
}
}
fn sequence(&mut self) -> u64 {
let next = self.next_message_seq;
self.next_message_seq += 1;
next
}
fn construct_message(&mut self, data: Vec<u8>, local_socket_closed: bool) -> S {
let data = SocketData::new(
self.sequence(),
self.connection_id,
local_socket_closed,
data,
);
(self.mix_message_adapter)(data)
}
async fn send_message(&self, message: S) {
if self.mixnet_sender.send(message).await.is_err() {
panic!("BatchRealMessageReceiver has stopped receiving!")
}
}
pub(crate) async fn send_empty_close(&mut self) {
let message = self.construct_message(Vec::new(), true);
self.send_message(message).await
}
pub(crate) async fn send_empty_keepalive(&mut self) {
log::trace!("Sending keepalive for connection: {}", self.connection_id);
let message = self.construct_message(Vec::new(), false);
self.send_message(message).await
}
pub(crate) fn process_data(&self, read_data: Option<io::Result<Bytes>>) -> ProcessedData {
let (read_data, is_finished) = match read_data {
Some(data) => match data {
Ok(data) => (data, false),
Err(err) => {
error!(target: &*format!("({}) socks5 inbound", self.connection_id), "failed to read request from the socket - {err}");
(Default::default(), true)
}
},
None => (Default::default(), true),
};
ProcessedData {
data: read_data,
is_done: is_finished,
}
}
fn log_sent_message(&self, data: &ProcessedData) {
debug!(
target: &*format!("({}) socks5 inbound", self.connection_id),
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
data.data.len(),
self.local_destination_address,
self.remote_source_address,
data.is_done
);
}
/// Send data read from local socket into the mixnet
pub(crate) async fn send_data(&mut self, data: ProcessedData) {
self.log_sent_message(&data);
let message = self.construct_message(data.data.into(), data.is_done);
self.send_message(message).await;
}
}
// helper wrapper to keep track of field meanings
pub(crate) struct ProcessedData {
data: Bytes,
pub(crate) is_done: bool,
}
@@ -1,106 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::MixProxySender;
use super::SHUTDOWN_TIMEOUT; use super::SHUTDOWN_TIMEOUT;
use crate::available_reader::AvailableReader; use crate::available_reader::AvailableReader;
use crate::ordered_sender::OrderedMessageSender;
use crate::proxy_runner::KEEPALIVE_INTERVAL; use crate::proxy_runner::KEEPALIVE_INTERVAL;
use bytes::Bytes;
use futures::FutureExt; use futures::FutureExt;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_ordered_buffer::OrderedMessageSender; use nym_socks5_requests::{ConnectionId, SocketData};
use nym_socks5_requests::ConnectionId;
use nym_task::connections::LaneQueueLengths; use nym_task::connections::LaneQueueLengths;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::TaskClient; use nym_task::TaskClient;
use std::fmt::Debug; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::{io, sync::Arc};
use tokio::select; use tokio::select;
use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep}; use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep};
async fn send_empty_close<F, S>(
connection_id: ConnectionId,
message_sender: &mut OrderedMessageSender,
mix_sender: &MixProxySender<S>,
adapter_fn: F,
) where
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
S: Debug,
{
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
mix_sender
.send(adapter_fn(connection_id, ordered_msg, true))
.await
.expect("BatchRealMessageReceiver has stopped receiving!");
}
async fn send_empty_keepalive<F, S>(
connection_id: ConnectionId,
message_sender: &mut OrderedMessageSender,
mix_sender: &MixProxySender<S>,
adapter_fn: F,
) where
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
S: Debug,
{
log::trace!("Sending keepalive for connection: {connection_id}");
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
mix_sender
.send(adapter_fn(connection_id, ordered_msg, false))
.await
.expect("BatchRealMessageReceiver has stopped receiving!");
}
async fn deal_with_data<F, S>(
read_data: Option<io::Result<Bytes>>,
local_destination_address: &str,
remote_source_address: &str,
connection_id: ConnectionId,
message_sender: &mut OrderedMessageSender,
mix_sender: &MixProxySender<S>,
adapter_fn: F,
) -> bool
where
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
S: Debug,
{
let (read_data, is_finished) = match read_data {
Some(data) => match data {
Ok(data) => (data, false),
Err(err) => {
error!(target: &*format!("({connection_id}) socks5 inbound"), "failed to read request from the socket - {err}");
(Default::default(), true)
}
},
None => (Default::default(), true),
};
debug!(
target: &*format!("({connection_id}) socks5 inbound"),
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
read_data.len(),
local_destination_address,
remote_source_address,
is_finished
);
// if we're sending through the mixnet increase the sequence number...
let ordered_msg = message_sender.wrap_message(read_data.to_vec()).into_bytes();
log::trace!(
"pushing data down the input sender: size: {}",
ordered_msg.len()
);
mix_sender
.send(adapter_fn(connection_id, ordered_msg, is_finished))
.await
.expect("InputMessageReceiver has stopped receiving!");
is_finished
}
async fn wait_until_lane_empty(lane_queue_lengths: &Option<LaneQueueLengths>, connection_id: u64) { async fn wait_until_lane_empty(lane_queue_lengths: &Option<LaneQueueLengths>, connection_id: u64) {
if let Some(lane_queue_lengths) = lane_queue_lengths { if let Some(lane_queue_lengths) = lane_queue_lengths {
if tokio::time::timeout( if tokio::time::timeout(
@@ -158,27 +74,21 @@ async fn wait_for_lane(
} }
} }
#[allow(clippy::too_many_arguments)]
pub(super) async fn run_inbound<F, S>( pub(super) async fn run_inbound<F, S>(
mut reader: OwnedReadHalf, mut reader: OwnedReadHalf,
local_destination_address: String, // addresses are provided for better logging mut message_sender: OrderedMessageSender<F, S>,
remote_source_address: String,
connection_id: ConnectionId, connection_id: ConnectionId,
mix_sender: MixProxySender<S>,
available_plaintext_per_mix_packet: usize, available_plaintext_per_mix_packet: usize,
adapter_fn: F,
shutdown_notify: Arc<Notify>, shutdown_notify: Arc<Notify>,
lane_queue_lengths: Option<LaneQueueLengths>, lane_queue_lengths: Option<LaneQueueLengths>,
mut shutdown_listener: TaskClient, mut shutdown_listener: TaskClient,
) -> OwnedReadHalf ) -> OwnedReadHalf
where where
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static, F: Fn(SocketData) -> S + Send + 'static,
S: Debug,
{ {
// TODO: this multiplication by 4 is completely arbitrary here // TODO: this multiplication by 4 is completely arbitrary here
let mut available_reader = let mut available_reader =
AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4)); AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4));
let mut message_sender = OrderedMessageSender::new();
// Shutdown if outbound signled to shutdown // Shutdown if outbound signled to shutdown
let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT)); let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT));
@@ -217,7 +127,7 @@ where
); );
// inform remote just in case it was closed because of lack of heartbeat. // inform remote just in case it was closed because of lack of heartbeat.
// worst case the remote will just have couple of false negatives // worst case the remote will just have couple of false negatives
send_empty_close(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await; message_sender.send_empty_close().await;
break; break;
} }
_ = shutdown_listener.recv() => { _ = shutdown_listener.recv() => {
@@ -233,7 +143,7 @@ where
break; break;
} }
_ = keepalive_timer.tick() => { _ = keepalive_timer.tick() => {
send_empty_keepalive(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await; message_sender.send_empty_keepalive().await;
} }
// Read the next data when there is space in the lane. // Read the next data when there is space in the lane.
// The purpose of chaining the wait here is that it makes sure we can cancel the // The purpose of chaining the wait here is that it makes sure we can cancel the
@@ -241,15 +151,12 @@ where
read_data = wait_until_lane_almost_empty(&lane_queue_lengths, connection_id) read_data = wait_until_lane_almost_empty(&lane_queue_lengths, connection_id)
.then(|_| available_reader.next()), if !we_are_closed => .then(|_| available_reader.next()), if !we_are_closed =>
{ {
if deal_with_data( let processed = message_sender.process_data(read_data);
read_data, let is_done = processed.is_done;
&local_destination_address,
&remote_source_address, message_sender.send_data(processed).await;
connection_id,
&mut message_sender, if is_done {
&mix_sender,
&adapter_fn,
).await {
// After reading the last data, notify the closing_future to wait until the // After reading the last data, notify the closing_future to wait until the
// lane is clear before exiting. // lane is clear before exiting.
// We don't wait here since we want to be able to cancel the wait on close or // We don't wait here since we want to be able to cancel the wait on close or

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