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/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/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
continue-on-error: true
+13 -5
View File
@@ -49,7 +49,7 @@ jobs:
"build-tools;$SDK_BUILDTOOLS_VERSION"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@1.70.0
- name: Install rust android targets
run: |
@@ -60,6 +60,7 @@ jobs:
working-directory: sdk/lib/socks5-listener/
env:
RELEASE: true
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
# build for arm64 and x86_64
run: ./build-android.sh aarch64 x86_64
@@ -68,19 +69,24 @@ jobs:
env:
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
# build for arm64 and x86_64
run: ./gradlew :app:assembleArch64Release
run: |
./gradlew :app:assembleArch64Debug
./gradlew :app:assembleArch64Release
- name: Prepare APKs
run: |
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 \
apk/nyms5-arch64-release.apk
- name: Upload APKs
uses: actions/upload-artifact@v3
with:
name: nyms5-apk-arch64-release
name: nyms5-apk-arch64
path: |
apk/nyms5-arch64-debug.apk
apk/nyms5-arch64-release.apk
gh-release:
@@ -93,10 +99,12 @@ jobs:
- name: Download binary artifact
uses: actions/download-artifact@v3
with:
name: nyms5-apk-arch64-release
name: nyms5-apk-arch64
path: apk
- name: Release
uses: softprops/action-gh-release@v1
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]
## [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)
- 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"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
"once_cell",
"version_check",
]
@@ -204,6 +204,17 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "async-file-watcher"
version = "0.1.0"
@@ -362,9 +373,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]]
name = "base64"
version = "0.21.0"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64ct"
@@ -378,6 +389,15 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bip32"
version = "0.3.0"
@@ -561,6 +581,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "castaway"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
[[package]]
name = "cc"
version = "1.0.79"
@@ -810,6 +836,15 @@ dependencies = [
"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]]
name = "console-api"
version = "0.5.0"
@@ -911,7 +946,7 @@ dependencies = [
"cosmos-sdk-proto",
"ecdsa",
"eyre",
"getrandom 0.2.9",
"getrandom 0.2.10",
"k256",
"prost 0.10.4",
"prost-types 0.10.1",
@@ -1229,6 +1264,36 @@ dependencies = [
"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]]
name = "curve25519-dalek"
version = "3.2.0"
@@ -1243,6 +1308,20 @@ dependencies = [
"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]]
name = "cw-storage-plus"
version = "0.13.4"
@@ -1653,7 +1732,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"chrono",
"clap 4.2.7",
@@ -1927,6 +2006,21 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "futures-macro"
version = "0.3.28"
@@ -2023,9 +2117,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.9"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"js-sys",
@@ -2630,6 +2724,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "integer-encoding"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
[[package]]
name = "inventory"
version = "0.1.11"
@@ -2699,6 +2799,31 @@ dependencies = [
"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]]
name = "isocountry"
version = "0.3.2"
@@ -3197,7 +3322,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.20"
version = "1.1.23"
dependencies = [
"anyhow",
"async-trait",
@@ -3298,18 +3423,20 @@ dependencies = [
[[package]]
name = "nym-bin-common"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"atty",
"clap 4.2.7",
"clap_complete",
"clap_complete_fig",
"log",
"opentelemetry",
"opentelemetry-jaeger",
"pretty_env_logger",
"semver 0.11.0",
"serde",
"serde_json",
"tracing-appender",
"tracing-opentelemetry",
"tracing-subscriber",
"tracing-tree",
"vergen",
@@ -3332,7 +3459,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -3381,6 +3508,7 @@ dependencies = [
"nym-name-service-common",
"nym-network-defaults",
"nym-service-provider-directory-common",
"nym-sphinx",
"nym-validator-client",
"nym-vesting-contract-common",
"rand 0.6.5",
@@ -3395,7 +3523,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"clap 4.2.7",
"dirs 4.0.0",
@@ -3431,9 +3559,10 @@ dependencies = [
[[package]]
name = "nym-client-core"
version = "1.1.14"
version = "1.1.15"
dependencies = [
"async-trait",
"base64 0.21.2",
"dashmap 5.4.0",
"dirs 4.0.0",
"futures",
@@ -3456,6 +3585,7 @@ dependencies = [
"rand 0.7.3",
"serde",
"serde_json",
"sha2 0.10.6",
"sqlx 0.6.3",
"tap",
"tempfile",
@@ -3492,7 +3622,7 @@ dependencies = [
"digest 0.9.0",
"doc-comment",
"ff 0.11.1",
"getrandom 0.2.9",
"getrandom 0.2.10",
"group 0.11.0",
"itertools",
"nym-dkg",
@@ -3553,7 +3683,7 @@ dependencies = [
[[package]]
name = "nym-contracts-common"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"bs58",
"cosmwasm-std",
@@ -3610,7 +3740,7 @@ dependencies = [
[[package]]
name = "nym-crypto"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"aes 0.8.2",
"blake3",
@@ -3667,7 +3797,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"anyhow",
"async-trait",
@@ -3720,7 +3850,7 @@ name = "nym-gateway-client"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.9",
"getrandom 0.2.10",
"log",
"nym-bandwidth-controller",
"nym-coconut-interface",
@@ -3799,7 +3929,7 @@ dependencies = [
[[package]]
name = "nym-mixnet-contract-common"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"bs58",
"cosmwasm-std",
@@ -3818,7 +3948,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.20"
version = "1.1.23"
dependencies = [
"anyhow",
"bs58",
@@ -3928,10 +4058,12 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"anyhow",
"async-file-watcher",
"async-trait",
"bs58",
"clap 4.2.7",
"dirs 4.0.0",
"futures",
@@ -3954,11 +4086,13 @@ dependencies = [
"nym-sphinx",
"nym-statistics-common",
"nym-task",
"nym-types",
"pretty_env_logger",
"publicsuffix",
"rand 0.7.3",
"reqwest",
"serde",
"serde_json",
"sqlx 0.6.3",
"tap",
"tempfile",
@@ -3970,7 +4104,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"dirs 4.0.0",
"log",
@@ -4016,6 +4150,22 @@ dependencies = [
"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]]
name = "nym-ordered-buffer"
version = "0.1.0"
@@ -4034,7 +4184,7 @@ dependencies = [
"criterion",
"curve25519-dalek",
"fastrand",
"getrandom 0.2.9",
"getrandom 0.2.10",
"log",
"rand 0.7.3",
"rayon",
@@ -4045,7 +4195,7 @@ dependencies = [
[[package]]
name = "nym-pemstore"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"pem",
]
@@ -4086,8 +4236,12 @@ name = "nym-service-provider-directory-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -4099,6 +4253,7 @@ dependencies = [
"log",
"nym-bin-common",
"nym-sdk",
"nym-socks5-requests",
"nym-sphinx-anonymous-replies",
"serde",
"serde_json",
@@ -4108,7 +4263,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.19"
version = "1.1.22"
dependencies = [
"clap 4.2.7",
"lazy_static",
@@ -4204,10 +4359,13 @@ dependencies = [
name = "nym-socks5-requests"
version = "0.1.0"
dependencies = [
"bincode",
"log",
"nym-service-providers-common",
"nym-sphinx-addressing",
"serde",
"serde_json",
"tap",
"thiserror",
]
@@ -4379,7 +4537,7 @@ dependencies = [
"aes-gcm",
"argon2",
"generic-array 0.14.7",
"getrandom 0.2.9",
"getrandom 0.2.10",
"rand 0.8.5",
"serde",
"serde_json",
@@ -4473,6 +4631,7 @@ dependencies = [
"nym-service-provider-directory-common",
"nym-vesting-contract",
"nym-vesting-contract-common",
"openssl",
"prost 0.10.4",
"reqwest",
"serde",
@@ -4505,7 +4664,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract-common"
version = "0.6.0"
version = "0.7.0"
dependencies = [
"cosmwasm-std",
"nym-contracts-common",
@@ -4635,12 +4794,55 @@ dependencies = [
"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]]
name = "opentelemetry_api"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2"
dependencies = [
"fnv",
"futures-channel",
"futures-util",
"indexmap",
@@ -4658,6 +4860,8 @@ checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1"
dependencies = [
"async-trait",
"crossbeam-channel",
"dashmap 5.4.0",
"fnv",
"futures-channel",
"futures-executor",
"futures-util",
@@ -4666,6 +4870,8 @@ dependencies = [
"percent-encoding",
"rand 0.8.5",
"thiserror",
"tokio",
"tokio-stream",
]
[[package]]
@@ -4674,6 +4880,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "os_str_bytes"
version = "6.5.0"
@@ -4704,6 +4919,12 @@ dependencies = [
"group 0.11.0",
]
[[package]]
name = "parking"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -4966,6 +5187,22 @@ dependencies = [
"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]]
name = "poly1305"
version = "0.8.0"
@@ -5280,7 +5517,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
]
[[package]]
@@ -5437,7 +5674,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
"redox_syscall 0.2.16",
"thiserror",
]
@@ -5500,7 +5737,7 @@ version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [
"base64 0.21.0",
"base64 0.21.2",
"bytes",
"encoding_rs",
"futures-core",
@@ -5770,7 +6007,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [
"base64 0.21.0",
"base64 0.21.2",
]
[[package]]
@@ -6197,6 +6434,17 @@ dependencies = [
"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]]
name = "smallvec"
version = "1.10.0"
@@ -6716,7 +6964,7 @@ dependencies = [
"bytes",
"flex-error",
"futures",
"getrandom 0.2.9",
"getrandom 0.2.10",
"http",
"hyper",
"hyper-proxy",
@@ -6784,6 +7032,28 @@ dependencies = [
"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]]
name = "time"
version = "0.1.45"
@@ -7049,7 +7319,7 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
dependencies = [
"async-trait",
"axum",
"base64 0.21.0",
"base64 0.21.2",
"bytes",
"futures-core",
"futures-util",
@@ -7108,22 +7378,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"log",
"pin-project-lite",
"tracing-attributes",
"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]]
name = "tracing-attributes"
version = "0.1.24"
@@ -7145,6 +7405,16 @@ dependencies = [
"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]]
name = "tracing-log"
version = "0.1.3"
@@ -7156,6 +7426,20 @@ dependencies = [
"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]]
name = "tracing-subscriber"
version = "0.3.17"
@@ -7450,7 +7734,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
"wasm-bindgen",
]
@@ -7489,6 +7773,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "walkdir"
version = "2.3.3"
@@ -7612,7 +7902,7 @@ name = "wasm-utils"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.9",
"getrandom 0.2.10",
"indexed_db_futures",
"js-sys",
"nym-store-cipher",
+6 -3
View File
@@ -87,6 +87,7 @@ members = [
"nym-api/nym-api-requests",
"nym-outfox",
"tools/nym-cli",
"tools/nym-nr-query",
"tools/ts-rs-cli"
]
@@ -101,7 +102,7 @@ default-members = [
"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]
authors = ["Nym Technologies SA"]
@@ -112,22 +113,24 @@ edition = "2021"
license = "Apache-2.0"
[workspace.dependencies]
async-trait = "0.1.64"
anyhow = "1.0.71"
async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0"
cosmwasm-derive = "=1.0.0"
cosmwasm-schema = "=1.0.0"
cosmwasm-std = "=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-utils = "=0.13.4"
cw2 = { version = "=0.13.4" }
cw3 = { version = "=0.13.4" }
cw3-fixed-multisig = { version = "=0.13.4" }
cw4 = { version = "=0.13.4" }
dotenvy = "0.15.6"
generic-array = "0.14.7"
getrandom = "0.2.10"
lazy_static = "1.4.0"
log = "0.4"
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
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/building-nym.html).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
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/wallet/desktop-wallet.html).
### Developing
@@ -32,7 +32,11 @@ For Typescript components, please see [ts-packages](./ts-packages).
### 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
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
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>"]
description = "Implementation of the Nym Client"
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 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 template;
@@ -52,19 +53,6 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
.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)]
pub struct Config {
#[serde(flatten)]
@@ -87,7 +75,7 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
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())),
logging: Default::default(),
socket: Default::default(),
@@ -187,6 +175,19 @@ impl Config {
// 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)]
#[serde(default, deny_unknown_fields)]
pub struct Socket {
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize};
@@ -13,7 +13,7 @@ pub struct OldConfigV1_1_13 {
#[serde(flatten)]
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub socket: SocketV1_1_19,
pub socket: SocketV1_1_20,
}
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 {
ConfigV1_1_19 {
ConfigV1_1_20 {
base: value.base.into(),
socket: value.socket,
}
@@ -1,13 +1,16 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{Config, Socket, SocketType};
use crate::client::config::old_config_v1_1_20_2::{
ClientPathsV1_1_20_2, ConfigV1_1_20_2, SocketTypeV1_1_20_2, SocketV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19;
use nym_client_core::config::{Client, Config as BaseConfig};
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::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::old_config_v1_1_20_2::{
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseConfigV1_1_20_2,
};
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize};
@@ -17,34 +20,34 @@ use std::path::PathBuf;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketTypeV1_1_19 {
pub enum SocketTypeV1_1_20 {
WebSocket,
None,
}
impl From<SocketTypeV1_1_19> for SocketType {
fn from(value: SocketTypeV1_1_19) -> Self {
impl From<SocketTypeV1_1_20> for SocketTypeV1_1_20_2 {
fn from(value: SocketTypeV1_1_20) -> Self {
match value {
SocketTypeV1_1_19::WebSocket => SocketType::WebSocket,
SocketTypeV1_1_19::None => SocketType::None,
SocketTypeV1_1_20::WebSocket => SocketTypeV1_1_20_2::WebSocket,
SocketTypeV1_1_20::None => SocketTypeV1_1_20_2::None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19 {
pub struct ConfigV1_1_20 {
#[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 {
fn from(value: ConfigV1_1_19) -> Self {
Config {
base: BaseConfig {
client: Client {
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
fn from(value: ConfigV1_1_20) -> Self {
ConfigV1_1_20_2 {
base: BaseConfigV1_1_20_2 {
client: ClientV1_1_20_2 {
version: value.base.client.version,
id: value.base.client.id,
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(),
},
socket: value.socket.into(),
storage_paths: ClientPaths {
common_paths: CommonClientPaths {
storage_paths: ClientPathsV1_1_20_2 {
common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_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 {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
@@ -85,15 +88,15 @@ impl MigrationNymConfig for ConfigV1_1_19 {
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct SocketV1_1_19 {
socket_type: SocketTypeV1_1_19,
pub struct SocketV1_1_20 {
socket_type: SocketTypeV1_1_20,
host: IpAddr,
listening_port: u16,
}
impl From<SocketV1_1_19> for Socket {
fn from(value: SocketV1_1_19) -> Self {
Socket {
impl From<SocketV1_1_20> for SocketV1_1_20_2 {
fn from(value: SocketV1_1_20) -> Self {
SocketV1_1_20_2 {
socket_type: value.socket_type.into(),
host: value.host,
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 {
SocketV1_1_19 {
socket_type: SocketTypeV1_1_19::WebSocket,
SocketV1_1_20 {
socket_type: SocketTypeV1_1_20::WebSocket,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
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.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# DEPRECATED
[client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
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 }}'
# Path to the file containing information about gateway used by this client,
# i.e. details such as its public key, owner address or the network information.
gateway_details = '{{ storage_paths.gateway_details }}'
##### socket config options #####
+14 -31
View File
@@ -6,18 +6,15 @@ use crate::error::ClientError;
use crate::websocket;
use futures::channel::mpsc;
use log::*;
use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::base_client::non_wasm_helpers::create_bandwidth_controller;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::storage::OnDiskPersistent;
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::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
};
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
@@ -45,17 +42,6 @@ impl SocketClient {
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(
config: &Config,
client_input: ClientInput,
@@ -105,29 +91,26 @@ impl SocketClient {
res
}
fn key_store(&self) -> OnDiskKeys {
OnDiskKeys::new(self.config.storage_paths.common_paths.keys.clone())
async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> {
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
async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> {
// don't create bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.base.client.disabled_credentials_mode {
// 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 {
None
} 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(
&self.config.base,
self.key_store(),
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?,
);
let storage = self.initialise_storage().await?;
let base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client);
Ok(base_client)
}
+18 -14
View File
@@ -12,7 +12,10 @@ use crate::{
};
use clap::Args;
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::config::GatewayEndpointConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize;
@@ -105,9 +108,9 @@ pub struct InitResults {
}
impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> 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_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
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
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
// one but with keys kept, or reusing the gateway configuration.
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,
&details_store,
register_gateway,
user_chosen_gateway_id,
&config.base,
args.latency_based_selection,
Some(&config.base.client.nym_api_urls),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
config.base.set_gateway_endpoint(gateway);
let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| {
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()
);
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys(
&config.storage_paths.common_paths.keys,
&config.base.client.gateway_endpoint,
)?;
let address = init_details.client_address()?;
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));
Ok(())
+77 -11
View File
@@ -2,7 +2,8 @@
// 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_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::error::ClientError;
use clap::CommandFactory;
@@ -11,6 +12,12 @@ use lazy_static::lazy_static;
use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation;
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 std::error::Error;
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> {
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 is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into();
let updated: Config = updated_step1.into();
let updated_step1: ConfigV1_1_20 = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated, gateway_config) = updated_step2.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
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;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20)
let Ok(old_config) = ConfigV1_1_19::load_from_file(id) else {
// 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_20::load_from_file(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.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.");
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()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
let upgraded = try_upgrade_v1_1_13_config(id)?;
if !upgraded {
try_upgrade_v1_1_19_config(id)?;
if try_upgrade_v1_1_13_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
}
Ok(())
}
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)?;
let config = match Config::read_from_default_path(id) {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.19"
version = "1.1.22"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+23 -14
View File
@@ -11,7 +11,10 @@ use crate::{
};
use clap::Args;
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::config::GatewayEndpointConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize;
@@ -107,9 +110,13 @@ pub struct InitResults {
}
impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> 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,
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
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
let mut config = override_config(
let config = override_config(
Config::new(id, &provider_address.to_string()),
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
// one but with keys kept, or reusing the gateway configuration.
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,
&details_store,
register_gateway,
user_chosen_gateway_id,
&config.core.base,
args.latency_based_selection,
Some(&config.core.base.client.nym_api_urls),
)
.await
.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
let config_save_location = config.default_location();
@@ -193,11 +204,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
config_save_location.display()
);
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys(
&config.storage_paths.common_paths.keys,
&config.core.base.client.gateway_endpoint,
)?;
let init_results = InitResults::new(&config, &address);
let address = init_details.client_address()?;
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
println!("{}", args.output.format(&init_results));
Ok(())
+77 -11
View File
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
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::error::Socks5ClientError;
use clap::CommandFactory;
@@ -11,6 +12,12 @@ use lazy_static::lazy_static;
use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation;
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_sphinx::params::PacketType;
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> {
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 is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into();
let updated: Config = updated_step1.into();
let updated_step1: ConfigV1_1_20 = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated, gateway_config) = updated_step2.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
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;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20)
let Ok(old_config) = ConfigV1_1_19::load_from_file(id) else {
// 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_20::load_from_file(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.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.");
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()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
let upgraded = try_upgrade_v1_1_13_config(id)?;
if !upgraded {
try_upgrade_v1_1_19_config(id)?;
if try_upgrade_v1_1_13_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
}
Ok(())
}
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)?;
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 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 template;
@@ -69,7 +70,11 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
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())),
logging: Default::default(),
}
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home;
@@ -14,7 +14,7 @@ pub struct OldConfigV1_1_13 {
#[serde(flatten)]
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub socks5: Socks5V1_1_19,
pub socks5: Socks5V1_1_20,
}
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 {
ConfigV1_1_19 {
ConfigV1_1_20 {
base: value.base.into(),
socks5: value.socks5,
}
@@ -1,18 +1,20 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{BaseClientConfig, Config, CoreConfig};
use crate::config::old_config_v1_1_20_2::{
ConfigV1_1_20_2, CoreConfigV1_1_20_2, SocksClientPathsV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19;
use nym_client_core::config::Client;
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::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::old_config_v1_1_20_2::ClientV1_1_20_2;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home;
use nym_socks5_client_core::config::{
ProviderInterfaceVersion, Socks5, Socks5Debug, Socks5ProtocolVersion,
use nym_socks5_client_core::config::old_config_v1_1_20_2::{
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 std::fmt::Debug;
use std::path::PathBuf;
@@ -22,19 +24,19 @@ const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19 {
pub struct ConfigV1_1_20 {
#[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 {
fn from(value: ConfigV1_1_19) -> Self {
Config {
core: CoreConfig {
base: BaseClientConfig {
client: Client {
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
fn from(value: ConfigV1_1_20) -> Self {
ConfigV1_1_20_2 {
core: CoreConfigV1_1_20_2 {
base: BaseClientConfigV1_1_20_2 {
client: ClientV1_1_20_2 {
version: value.base.client.version,
id: value.base.client.id,
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
@@ -46,8 +48,8 @@ impl From<ConfigV1_1_19> for Config {
},
socks5: value.socks5.into(),
},
storage_paths: SocksClientPaths {
common_paths: CommonClientPaths {
storage_paths: SocksClientPathsV1_1_20_2 {
common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_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 {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let base_dir = must_get_home();
@@ -78,7 +80,7 @@ impl MigrationNymConfig for ConfigV1_1_19 {
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5V1_1_19 {
pub struct Socks5V1_1_20 {
pub listening_port: u16,
pub provider_mix_address: String,
@@ -93,12 +95,12 @@ pub struct Socks5V1_1_19 {
pub send_anonymously: bool,
#[serde(default)]
pub socks5_debug: Socks5DebugV1_1_19,
pub socks5_debug: Socks5DebugV1_1_20,
}
impl From<Socks5V1_1_19> for Socks5 {
fn from(value: Socks5V1_1_19) -> Self {
Socks5 {
impl From<Socks5V1_1_20> for Socks5V1_1_20_2 {
fn from(value: Socks5V1_1_20) -> Self {
Socks5V1_1_20_2 {
listening_port: value.listening_port,
provider_mix_address: value.provider_mix_address,
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)]
#[serde(deny_unknown_fields)]
pub struct Socks5DebugV1_1_19 {
pub struct Socks5DebugV1_1_20 {
connection_start_surbs: u32,
per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_19> for Socks5Debug {
fn from(value: Socks5DebugV1_1_19) -> Self {
Socks5Debug {
impl From<Socks5DebugV1_1_20> for Socks5DebugV1_1_20_2 {
fn from(value: Socks5DebugV1_1_20) -> Self {
Socks5DebugV1_1_20_2 {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
}
}
impl Default for Socks5DebugV1_1_19 {
impl Default for Socks5DebugV1_1_20 {
fn default() -> Self {
Socks5DebugV1_1_19 {
Socks5DebugV1_1_20 {
connection_start_surbs: DEFAULT_CONNECTION_START_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.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# DEPRECATED
[core.client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
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 }}'
# Path to the file containing information about gateway used by this client,
# i.e. details such as its public key, owner address or the network information.
gateway_details = '{{ storage_paths.gateway_details }}'
##### 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"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
"once_cell",
"version_check",
]
@@ -225,9 +225,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.0"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64ct"
@@ -622,7 +622,7 @@ dependencies = [
"cosmos-sdk-proto",
"ecdsa",
"eyre",
"getrandom 0.2.9",
"getrandom 0.2.10",
"k256",
"prost",
"prost-types",
@@ -865,6 +865,20 @@ dependencies = [
"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]]
name = "cw-storage-plus"
version = "0.13.4"
@@ -1428,9 +1442,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.9"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -2244,6 +2258,7 @@ name = "nym-client-core"
version = "1.1.14"
dependencies = [
"async-trait",
"base64 0.21.2",
"dashmap",
"dirs 4.0.0",
"futures",
@@ -2266,6 +2281,7 @@ dependencies = [
"rand 0.7.3",
"serde",
"serde_json",
"sha2 0.10.6",
"sqlx 0.6.3",
"tap",
"thiserror",
@@ -2329,7 +2345,7 @@ dependencies = [
"bs58",
"digest 0.9.0",
"ff 0.11.1",
"getrandom 0.2.9",
"getrandom 0.2.10",
"group 0.11.0",
"itertools",
"nym-dkg",
@@ -2389,7 +2405,7 @@ dependencies = [
[[package]]
name = "nym-contracts-common"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"bs58",
"cosmwasm-std",
@@ -2424,7 +2440,7 @@ dependencies = [
[[package]]
name = "nym-crypto"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"aes 0.8.2",
"blake3",
@@ -2473,7 +2489,7 @@ name = "nym-gateway-client"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.9",
"getrandom 0.2.10",
"log",
"nym-bandwidth-controller",
"nym-coconut-interface",
@@ -2531,7 +2547,7 @@ dependencies = [
[[package]]
name = "nym-mixnet-contract-common"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"bs58",
"cosmwasm-std",
@@ -2620,7 +2636,7 @@ dependencies = [
"chacha20",
"chacha20poly1305",
"curve25519-dalek",
"getrandom 0.2.9",
"getrandom 0.2.10",
"log",
"rand 0.7.3",
"rayon",
@@ -2631,7 +2647,7 @@ dependencies = [
[[package]]
name = "nym-pemstore"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"pem",
]
@@ -2641,8 +2657,12 @@ name = "nym-service-provider-directory-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"nym-contracts-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -2796,7 +2816,7 @@ dependencies = [
"aes-gcm",
"argon2",
"generic-array 0.14.7",
"getrandom 0.2.9",
"getrandom 0.2.10",
"rand 0.8.5",
"serde",
"serde_json",
@@ -2895,7 +2915,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract-common"
version = "0.6.0"
version = "0.7.0"
dependencies = [
"cosmwasm-std",
"nym-contracts-common",
@@ -3382,7 +3402,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
]
[[package]]
@@ -3450,7 +3470,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
"redox_syscall 0.2.16",
"thiserror",
]
@@ -3478,7 +3498,7 @@ version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [
"base64 0.21.0",
"base64 0.21.2",
"bytes",
"encoding_rs",
"futures-core",
@@ -3612,7 +3632,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [
"base64 0.21.0",
"base64 0.21.2",
]
[[package]]
@@ -4368,7 +4388,7 @@ dependencies = [
"bytes",
"flex-error",
"futures",
"getrandom 0.2.9",
"getrandom 0.2.10",
"http",
"hyper",
"hyper-proxy",
@@ -4768,7 +4788,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom 0.2.9",
"getrandom 0.2.10",
"wasm-bindgen",
]
@@ -4944,7 +4964,7 @@ name = "wasm-utils"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.9",
"getrandom 0.2.10",
"indexed_db_futures",
"js-sys",
"nym-store-cipher",
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.1.0"
version = "1.1.1"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
+41 -4
View File
@@ -116,11 +116,11 @@ async function testWithTester() {
// B) first get topology directly from nym-api
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// 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)
// 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
// 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() {
@@ -330,14 +364,17 @@ async function main() {
// sets up better stack traces in case of in-rust panics
set_panic_hook();
// show reconnection capabilities
// await testerReconnection()
// run test on simplified and dedicated tester:
// await testWithTester()
await testWithTester()
// hook-up the whole client for testing
// await testWithNymClient()
// 'Normal' client setup (to send 'normal' messages)
await normalNymClientUsage()
// await normalNymClientUsage()
}
// Let's get started!
+2 -2
View File
@@ -29,7 +29,7 @@ impl Config {
#[wasm_bindgen(constructor)]
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
Config {
base: BaseClientConfig::new(id)
base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string())
.with_custom_nyxd(vec![validator_server
.parse()
.expect("provided url was malformed")])
@@ -39,7 +39,7 @@ impl Config {
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id)
base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
.with_disabled_credentials(true)
.with_disabled_cover_traffic(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::error::WasmClientError;
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,
};
use crate::storage::traits::FullWasmClientStorage;
@@ -15,13 +15,11 @@ use crate::storage::ClientStorage;
use crate::topology::WasmNymTopology;
use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController;
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::replies::reply_storage::browser_backend;
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
use nym_task::TaskManager;
@@ -29,7 +27,7 @@ use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use rand::{thread_rng, RngCore};
use rand::RngCore;
use std::sync::Arc;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
@@ -63,14 +61,7 @@ pub struct NymClientBuilder {
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
reply_surb_storage_backend: browser_backend::Backend,
on_message: js_sys::Function,
// unimplemented:
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
}
#[wasm_bindgen]
@@ -83,15 +74,10 @@ impl NymClientBuilder {
storage_passphrase: Option<String>,
) -> Self {
NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(
config.base.debug.reply_surbs,
),
config,
custom_topology: None,
storage_passphrase,
on_message,
bandwidth_controller: None,
disabled_credentials: true,
preferred_gateway,
}
}
@@ -114,14 +100,9 @@ impl NymClientBuilder {
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID);
NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(
full_config.base.debug.reply_surbs,
),
config: full_config,
custom_topology: Some(topology.into()),
on_message,
bandwidth_controller: None,
disabled_credentials: true,
storage_passphrase: None,
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> {
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();
// 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())
.await?;
let user_chosen = self.preferred_gateway.clone();
// 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 {
gateway_from_topology(
&mut thread_rng(),
self.preferred_gateway.as_deref(),
topology,
&client_store,
)
.await?
if let Some(topology) = &self.custom_topology {
setup_from_topology(user_chosen, topology, &client_store).await?
} else {
choose_gateway(
&client_store,
self.preferred_gateway.clone(),
&nym_api_endpoints,
)
.await?
setup_gateway_from_api(&client_store, user_chosen, &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 mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = BaseClientBuilder::new(
&gateway_endpoint,
&self.config.base.debug,
client_store,
self.bandwidth_controller,
self.reply_surb_storage_backend,
disabled_credentials,
nym_api_endpoints,
);
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> =
BaseClientBuilder::<FakeClient<DirectSigningNyxdClient>, _>::new(
&self.config.base,
storage,
None,
);
if let Some(topology_provider) = maybe_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),
_full_topology: None,
_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 nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails};
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::NymTopology;
use nym_validator_client::client::{IdentityKey, IdentityKeyRef};
use nym_topology::{gateway, NymTopology};
use nym_validator_client::client::IdentityKey;
use nym_validator_client::NymApiClient;
use rand::{CryptoRng, Rng};
use rand::thread_rng;
use url::Url;
use wasm_bindgen::prelude::wasm_bindgen;
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
// 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,
chosen_gateway: Option<IdentityKey>,
nym_apis: &[Url],
) -> Result<GatewayEndpointConfig, WasmClientError> {
let existing_gateway_config = client_store.read_gateway_config().await?;
console_log!("loaded: {:?}", existing_gateway_config);
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)
) -> Result<InitialisationDetails, WasmClientError> {
let mut rng = thread_rng();
let gateways = current_gateways(&mut rng, nym_apis).await?;
setup_gateway(client_store, chosen_gateway, &gateways).await
}
pub(crate) async fn gateway_from_topology<R: Rng + CryptoRng>(
rng: &mut R,
explicit_gateway: Option<IdentityKeyRef<'_>>,
pub(crate) async fn setup_from_topology(
explicit_gateway: Option<IdentityKey>,
topology: &NymTopology,
client_store: &ClientStorage,
) -> Result<GatewayEndpointConfig, WasmClientError> {
let existing_gateway_config = client_store.read_gateway_config().await?;
console_log!("loaded: {:?}", existing_gateway_config);
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)
) -> Result<InitialisationDetails, WasmClientError> {
let gateways = topology.gateways();
setup_gateway(client_store, explicit_gateway, gateways).await
}
@@ -16,6 +16,9 @@ pub enum ClientStorageError {
#[error("{typ} cryptographic key is not available in storage")]
CryptoKeyNotInStorage { typ: String },
#[error("the prior gateway details are not available in the storage")]
GatewayDetailsNotInStorage,
}
impl From<ClientStorageError> for JsValue {
+44 -10
View File
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::storage::errors::ClientStorageError;
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_gateway_client::SharedKeys;
use nym_sphinx::acknowledgements::AckKey;
@@ -27,8 +28,8 @@ mod v1 {
pub const CORE_STORE: &str = "core";
// keys
// TODO: to replace with FULL config
pub const GATEWAY_CONFIG: &str = "gateway_config";
pub const CONFIG: &str = "config";
pub const GATEWAY_DETAILS: &str = "gateway_details";
pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair";
pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair";
@@ -110,15 +111,32 @@ impl ClientStorage {
})
}
pub(crate) async fn read_gateway_config(
&self,
) -> Result<Option<GatewayEndpointConfig>, ClientStorageError> {
// TODO: persist client's config
#[allow(dead_code)]
pub(crate) async fn read_config(&self) -> Result<Option<Config>, ClientStorageError> {
self.inner
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_CONFIG))
.read_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG))
.await
.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(
&self,
) -> Result<Option<identity::KeyPair>, ClientStorageError> {
@@ -244,17 +262,33 @@ impl ClientStorage {
.map_err(Into::into)
}
pub(crate) async fn store_gateway_config(
pub(crate) async fn store_gateway_details(
&self,
gateway_endpoint: &GatewayEndpointConfig,
gateway_endpoint: &PersistedGatewayDetails,
) -> Result<(), ClientStorageError> {
self.inner
.store_value(
v1::CORE_STORE,
JsValue::from_str(v1::GATEWAY_CONFIG),
JsValue::from_str(v1::GATEWAY_DETAILS),
gateway_endpoint,
)
.await
.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::ClientStorage;
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::key_manager::persistence::KeyStore;
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`
// implementing all traits and everything getting combined
pub struct FullWasmClientStorage {
key_store: ClientStorage,
reply_storage: browser_backend::Backend,
credential_storage: EphemeralCredentialStorage,
pub(crate) keys_and_gateway_store: ClientStorage,
pub(crate) reply_storage: browser_backend::Backend,
pub(crate) credential_storage: EphemeralCredentialStorage,
}
impl MixnetClientStorage for FullWasmClientStorage {
@@ -24,12 +27,14 @@ impl MixnetClientStorage for FullWasmClientStorage {
type ReplyStore = browser_backend::Backend;
type CredentialStore = EphemeralCredentialStorage;
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
(self.key_store, self.reply_storage, self.credential_storage)
type GatewayDetailsStore = ClientStorage;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_storage, self.credential_storage)
}
fn key_store(&self) -> &Self::KeyStore {
&self.key_store
&self.keys_and_gateway_store
}
fn reply_store(&self) -> &Self::ReplyStore {
@@ -39,6 +44,10 @@ impl MixnetClientStorage for FullWasmClientStorage {
fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_storage
}
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.keys_and_gateway_store
}
}
#[async_trait(?Send)]
@@ -74,3 +83,19 @@ impl KeyStore for ClientStorage {
.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
#![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::receiver::ReceivedReceiver;
use serde::{Deserialize, Serialize};
@@ -12,6 +15,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, console_warn};
#[derive(Clone)]
@@ -107,3 +111,37 @@ impl Drop for TestMarker {
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::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::tester::ephemeral_receiver::EphemeralTestReceiver;
use crate::tester::helpers::{
NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
GatewayReconnection, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
};
use crate::topology::WasmNymTopology;
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::BandwidthController;
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_gateway_client::GatewayClient;
use nym_node_tester_utils::receiver::SimpleMessageReceiver;
@@ -28,7 +28,6 @@ use nym_task::TaskManager;
use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use rand::{CryptoRng, Rng};
use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex as SyncMutex};
@@ -74,6 +73,7 @@ pub struct NymNodeTester {
#[wasm_bindgen]
pub struct NymNodeTesterBuilder {
gateway: Option<IdentityKey>,
id: Option<String>,
base_topology: NymTopology,
@@ -95,9 +95,11 @@ impl NymNodeTesterBuilder {
#[wasm_bindgen(constructor)]
pub fn new(
base_topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> NymNodeTesterBuilder {
NymNodeTesterBuilder {
id,
gateway,
base_topology: base_topology.into(),
bandwidth_controller: None,
@@ -106,43 +108,51 @@ impl NymNodeTesterBuilder {
async fn _new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> {
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 {
Self::_new_with_api(api_url, gateway)
Self::_new_with_api(api_url, id, gateway)
.await
.into_promise_result()
})
}
async fn gateway_info<R: Rng + CryptoRng>(
async fn gateway_info(
&self,
rng: &mut R,
client_store: &ClientStorage,
) -> Result<GatewayEndpointConfig, WasmClientError> {
gateway_from_topology(
rng,
self.gateway.as_deref(),
&self.base_topology,
client_store,
)
.await
) -> Result<InitialisationDetails, WasmClientError> {
if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await {
Ok(loaded)
} else {
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await
}
}
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
let mut rng = OsRng;
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 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 (ack_sender, ack_receiver) = mpsc::unbounded();
@@ -151,7 +161,7 @@ impl NymNodeTesterBuilder {
gateway_endpoint.gateway_listener,
managed_keys.identity_keypair(),
gateway_identity,
managed_keys.gateway_shared_key(),
Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender,
ack_sender,
Duration::from_secs(10),
@@ -160,14 +170,11 @@ impl NymNodeTesterBuilder {
);
gateway_client.set_disabled_credentials_mode(true);
let shared_keys = gateway_client.authenticate_and_start().await?;
managed_keys
.deal_with_gateway_key(shared_keys, &client_store)
.await?;
gateway_client.authenticate_and_start().await?;
// TODO: make those values configurable later
let tester = NodeTester::new(
rng,
OsRng,
self.base_topology,
Some(address(&managed_keys, gateway_identity)),
PacketSize::default(),
@@ -241,29 +248,46 @@ async fn test_mixnode(
impl NymNodeTester {
#[wasm_bindgen(constructor)]
#[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!");
NymNodeTesterBuilder::new(topology, gateway).setup_client()
NymNodeTesterBuilder::new(topology, id, gateway).setup_client()
}
async fn _new_with_api(
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> {
NymNodeTesterBuilder::_new_with_api(api_url, gateway)
NymNodeTesterBuilder::_new_with_api(api_url, id, gateway)
.await?
._setup_client()
.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 {
Self::_new_with_api(api_url, gateway)
Self::_new_with_api(api_url, id, gateway)
.await
.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(
&self,
mixnode_identity: String,
+15 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bin-common"
version = "0.5.0"
version = "0.6.0"
description = "Common code for nym binaries"
edition = { workspace = true }
authors = { workspace = true }
@@ -19,11 +19,17 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
## tracing
tracing-appender = { version = "0.2.2", optional = true }
tracing-subscriber = { version = "0.3.16", features = [
"env-filter",
], 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]
@@ -37,4 +43,10 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features]
default = []
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 {
// VERGEN_BUILD_TIMESTAMP
/// 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};
#[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")]
pub use tracing_subscriber;
#[cfg(feature = "tracing")]
@@ -43,27 +47,35 @@ pub fn setup_logging() {
#[cfg(feature = "tracing")]
#[macro_export]
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::util::SubscriberInitExt;
let file_appender =
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()
let registry = nym_bin_common::logging::tracing_subscriber::Registry::default()
.with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env())
.with(appender_layer)
.with(
nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4)
.with_targets(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]
name = "nym-client-core"
version = "1.1.14"
version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
@@ -9,6 +9,7 @@ rust-version = "1.66"
[dependencies]
async-trait = { workspace = true }
base64 = "0.21.2"
dirs = "4.0"
dashmap = "5.4.0"
futures = "0.3"
@@ -17,6 +18,7 @@ log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = "0.10.6"
tap = "1.0.1"
thiserror = "1.0.34"
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::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider;
use rand::rngs::OsRng;
use std::sync::Arc;
use tap::TapFallible;
use url::Url;
@@ -50,6 +49,8 @@ use url::Url;
#[cfg(target_arch = "wasm32")]
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"))]
use nym_validator_client::nyxd::traits::DkgQueryClient;
@@ -157,17 +158,11 @@ impl From<bool> for CredentialsToggle {
}
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
// due to wasm limitations I had to split it like this : (
gateway_config: &'a GatewayEndpointConfig,
debug_config: &'a DebugConfig,
disabled_credentials: bool,
nym_api_endpoints: Vec<Url>,
reply_storage_backend: S::ReplyStore,
key_store: S::KeyStore,
config: &'a Config,
client_store: S,
dkg_query_client: Option<C>,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
managed_keys: ManagedKeys,
setup_method: GatewaySetup,
}
impl<'a, C, S> BaseClientBuilder<'a, C, S>
@@ -175,47 +170,23 @@ where
S: MixnetClientStorage + 'static,
C: DkgQueryClient + Send + Sync + 'static,
{
// TODO: combine all storages
pub fn new_from_base_config(
pub fn new(
base_config: &'a Config,
key_store: S::KeyStore,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
reply_storage_backend: S::ReplyStore,
client_store: S,
dkg_query_client: Option<C>,
) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder {
gateway_config: base_config.get_gateway_endpoint_config(),
debug_config: &base_config.debug,
disabled_credentials: base_config.get_disabled_credentials_mode(),
nym_api_endpoints: base_config.get_nym_api_endpoints(),
bandwidth_controller,
reply_storage_backend,
key_store,
managed_keys: ManagedKeys::Invalidated,
config: base_config,
client_store,
dkg_query_client,
custom_topology_provider: None,
setup_method: GatewaySetup::MustLoad,
}
}
// TODO: combine all storages
pub fn new(
gateway_config: &'a GatewayEndpointConfig,
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_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
self
}
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`
// 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(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
*managed_keys.identity_public_key(),
*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_config.gateway_id).unwrap(),
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
)
}
@@ -318,7 +292,10 @@ where
}
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,
ack_sender: AcknowledgementSender,
shutdown: TaskClient,
@@ -327,33 +304,26 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let gateway_id = self.gateway_config.gateway_id.clone();
if gateway_id.is_empty() {
return Err(ClientCoreError::GatewayIdUnknown);
}
let gateway_address = self.gateway_config.gateway_listener.clone();
if gateway_address.is_empty() {
return Err(ClientCoreError::GatewayAddressUnknown);
}
let gateway_address = gateway_config.gateway_listener.clone();
let gateway_id = gateway_config.gateway_id;
// TODO: in theory, at this point, this should be infallible
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let mut gateway_client = GatewayClient::new(
gateway_address,
self.managed_keys.identity_keypair(),
managed_keys.identity_keypair(),
gateway_identity,
self.managed_keys.gateway_shared_key(),
Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender,
ack_sender,
self.debug_config
.gateway_connection
.gateway_response_timeout,
self.bandwidth_controller.take(),
config.debug.gateway_connection.gateway_response_timeout,
bandwidth_controller,
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
.authenticate_and_start()
@@ -362,12 +332,7 @@ where
log::error!("Could not authenticate and start up the gateway connection - {err}")
})?;
self.managed_keys
.deal_with_gateway_key(shared_key, &self.key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
managed_keys.ensure_gateway_key(shared_key);
Ok(gateway_client)
}
@@ -473,21 +438,41 @@ where
Ok(mem_store)
}
async fn initial_key_setup(&mut self) {
assert!(!self.managed_keys.is_valid());
let mut rng = OsRng;
self.managed_keys = ManagedKeys::load_or_generate(&mut rng, &self.key_store).await;
async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
<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>
where
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
S::ReplyStore: 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::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{
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
// TODO: make the channels be internally created by the relevant components
@@ -515,34 +500,39 @@ where
let (reply_controller_sender, reply_controller_receiver) =
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,
// do not change that.
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe())
.await?;
let reply_storage = Self::setup_persistent_reply_storage(
self.reply_storage_backend,
let gateway_client = Self::start_gateway_client(
self.config,
gateway_config,
&managed_keys,
bandwidth_controller,
mixnet_messages_sender,
ack_sender,
task_manager.subscribe(),
)
.await?;
let reply_storage =
Self::setup_persistent_reply_storage(reply_storage_backend, task_manager.subscribe())
.await?;
let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(),
self.nym_api_endpoints,
self.config.get_nym_api_endpoints(),
);
Self::start_topology_refresher(
topology_provider,
self.debug_config.topology,
self.config.debug.topology,
shared_topology_accessor.clone(),
task_manager.subscribe(),
)
.await?;
Self::start_received_messages_buffer_controller(
self.managed_keys.encryption_keypair(),
managed_keys.encryption_keypair(),
received_buffer_request_receiver,
mixnet_messages_receiver,
reply_storage.key_storage(),
@@ -566,8 +556,8 @@ where
let shared_lane_queue_lengths = LaneQueueLengths::new();
let controller_config = real_messages_control::Config::new(
self.debug_config,
self.managed_keys.ack_key(),
&self.config.debug,
managed_keys.ack_key(),
self_address,
);
@@ -583,17 +573,18 @@ where
shared_lane_queue_lengths.clone(),
client_connection_rx,
task_manager.subscribe(),
self.debug_config.traffic.packet_type,
self.config.debug.traffic.packet_type,
);
if !self
.debug_config
.config
.debug
.cover_traffic
.disable_loop_cover_traffic_stream
{
Self::start_cover_traffic_stream(
self.debug_config,
self.managed_keys.ack_key(),
&self.config.debug,
managed_keys.ack_key(),
self_address,
shared_topology_accessor.clone(),
message_sender,
@@ -122,13 +122,30 @@ pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>(
nym_api_url: Url,
storage: 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 mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config");
// overwrite env configuration with config URLs
client_config = client_config.with_urls(nyxd_url, nym_api_url);
let client = nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
BandwidthController::new(storage, client)
nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query 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.
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, InMemGatewayDetails,
};
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend;
use nym_credential_storage::ephemeral_storage::{
EphemeralStorage as EphemeralCredentialStorage, EphemeralStorage,
};
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_credential_storage::storage::Storage as CredentialStorage;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::non_wasm_helpers;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::key_manager::persistence::OnDiskKeys;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend;
@@ -25,24 +28,33 @@ use crate::error::ClientCoreError;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
pub mod gateway_details;
// TODO: ideally this should be changed into
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
pub trait MixnetClientStorage {
type KeyStore: KeyStore;
type ReplyStore: ReplyStorageBackend;
type CredentialStore: CredentialStorage;
type GatewayDetailsStore: GatewayDetailsStore;
// 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 reply_store(&self) -> &Self::ReplyStore;
fn credential_store(&self) -> &Self::CredentialStore;
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore;
}
#[derive(Default)]
pub struct Ephemeral {
key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty,
credential_store: EphemeralStorage,
credential_store: EphemeralCredentialStorage,
gateway_details_store: InMemGatewayDetails,
}
impl Ephemeral {
@@ -55,9 +67,10 @@ impl MixnetClientStorage for Ephemeral {
type KeyStore = InMemEphemeralKeys;
type ReplyStore = reply_storage::Empty;
type CredentialStore = EphemeralCredentialStorage;
type GatewayDetailsStore = InMemGatewayDetails;
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
(self.key_store, self.reply_store, self.credential_store)
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
}
fn key_store(&self) -> &Self::KeyStore {
@@ -71,6 +84,10 @@ impl MixnetClientStorage for Ephemeral {
fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_store
}
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.gateway_details_store
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -78,6 +95,7 @@ pub struct OnDiskPersistent {
pub(crate) key_store: OnDiskKeys,
pub(crate) reply_store: fs_backend::Backend,
pub(crate) credential_store: PersistentCredentialStorage,
pub(crate) gateway_details_store: OnDiskGatewayDetails,
}
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -86,11 +104,13 @@ impl OnDiskPersistent {
key_store: OnDiskKeys,
reply_store: fs_backend::Backend,
credential_store: PersistentCredentialStorage,
gateway_details_store: OnDiskGatewayDetails,
) -> Self {
Self {
key_store,
reply_store,
credential_store,
gateway_details_store,
}
}
@@ -109,10 +129,13 @@ impl OnDiskPersistent {
let credential_store =
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await;
let gateway_details_store = OnDiskGatewayDetails::new(paths.gateway_details);
Ok(OnDiskPersistent {
key_store,
reply_store,
credential_store,
gateway_details_store,
})
}
}
@@ -122,9 +145,10 @@ impl MixnetClientStorage for OnDiskPersistent {
type KeyStore = OnDiskKeys;
type ReplyStore = fs_backend::Backend;
type CredentialStore = PersistentCredentialStorage;
type GatewayDetailsStore = OnDiskGatewayDetails;
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
(self.key_store, self.reply_store, self.credential_store)
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store)
}
fn key_store(&self) -> &Self::KeyStore {
@@ -138,4 +162,8 @@ impl MixnetClientStorage for OnDiskPersistent {
fn credential_store(&self) -> &Self::CredentialStore {
&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::addressing::clients::Recipient;
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 rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin;
@@ -63,6 +63,8 @@ where
/// Optional secondary predefined packet size used for the loop cover messages.
secondary_packet_size: Option<PacketSize>,
packet_type: PacketType,
}
impl<R> Stream for LoopCoverTrafficStream<R>
@@ -135,6 +137,7 @@ impl LoopCoverTrafficStream<OsRng> {
topology_access,
primary_packet_size: traffic_config.primary_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.cover_traffic.loop_cover_traffic_average_delay,
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");
@@ -6,6 +6,7 @@ use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore};
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use zeroize::ZeroizeOnDrop;
@@ -20,6 +21,16 @@ pub enum ManagedKeys {
Invalidated,
}
impl Debug for ManagedKeys {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ManagedKeys::Initial(_) => write!(f, "initial"),
ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
ManagedKeys::Invalidated => write!(f, "invalidated"),
}
}
}
impl From<KeyManagerBuilder> for ManagedKeys {
fn from(value: KeyManagerBuilder) -> Self {
ManagedKeys::Initial(value)
@@ -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>> {
match self {
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>(
&mut self,
gateway_shared_key: Arc<SharedKeys>,
@@ -120,12 +147,7 @@ impl ManagedKeys {
key_manager
}
ManagedKeys::FullyDerived(key_manager) => {
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!")
}
self.ensure_gateway_key(gateway_shared_key);
key_manager
}
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
@@ -4,6 +4,7 @@
use crate::client::key_manager::KeyManager;
use async_trait::async_trait;
use std::error::Error;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))]
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
@@ -36,6 +37,7 @@ pub enum OnDiskKeysError {
KeyPairLoadFailure {
keys: String,
paths: nym_pemstore::KeyPairPath,
#[source]
err: std::io::Error,
},
@@ -43,6 +45,7 @@ pub enum OnDiskKeysError {
KeyPairStoreFailure {
keys: String,
paths: nym_pemstore::KeyPairPath,
#[source]
err: std::io::Error,
},
@@ -50,6 +53,7 @@ pub enum OnDiskKeysError {
KeyLoadFailure {
key: String,
path: String,
#[source]
err: std::io::Error,
},
@@ -57,6 +61,7 @@ pub enum OnDiskKeysError {
KeyStoreFailure {
key: String,
path: String,
#[source]
err: std::io::Error,
},
}
@@ -79,6 +84,26 @@ impl OnDiskKeys {
OnDiskKeys { paths }
}
#[doc(hidden)]
pub fn ephemeral_load_gateway_keys(
&self,
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
self.load_key(self.paths.gateway_shared_key(), "gateway shared 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>(
&self,
path: &std::path::Path,
@@ -132,13 +157,8 @@ impl OnDiskKeys {
}
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
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 identity_keypair = self.load_identity_keypair()?;
let encryption_keypair = self.load_encryption_keypair()?;
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
let gateway_shared_key: SharedKeys =
@@ -193,10 +213,12 @@ impl KeyStore for OnDiskKeys {
}
#[derive(Default)]
pub struct InMemEphemeralKeys;
pub struct InMemEphemeralKeys {
keys: Mutex<Option<KeyManager>>,
}
#[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;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -205,10 +227,11 @@ impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError;
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(())
}
}
@@ -565,7 +565,7 @@ where
fragments: Vec<Fragment>,
reply_surbs: Vec<ReplySurb>,
) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> {
debug_assert_ne!(
debug_assert_eq!(
fragments.len(),
reply_surbs.len(),
"attempted to send {} fragments with {} reply surbs",
@@ -6,16 +6,21 @@ use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
pub mod keys_paths;
pub mod old_v1_1_20_2;
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
// TODO:
// pub gateway_config_pathfinder: (),
/// Path to the file containing information about gateway used by this client,
/// i.e. details such as its public key, owner address or the network information.
pub gateway_details: PathBuf,
/// Path to the database containing bandwidth credentials of this client.
pub credentials_database: PathBuf,
@@ -30,6 +35,7 @@ impl CommonClientPaths {
CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
keys: ClientKeysPaths::new_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 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'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -65,15 +66,19 @@ pub struct Config {
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
pub fn new<S: Into<String>>(id: S, version: S) -> Self {
Config {
client: Client::new_default(id),
client: Client::new_default(id, version),
debug: Default::default(),
}
}
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self {
Config { client, debug }
}
pub fn validate(&self) -> bool {
self.client.validate() && self.debug.validate()
self.debug.validate()
}
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
@@ -86,19 +91,6 @@ impl Config {
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 {
self.client.nyxd_urls = urls;
self
@@ -174,22 +166,6 @@ impl Config {
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
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)]
@@ -242,6 +218,8 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct Client {
/// Version of the client for which this configuration was created.
pub version: String,
@@ -251,6 +229,7 @@ pub struct Client {
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
@@ -261,15 +240,10 @@ pub struct Client {
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// 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 {
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 nyxd_urls = network
.endpoints
@@ -283,19 +257,27 @@ impl Client {
.collect::<Vec<_>>();
Client {
version: env!("CARGO_PKG_VERSION").to_string(),
version: version.into(),
id: id.into(),
disabled_credentials_mode: true,
nyxd_urls,
nym_api_urls,
gateway_endpoint: Default::default(),
}
}
pub fn validate(&self) -> bool {
!self.gateway_endpoint.gateway_id.is_empty()
&& !self.gateway_endpoint.gateway_owner.is_empty()
&& !self.gateway_endpoint.gateway_owner.is_empty()
pub fn new<S: Into<String>>(
id: S,
disabled_credentials_mode: bool,
nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
) -> Self {
Client {
version: env!("CARGO_PKG_VERSION").to_string(),
id: id.into(),
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
}
}
}
@@ -1,9 +1,9 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_19::{
AcknowledgementsV1_1_19, ClientV1_1_19, ConfigV1_1_19, CoverTrafficV1_1_19, DebugConfigV1_1_19,
GatewayConnectionV1_1_19, LoggingV1_1_19, ReplySurbsV1_1_19, TopologyV1_1_19, TrafficV1_1_19,
use crate::config::old_config_v1_1_20::{
AcknowledgementsV1_1_20, ClientV1_1_20, ConfigV1_1_20, CoverTrafficV1_1_20, DebugConfigV1_1_20,
GatewayConnectionV1_1_20, LoggingV1_1_20, ReplySurbsV1_1_20, TopologyV1_1_20, TrafficV1_1_20,
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE,
@@ -39,7 +39,7 @@ impl From<ExtendedPacketSize> for PacketSize {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> {
pub client: ClientV1_1_19<T>,
pub client: ClientV1_1_20<T>,
#[serde(default)]
pub logging: OldLoggingV1_1_13,
@@ -51,9 +51,9 @@ pub struct OldConfigV1_1_13<T> {
#[serde(deny_unknown_fields)]
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 {
LoggingV1_1_19 {}
LoggingV1_1_20 {}
}
}
@@ -114,10 +114,10 @@ pub struct OldDebugConfigV1_1_13 {
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 {
DebugConfigV1_1_19 {
traffic: TrafficV1_1_19 {
DebugConfigV1_1_20 {
traffic: TrafficV1_1_20 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -125,25 +125,25 @@ impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_19 {
primary_packet_size: PacketSize::RegularPacket,
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,
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,
},
acknowledgements: AcknowledgementsV1_1_19 {
acknowledgements: AcknowledgementsV1_1_20 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
},
topology: TopologyV1_1_19 {
topology: TopologyV1_1_20 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: false,
},
reply_surbs: ReplySurbsV1_1_19 {
reply_surbs: ReplySurbsV1_1_20 {
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,
@@ -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 {
ConfigV1_1_19 {
client: ClientV1_1_19 {
ConfigV1_1_20 {
client: ClientV1_1_20 {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
@@ -1,9 +1,10 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, CoverTraffic, DebugConfig, GatewayConnection, GatewayEndpointConfig,
ReplySurbs, Topology, Traffic,
use crate::config::old_config_v1_1_20_2::{
AcknowledgementsV1_1_20_2, CoverTrafficV1_1_20_2, DebugConfigV1_1_20_2,
GatewayConnectionV1_1_20_2, GatewayEndpointConfigV1_1_20_2, ReplySurbsV1_1_20_2,
TopologyV1_1_20_2, TrafficV1_1_20_2,
};
use nym_sphinx::params::{PacketSize, PacketType};
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)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19<T> {
pub client: ClientV1_1_19<T>,
pub struct ConfigV1_1_20<T> {
pub client: ClientV1_1_20<T>,
#[serde(default)]
pub logging: LoggingV1_1_19,
pub logging: LoggingV1_1_20,
#[serde(default)]
pub debug: DebugConfigV1_1_19,
pub debug: DebugConfigV1_1_20,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_19 {
pub struct GatewayEndpointConfigV1_1_20 {
pub gateway_id: String,
pub gateway_owner: String,
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV1_1_19> for GatewayEndpointConfig {
fn from(value: GatewayEndpointConfigV1_1_19) -> Self {
GatewayEndpointConfig {
impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
GatewayEndpointConfigV1_1_20_2 {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
@@ -82,7 +83,7 @@ impl From<GatewayEndpointConfigV1_1_19> for GatewayEndpointConfig {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_19<T> {
pub struct ClientV1_1_20<T> {
pub version: String,
pub id: String,
#[serde(default)]
@@ -97,7 +98,7 @@ pub struct ClientV1_1_19<T> {
pub public_encryption_key_file: PathBuf,
pub gateway_shared_key_file: PathBuf,
pub ack_key_file: PathBuf,
pub gateway_endpoint: GatewayEndpointConfigV1_1_19,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20,
pub database_path: PathBuf,
#[serde(default)]
pub reply_surb_database_path: PathBuf,
@@ -109,11 +110,11 @@ pub struct ClientV1_1_19<T> {
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingV1_1_19 {}
pub struct LoggingV1_1_20 {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_19 {
pub struct TrafficV1_1_20 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
@@ -123,9 +124,9 @@ pub struct TrafficV1_1_19 {
pub secondary_packet_size: Option<PacketSize>,
}
impl From<TrafficV1_1_19> for Traffic {
fn from(value: TrafficV1_1_19) -> Self {
Traffic {
impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
fn from(value: TrafficV1_1_20) -> Self {
TrafficV1_1_20_2 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
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 {
TrafficV1_1_19 {
TrafficV1_1_20 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -151,16 +152,16 @@ impl Default for TrafficV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV1_1_19 {
pub struct CoverTrafficV1_1_20 {
#[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_19> for CoverTraffic {
fn from(value: CoverTrafficV1_1_19) -> Self {
CoverTraffic {
impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
fn from(value: CoverTrafficV1_1_20) -> Self {
CoverTrafficV1_1_20_2 {
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,
@@ -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 {
CoverTrafficV1_1_19 {
CoverTrafficV1_1_20 {
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,
@@ -180,22 +181,22 @@ impl Default for CoverTrafficV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_19 {
pub struct GatewayConnectionV1_1_20 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_19> for GatewayConnection {
fn from(value: GatewayConnectionV1_1_19) -> Self {
GatewayConnection {
impl From<GatewayConnectionV1_1_20> for GatewayConnectionV1_1_20_2 {
fn from(value: GatewayConnectionV1_1_20) -> Self {
GatewayConnectionV1_1_20_2 {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV1_1_19 {
impl Default for GatewayConnectionV1_1_20 {
fn default() -> Self {
GatewayConnectionV1_1_19 {
GatewayConnectionV1_1_20 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -203,7 +204,7 @@ impl Default for GatewayConnectionV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_19 {
pub struct AcknowledgementsV1_1_20 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
@@ -211,9 +212,9 @@ pub struct AcknowledgementsV1_1_19 {
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV1_1_19> for Acknowledgements {
fn from(value: AcknowledgementsV1_1_19) -> Self {
Acknowledgements {
impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
fn from(value: AcknowledgementsV1_1_20) -> Self {
AcknowledgementsV1_1_20_2 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
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 {
AcknowledgementsV1_1_19 {
AcknowledgementsV1_1_20 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -233,7 +234,7 @@ impl Default for AcknowledgementsV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_19 {
pub struct TopologyV1_1_20 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
@@ -241,9 +242,9 @@ pub struct TopologyV1_1_19 {
pub disable_refreshing: bool,
}
impl From<TopologyV1_1_19> for Topology {
fn from(value: TopologyV1_1_19) -> Self {
Topology {
impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
fn from(value: TopologyV1_1_20) -> Self {
TopologyV1_1_20_2 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
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 {
TopologyV1_1_19 {
TopologyV1_1_20 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
@@ -263,7 +264,7 @@ impl Default for TopologyV1_1_19 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV1_1_19 {
pub struct ReplySurbsV1_1_20 {
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
@@ -279,9 +280,9 @@ pub struct ReplySurbsV1_1_19 {
pub maximum_reply_key_age: Duration,
}
impl From<ReplySurbsV1_1_19> for ReplySurbs {
fn from(value: ReplySurbsV1_1_19) -> Self {
ReplySurbs {
impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
fn from(value: ReplySurbsV1_1_20) -> Self {
ReplySurbsV1_1_20_2 {
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,
@@ -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 {
ReplySurbsV1_1_19 {
ReplySurbsV1_1_20 {
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,
@@ -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)]
pub struct DebugConfigV1_1_19 {
pub traffic: TrafficV1_1_19,
pub cover_traffic: CoverTrafficV1_1_19,
pub gateway_connection: GatewayConnectionV1_1_19,
pub acknowledgements: AcknowledgementsV1_1_19,
pub topology: TopologyV1_1_19,
pub reply_surbs: ReplySurbsV1_1_19,
pub struct DebugConfigV1_1_20 {
pub traffic: TrafficV1_1_20,
pub cover_traffic: CoverTrafficV1_1_20,
pub gateway_connection: GatewayConnectionV1_1_20,
pub acknowledgements: AcknowledgementsV1_1_20,
pub topology: TopologyV1_1_20,
pub reply_surbs: ReplySurbsV1_1_20,
}
impl From<DebugConfigV1_1_19> for DebugConfig {
fn from(value: DebugConfigV1_1_19) -> Self {
DebugConfig {
impl From<DebugConfigV1_1_20> for DebugConfigV1_1_20_2 {
fn from(value: DebugConfigV1_1_20) -> Self {
DebugConfigV1_1_20_2 {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.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>,
},
#[error("experienced a failure with our gateway details storage: {source}")]
GatewayDetailsStoreError {
source: Box<dyn Error + Send + Sync>,
},
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
@@ -97,6 +102,20 @@ pub enum ClientCoreError {
"This operation would have resulted in clients keys being overwritten without permission"
)]
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
+9 -9
View File
@@ -44,18 +44,18 @@ const MEASUREMENTS: usize = 3;
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
const PING_TIMEOUT: Duration = Duration::from_millis(1000);
struct GatewayWithLatency {
gateway: gateway::Node,
struct GatewayWithLatency<'a> {
gateway: &'a gateway::Node,
latency: Duration,
}
impl GatewayWithLatency {
fn new(gateway: gateway::Node, latency: Duration) -> Self {
impl<'a> GatewayWithLatency<'a> {
fn new(gateway: &'a gateway::Node, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency }
}
}
pub(super) async fn current_gateways<R: Rng>(
pub async fn current_gateways<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
) -> Result<Vec<gateway::Node>, ClientCoreError> {
@@ -64,7 +64,7 @@ pub(super) async fn current_gateways<R: Rng>(
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
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 valid_gateways = gateways
@@ -91,7 +91,7 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
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();
trace!(
"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>(
rng: &mut R,
gateways: Vec<gateway::Node>,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
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>(
rng: &mut R,
gateways: Vec<gateway::Node>,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
gateways
.choose(rng)
+273 -145
View File
@@ -3,26 +3,82 @@
//! 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::{KeyManager, ManagedKeys};
use crate::client::key_manager::ManagedKeys;
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
use crate::{
config::{disk_persistence::keys_paths::ClientKeysPaths, Config, GatewayEndpointConfig},
config::{Config, GatewayEndpointConfig},
error::ClientCoreError,
};
use nym_crypto::asymmetric::{encryption, identity};
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use serde::Serialize;
use std::fmt::{Debug, Display};
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 {
/// The gateway specification MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new, random, gateway.
New {
/// Should the new gateway be selected based on latency.
@@ -34,13 +90,13 @@ pub enum GatewaySetup {
},
Predefined {
/// Full gateway configuration
config: GatewayEndpointConfig,
details: PersistedGatewayDetails,
},
}
impl From<GatewayEndpointConfig> for GatewaySetup {
fn from(config: GatewayEndpointConfig) -> Self {
GatewaySetup::Predefined { config }
impl From<PersistedGatewayDetails> for GatewaySetup {
fn from(details: PersistedGatewayDetails) -> Self {
GatewaySetup::Predefined { details }
}
}
@@ -57,14 +113,11 @@ impl Default for GatewaySetup {
}
impl GatewaySetup {
pub fn new(
full_config: Option<GatewayEndpointConfig>,
gateway_identity: Option<IdentityKey>,
pub fn new_fresh(
gateway_identity: Option<String>,
latency_based_selection: Option<bool>,
) -> Self {
if let Some(config) = full_config {
GatewaySetup::Predefined { config }
} else if let Some(gateway_identity) = gateway_identity {
if let Some(gateway_identity) = gateway_identity {
GatewaySetup::Specified { gateway_identity }
} else {
GatewaySetup::New {
@@ -73,15 +126,22 @@ impl GatewaySetup {
}
}
pub async fn try_get_gateway_details(
self,
validator_servers: &[Url],
pub fn is_must_load(&self) -> bool {
matches!(self, GatewaySetup::MustLoad)
}
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> {
match self {
GatewaySetup::New { by_latency } => {
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
} else {
uniformly_random_gateway(&mut rng, gateways)
@@ -89,20 +149,28 @@ impl GatewaySetup {
}
.map(Into::into),
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)?;
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
gateways
.into_iter()
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
}
.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.
@@ -117,14 +185,14 @@ pub struct InitResults {
}
impl InitResults {
pub fn new(config: &Config, address: &Recipient) -> Self {
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
Self {
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: config.get_gateway_id(),
gateway_listener: config.get_gateway_listener(),
gateway_id: gateway.gateway_id.clone(),
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
/// and stores the newly obtained key
pub async fn get_registered_gateway<S>(
validator_servers: Vec<Url>,
key_store: &S::KeyStore,
setup: GatewaySetup,
overwrite_keys: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError>
// helpers for error wrapping
async fn _store_gateway_details<D>(
details_store: &D,
details: &PersistedGatewayDetails,
) -> Result<(), ClientCoreError>
where
S: MixnetClientStorage,
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
D: GatewayDetailsStore,
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;
// 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 {
Ok(_) => {
// if we loaded something and we don't have full gateway details, check if we can overwrite the data
if let GatewaySetup::Predefined { config } = setup {
// we already have defined gateway details AND a shared key, so nothing more for us to do
return Ok(config);
} else if overwrite_keys {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
Ok(loaded_keys) => {
match setup {
GatewaySetup::MustLoad => {
// get EVERYTHING from the storage
let details = loaded_details?;
ensure_valid_details(&details, &loaded_keys)?;
// no need to persist anything as we got everything from the storage
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
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
let our_identity = managed_keys.identity_keypair();
@@ -179,6 +373,9 @@ where
// Establish connection, authenticate and generate keys for talking with the gateway
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
.deal_with_gateway_key(shared_keys, key_store)
.await
@@ -186,108 +383,39 @@ where
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
/// arguments given it will do the sensible thing. Either it will
///
/// a. Reuse existing gateway configuration from storage.
/// b. Create a new gateway configuration but keep existing keys. This assumes that the caller
/// knows what they are doing and that the keys match the requested gateway.
/// c. Create a new gateway configuration with a newly registered gateway and keys.
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>
pub async fn setup_gateway<K, D>(
setup: &GatewaySetup,
key_store: &K,
details_store: &D,
overwrite_data: bool,
validator_servers: Option<&[Url]>,
) -> Result<InitialisationDetails, ClientCoreError>
where
KSt: KeyStore,
<KSt as KeyStore>::StorageError: Send + Sync + 'static,
K: KeyStore,
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 managed_keys =
crate::client::key_manager::ManagedKeys::load_or_generate(&mut rng, key_store).await;
let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
// Create new keys and derive our identity
let our_identity = managed_keys.identity_keypair();
// Establish connection, authenticate and generate keys for talking with the gateway
eprintln!("Registering with new gateway");
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
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(),
setup_gateway_from(
setup,
key_store,
details_store,
overwrite_data,
Some(&gateways),
)
}
/// 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)
.await
}
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
@@ -724,7 +724,7 @@ impl<C, St> GatewayClient<C, St> {
Ok(())
}
async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
pub async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
self.establish_connection().await?;
}
@@ -738,6 +738,12 @@ impl<C, St> GatewayClient<C, St> {
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>
where
C: DkgQueryClient + Send + Sync,
@@ -48,17 +48,21 @@ impl PacketRouter {
// 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
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 {
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);
} else if received_packet.len()
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.plaintext_size() - ack_overhead
|| received_packet.len() == PacketSize::OutfoxRegularPacket.size() - 6
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead
|| received_packet.len()
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
{
trace!("routing regular packet");
received_messages.push(received_packet);
@@ -29,6 +29,7 @@ log = { workspace = true }
url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = "0.3"
openssl = { version = "0.10", features = ["vendored"], optional = true }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -77,13 +78,14 @@ required-features = ["nyxd-client"]
nyxd-client = [
"async-trait",
"cosmrs",
"cosmwasm-std",
"cw3",
"cw4",
"prost",
"flate2",
"sha2",
"itertools",
"cosmwasm-std",
"openssl",
"prost",
"sha2",
"signing"
]
signing = [
@@ -1,12 +1,12 @@
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::ContractBuildInformation;
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
use nym_service_provider_directory_common::{
msg::QueryMsg as SpQueryMsg,
response::{
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
},
NymAddress, ServiceId, ServiceInfo,
NymAddress, Service, ServiceId,
};
use serde::Deserialize;
@@ -63,17 +63,14 @@ pub trait SpDirectoryQueryClient {
.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 start_after = None;
loop {
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);
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)
} else {
break;
@@ -82,6 +79,13 @@ pub trait SpDirectoryQueryClient {
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]
@@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_service_provider_directory_common::{
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceId, ServiceType,
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
};
use crate::nyxd::{
@@ -22,16 +23,16 @@ pub trait SpDirectorySigningClient {
async fn announce_service_provider(
&self,
nym_address: NymAddress,
service_type: ServiceType,
service: ServiceDetails,
owner_signature: MessageSignature,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::Announce {
nym_address,
service_type,
service,
owner_signature,
},
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-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
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,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
pub enum MixnetCommands {
/// Query the mixnet directory
@@ -15,6 +15,7 @@ pub struct MixnetOperators {
pub command: MixnetOperatorsCommands,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsCommands {
/// Manage your mixnode
@@ -1,6 +1,7 @@
use clap::Parser;
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 crate::context::SigningClient;
@@ -10,9 +11,15 @@ pub struct Args {
#[clap(long)]
pub nym_address: String,
#[clap(long)]
pub signature: MessageSignature,
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
#[clap(long)]
pub deposit: u128,
#[clap(long)]
pub identity_key: String,
}
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 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 deposit = Coin::new(args.deposit, denom);
let res = client
.announce_service_provider(nym_address, service_type, deposit.into(), None)
.announce_service_provider(service, args.signature, deposit.into(), None)
.await
.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};
pub mod announce;
pub mod announce_sign_payload;
pub mod delete;
#[derive(Debug, Args)]
@@ -10,10 +11,13 @@ pub struct MixnetOperatorsService {
pub command: MixnetOperatorsServiceCommands,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsServiceCommands {
/// Announce service provider to the world
Announce(announce::Args),
/// Delete entry for service provider from the directory
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 {
table.add_row(vec![
service.service_id.to_string(),
service.service.announcer.to_string(),
service.announcer.to_string(),
service.service.service_type.to_string(),
service.service.nym_address.to_string(),
]);
@@ -1,6 +1,6 @@
[package]
name = "nym-contracts-common"
version = "0.4.0"
version = "0.5.0"
description = "Common library for Nym cosmwasm contracts"
edition = { workspace = true }
authors = { workspace = true }
@@ -11,6 +11,9 @@ use std::ops::Mul;
use std::str::FromStr;
use thiserror::Error;
pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str;
pub fn truncate_decimal(amount: Decimal) -> Uint128 {
amount * Uint128::new(1)
}
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-contract-common"
version = "0.5.0"
version = "0.6.0"
description = "Common library for the Nym mixnet contract"
rust-version = "1.62"
edition = { workspace = true }
@@ -15,7 +15,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
schemars = "0.8"
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
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=0.4.1"
@@ -4,6 +4,7 @@
use crate::error::MixnetContractError;
use crate::families::{Family, FamilyHead};
use crate::{Layer, RewardedSetNodeStatus};
use contracts_common::IdentityKey;
use cosmwasm_std::Addr;
use cosmwasm_std::Coin;
use schemars::JsonSchema;
@@ -11,8 +12,6 @@ use serde::{Deserialize, Serialize};
use std::ops::Index;
// type aliases for better reasoning about available data
pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str;
pub type SphinxKey = String;
pub type SphinxKeyRef<'a> = &'a str;
pub type EpochId = u32;
@@ -7,5 +7,9 @@ edition = "2021"
[dependencies]
cosmwasm-std = { workspace = true }
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
schemars = "0.8"
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 cw_controllers::AdminError;
use nym_service_provider_directory_common::{NymAddress, ServiceId};
use nym_contracts_common::signing::verifier::ApiVerifierError;
use thiserror::Error;
use crate::{NymAddress, ServiceId};
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
pub enum SpContractError {
#[error("{0}")]
Std(#[from] StdError),
@@ -46,6 +48,21 @@ pub enum ContractError {
value: 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)
.add_attribute(ACTION, ServiceProviderEventType::Announce)
.add_attribute(SERVICE_ID, service_id.to_string())
.add_attribute(SERVICE_TYPE, service.service_type.to_string())
.add_attribute(NYM_ADDRESS, service.nym_address.to_string())
.add_attribute(SERVICE_TYPE, service.service.service_type.to_string())
.add_attribute(NYM_ADDRESS, service.service.nym_address.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)
.add_attribute(ACTION, ServiceProviderEventType::DeleteId)
.add_attribute(SERVICE_ID, service_id.to_string())
.add_attribute(NYM_ADDRESS, service.nym_address.to_string())
.add_attribute(SERVICE_ID, service.service_id.to_string())
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
}
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
@@ -1,6 +1,8 @@
pub mod error;
pub mod events;
pub mod msg;
pub mod response;
pub mod signing_types;
pub mod types;
// 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 nym_contracts_common::signing::MessageSignature;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -22,8 +23,8 @@ pub struct MigrateMsg {}
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
Announce {
nym_address: NymAddress,
service_type: ServiceType,
service: ServiceDetails,
owner_signature: MessageSignature,
},
DeleteId {
service_id: ServiceId,
@@ -44,9 +45,12 @@ impl ExecuteMsg {
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::Announce {
nym_address,
service_type,
} => format!("announcing {nym_address} as type {service_type}"),
service,
owner_signature: _,
} => format!(
"announcing {} as type {}",
service.nym_address, service.service_type
),
ExecuteMsg::DeleteId { service_id } => {
format!("deleting service with service id {service_id}")
}
@@ -76,6 +80,9 @@ pub enum QueryMsg {
limit: Option<u32>,
start_after: Option<ServiceId>,
},
SigningNonce {
address: String,
},
Config {},
GetContractVersion {},
#[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 schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -13,22 +13,17 @@ pub struct ServiceInfoResponse {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct ServicesListResponse {
pub services: Vec<ServiceInfo>,
pub services: Vec<Service>,
}
impl ServicesListResponse {
pub fn new(services: Vec<(ServiceId, Service)>) -> ServicesListResponse {
ServicesListResponse {
services: services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect(),
}
pub fn new(services: Vec<Service>) -> ServicesListResponse {
ServicesListResponse { services }
}
}
impl From<&[ServiceInfo]> for ServicesListResponse {
fn from(services: &[ServiceInfo]) -> Self {
impl From<&[Service]> for ServicesListResponse {
fn from(services: &[Service]) -> Self {
Self {
services: services.to_vec(),
}
@@ -38,21 +33,17 @@ impl From<&[ServiceInfo]> for ServicesListResponse {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedServicesListResponse {
pub services: Vec<ServiceInfo>,
pub services: Vec<Service>,
pub per_page: usize,
pub start_next_after: Option<ServiceId>,
}
impl PagedServicesListResponse {
pub fn new(
services: Vec<(ServiceId, Service)>,
services: Vec<Service>,
per_page: usize,
start_next_after: Option<ServiceId>,
) -> PagedServicesListResponse {
let services = services
.into_iter()
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
.collect();
PagedServicesListResponse {
services,
per_page,
@@ -66,12 +57,3 @@ impl PagedServicesListResponse {
pub struct ConfigResponse {
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 cosmwasm_std::{Addr, Coin};
use nym_contracts_common::IdentityKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -9,11 +10,11 @@ pub type ServiceId = u32;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct Service {
/// The address of the service.
pub nym_address: NymAddress,
/// The service type.
pub service_type: ServiceType,
/// Service owner.
/// Unique id assigned to the anounced service.
pub service_id: ServiceId,
/// The announced service.
pub service: ServiceDetails,
/// Address of the service owner.
pub announcer: Addr,
/// Block height at which the service was added.
pub block_height: u64,
@@ -21,6 +22,16 @@ pub struct Service {
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.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")]
@@ -28,7 +39,7 @@ pub enum NymAddress {
/// String representation of a nym address, which is of the form
/// client_id.client_enc@gateway_id.
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),
}
@@ -41,6 +52,7 @@ impl NymAddress {
pub fn as_str(&self) -> &str {
match self {
NymAddress::Address(address) => address,
//NymAddress::Name(name) => name,
}
}
}
@@ -66,19 +78,3 @@ impl std::fmt::Display for ServiceType {
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]
name = "nym-vesting-contract-common"
version = "0.6.0"
version = "0.7.0"
description = "Common library for the Nym vesting contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -9,8 +9,8 @@ repository = { workspace = true }
[dependencies]
cosmwasm-std = { workspace = true }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.5.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.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.5.0" }
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
ts-rs = {version = "6.1.2", optional = true}
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-crypto"
version = "0.3.0"
version = "0.4.0"
description = "Crypto library for the nym mixnet"
edition = { workspace = true }
authors = { workspace = true }
@@ -28,7 +28,7 @@ zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
# internal
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]
rand_chacha = "0.2"
+14 -14
View File
@@ -19,20 +19,20 @@ macro_rules! measure {
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
let start_cycles = $crate::cpu_cycles();
// if the block needs to return something, we can return it
let r = $x;
let end_cycles = $crate::cpu_cycles();
let name = if let Some(meta) = tracing::Span::current().metadata() {
meta.name()
} else {
"measure"
};
match (start_cycles, end_cycles) {
(Ok(start), Ok(end)) => info!("{} cpucycles: {}", name, end - start),
(Err(e), _) => error!("{e}"),
(_, Err(e)) => error!("{e}"),
}
r
// if the block needs to return something, we can return it
let r = $x;
let end_cycles = $crate::cpu_cycles();
let name = if let Some(meta) = tracing::Span::current().metadata() {
meta.name()
} else {
"measure"
};
match (start_cycles, end_cycles) {
(Ok(start), Ok(end)) => log::trace!("{} cpucycles: {}", name, end - start),
(Err(e), _) => error!("{e}"),
(_, Err(e)) => error!("{e}"),
}
r
} else {
$x
}
@@ -3,7 +3,7 @@
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
use nym_sphinx_types::{NymPacketError, SphinxError};
use nym_sphinx_types::{NymPacketError, OutfoxError, SphinxError};
use thiserror::Error;
#[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")]
ReceivedOldTypeVpnPacket,
#[error("failed to process received outfox packet: {0}")]
OutfoxProcessingError(#[from] OutfoxError),
}
@@ -20,12 +20,14 @@ use tracing::instrument;
type ForwardAck = MixPacket;
#[derive(Debug)]
pub struct ProcessedFinalHop {
pub destination: DestinationAddressBytes,
pub forward_ack: Option<ForwardAck>,
pub message: Vec<u8>,
}
#[derive(Debug)]
pub enum MixProcessingResult {
/// Contains unwrapped data that should first get delayed before being sent to next hop.
ForwardHop(MixPacket, Option<SphinxDelay>),
@@ -141,7 +143,7 @@ impl SphinxPacketProcessor {
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
Ok((first_hop, packet)) => (first_hop, packet),
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());
}
};
@@ -205,7 +207,7 @@ impl SphinxPacketProcessor {
if packet.is_final_hop() {
self.process_final_hop(
DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext().to_vec(),
packet.recover_plaintext()?.to_vec(),
packet_size,
packet_type,
)
@@ -239,7 +241,14 @@ impl SphinxPacketProcessor {
// for forward packets, extract next hop and set delay (but do NOT delay here)
// 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
# to separate crate?
nym-crypto = { path = "../crypto", version = "0.3.0" }
nym-crypto = { path = "../crypto", version = "0.4.0" }
nym-topology = { path = "../topology" }
[dev-dependencies]
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
# net2 via tokio-util -> tokio -> mio -> net2
+27 -10
View File
@@ -91,6 +91,7 @@ where
>(rng, full_address.encryption_key());
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 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 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 =
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.
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-pemstore"
description = "Store private-public keypairs in PEM format"
version = "0.2.0"
version = "0.3.0"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
+4 -2
View File
@@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::str::FromStr;
pub mod old_config_v1_1_20_2;
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
@@ -25,9 +27,9 @@ pub struct 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 {
base: BaseClientConfig::new(id),
base: BaseClientConfig::new(id, version),
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::StreamExt;
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::{
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::replies::reply_storage::ReplyStorageBackend;
use nym_client_core::config::DebugConfig;
use nym_client_core::init::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
@@ -51,6 +54,8 @@ pub struct NymClient<S> {
config: Config,
storage: S,
setup_method: GatewaySetup,
}
impl<S> NymClient<S>
@@ -59,10 +64,20 @@ where
S::ReplyStore: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
{
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)]
@@ -187,24 +202,16 @@ where
}
pub async fn start(self) -> Result<StartedSocks5Client, Socks5ClientCoreError> {
let (key_store, reply_storage_backend, credential_store) = self.storage.into_split();
// don't create bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.base.client.disabled_credentials_mode {
// 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 {
None
} else {
Some(non_wasm_helpers::create_bandwidth_controller(
&self.config.base,
credential_store,
))
Some(default_query_dkg_client_from_config(&self.config.base))
};
let base_builder = BaseClientBuilder::<_, S>::new_from_base_config(
&self.config.base,
key_store,
bandwidth_controller,
reply_storage_backend,
);
let base_builder =
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client)
.with_gateway_setup(self.setup_method);
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
@@ -428,18 +428,14 @@ impl SocksClient {
Some(self.lane_queue_lengths.clone()),
self.shutdown_listener.clone(),
)
.run(move |conn_id, read_data, socket_closed| {
let provider_request = Socks5Request::new_send(
request_version.provider_protocol,
conn_id,
read_data,
socket_closed,
);
.run(move |socket_data| {
let lane = TransmissionLane::ConnectionId(socket_data.header.connection_id);
let provider_request =
Socks5Request::new_send(request_version.provider_protocol, socket_data);
let provider_message = Socks5ProviderRequest::new_provider_data(
request_version.provider_interface,
provider_request,
);
let lane = TransmissionLane::ConnectionId(conn_id);
if anonymous {
InputMessage::new_anonymous(
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::StreamExt;
use log::*;
use nym_client_core::client::received_buffer::ReconstructedMessagesReceiver;
use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender,
};
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_sphinx::receiver::ReconstructedMessage;
use nym_task::TaskClient;
use crate::error::Socks5ClientCoreError;
pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender,
mix_response_receiver: ReconstructedMessagesReceiver,
@@ -79,12 +80,20 @@ impl MixnetResponseListener {
);
Err(err_response.into())
}
Socks5ResponseContent::NetworkData(response) => {
Socks5ResponseContent::NetworkData { content } => {
self.controller_sender
.unbounded_send(response.into())
.unbounded_send(ControllerCommand::new_send(content))
.unwrap();
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 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.
///
@@ -9,36 +24,58 @@ use std::collections::HashMap;
/// to fill up with the full sequence.
#[derive(Debug)]
pub struct OrderedMessageBuffer {
next_index: u64,
messages: HashMap<u64, OrderedMessage>,
next_sequence: u64,
messages: BTreeMap<u64, Vec<u8>>,
}
/// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data.
#[derive(Debug, PartialEq, Eq)]
pub struct ReadContiguousData {
pub data: Vec<u8>,
pub last_index: u64,
pub last_sequence: u64,
}
const MAX_REASONABLE_OFFSET: u64 = 1000;
impl OrderedMessageBuffer {
pub fn new() -> OrderedMessageBuffer {
OrderedMessageBuffer {
next_index: 0,
messages: HashMap::new(),
next_sequence: 0,
messages: BTreeMap::new(),
}
}
/// Writes a message to the buffer. messages are sort on insertion, so
/// that later on multiple reads for incomplete sequences don't result in
/// 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!(
"Writing message index: {} length {:?} to OrderedMessageBuffer.",
message.index,
message.data.len()
"Writing message index: {} length {} to OrderedMessageBuffer.",
sequence,
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
@@ -49,33 +86,31 @@ impl OrderedMessageBuffer {
/// 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
/// further contiguous messages which have arrived will be returned.
#[must_use]
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;
}
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) {
contiguous_messages.push(ordered_message);
index += 1;
while let Some(mut data) = self.messages.remove(&seq) {
contiguous_messages.append(&mut data);
seq += 1;
}
let high_water = index;
self.next_index = high_water;
trace!("Next high water mark is: {}", high_water);
let high_water = seq;
self.next_sequence = high_water;
trace!("Next high water mark is: {high_water}");
// dig out the bytes from inside the struct
let data: Vec<u8> = contiguous_messages
.into_iter()
.flat_map(|message| message.data)
.collect();
trace!("Returning {} bytes from ordered message buffer", data.len());
trace!(
"Returning {} bytes from ordered message buffer",
contiguous_messages.len()
);
Some(ReadContiguousData {
data,
last_index: index,
data: contiguous_messages,
last_sequence: seq,
})
}
}
@@ -90,6 +125,64 @@ impl Default for OrderedMessageBuffer {
mod test_chunking_and_reassembling {
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)]
mod reading_from_and_writing_to_the_buffer {
use super::*;
@@ -102,20 +195,14 @@ mod test_chunking_and_reassembling {
fn read_returns_ordered_bytes_and_resets_buffer() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = OrderedMessage {
data: vec![1, 2, 3, 4],
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
buffer.write(first_message);
buffer.write(0, first_message).unwrap();
let first_read = buffer.read().unwrap().data;
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;
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() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = OrderedMessage {
data: vec![1, 2, 3, 4],
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
buffer.write(first_message);
buffer.write(second_message);
buffer.write(0, first_message).unwrap();
buffer.write(1, second_message).unwrap();
let second_read = buffer.read();
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
@@ -146,17 +227,11 @@ mod test_chunking_and_reassembling {
fn out_of_order_adds_results_in_ordered_byte_vector() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = OrderedMessage {
data: vec![1, 2, 3, 4],
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
buffer.write(second_message);
buffer.write(first_message);
buffer.write(1, second_message).unwrap();
buffer.write(0, first_message).unwrap();
let read = buffer.read().unwrap().data;
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
@@ -170,23 +245,13 @@ mod test_chunking_and_reassembling {
fn setup() -> OrderedMessageBuffer {
let mut buffer = OrderedMessageBuffer::new();
let zero_message = OrderedMessage {
data: vec![0, 0, 0, 0],
index: 0,
};
let one_message = OrderedMessage {
data: vec![1, 1, 1, 1],
index: 1,
};
let zero_message = vec![0, 0, 0, 0];
let one_message = vec![1, 1, 1, 1];
let three_message = vec![3, 3, 3, 3];
let three_message = OrderedMessage {
data: vec![3, 3, 3, 3],
index: 3,
};
buffer.write(zero_message);
buffer.write(one_message);
buffer.write(three_message);
buffer.write(0, zero_message).unwrap();
buffer.write(1, one_message).unwrap();
buffer.write(3, three_message).unwrap();
buffer
}
#[test]
@@ -199,43 +264,31 @@ mod test_chunking_and_reassembling {
assert_eq!(None, buffer.read());
// let's add another message, leaving a gap in place at index 2
let five_message = OrderedMessage {
data: vec![5, 5, 5, 5],
index: 5,
};
buffer.write(five_message);
let five_message = vec![5, 5, 5, 5];
buffer.write(5, five_message).unwrap();
assert_eq!(None, buffer.read());
}
#[test]
fn filling_the_gap_allows_us_to_get_everything() {
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 {
data: vec![2, 2, 2, 2],
index: 2,
};
buffer.write(two_message);
let two_message = vec![2, 2, 2, 2];
buffer.write(2, two_message).unwrap();
let more_ordered_bytes = buffer.read().unwrap().data;
assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes);
// let's add another message
let five_message = OrderedMessage {
data: vec![5, 5, 5, 5],
index: 5,
};
buffer.write(five_message);
let five_message = vec![5, 5, 5, 5];
buffer.write(5, five_message).unwrap();
assert_eq!(None, buffer.read());
// let's fill in the gap of 4s now and read again
let four_message = OrderedMessage {
data: vec![4, 4, 4, 4],
index: 4,
};
buffer.write(four_message);
let four_message = vec![4, 4, 4, 4];
buffer.write(4, four_message).unwrap();
assert_eq!(
[4, 4, 4, 4, 5, 5, 5, 5].to_vec(),
@@ -249,70 +302,47 @@ mod test_chunking_and_reassembling {
#[test]
fn filling_the_gap_allows_us_to_get_everything_when_last_element_is_empty() {
let mut buffer = OrderedMessageBuffer::new();
let zero_message = OrderedMessage {
data: vec![0, 0, 0, 0],
index: 0,
};
let one_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 1,
};
let two_message = OrderedMessage {
data: vec![],
index: 2,
};
let zero_message = vec![0, 0, 0, 0];
let one_message = vec![2, 2, 2, 2];
let two_message = vec![];
buffer.write(zero_message);
buffer.write(0, zero_message).unwrap();
assert!(buffer.read().is_some()); // burn the buffer
buffer.write(two_message);
buffer.write(one_message);
buffer.write(2, two_message).unwrap();
buffer.write(1, one_message).unwrap();
assert!(buffer.read().is_some());
assert_eq!(buffer.next_index, 3);
assert_eq!(buffer.next_sequence, 3);
}
#[test]
fn works_with_gaps_bigger_than_one() {
let mut buffer = OrderedMessageBuffer::new();
let zero_message = OrderedMessage {
data: vec![0, 0, 0, 0],
index: 0,
};
let one_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 1,
};
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);
let zero_message = vec![0, 0, 0, 0];
let one_message = vec![2, 2, 2, 2];
let two_message = vec![2, 2, 2, 2];
let three_message = vec![2, 2, 2, 2];
let four_message = vec![2, 2, 2, 2];
buffer.write(0, zero_message).unwrap();
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_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_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_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_eq!(buffer.next_index, 5)
assert_eq!(buffer.next_sequence, 5)
}
}
}
+1 -6
View File
@@ -1,8 +1,3 @@
mod buffer;
mod message;
mod sender;
pub use buffer::{OrderedMessageBuffer, ReadContiguousData};
pub use message::MessageError;
pub use message::OrderedMessage;
pub use sender::OrderedMessageSender;
pub use buffer::{OrderedMessageBuffer, OrderedMessageError, ReadContiguousData};
-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
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData};
use nym_socks5_requests::{ConnectionId, NetworkData, SendRequest};
use nym_ordered_buffer::{OrderedMessageBuffer, ReadContiguousData};
use nym_socks5_requests::{ConnectionId, SocketData};
use nym_task::connections::{ConnectionCommand, ConnectionCommandSender};
use nym_task::TaskClient;
use std::collections::{HashMap, HashSet};
@@ -40,29 +40,13 @@ pub enum ControllerCommand {
connection_id: ConnectionId,
},
Send {
connection_id: ConnectionId,
data: Vec<u8>,
is_closed: bool,
data: SocketData,
},
}
impl From<NetworkData> for ControllerCommand {
fn from(value: NetworkData) -> Self {
ControllerCommand::Send {
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,
}
impl ControllerCommand {
pub fn new_send(data: SocketData) -> Self {
ControllerCommand::Send { data }
}
}
@@ -74,18 +58,13 @@ struct ActiveConnection {
}
impl ActiveConnection {
fn write_to_buf(&mut self, 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;
}
};
fn write_to_buf(&mut self, seq: u64, payload: Vec<u8>, is_closed: bool) {
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> {
@@ -117,7 +96,7 @@ pub struct Controller {
// 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
pending_messages: HashMap<ConnectionId, Vec<(Vec<u8>, bool)>>,
pending_messages: HashMap<ConnectionId, Vec<SocketData>>,
shutdown: TaskClient,
}
@@ -154,8 +133,8 @@ impl Controller {
// check if there were any pending messages
if let Some(pending) = self.pending_messages.remove(&conn_id) {
debug!("There were some pending messages for {}", conn_id);
for (payload, is_closed) in pending {
self.send_to_connection(conn_id, payload, is_closed)
for data in pending {
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) {
if let Some(active_connection) = self.active_connections.get_mut(&conn_id) {
if !payload.is_empty() {
active_connection.write_to_buf(payload, is_closed);
} else if !is_closed {
error!("Tried to write an empty message to a not-closing connection. Please let us know if you see this message");
}
fn send_to_connection(&mut self, message: SocketData) {
let hdr = message.header;
if let Some(active_connection) = self.active_connections.get_mut(&hdr.connection_id) {
// always write to the buffer even if payload is empty (because it could have been the keep-alive message)
active_connection.write_to_buf(hdr.seq, message.data, hdr.local_socket_closed);
if let Some(payload) = active_connection.read_from_buf() {
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;
}
}
// 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
.connection_sender
.as_mut()
@@ -207,34 +191,26 @@ impl Controller {
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");
let pending = self
.pending_messages
.entry(conn_id)
.entry(hdr.connection_id)
.or_insert_with(Vec::new);
pending.push((payload, is_closed));
} else if !is_closed {
pending.push(message);
} else if !hdr.local_socket_closed {
error!(
"Tried to write to closed connection {} ({} bytes were 'lost)",
conn_id,
payload.len()
hdr.connection_id,
message.data.len()
);
} else {
debug!(
"Tried to write to closed connection {}, but remote is already closed",
conn_id
hdr.connection_id
)
}
}
@@ -243,8 +219,8 @@ impl Controller {
loop {
tokio::select! {
command = self.receiver.next() => match command {
Some(ControllerCommand::Send{connection_id, data, is_closed}) => {
self.send_to_connection(connection_id, data, is_closed)
Some(ControllerCommand::Send{data}) => {
self.send_to_connection(data)
}
Some(ControllerCommand::Insert{connection_id, connection_sender}) => {
self.insert_connection(connection_id, connection_sender)
+1
View File
@@ -3,4 +3,5 @@
pub mod available_reader;
pub mod connection_controller;
pub mod ordered_sender;
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
use super::MixProxySender;
use super::SHUTDOWN_TIMEOUT;
use crate::available_reader::AvailableReader;
use crate::ordered_sender::OrderedMessageSender;
use crate::proxy_runner::KEEPALIVE_INTERVAL;
use bytes::Bytes;
use futures::FutureExt;
use futures::StreamExt;
use log::*;
use nym_ordered_buffer::OrderedMessageSender;
use nym_socks5_requests::ConnectionId;
use nym_socks5_requests::{ConnectionId, SocketData};
use nym_task::connections::LaneQueueLengths;
use nym_task::connections::TransmissionLane;
use nym_task::TaskClient;
use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
use std::{io, sync::Arc};
use tokio::select;
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) {
if let Some(lane_queue_lengths) = lane_queue_lengths {
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>(
mut reader: OwnedReadHalf,
local_destination_address: String, // addresses are provided for better logging
remote_source_address: String,
mut message_sender: OrderedMessageSender<F, S>,
connection_id: ConnectionId,
mix_sender: MixProxySender<S>,
available_plaintext_per_mix_packet: usize,
adapter_fn: F,
shutdown_notify: Arc<Notify>,
lane_queue_lengths: Option<LaneQueueLengths>,
mut shutdown_listener: TaskClient,
) -> OwnedReadHalf
where
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static,
S: Debug,
F: Fn(SocketData) -> S + Send + 'static,
{
// TODO: this multiplication by 4 is completely arbitrary here
let mut available_reader =
AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4));
let mut message_sender = OrderedMessageSender::new();
// Shutdown if outbound signled to shutdown
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.
// 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;
}
_ = shutdown_listener.recv() => {
@@ -233,7 +143,7 @@ where
break;
}
_ = 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.
// 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)
.then(|_| available_reader.next()), if !we_are_closed =>
{
if deal_with_data(
read_data,
&local_destination_address,
&remote_source_address,
connection_id,
&mut message_sender,
&mix_sender,
&adapter_fn,
).await {
let processed = message_sender.process_data(read_data);
let is_done = processed.is_done;
message_sender.send_data(processed).await;
if is_done {
// After reading the last data, notify the closing_future to wait until the
// lane is clear before exiting.
// 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