Merge branch 'release/v1.1.5' into feat/2130-tables-update
This commit is contained in:
+43
-28
@@ -4,13 +4,34 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- socks5: send status message for service ready, and network-requester error response
|
||||
|
||||
### Changed
|
||||
|
||||
- all-binaries: improved error logging ([#2686])
|
||||
- native client: bring shutdown logic up to the same level as socks5-client
|
||||
- nym-api, coconut-dkg contract: automatic, time-based dkg epoch state advancement ([#2670])
|
||||
|
||||
[#2686]: https://github.com/nymtech/nym/pull/2686
|
||||
[#2670]: https://github.com/nymtech/nym/pull/2670
|
||||
|
||||
## [v1.1.4] (2022-12-20)
|
||||
|
||||
This release adds multiple Single Use Reply Blocks (SURBs) to allow arbitrarily-sized anonymized replies.
|
||||
At the moment this is turned off by default, but available for use by application developers.
|
||||
We will need to wait for network-requesters to upgrade to this new release, after which multi-SURB anonymization will become the default setting for the SOCKS proxy clients.
|
||||
|
||||
The release also include some additional work for distributed key generation in the Coconut signing authority nodes.
|
||||
|
||||
### Changed
|
||||
|
||||
- Feature/dkg contract threshold by @neacsu in https://github.com/nymtech/nym/pull/1885
|
||||
- Multi-surbs by @jstuczyn in https://github.com/nymtech/nym/pull/2667
|
||||
- Fix multi-surb backwards compatibility in pre 1.1.4 client config files by @jstuczyn in https://github.com/nymtech/nym/pull/2703
|
||||
- fix: ignore corrupted surb storage and instead create fresh one by @jstuczyn in https://github.com/nymtech/nym/pull/2711
|
||||
- socks5: rework waiting in inbound.rs by @octol in https://github.com/nymtech/nym/pull/1880
|
||||
|
||||
## [v1.1.3] (2022-12-13)
|
||||
|
||||
@@ -25,7 +46,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- network-requester: fix bug where websocket connection disconnect resulted in success error code
|
||||
- clients: fix a few panics handling the gateway-client
|
||||
- mixnode, gateway, validator-api: Use mainnet values as defaults for URLs and mixnet contract ([#1884])
|
||||
- mixnode, gateway, validator-api: Use mainnet values as defaults for URLs and mixnet contract ([#1884])
|
||||
- socks5: fixed bug where connections sometimes where closed too early
|
||||
- clients: improve message logging when received message fails to get reconstructed ([#1803])
|
||||
|
||||
@@ -45,7 +66,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- gateway: Renamed flag from `enabled/disabled_credentials_mode` to `only-coconut-credentials`
|
||||
- "Family" feature for node families + layers
|
||||
- Initial coconut functionality including credentials and distributed key generation
|
||||
- Initial coconut functionality including credentials and distributed key generation
|
||||
|
||||
## [v1.1.1](https://github.com/nymtech/nym/tree/v1.1.1) (2022-11-29)
|
||||
|
||||
@@ -81,7 +102,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#1786]: https://github.com/nymtech/nym/pull/1786
|
||||
[#1805]: https://github.com/nymtech/nym/pull/1805
|
||||
|
||||
|
||||
## [v1.1.0](https://github.com/nymtech/nym/tree/v1.1.0) (2022-11-09)
|
||||
|
||||
### Added
|
||||
@@ -108,7 +128,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669])
|
||||
- moved `Percent` struct to `contracts-common`, change affects explorer-api
|
||||
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
|
||||
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
|
||||
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
|
||||
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
|
||||
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
|
||||
- validator-api: changes to internal SQL schema due to the mixnet contract revamp ([#1472])
|
||||
@@ -136,7 +156,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#1724]: https://github.com/nymtech/nym/pull/1724
|
||||
[#1725]: https://github.com/nymtech/nym/pull/1725
|
||||
|
||||
|
||||
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
|
||||
|
||||
### Added
|
||||
@@ -190,8 +209,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
|
||||
- network explorer: tweak how active set probability is shown ([#1503])
|
||||
- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520])
|
||||
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
|
||||
|
||||
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
@@ -285,9 +303,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /clients/tauri-client [\#1163](https://github.com/nymtech/nym/pull/1163) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /clients/webassembly/js-example [\#1162](https://github.com/nymtech/nym/pull/1162) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /clients/native/examples/js-examples/websocket [\#1160](https://github.com/nymtech/nym/pull/1160) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript\_client/upload\_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript_client/upload_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/vesting full [\#1158](https://github.com/nymtech/nym/pull/1158) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- get\_current\_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch))
|
||||
- get_current_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch))
|
||||
- Cleanup [\#1155](https://github.com/nymtech/nym/pull/1155) ([durch](https://github.com/durch))
|
||||
- Feature flag reward payments [\#1154](https://github.com/nymtech/nym/pull/1154) ([durch](https://github.com/durch))
|
||||
- Add Query endpoints for calculating rewards [\#1152](https://github.com/nymtech/nym/pull/1152) ([durch](https://github.com/durch))
|
||||
@@ -296,7 +314,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- wallet: use Urls rather than Strings for validator urls [\#1148](https://github.com/nymtech/nym/pull/1148) ([octol](https://github.com/octol))
|
||||
- Change accumulated reward to Option, migrate delegations [\#1147](https://github.com/nymtech/nym/pull/1147) ([durch](https://github.com/durch))
|
||||
- wallet: fetch validators url remotely if available [\#1146](https://github.com/nymtech/nym/pull/1146) ([octol](https://github.com/octol))
|
||||
- Fix delegated\_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch))
|
||||
- Fix delegated_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch))
|
||||
- Update Nym wallet dependencies to use `ts-packages` [\#1144](https://github.com/nymtech/nym/pull/1144) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: try validators one by one if available [\#1143](https://github.com/nymtech/nym/pull/1143) ([octol](https://github.com/octol))
|
||||
- Update Network Explorer Packages and add mix node identity key copy [\#1142](https://github.com/nymtech/nym/pull/1142) ([mmsinclair](https://github.com/mmsinclair))
|
||||
@@ -336,14 +354,13 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
|
||||
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
|
||||
|
||||
|
||||
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.0...v0.12.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967)
|
||||
- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
@@ -373,7 +390,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Bugfix/remove mixnode bonding overwrite [\#917](https://github.com/nymtech/nym/pull/917) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Fixes crash condition in validator API when calculating last day uptime [\#909](https://github.com/nymtech/nym/pull/909) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bugfix/monitor initial values wait [\#907](https://github.com/nymtech/nym/pull/907) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions [\#806](https://github.com/nymtech/nym/pull/806) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions [\#806](https://github.com/nymtech/nym/pull/806) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Explorer API: port test now split out address resolution and add units tests [\#755](https://github.com/nymtech/nym/pull/755) ([mmsinclair](https://github.com/mmsinclair))
|
||||
|
||||
**Closed issues:**
|
||||
@@ -388,7 +405,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- help!!! [\#712](https://github.com/nymtech/nym/issues/712)
|
||||
- UX feature request: show all delegated nodes in wallet [\#711](https://github.com/nymtech/nym/issues/711)
|
||||
- UX feature request: add current balance on wallet pages [\#710](https://github.com/nymtech/nym/issues/710)
|
||||
- got sign issue from bot [\#709](https://github.com/nymtech/nym/issues/709)
|
||||
- got sign issue from bot [\#709](https://github.com/nymtech/nym/issues/709)
|
||||
- As a wallet user, I would like to be able to log out of the wallet [\#706](https://github.com/nymtech/nym/issues/706)
|
||||
- As a wallet user, I would like to have a "receive" page where I can see my own wallet address [\#705](https://github.com/nymtech/nym/issues/705)
|
||||
- Update native client/socks client/mixnode/gateway `upgrade` command [\#689](https://github.com/nymtech/nym/issues/689)
|
||||
@@ -398,7 +415,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- nym-socks5-client crash after opening Keybase team "Browse all channels" [\#494](https://github.com/nymtech/nym/issues/494)
|
||||
- Mixed Content problem [\#400](https://github.com/nymtech/nym/issues/400)
|
||||
- Gateway disk quota [\#137](https://github.com/nymtech/nym/issues/137)
|
||||
- Simplify message encapsulation with regards to topology [\#127](https://github.com/nymtech/nym/issues/127)
|
||||
- Simplify message encapsulation with regards to topology [\#127](https://github.com/nymtech/nym/issues/127)
|
||||
- Create constants for cli argument names [\#115](https://github.com/nymtech/nym/issues/115)
|
||||
- Using Blake3 as a hash function [\#103](https://github.com/nymtech/nym/issues/103)
|
||||
- Validator should decide which layer a node is in [\#86](https://github.com/nymtech/nym/issues/86)
|
||||
@@ -454,10 +471,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Feature/pre cosmrs updates [\#935](https://github.com/nymtech/nym/pull/935) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/client on behalf [\#934](https://github.com/nymtech/nym/pull/934) ([neacsu](https://github.com/neacsu))
|
||||
- Webpack wallet prod configuration [\#933](https://github.com/nymtech/nym/pull/933) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Adding tx\_hash to wallet response [\#932](https://github.com/nymtech/nym/pull/932) ([futurechimp](https://github.com/futurechimp))
|
||||
- Adding tx_hash to wallet response [\#932](https://github.com/nymtech/nym/pull/932) ([futurechimp](https://github.com/futurechimp))
|
||||
- Release/1.0.0 pre1 [\#931](https://github.com/nymtech/nym/pull/931) ([durch](https://github.com/durch))
|
||||
- Feature/identity verification [\#930](https://github.com/nymtech/nym/pull/930) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Move cleaned up smart contracts to main code repo [\#929](https://github.com/nymtech/nym/pull/929) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Move cleaned up smart contracts to main code repo [\#929](https://github.com/nymtech/nym/pull/929) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Feature/mixnet contract further adjustments [\#928](https://github.com/nymtech/nym/pull/928) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- typo copy change for nodemap [\#926](https://github.com/nymtech/nym/pull/926) ([Aid19801](https://github.com/Aid19801))
|
||||
- Feature/UI enhancements for Desktop Wallet [\#925](https://github.com/nymtech/nym/pull/925) ([fmtabbara](https://github.com/fmtabbara))
|
||||
@@ -470,7 +487,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Feature/faucet page react [\#911](https://github.com/nymtech/nym/pull/911) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/mixnet contract refactor [\#910](https://github.com/nymtech/nym/pull/910) ([futurechimp](https://github.com/futurechimp))
|
||||
- Update README.md [\#905](https://github.com/nymtech/nym/pull/905) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- BUG: Bond cell denom [\#904](https://github.com/nymtech/nym/pull/904) ([Aid19801](https://github.com/Aid19801))
|
||||
- BUG: Bond cell denom [\#904](https://github.com/nymtech/nym/pull/904) ([Aid19801](https://github.com/Aid19801))
|
||||
- Explorer UI tests missing data-testid [\#903](https://github.com/nymtech/nym/pull/903) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Fix up Nym-Wallet README.md [\#899](https://github.com/nymtech/nym/pull/899) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/batch delegator rewarding [\#898](https://github.com/nymtech/nym/pull/898) ([jstuczyn](https://github.com/jstuczyn))
|
||||
@@ -488,7 +505,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Reverted gateway registration handshake to its 0.11.0 version [\#882](https://github.com/nymtech/nym/pull/882) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Network Explorer [\#881](https://github.com/nymtech/nym/pull/881) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Feature/rewarding interval updates [\#880](https://github.com/nymtech/nym/pull/880) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Put client\_address and id in the correct order [\#875](https://github.com/nymtech/nym/pull/875) ([neacsu](https://github.com/neacsu))
|
||||
- Put client_address and id in the correct order [\#875](https://github.com/nymtech/nym/pull/875) ([neacsu](https://github.com/neacsu))
|
||||
- remove gateway selection on delegation and undelegation pages [\#873](https://github.com/nymtech/nym/pull/873) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Set MSRV on all binaries to 1.56 [\#872](https://github.com/nymtech/nym/pull/872) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add native window items \(copy/paste\) via tauri [\#871](https://github.com/nymtech/nym/pull/871) ([fmtabbara](https://github.com/fmtabbara))
|
||||
@@ -504,7 +521,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Overflow checks in release [\#846](https://github.com/nymtech/nym/pull/846) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix delegate success overflow [\#842](https://github.com/nymtech/nym/pull/842) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature NYM wallet webdriverio test [\#841](https://github.com/nymtech/nym/pull/841) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Update nym\_wallet.yml [\#840](https://github.com/nymtech/nym/pull/840) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Update nym_wallet.yml [\#840](https://github.com/nymtech/nym/pull/840) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/vouchers [\#837](https://github.com/nymtech/nym/pull/837) ([aniampio](https://github.com/aniampio))
|
||||
- Apply readable ids to elements on Nym Wallet [\#836](https://github.com/nymtech/nym/pull/836) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/removal of monitor good nodes [\#833](https://github.com/nymtech/nym/pull/833) ([jstuczyn](https://github.com/jstuczyn))
|
||||
@@ -528,8 +545,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Created getters for AccountData [\#787](https://github.com/nymtech/nym/pull/787) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/migrate hidden delegations [\#786](https://github.com/nymtech/nym/pull/786) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/persistent gateway storage [\#784](https://github.com/nymtech/nym/pull/784) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Replaced unwrap\_or\_else with unwrap\_or\_default [\#780](https://github.com/nymtech/nym/pull/780) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add block\_height method to Delegation [\#778](https://github.com/nymtech/nym/pull/778) ([durch](https://github.com/durch))
|
||||
- Replaced unwrap_or_else with unwrap_or_default [\#780](https://github.com/nymtech/nym/pull/780) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add block_height method to Delegation [\#778](https://github.com/nymtech/nym/pull/778) ([durch](https://github.com/durch))
|
||||
- Make fee helpers public [\#777](https://github.com/nymtech/nym/pull/777) ([durch](https://github.com/durch))
|
||||
- re-enable bonding [\#776](https://github.com/nymtech/nym/pull/776) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Explorer-api: add API resource to show the delegations for each mix node [\#774](https://github.com/nymtech/nym/pull/774) ([mmsinclair](https://github.com/mmsinclair))
|
||||
@@ -538,14 +555,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Adding deps for building the Tauri wallet under Ubuntu [\#770](https://github.com/nymtech/nym/pull/770) ([futurechimp](https://github.com/futurechimp))
|
||||
- remove alert [\#767](https://github.com/nymtech/nym/pull/767) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/consumable bandwidth [\#766](https://github.com/nymtech/nym/pull/766) ([neacsu](https://github.com/neacsu))
|
||||
- Update coconut-rs and use hash\_to\_scalar from there [\#765](https://github.com/nymtech/nym/pull/765) ([neacsu](https://github.com/neacsu))
|
||||
- Update coconut-rs and use hash_to_scalar from there [\#765](https://github.com/nymtech/nym/pull/765) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/active sets [\#764](https://github.com/nymtech/nym/pull/764) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add app alert banner [\#762](https://github.com/nymtech/nym/pull/762) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Updated cosmos-sdk [\#761](https://github.com/nymtech/nym/pull/761) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/bond blockstamp [\#760](https://github.com/nymtech/nym/pull/760) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/revert migration code [\#759](https://github.com/nymtech/nym/pull/759) ([neacsu](https://github.com/neacsu))
|
||||
- Bump next from 11.1.0 to 11.1.1 in /wallet-web [\#758](https://github.com/nymtech/nym/pull/758) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Add block\_height in the Delegation structure as well [\#757](https://github.com/nymtech/nym/pull/757) ([neacsu](https://github.com/neacsu))
|
||||
- Add block_height in the Delegation structure as well [\#757](https://github.com/nymtech/nym/pull/757) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/add blockstamp [\#756](https://github.com/nymtech/nym/pull/756) ([neacsu](https://github.com/neacsu))
|
||||
- NetworkMonitorBuilder - starting the monitor after rocket has launched [\#754](https://github.com/nymtech/nym/pull/754) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Enabled validators api argument [\#753](https://github.com/nymtech/nym/pull/753) ([jstuczyn](https://github.com/jstuczyn))
|
||||
@@ -557,7 +574,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Feature/more reliable uptime calculation [\#747](https://github.com/nymtech/nym/pull/747) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Update template toml key [\#746](https://github.com/nymtech/nym/pull/746) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/cred after handshake [\#745](https://github.com/nymtech/nym/pull/745) ([neacsu](https://github.com/neacsu))
|
||||
- Reinstate the POST method blind\_sign [\#744](https://github.com/nymtech/nym/pull/744) ([neacsu](https://github.com/neacsu))
|
||||
- Reinstate the POST method blind_sign [\#744](https://github.com/nymtech/nym/pull/744) ([neacsu](https://github.com/neacsu))
|
||||
- explorer-api: add pending field to port check response [\#742](https://github.com/nymtech/nym/pull/742) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Feature/use delegation rates [\#741](https://github.com/nymtech/nym/pull/741) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/copy to clipboard [\#740](https://github.com/nymtech/nym/pull/740) ([fmtabbara](https://github.com/fmtabbara))
|
||||
@@ -594,6 +611,4 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Hang coconut issuance off the validator-api [\#679](https://github.com/nymtech/nym/pull/679) ([durch](https://github.com/durch))
|
||||
- Update hmac and blake3 [\#673](https://github.com/nymtech/nym/pull/673) ([durch](https://github.com/durch))
|
||||
|
||||
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
\* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_
|
||||
|
||||
Generated
+170
-22
@@ -23,6 +23,16 @@ dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -53,7 +63,7 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aead 0.4.3",
|
||||
"aes 0.7.5",
|
||||
"cipher 0.3.0",
|
||||
"ctr 0.8.0",
|
||||
@@ -81,6 +91,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
@@ -443,6 +459,12 @@ dependencies = [
|
||||
"rustc_version 0.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
@@ -474,6 +496,30 @@ dependencies = [
|
||||
"keystream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cipher 0.4.3",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20poly1305"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
||||
dependencies = [
|
||||
"aead 0.5.1",
|
||||
"chacha20",
|
||||
"cipher 0.4.3",
|
||||
"poly1305",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
@@ -488,6 +534,33 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.3.0"
|
||||
@@ -505,6 +578,7 @@ checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -586,7 +660,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "client-core"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"client-connections",
|
||||
@@ -980,9 +1054,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"cast 0.2.7",
|
||||
"clap 2.34.0",
|
||||
"criterion-plot",
|
||||
"criterion-plot 0.4.4",
|
||||
"csv",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
@@ -999,13 +1073,49 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"atty",
|
||||
"cast 0.3.0",
|
||||
"ciborium",
|
||||
"clap 3.2.8",
|
||||
"criterion-plot 0.5.0",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"cast 0.2.7",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast 0.3.0",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
@@ -1134,11 +1244,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.3"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
"rand_core 0.6.3",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
@@ -1466,7 +1577,7 @@ dependencies = [
|
||||
"bitvec",
|
||||
"bls12_381 0.6.0",
|
||||
"bs58",
|
||||
"criterion",
|
||||
"criterion 0.3.5",
|
||||
"ff 0.11.0",
|
||||
"group 0.11.0",
|
||||
"lazy_static",
|
||||
@@ -1698,9 +1809,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
@@ -3123,7 +3234,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@@ -3177,7 +3288,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"clap 3.2.8",
|
||||
"client-connections",
|
||||
@@ -3216,7 +3327,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3263,7 +3374,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -3305,7 +3416,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap 3.2.8",
|
||||
@@ -3337,7 +3448,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -3351,9 +3462,25 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-outfox"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"criterion 0.4.0",
|
||||
"curve25519-dalek",
|
||||
"fastrand",
|
||||
"getrandom 0.2.6",
|
||||
"rayon",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"clap 3.2.8",
|
||||
"client-connections",
|
||||
@@ -3420,7 +3547,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-validator-api"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3509,7 +3636,7 @@ dependencies = [
|
||||
"bincode",
|
||||
"bls12_381 0.6.0",
|
||||
"bs58",
|
||||
"criterion",
|
||||
"criterion 0.3.5",
|
||||
"digest 0.9.0",
|
||||
"dkg",
|
||||
"doc-comment",
|
||||
@@ -4021,6 +4148,17 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"opaque-debug 0.3.0",
|
||||
"universal-hash 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.5.3"
|
||||
@@ -4030,7 +4168,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"opaque-debug 0.3.0",
|
||||
"universal-hash",
|
||||
"universal-hash 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6388,6 +6526,16 @@ dependencies = [
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
@@ -6911,9 +7059,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.4.3"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619"
|
||||
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
@@ -76,6 +76,7 @@ members = [
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-outfox",
|
||||
"tools/nym-cli",
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
|
||||
@@ -28,7 +28,7 @@ clippy-coconut:
|
||||
cargo clippy --workspace --features coconut -- -D warnings
|
||||
|
||||
clippy-wasm:
|
||||
cargo clippy --workspace --features wasm -- -D warnings
|
||||
cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings
|
||||
|
||||
|
||||
clippy-all-contracts:
|
||||
@@ -49,9 +49,6 @@ test-main:
|
||||
test-coconut:
|
||||
cargo test --workspace --features coconut
|
||||
|
||||
test-wasm:
|
||||
cargo test --workspace --features wasm
|
||||
|
||||
|
||||
test-main-expensive:
|
||||
cargo test --workspace -- --ignored
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -371,7 +371,7 @@ where
|
||||
|
||||
// channels responsible for dealing with reply-related fun
|
||||
let (reply_controller_sender, reply_controller_receiver) =
|
||||
reply_controller::new_control_channels();
|
||||
reply_controller::requests::new_control_channels();
|
||||
|
||||
let self_address = self.as_mix_recipient();
|
||||
|
||||
@@ -437,7 +437,7 @@ where
|
||||
input_receiver,
|
||||
sphinx_message_sender.clone(),
|
||||
reply_storage,
|
||||
reply_controller_sender,
|
||||
reply_controller_sender.clone(),
|
||||
reply_controller_receiver,
|
||||
shared_lane_queue_lengths.clone(),
|
||||
client_connection_rx,
|
||||
@@ -471,6 +471,7 @@ where
|
||||
received_buffer_request_sender,
|
||||
},
|
||||
},
|
||||
reply_controller_sender,
|
||||
task_manager,
|
||||
})
|
||||
}
|
||||
@@ -480,5 +481,8 @@ pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
|
||||
// it feels very wrong to put this channel here, but I can't think of any other way of passing it to the native client
|
||||
pub reply_controller_sender: ReplyControllerSender,
|
||||
|
||||
pub task_manager: TaskManager,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,57 @@ use crate::config::DebugConfig;
|
||||
use crate::error::ClientCoreError;
|
||||
use log::{error, info};
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
debug_config: &DebugConfig,
|
||||
) -> Result<fs_backend::Backend, ClientCoreError<fs_backend::Backend>> {
|
||||
info!("creating fresh surb database");
|
||||
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
|
||||
Ok(backend) => backend,
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}");
|
||||
return Err(ClientCoreError::SurbStorageError { source: err });
|
||||
}
|
||||
};
|
||||
|
||||
// while I kinda hate that we're going to be creating `CombinedReplyStorage` twice,
|
||||
// it will only be happening on the very first run and in practice won't incur huge
|
||||
// costs since the storage is going to be empty
|
||||
let mem_store = CombinedReplyStorage::new(
|
||||
debug_config.minimum_reply_surb_storage_threshold,
|
||||
debug_config.maximum_reply_surb_storage_threshold,
|
||||
);
|
||||
storage_backend
|
||||
.init_fresh(&mem_store)
|
||||
.await
|
||||
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
|
||||
|
||||
Ok(storage_backend)
|
||||
}
|
||||
|
||||
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||
let db_path = db_path.as_ref();
|
||||
debug_assert!(db_path.exists());
|
||||
|
||||
let now = OffsetDateTime::now_utc().unix_timestamp();
|
||||
|
||||
let suffix = format!("_{now}.corrupted");
|
||||
|
||||
let new_extension =
|
||||
if let Some(existing_extension) = db_path.extension().and_then(|ext| ext.to_str()) {
|
||||
format!("{existing_extension}.{}", suffix)
|
||||
} else {
|
||||
suffix
|
||||
};
|
||||
|
||||
let mut renamed = db_path.to_owned();
|
||||
renamed.set_extension(new_extension);
|
||||
|
||||
fs::rename(db_path, renamed)
|
||||
}
|
||||
|
||||
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
@@ -20,32 +71,13 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
match fs_backend::Backend::try_load(db_path).await {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}");
|
||||
Err(ClientCoreError::SurbStorageError { source: err })
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
|
||||
archive_corrupted_database(db_path)?;
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("creating fresh surb database");
|
||||
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
|
||||
Ok(backend) => backend,
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}");
|
||||
return Err(ClientCoreError::SurbStorageError { source: err });
|
||||
}
|
||||
};
|
||||
|
||||
// while I kinda hate that we're going to be creating `CombinedReplyStorage` twice,
|
||||
// it will only be happening on the very first run and in practice won't incur huge
|
||||
// costs since the storage is going to be empty
|
||||
let mem_store = CombinedReplyStorage::new(
|
||||
debug_config.minimum_reply_surb_storage_threshold,
|
||||
debug_config.maximum_reply_surb_storage_threshold,
|
||||
);
|
||||
storage_backend
|
||||
.init_fresh(&mem_store)
|
||||
.await
|
||||
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
|
||||
|
||||
Ok(storage_backend)
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod non_wasm;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use non_wasm::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use wasm::*;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use tokio::time::*;
|
||||
pub type IntervalStream = tokio_stream::wrappers::IntervalStream;
|
||||
|
||||
pub(crate) fn get_time_now() -> Instant {
|
||||
Instant::now()
|
||||
}
|
||||
|
||||
pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::Duration;
|
||||
use wasm_timer;
|
||||
|
||||
pub use wasm_timer::*;
|
||||
pub type IntervalStream = gloo_timers::future::IntervalStream;
|
||||
|
||||
pub(crate) fn get_time_now() -> Instant {
|
||||
wasm_timer::Instant::now()
|
||||
}
|
||||
|
||||
pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
pub mod base_client;
|
||||
pub mod cover_traffic_stream;
|
||||
pub(crate) mod helpers;
|
||||
pub mod inbound_messages;
|
||||
pub mod key_manager;
|
||||
pub mod mix_traffic;
|
||||
@@ -10,3 +11,4 @@ pub mod real_messages_control;
|
||||
pub mod received_buffer;
|
||||
pub mod replies;
|
||||
pub mod topology_control;
|
||||
pub(crate) mod transmission_buffer;
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ where
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
_ = shutdown.recv() => {
|
||||
log::trace!("RetransmissionRequestListener: Received shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use nymsphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
|
||||
use nymsphinx::preparer::{MessagePreparer, PreparedFragment};
|
||||
use nymsphinx::Delay;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
@@ -278,7 +279,7 @@ where
|
||||
reply_surb: ReplySurb,
|
||||
amount: u32,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
debug!("requesting {amount} reply SURBs from {from:?}");
|
||||
debug!("requesting {amount} reply SURBs from {from}");
|
||||
|
||||
let surbs_request =
|
||||
ReplyMessage::new_surb_request_message(self.config.sender_address, amount);
|
||||
@@ -294,19 +295,11 @@ where
|
||||
)))
|
||||
}
|
||||
|
||||
// the only difference between this method and `try_send_reply_chunks` is that
|
||||
// here we are not creating acks as acks are already in memory waiting to get cleared.
|
||||
// we are only updating their existing delays
|
||||
pub(crate) async fn try_send_retransmission_reply_chunks(
|
||||
pub(crate) async fn send_retransmission_reply_chunks(
|
||||
&mut self,
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
prepared_fragments: Vec<PreparedFragment>,
|
||||
lane: TransmissionLane,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
let prepared_fragments = self
|
||||
.prepare_reply_chunks_for_sending(fragments.clone(), reply_surbs)
|
||||
.await?;
|
||||
|
||||
) {
|
||||
let mut real_messages = Vec::with_capacity(prepared_fragments.len());
|
||||
|
||||
for prepared in prepared_fragments {
|
||||
@@ -315,33 +308,58 @@ where
|
||||
}
|
||||
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn try_send_reply_chunks(
|
||||
pub(crate) async fn try_send_reply_chunks_on_lane(
|
||||
&mut self,
|
||||
target: AnonymousSenderTag,
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
lane: TransmissionLane,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
// TODO: technically this is performing an unnecessary cloning, but in the grand scheme of things
|
||||
// is it really that bad?
|
||||
self.try_send_reply_chunks(
|
||||
target,
|
||||
fragments.into_iter().map(|f| (lane, f)).collect(),
|
||||
reply_surbs,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn try_send_reply_chunks(
|
||||
&mut self,
|
||||
target: AnonymousSenderTag,
|
||||
fragments: Vec<(TransmissionLane, Fragment)>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
let prepared_fragments = self
|
||||
.prepare_reply_chunks_for_sending(fragments.clone(), reply_surbs)
|
||||
.prepare_reply_chunks_for_sending(
|
||||
fragments.iter().map(|(_, f)| f.clone()).collect(),
|
||||
reply_surbs,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut pending_acks = Vec::with_capacity(fragments.len());
|
||||
let mut real_messages = Vec::with_capacity(fragments.len());
|
||||
let mut to_forward: HashMap<_, Vec<_>> = HashMap::new();
|
||||
|
||||
for (raw, prepared) in fragments.into_iter().zip(prepared_fragments.into_iter()) {
|
||||
let lane = raw.0;
|
||||
let fragment = raw.1;
|
||||
|
||||
let real_message = RealMessage::new(prepared.mix_packet, prepared.fragment_identifier);
|
||||
let delay = prepared.total_delay;
|
||||
let pending_ack = PendingAcknowledgement::new_anonymous(raw, delay, target, false);
|
||||
let pending_ack = PendingAcknowledgement::new_anonymous(fragment, delay, target, false);
|
||||
|
||||
real_messages.push(real_message);
|
||||
let entry = to_forward.entry(lane).or_default();
|
||||
entry.push(real_message);
|
||||
pending_acks.push(pending_ack);
|
||||
}
|
||||
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
for (lane, real_messages) in to_forward {
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
}
|
||||
|
||||
self.insert_pending_acks(pending_acks);
|
||||
Ok(())
|
||||
}
|
||||
@@ -467,7 +485,7 @@ where
|
||||
Ok(prepared_fragment)
|
||||
}
|
||||
|
||||
async fn prepare_reply_chunks_for_sending(
|
||||
pub(crate) async fn prepare_reply_chunks_for_sending(
|
||||
&mut self,
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use self::sending_delay_controller::SendingDelayController;
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
use client_connections::{
|
||||
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
|
||||
};
|
||||
@@ -29,22 +31,7 @@ use tokio::time;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
|
||||
use self::{
|
||||
sending_delay_controller::SendingDelayController, transmission_buffer::TransmissionBuffer,
|
||||
};
|
||||
|
||||
mod sending_delay_controller;
|
||||
mod transmission_buffer;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn get_time_now() -> time::Instant {
|
||||
time::Instant::now()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn get_time_now() -> wasm_timer::Instant {
|
||||
wasm_timer::Instant::now()
|
||||
}
|
||||
|
||||
/// Configurable parameters of the `OutQueueControl`
|
||||
pub(crate) struct Config {
|
||||
@@ -135,7 +122,7 @@ where
|
||||
|
||||
/// Buffer containing all incoming real messages keyed by transmission lane, that we will send
|
||||
/// out to the mixnet.
|
||||
transmission_buffer: TransmissionBuffer,
|
||||
transmission_buffer: TransmissionBuffer<RealMessage>,
|
||||
|
||||
/// Incoming channel for being notified of closed connections, so that we can close lanes
|
||||
/// corresponding to connections. To avoid sending traffic unnecessary
|
||||
@@ -162,6 +149,10 @@ impl From<PreparedFragment> for RealMessage {
|
||||
}
|
||||
|
||||
impl RealMessage {
|
||||
pub(crate) fn packet_size(&self) -> usize {
|
||||
self.mix_packet.sphinx_packet().len()
|
||||
}
|
||||
|
||||
pub(crate) fn new(mix_packet: MixPacket, fragment_id: FragmentIdentifier) -> Self {
|
||||
RealMessage {
|
||||
mix_packet,
|
||||
@@ -207,7 +198,7 @@ where
|
||||
real_receiver,
|
||||
rng,
|
||||
topology_access,
|
||||
transmission_buffer: Default::default(),
|
||||
transmission_buffer: TransmissionBuffer::new(),
|
||||
client_connection_rx,
|
||||
lane_queue_lengths,
|
||||
}
|
||||
@@ -328,7 +319,9 @@ where
|
||||
|
||||
fn pop_next_message(&mut self) -> Option<RealMessage> {
|
||||
// Pop the next message from the transmission buffer
|
||||
let (lane, real_next) = self.transmission_buffer.pop_next_message_at_random()?;
|
||||
let (lane, real_next) = self
|
||||
.transmission_buffer
|
||||
.pop_next_message_at_random(&mut self.rng)?;
|
||||
|
||||
// Update the published queue length
|
||||
let lane_length = self.transmission_buffer.lane_length(&lane);
|
||||
|
||||
+3
-16
@@ -1,14 +1,9 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::get_time_now;
|
||||
use crate::client::helpers::{get_time_now, Instant};
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
|
||||
// The minimum time between increasing the average delay between packets. If we hit the ceiling in
|
||||
// the available buffer space we want to take somewhat swift action, but we still need to give a
|
||||
// short time to give the channel a chance reduce pressure.
|
||||
@@ -39,19 +34,11 @@ pub(crate) struct SendingDelayController {
|
||||
lower_bound: u32,
|
||||
|
||||
/// To make sure we don't change the multiplier to fast, we limit a change to some duration
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
time_when_changed: time::Instant,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
time_when_changed: wasm_timer::Instant,
|
||||
time_when_changed: Instant,
|
||||
|
||||
/// If we have a long enough time without any backpressure detected we try reducing the sending
|
||||
/// delay multiplier
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
time_when_backpressure_detected: time::Instant,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
time_when_backpressure_detected: wasm_timer::Instant,
|
||||
time_when_backpressure_detected: Instant,
|
||||
}
|
||||
|
||||
impl Default for SendingDelayController {
|
||||
|
||||
+155
-218
@@ -4,8 +4,8 @@
|
||||
use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement;
|
||||
use crate::client::real_messages_control::message_handler::{MessageHandler, PreparationError};
|
||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||
use client_connections::TransmissionLane;
|
||||
use futures::channel::mpsc;
|
||||
use client_connections::{ConnectionId, TransmissionLane};
|
||||
use futures::channel::oneshot;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
@@ -15,116 +15,16 @@ use nymsphinx::chunking::fragment::{Fragment, FragmentIdentifier};
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::{BTreeMap, HashMap, VecDeque};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type IntervalStream = tokio_stream::wrappers::IntervalStream;
|
||||
use crate::client::helpers::new_interval_stream;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
pub(crate) use requests::{ReplyControllerMessage, ReplyControllerReceiver, ReplyControllerSender};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
type IntervalStream = gloo_timers::future::IntervalStream;
|
||||
|
||||
pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
(tx.into(), rx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ReplyControllerSender(mpsc::UnboundedSender<ReplyControllerMessage>);
|
||||
|
||||
impl From<mpsc::UnboundedSender<ReplyControllerMessage>> for ReplyControllerSender {
|
||||
fn from(inner: mpsc::UnboundedSender<ReplyControllerMessage>) -> Self {
|
||||
ReplyControllerSender(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplyControllerSender {
|
||||
pub(crate) fn send_retransmission_data(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::RetransmitReply {
|
||||
recipient,
|
||||
timed_out_ack,
|
||||
extra_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_reply(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::SendReply {
|
||||
recipient,
|
||||
message,
|
||||
lane,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs(
|
||||
&self,
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbs {
|
||||
sender_tag,
|
||||
reply_surbs,
|
||||
from_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs_request(&self, recipient: Recipient, amount: u32) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest {
|
||||
recipient: Box::new(recipient),
|
||||
amount,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ReplyControllerMessage {
|
||||
RetransmitReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
},
|
||||
|
||||
SendReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
},
|
||||
|
||||
AdditionalSurbs {
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
},
|
||||
|
||||
// Should this also be handled in here? it's technically a completely different side of the pipe
|
||||
// let's see how it works when combined, might split it before creating PR
|
||||
AdditionalSurbsRequest {
|
||||
recipient: Box<Recipient>,
|
||||
amount: u32,
|
||||
},
|
||||
}
|
||||
pub mod requests;
|
||||
|
||||
pub struct Config {
|
||||
min_surb_request_size: u32,
|
||||
@@ -172,7 +72,7 @@ pub struct ReplyController<R> {
|
||||
// of surbs required to send the message through
|
||||
// expected_reliability: f32,
|
||||
request_receiver: ReplyControllerReceiver,
|
||||
pending_replies: HashMap<AnonymousSenderTag, VecDeque<Fragment>>,
|
||||
pending_replies: HashMap<AnonymousSenderTag, TransmissionBuffer<Fragment>>,
|
||||
|
||||
/// Retransmission packets that have already timed out and are waiting for additional reply SURBs
|
||||
/// so that they could be sent back to the network. Once we receive more SURBs, we should send them ASAP.
|
||||
@@ -204,17 +104,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts the pending replies into the BACK of the queue fn insert_pending_replies<V: Into<VecDeque<Fragment>>>(
|
||||
fn insert_pending_replies<V: Into<VecDeque<Fragment>>>(
|
||||
fn insert_pending_replies<I: IntoIterator<Item = Fragment>>(
|
||||
&mut self,
|
||||
recipient: &AnonymousSenderTag,
|
||||
fragments: V,
|
||||
fragments: I,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
if let Some(existing) = self.pending_replies.get_mut(recipient) {
|
||||
existing.append(&mut fragments.into())
|
||||
} else {
|
||||
self.pending_replies.insert(*recipient, fragments.into());
|
||||
}
|
||||
self.pending_replies
|
||||
.entry(*recipient)
|
||||
.or_insert_with(TransmissionBuffer::new)
|
||||
.store(&lane, fragments)
|
||||
}
|
||||
|
||||
fn re_insert_pending_replies(
|
||||
&mut self,
|
||||
recipient: &AnonymousSenderTag,
|
||||
fragments: Vec<(TransmissionLane, Fragment)>,
|
||||
) {
|
||||
// the buffer should ALWAYS exist at this point, if it doesn't, it's a bug...
|
||||
self.pending_replies
|
||||
.entry(*recipient)
|
||||
.or_insert_with(TransmissionBuffer::new)
|
||||
.store_multiple(fragments)
|
||||
}
|
||||
|
||||
fn re_insert_pending_retransmission(
|
||||
@@ -244,7 +155,7 @@ where
|
||||
let pending_queue_size = self
|
||||
.pending_replies
|
||||
.get(target)
|
||||
.map(|pending_queue| pending_queue.len())
|
||||
.map(|pending_queue| pending_queue.total_size())
|
||||
.unwrap_or_default();
|
||||
|
||||
let retransmission_queue = self
|
||||
@@ -299,43 +210,62 @@ where
|
||||
}
|
||||
|
||||
trace!("handling reply to {:?}", recipient_tag);
|
||||
let fragments = self.message_handler.split_reply_message(data);
|
||||
let mut fragments = self.message_handler.split_reply_message(data);
|
||||
let total_size = fragments.len();
|
||||
trace!("This reply requires {:?} SURBs", total_size);
|
||||
|
||||
let required_surbs = fragments.len();
|
||||
trace!("This reply requires {:?} SURBs", required_surbs);
|
||||
|
||||
// TODO: edge case:
|
||||
// we're making a lot of requests and have to request a lot of surbs
|
||||
// (but at some point we run out of surbs for surb requests)
|
||||
|
||||
let (surbs, _surbs_left) = self
|
||||
let available_surbs = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&recipient_tag, required_surbs);
|
||||
.available_surbs(&recipient_tag);
|
||||
let min_surbs_threshold = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.min_surb_threshold();
|
||||
|
||||
if let Some(reply_surbs) = surbs {
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_send_reply_chunks(recipient_tag, fragments, reply_surbs, lane)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(
|
||||
self.full_reply_storage.surbs_storage_ref(),
|
||||
&recipient_tag,
|
||||
);
|
||||
warn!("failed to send reply to {:?} - {err}", recipient_tag);
|
||||
|
||||
// TODO: should we buffer that data to try again?
|
||||
}
|
||||
let max_to_send = if available_surbs > min_surbs_threshold {
|
||||
min(fragments.len(), available_surbs - min_surbs_threshold)
|
||||
} else {
|
||||
// we don't have enough surbs for this reply
|
||||
self.insert_pending_replies(&recipient_tag, fragments);
|
||||
0
|
||||
};
|
||||
|
||||
if self.should_request_more_surbs(&recipient_tag) {
|
||||
self.request_reply_surbs_for_queue_clearing(recipient_tag)
|
||||
.await;
|
||||
if max_to_send > 0 {
|
||||
let (surbs, _surbs_left) = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&recipient_tag, max_to_send);
|
||||
|
||||
if let Some(reply_surbs) = surbs {
|
||||
let to_send = fragments.drain(..max_to_send).collect::<Vec<_>>();
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_send_reply_chunks_on_lane(
|
||||
recipient_tag,
|
||||
to_send.clone(),
|
||||
reply_surbs,
|
||||
lane,
|
||||
)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(
|
||||
self.full_reply_storage.surbs_storage_ref(),
|
||||
&recipient_tag,
|
||||
);
|
||||
warn!("failed to send reply to {recipient_tag}: {err}");
|
||||
self.insert_pending_replies(&recipient_tag, to_send, lane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there's leftover data we didn't send because we didn't have enough (or any) surbs - buffer it
|
||||
if !fragments.is_empty() {
|
||||
self.insert_pending_replies(&recipient_tag, fragments, lane);
|
||||
}
|
||||
|
||||
if self.should_request_more_surbs(&recipient_tag) {
|
||||
self.request_reply_surbs_for_queue_clearing(recipient_tag)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_additional_reply_surbs(
|
||||
@@ -398,35 +328,18 @@ where
|
||||
};
|
||||
|
||||
let mut to_take = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
// TODO: once rust 1.66.0 is stabilised on 15.12.22, just change it to
|
||||
// `.pop_front()` to directly take ownership
|
||||
for (k, data) in pending.iter() {
|
||||
let upgraded = match data.upgrade() {
|
||||
Some(upgraded) => upgraded,
|
||||
None => {
|
||||
// we got the ack while the data was waiting in the queue
|
||||
to_remove.push(*k);
|
||||
continue;
|
||||
while to_take.len() < max_to_clear {
|
||||
if let Some((_, data)) = pending.pop_first() {
|
||||
// no need to do anything if we failed to upgrade the reference,
|
||||
// it means we got the ack while the data was waiting in the queue
|
||||
if let Some(upgraded) = data.upgrade() {
|
||||
to_take.push(upgraded)
|
||||
}
|
||||
};
|
||||
|
||||
to_take.push(upgraded);
|
||||
|
||||
// we have taken as many entries as we could have
|
||||
if to_take.len() >= max_to_clear {
|
||||
} else {
|
||||
// our map is empty!
|
||||
break;
|
||||
}
|
||||
// TODO: use if upgraded.is_extra_surb_request() to bypass the limit
|
||||
}
|
||||
|
||||
for ack in &to_take {
|
||||
pending.remove(&ack.inner_fragment_identifier());
|
||||
}
|
||||
|
||||
for id in to_remove {
|
||||
pending.remove(&id);
|
||||
}
|
||||
|
||||
if to_take.is_empty() {
|
||||
@@ -447,46 +360,47 @@ where
|
||||
|
||||
let to_send_vec = to_take.iter().map(|ack| ack.fragment_data()).collect();
|
||||
|
||||
if let Err(err) = self
|
||||
let prepared_fragments = match self
|
||||
.message_handler
|
||||
.try_send_retransmission_reply_chunks(
|
||||
to_send_vec,
|
||||
surbs_for_reply,
|
||||
TransmissionLane::Retransmission,
|
||||
)
|
||||
.prepare_reply_chunks_for_sending(to_send_vec, surbs_for_reply)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.re_insert_pending_retransmission(&target, to_take);
|
||||
Ok(prepared) => prepared,
|
||||
Err(err) => {
|
||||
let err =
|
||||
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.re_insert_pending_retransmission(&target, to_take);
|
||||
|
||||
warn!(
|
||||
"failed to clear pending retransmission queue for {:?} - {err}",
|
||||
target
|
||||
);
|
||||
}
|
||||
warn!(
|
||||
"failed to clear pending retransmission queue for {:?} - {err}",
|
||||
target
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// we can't fail at this point, so drop all references to acks so that timer updates wouldn't blow up
|
||||
drop(to_take);
|
||||
|
||||
self.message_handler
|
||||
.send_retransmission_reply_chunks(prepared_fragments, TransmissionLane::Retransmission)
|
||||
.await;
|
||||
}
|
||||
|
||||
fn pop_at_most_pending_replies(
|
||||
&mut self,
|
||||
from: &AnonymousSenderTag,
|
||||
amount: usize,
|
||||
) -> Option<VecDeque<Fragment>> {
|
||||
) -> Option<Vec<(TransmissionLane, Fragment)>> {
|
||||
// if possible, pop all pending replies, if not, pop only entries for which we'd have a reply surb
|
||||
let total = self.pending_replies.get(from)?.len();
|
||||
let total = self.pending_replies.get(from)?.total_size();
|
||||
trace!("pending queue has {total} elements");
|
||||
if total == 0 {
|
||||
return None;
|
||||
}
|
||||
if total < amount {
|
||||
self.pending_replies.remove(from)
|
||||
} else {
|
||||
Some(
|
||||
self.pending_replies
|
||||
.get_mut(from)?
|
||||
.drain(..amount)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
self.pending_replies
|
||||
.get_mut(from)?
|
||||
.pop_at_most_n_next_messages_at_random(amount)
|
||||
}
|
||||
|
||||
async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) {
|
||||
@@ -510,9 +424,9 @@ where
|
||||
|
||||
// we're guaranteed to not get more entries than we have reply surbs for
|
||||
if let Some(to_send) = self.pop_at_most_pending_replies(&target, max_to_clear) {
|
||||
let to_send_vec = to_send.iter().cloned().collect::<Vec<_>>();
|
||||
let to_send_clone = to_send.clone();
|
||||
|
||||
if to_send_vec.is_empty() {
|
||||
if to_send_clone.is_empty() {
|
||||
panic!(
|
||||
"please let the devs know if you ever see this message (reply_controller.rs)"
|
||||
);
|
||||
@@ -521,27 +435,22 @@ where
|
||||
let (surbs_for_reply, _) = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&target, to_send_vec.len());
|
||||
.get_reply_surbs(&target, to_send_clone.len());
|
||||
|
||||
let Some(surbs_for_reply) = surbs_for_reply else {
|
||||
error!("somehow different task has stolen our reply surbs! - this should have been impossible");
|
||||
self.insert_pending_replies(&target, to_send);
|
||||
self.re_insert_pending_replies(&target, to_send);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_send_reply_chunks(
|
||||
target,
|
||||
to_send_vec,
|
||||
surbs_for_reply,
|
||||
TransmissionLane::General,
|
||||
)
|
||||
.try_send_reply_chunks(target, to_send_clone, surbs_for_reply)
|
||||
.await
|
||||
{
|
||||
let err =
|
||||
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.insert_pending_replies(&target, to_send);
|
||||
self.re_insert_pending_replies(&target, to_send);
|
||||
warn!("failed to clear pending queue for {:?} - {err}", target);
|
||||
}
|
||||
} else {
|
||||
@@ -712,6 +621,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// to be honest this doesn't make a lot of sense in the context of `connection_id`,
|
||||
// it should really be asked per tag
|
||||
fn handle_lane_queue_length(
|
||||
&self,
|
||||
connection_id: ConnectionId,
|
||||
response_channel: oneshot::Sender<usize>,
|
||||
) {
|
||||
// TODO: if we ever have duplicate ids for different senders, it means our rng is super weak
|
||||
// thus I don't think we have to worry about it?
|
||||
let lane = TransmissionLane::ConnectionId(connection_id);
|
||||
for buf in self.pending_replies.values() {
|
||||
if let Some(length) = buf.lane_length(&lane) {
|
||||
if response_channel.send(length).is_err() {
|
||||
error!("the requester for lane queue length has dropped the response channel!")
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// make sure that if we didn't find that lane, we reply with 0
|
||||
if response_channel.send(0).is_err() {
|
||||
error!("the requester for lane queue length has dropped the response channel!")
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, request: ReplyControllerMessage) {
|
||||
match request {
|
||||
ReplyControllerMessage::RetransmitReply {
|
||||
@@ -735,19 +668,26 @@ where
|
||||
self.handle_received_surbs(sender_tag, reply_surbs, from_surb_request)
|
||||
.await
|
||||
}
|
||||
ReplyControllerMessage::LaneQueueLength {
|
||||
connection_id,
|
||||
response_channel,
|
||||
} => self.handle_lane_queue_length(connection_id, response_channel),
|
||||
ReplyControllerMessage::AdditionalSurbsRequest { recipient, amount } => {
|
||||
self.handle_surb_request(*recipient, amount).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: modify this method to more accurately determine the amount of surbs it needs to request
|
||||
// it should take into consideration the average latency, sending rate and queue size.
|
||||
// it should request as many surbs as it takes to saturate its sending rate before next batch arrives
|
||||
async fn request_reply_surbs_for_queue_clearing(&mut self, target: AnonymousSenderTag) {
|
||||
trace!("requesting surbs for queues clearing");
|
||||
|
||||
let pending_queue_size = self
|
||||
.pending_replies
|
||||
.get(&target)
|
||||
.map(|pending_queue| pending_queue.len())
|
||||
.map(|pending_queue| pending_queue.total_size())
|
||||
.unwrap_or_default();
|
||||
|
||||
let retransmission_queue = self
|
||||
@@ -787,7 +727,7 @@ where
|
||||
}
|
||||
|
||||
let Some(last_received) = self.full_reply_storage.surbs_storage_ref().surbs_last_received_at(pending_reply_target) else {
|
||||
error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.len());
|
||||
error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.total_size());
|
||||
to_remove.push(*pending_reply_target);
|
||||
continue;
|
||||
};
|
||||
@@ -883,23 +823,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn create_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
return tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate));
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
return gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32);
|
||||
}
|
||||
// #[cfg(not(target_arch = "wasm32"))]
|
||||
// async fn log_status(&self) {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
|
||||
debug!("Started ReplyController with graceful shutdown support");
|
||||
|
||||
let polling_rate = Duration::from_secs(5);
|
||||
let mut stale_inspection = Self::create_interval_stream(polling_rate);
|
||||
let mut stale_inspection = new_interval_stream(polling_rate);
|
||||
|
||||
// this is in the order of hours/days so we don't have to poll it that often
|
||||
let polling_rate = Duration::from_secs(self.config.max_reply_surb_age.as_secs() / 10);
|
||||
let mut invalidation_inspection = Self::create_interval_stream(polling_rate);
|
||||
let mut invalidation_inspection = new_interval_stream(polling_rate);
|
||||
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement;
|
||||
use client_connections::{ConnectionId, TransmissionLane};
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use log::error;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::anonymous_replies::ReplySurb;
|
||||
use std::sync::Weak;
|
||||
|
||||
pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
(tx.into(), rx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReplyControllerSender(mpsc::UnboundedSender<ReplyControllerMessage>);
|
||||
|
||||
impl From<mpsc::UnboundedSender<ReplyControllerMessage>> for ReplyControllerSender {
|
||||
fn from(inner: mpsc::UnboundedSender<ReplyControllerMessage>) -> Self {
|
||||
ReplyControllerSender(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplyControllerSender {
|
||||
pub(crate) fn send_retransmission_data(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::RetransmitReply {
|
||||
recipient,
|
||||
timed_out_ack,
|
||||
extra_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_reply(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::SendReply {
|
||||
recipient,
|
||||
message,
|
||||
lane,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs(
|
||||
&self,
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbs {
|
||||
sender_tag,
|
||||
reply_surbs,
|
||||
from_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs_request(&self, recipient: Recipient, amount: u32) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest {
|
||||
recipient: Box::new(recipient),
|
||||
amount,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::LaneQueueLength {
|
||||
connection_id,
|
||||
response_channel: response_tx,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!");
|
||||
|
||||
match response_rx.await {
|
||||
Ok(length) => length,
|
||||
Err(_) => {
|
||||
error!("The reply controller has dropped our response channel!");
|
||||
// TODO: should we panic here instead? this message implies something weird and unrecoverable has happened
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ReplyControllerMessage {
|
||||
RetransmitReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
},
|
||||
|
||||
SendReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
},
|
||||
|
||||
AdditionalSurbs {
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
},
|
||||
|
||||
// this one doesn't belong here either...
|
||||
LaneQueueLength {
|
||||
connection_id: ConnectionId,
|
||||
response_channel: oneshot::Sender<usize>,
|
||||
},
|
||||
|
||||
// Should this also be handled in here? it's technically a completely different side of the pipe
|
||||
// let's see how it works when combined, might split it before creating PR
|
||||
AdditionalSurbsRequest {
|
||||
recipient: Box<Recipient>,
|
||||
amount: u32,
|
||||
},
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use self::error::StorageError;
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager;
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::models::{
|
||||
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
|
||||
@@ -17,6 +16,8 @@ use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub use self::error::StorageError;
|
||||
|
||||
mod error;
|
||||
mod manager;
|
||||
mod models;
|
||||
@@ -66,6 +67,12 @@ impl Backend {
|
||||
return Err(StorageError::IncompleteDataFlush);
|
||||
}
|
||||
|
||||
let last_flush_timestamp = manager.get_previous_flush_timestamp().await?;
|
||||
if last_flush_timestamp == 0 {
|
||||
// either this client has been running since 1970 or the flush failed
|
||||
return Err(StorageError::IncompleteDataFlush);
|
||||
}
|
||||
|
||||
// the process has gone down without full graceful shutdown,
|
||||
// meaning the database doesn't contain valid data anymore
|
||||
// so we have to purge it
|
||||
@@ -75,7 +82,12 @@ impl Backend {
|
||||
manager.delete_all_reply_keys().await?;
|
||||
}
|
||||
|
||||
let last_flush_timestamp = manager.get_previous_flush_timestamp().await?;
|
||||
if let Err(err) = manager.get_reply_surb_storage_metadata().await {
|
||||
// we can't recover here, we HAVE TO initialise fresh (because we don't know correct starting metadata)
|
||||
error!("it seems the client has been shutdown gracefully - we're missing valid surb data dump. the existing database cannot be used");
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
let last_flush = match OffsetDateTime::from_unix_timestamp(last_flush_timestamp) {
|
||||
Ok(last_flush) => last_flush,
|
||||
Err(err) => {
|
||||
|
||||
+118
-48
@@ -1,38 +1,57 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::helpers::{get_time_now, Instant};
|
||||
use crate::client::real_messages_control::real_traffic_stream::RealMessage;
|
||||
use client_connections::TransmissionLane;
|
||||
use nymsphinx::chunking::fragment::Fragment;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
|
||||
use super::{get_time_now, RealMessage};
|
||||
|
||||
// The number of lanes included in the oldest set. Used when we need to prioritize traffic.
|
||||
const OLDEST_LANE_SET_SIZE: usize = 4;
|
||||
// As a way of prune connections we also check for timeouts.
|
||||
const MSG_CONSIDERED_STALE_AFTER_SECS: u64 = 10 * 60;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TransmissionBuffer {
|
||||
buffer: HashMap<TransmissionLane, LaneBufferEntry>,
|
||||
pub(crate) trait SizedData {
|
||||
fn data_size(&self) -> usize;
|
||||
}
|
||||
|
||||
impl TransmissionBuffer {
|
||||
impl SizedData for RealMessage {
|
||||
fn data_size(&self) -> usize {
|
||||
self.packet_size()
|
||||
}
|
||||
}
|
||||
|
||||
impl SizedData for Fragment {
|
||||
fn data_size(&self) -> usize {
|
||||
// note that raw `Fragment` is smaller than sphinx packet payload
|
||||
// as it doesn't include surb-ack or the [shared] key materials
|
||||
self.payload_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TransmissionBuffer<T> {
|
||||
buffer: HashMap<TransmissionLane, LaneBufferEntry<T>>,
|
||||
}
|
||||
|
||||
impl<T> TransmissionBuffer<T> {
|
||||
pub(crate) fn new() -> Self {
|
||||
TransmissionBuffer {
|
||||
buffer: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.buffer.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option<LaneBufferEntry> {
|
||||
pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option<LaneBufferEntry<T>> {
|
||||
self.buffer.remove(lane)
|
||||
}
|
||||
|
||||
@@ -57,20 +76,22 @@ impl TransmissionBuffer {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn total_size(&self) -> usize {
|
||||
self.buffer.values().map(LaneBufferEntry::len).sum()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn total_size_in_bytes(&self) -> usize {
|
||||
pub(crate) fn total_size_in_bytes(&self) -> usize
|
||||
where
|
||||
T: SizedData,
|
||||
{
|
||||
self.buffer
|
||||
.values()
|
||||
.map(|lane_buffer_entry| {
|
||||
lane_buffer_entry
|
||||
.real_messages
|
||||
.items
|
||||
.iter()
|
||||
.map(|real_message| real_message.mix_packet.sphinx_packet().len())
|
||||
.map(|item| item.data_size())
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
@@ -92,42 +113,51 @@ impl TransmissionBuffer {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&mut self, lane: &TransmissionLane, real_messages: Vec<RealMessage>) {
|
||||
pub(crate) fn store<I: IntoIterator<Item = T>>(&mut self, lane: &TransmissionLane, items: I) {
|
||||
if let Some(lane_buffer_entry) = self.buffer.get_mut(lane) {
|
||||
lane_buffer_entry.append(real_messages);
|
||||
lane_buffer_entry.extend(items);
|
||||
} else {
|
||||
self.buffer
|
||||
.insert(*lane, LaneBufferEntry::new(real_messages));
|
||||
.insert(*lane, LaneBufferEntry::new(items.into_iter().collect()));
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_random_lane(&self) -> Option<&TransmissionLane> {
|
||||
let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect();
|
||||
lanes.choose(&mut rand::thread_rng()).copied()
|
||||
pub(crate) fn store_multiple(&mut self, items: Vec<(TransmissionLane, T)>) {
|
||||
for (lane, item) in items {
|
||||
self.buffer
|
||||
.entry(lane)
|
||||
.or_insert_with(LaneBufferEntry::new_empty)
|
||||
.push_item(item)
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_random_small_lane(&self) -> Option<&TransmissionLane> {
|
||||
fn pick_random_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&TransmissionLane> {
|
||||
let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect();
|
||||
lanes.choose(rng).copied()
|
||||
}
|
||||
|
||||
fn pick_random_small_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&TransmissionLane> {
|
||||
let lanes: Vec<&TransmissionLane> = self
|
||||
.buffer
|
||||
.iter()
|
||||
.filter(|(_, v)| v.is_small())
|
||||
.map(|(k, _)| k)
|
||||
.collect();
|
||||
lanes.choose(&mut rand::thread_rng()).copied()
|
||||
lanes.choose(rng).copied()
|
||||
}
|
||||
|
||||
// 2/3 chance to pick from the old lanes
|
||||
fn pick_random_old_lane(&self) -> Option<TransmissionLane> {
|
||||
fn pick_random_old_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<TransmissionLane> {
|
||||
let rand = &mut rand::thread_rng();
|
||||
if rand.gen_ratio(2, 3) {
|
||||
let lanes = self.get_oldest_set();
|
||||
lanes.choose(rand).copied()
|
||||
} else {
|
||||
self.pick_random_lane().copied()
|
||||
self.pick_random_lane(rng).copied()
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<RealMessage> {
|
||||
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<T> {
|
||||
let real_msgs_queued = self.buffer.get_mut(lane)?;
|
||||
let real_next = real_msgs_queued.pop_front()?;
|
||||
real_msgs_queued.messages_transmitted += 1;
|
||||
@@ -137,19 +167,48 @@ impl TransmissionBuffer {
|
||||
Some(real_next)
|
||||
}
|
||||
|
||||
pub(crate) fn pop_next_message_at_random(&mut self) -> Option<(TransmissionLane, RealMessage)> {
|
||||
pub(crate) fn pop_at_most_n_next_messages_at_random(
|
||||
&mut self,
|
||||
n: usize,
|
||||
) -> Option<Vec<(TransmissionLane, T)>> {
|
||||
// let start = Instant::now();
|
||||
|
||||
if self.buffer.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rng = &mut rand::thread_rng();
|
||||
let mut items = Vec::with_capacity(n);
|
||||
|
||||
while items.len() < n {
|
||||
let Some(next) = self.pop_next_message_at_random(rng) else {
|
||||
break
|
||||
};
|
||||
items.push(next)
|
||||
}
|
||||
|
||||
// todo!("time time taken");
|
||||
|
||||
Some(items)
|
||||
}
|
||||
|
||||
pub(crate) fn pop_next_message_at_random<R: Rng + ?Sized>(
|
||||
&mut self,
|
||||
// turns out the caller always have access to some rng, so no point in instantiating new one
|
||||
rng: &mut R,
|
||||
) -> Option<(TransmissionLane, T)> {
|
||||
if self.buffer.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Very basic heuristic where we prioritize according to small lanes first, the older lanes
|
||||
// to try to finish lanes when possible, then the rest.
|
||||
let lane = if let Some(small_lane) = self.pick_random_small_lane() {
|
||||
let lane = if let Some(small_lane) = self.pick_random_small_lane(rng) {
|
||||
*small_lane
|
||||
} else if let Some(old_lane) = self.pick_random_old_lane() {
|
||||
} else if let Some(old_lane) = self.pick_random_old_lane(rng) {
|
||||
old_lane
|
||||
} else {
|
||||
*self.pick_random_lane()?
|
||||
*self.pick_random_lane(rng)?
|
||||
};
|
||||
|
||||
let msg = self.pop_front_from_lane(&lane)?;
|
||||
@@ -171,35 +230,46 @@ impl TransmissionBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LaneBufferEntry {
|
||||
pub real_messages: VecDeque<RealMessage>,
|
||||
pub(crate) struct LaneBufferEntry<T> {
|
||||
pub items: VecDeque<T>,
|
||||
pub messages_transmitted: usize,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub time_for_last_activity: time::Instant,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub time_for_last_activity: wasm_timer::Instant,
|
||||
pub time_for_last_activity: Instant,
|
||||
}
|
||||
|
||||
impl LaneBufferEntry {
|
||||
fn new(real_messages: Vec<RealMessage>) -> Self {
|
||||
impl<T> LaneBufferEntry<T> {
|
||||
fn new_empty() -> Self {
|
||||
LaneBufferEntry {
|
||||
real_messages: real_messages.into(),
|
||||
items: VecDeque::new(),
|
||||
messages_transmitted: 0,
|
||||
time_for_last_activity: get_time_now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, real_messages: Vec<RealMessage>) {
|
||||
self.real_messages.append(&mut real_messages.into());
|
||||
fn new(items: VecDeque<T>) -> Self {
|
||||
LaneBufferEntry {
|
||||
items,
|
||||
messages_transmitted: 0,
|
||||
time_for_last_activity: get_time_now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_item(&mut self, item: T) {
|
||||
self.items.push_back(item);
|
||||
// I'm not updating time here on purpose. This method is called just after `new_empty`,
|
||||
// where the time is already set. Furthermore, this method is called there multiple times at once
|
||||
}
|
||||
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, items: I) {
|
||||
self.items.extend(items);
|
||||
self.time_for_last_activity = get_time_now();
|
||||
}
|
||||
|
||||
fn pop_front(&mut self) -> Option<RealMessage> {
|
||||
self.real_messages.pop_front()
|
||||
fn pop_front(&mut self) -> Option<T> {
|
||||
self.items.pop_front()
|
||||
}
|
||||
|
||||
fn is_small(&self) -> bool {
|
||||
self.real_messages.len() < 100
|
||||
self.items.len() < 100
|
||||
}
|
||||
|
||||
fn is_stale(&self) -> bool {
|
||||
@@ -208,10 +278,10 @@ impl LaneBufferEntry {
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.real_messages.len()
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.real_messages.is_empty()
|
||||
self.items.is_empty()
|
||||
}
|
||||
}
|
||||
@@ -88,16 +88,28 @@ impl<T> Config<T> {
|
||||
where
|
||||
T: NymConfig,
|
||||
{
|
||||
let id = id.into();
|
||||
self.client.id = id.into();
|
||||
self.set_empty_fields_to_defaults();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_empty_fields_to_defaults(&mut self) -> bool
|
||||
where
|
||||
T: NymConfig,
|
||||
{
|
||||
let id = &self.client.id;
|
||||
let mut changes_made = false;
|
||||
|
||||
// identity key setting
|
||||
if self.client.private_identity_key_file.as_os_str().is_empty() {
|
||||
changes_made = true;
|
||||
self.client.private_identity_key_file =
|
||||
self::Client::<T>::default_private_identity_key_file(&id);
|
||||
self::Client::<T>::default_private_identity_key_file(id);
|
||||
}
|
||||
if self.client.public_identity_key_file.as_os_str().is_empty() {
|
||||
changes_made = true;
|
||||
self.client.public_identity_key_file =
|
||||
self::Client::<T>::default_public_identity_key_file(&id);
|
||||
self::Client::<T>::default_public_identity_key_file(id);
|
||||
}
|
||||
|
||||
// encryption key setting
|
||||
@@ -107,8 +119,9 @@ impl<T> Config<T> {
|
||||
.as_os_str()
|
||||
.is_empty()
|
||||
{
|
||||
changes_made = true;
|
||||
self.client.private_encryption_key_file =
|
||||
self::Client::<T>::default_private_encryption_key_file(&id);
|
||||
self::Client::<T>::default_private_encryption_key_file(id);
|
||||
}
|
||||
if self
|
||||
.client
|
||||
@@ -116,32 +129,35 @@ impl<T> Config<T> {
|
||||
.as_os_str()
|
||||
.is_empty()
|
||||
{
|
||||
changes_made = true;
|
||||
self.client.public_encryption_key_file =
|
||||
self::Client::<T>::default_public_encryption_key_file(&id);
|
||||
self::Client::<T>::default_public_encryption_key_file(id);
|
||||
}
|
||||
|
||||
// shared gateway key setting
|
||||
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
|
||||
changes_made = true;
|
||||
self.client.gateway_shared_key_file =
|
||||
self::Client::<T>::default_gateway_shared_key_file(&id);
|
||||
self::Client::<T>::default_gateway_shared_key_file(id);
|
||||
}
|
||||
|
||||
// ack key setting
|
||||
if self.client.ack_key_file.as_os_str().is_empty() {
|
||||
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(&id);
|
||||
changes_made = true;
|
||||
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(id);
|
||||
}
|
||||
|
||||
if self.client.reply_surb_database_path.as_os_str().is_empty() {
|
||||
changes_made = true;
|
||||
self.client.reply_surb_database_path =
|
||||
self::Client::<T>::default_reply_surb_database_path(&id);
|
||||
self::Client::<T>::default_reply_surb_database_path(id);
|
||||
}
|
||||
|
||||
if self.client.database_path.as_os_str().is_empty() {
|
||||
self.client.database_path = self::Client::<T>::default_database_path(&id);
|
||||
changes_made = true;
|
||||
self.client.database_path = self::Client::<T>::default_database_path(id);
|
||||
}
|
||||
|
||||
self.client.id = id;
|
||||
self
|
||||
changes_made
|
||||
}
|
||||
|
||||
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) {
|
||||
@@ -439,6 +455,10 @@ pub struct Client<T> {
|
||||
database_path: PathBuf,
|
||||
|
||||
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
||||
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
|
||||
// if you're reading this message and we have already introduced some breaking changes, feel free
|
||||
// to remove that attribute since at this point the client configs should have gotten regenerated
|
||||
#[serde(default)]
|
||||
reply_surb_database_path: PathBuf,
|
||||
|
||||
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -13,6 +13,7 @@ use client_core::client::base_client::{
|
||||
use client_core::client::inbound_messages::InputMessage;
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
|
||||
use client_core::client::replies::reply_controller::requests::ReplyControllerSender;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
@@ -86,7 +87,8 @@ impl SocketClient {
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
self_address: Recipient,
|
||||
self_address: &Recipient,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
shutdown: task::TaskClient,
|
||||
) {
|
||||
info!("Starting websocket listener...");
|
||||
@@ -101,12 +103,13 @@ impl SocketClient {
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let websocket_handler = websocket::Handler::new(
|
||||
let websocket_handler = websocket::HandlerBuilder::new(
|
||||
input_sender,
|
||||
connection_command_sender,
|
||||
received_buffer_request_sender,
|
||||
self_address,
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
);
|
||||
|
||||
websocket::Listener::new(config.get_listening_port()).start(websocket_handler, shutdown);
|
||||
@@ -153,7 +156,8 @@ impl SocketClient {
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
self_address,
|
||||
&self_address,
|
||||
started_client.reply_controller_sender,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
|
||||
|
||||
@@ -105,6 +105,10 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Syn
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
if config.get_base_mut().set_empty_fields_to_defaults() {
|
||||
warn!("some of the core config options were left unset. the default values are going to get used instead.");
|
||||
}
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return Err(Box::new(ClientError::FailedLocalVersionCheck));
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use client_connections::{
|
||||
ConnectionCommand, ConnectionCommandSender, LaneQueueLengths, TransmissionLane,
|
||||
ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane,
|
||||
};
|
||||
use client_core::client::replies::reply_controller::requests::ReplyControllerSender;
|
||||
use client_core::client::{
|
||||
inbound_messages::{InputMessage, InputMessageSender},
|
||||
received_buffer::{
|
||||
@@ -16,7 +17,9 @@ use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::Instant;
|
||||
use tokio_tungstenite::{
|
||||
accept_async,
|
||||
tungstenite::{protocol::Message as WsMessage, Error as WsError},
|
||||
@@ -35,53 +38,36 @@ impl Default for ReceivedResponseType {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Handler {
|
||||
pub(crate) struct HandlerBuilder {
|
||||
msg_input: InputMessageSender,
|
||||
client_connection_tx: ConnectionCommandSender,
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
self_full_address: Recipient,
|
||||
socket: Option<WebSocketStream<TcpStream>>,
|
||||
received_response_type: ReceivedResponseType,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
is_active: bool,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl Drop for Handler {
|
||||
fn drop(&mut self) {
|
||||
if self.is_active
|
||||
&& self
|
||||
.buffer_requester
|
||||
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
|
||||
.is_err()
|
||||
{
|
||||
error!("we failed to disconnect the receiver from the buffer! presumably the shutdown procedure has been initiated!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
impl HandlerBuilder {
|
||||
pub(crate) fn new(
|
||||
msg_input: InputMessageSender,
|
||||
client_connection_tx: ConnectionCommandSender,
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
self_full_address: Recipient,
|
||||
self_full_address: &Recipient,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
) -> Self {
|
||||
Handler {
|
||||
Self {
|
||||
msg_input,
|
||||
client_connection_tx,
|
||||
buffer_requester,
|
||||
self_full_address,
|
||||
socket: None,
|
||||
received_response_type: Default::default(),
|
||||
self_full_address: *self_full_address,
|
||||
lane_queue_lengths,
|
||||
is_active: false,
|
||||
reply_controller_sender,
|
||||
}
|
||||
}
|
||||
|
||||
// Used to use handler on a new connection, which initially is `None`
|
||||
// TODO: make sure we only ever have one active handler
|
||||
pub fn create_active_handler(&self) -> Self {
|
||||
pub fn create_active_handler(&self) -> Handler {
|
||||
Handler {
|
||||
msg_input: self.msg_input.clone(),
|
||||
client_connection_tx: self.client_connection_tx.clone(),
|
||||
@@ -90,9 +76,76 @@ impl Handler {
|
||||
socket: None,
|
||||
received_response_type: Default::default(),
|
||||
lane_queue_lengths: self.lane_queue_lengths.clone(),
|
||||
is_active: true,
|
||||
reply_controller_sender: self.reply_controller_sender.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Handler {
|
||||
msg_input: InputMessageSender,
|
||||
client_connection_tx: ConnectionCommandSender,
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
self_full_address: Recipient,
|
||||
socket: Option<WebSocketStream<TcpStream>>,
|
||||
received_response_type: ReceivedResponseType,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl Drop for Handler {
|
||||
fn drop(&mut self) {
|
||||
if self
|
||||
.buffer_requester
|
||||
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
|
||||
.is_err()
|
||||
{
|
||||
error!("we failed to disconnect the receiver from the buffer! presumably the shutdown procedure has been initiated!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> Option<ServerResponse> {
|
||||
let req_start = Instant::now();
|
||||
|
||||
// get the base queue length
|
||||
// Note that this does _NOT_ take into account the packets that have been received but not
|
||||
// yet reach `OutQueueControl`, so it might be a tad low.
|
||||
let conn_lane = TransmissionLane::ConnectionId(connection_id);
|
||||
let Ok(base_length) = self
|
||||
.lane_queue_lengths
|
||||
.lock()
|
||||
.map(|guard| guard.get(&conn_lane).unwrap_or_default()) else {
|
||||
// I'd argue we should panic here as this error it not recoverable
|
||||
error!("The lane queue length lock is poisoned!!");
|
||||
return None
|
||||
};
|
||||
|
||||
// get the number of pending replies waiting for reply surbs
|
||||
let reply_queue_length = self
|
||||
.reply_controller_sender
|
||||
.get_lane_queue_length(connection_id)
|
||||
.await;
|
||||
|
||||
let queue_length = base_length + reply_queue_length;
|
||||
|
||||
let time_taken = req_start.elapsed();
|
||||
let msg =
|
||||
format!("it took {time_taken:?} to get lane length for connection {connection_id}. The length is: {queue_length} = {base_length} (already queued up) + {reply_queue_length} (waiting for reply SURBs)");
|
||||
|
||||
if time_taken > Duration::from_millis(1) {
|
||||
info!("{msg}");
|
||||
} else if time_taken > Duration::from_millis(10) {
|
||||
warn!("{msg}");
|
||||
} else if time_taken > Duration::from_millis(50) {
|
||||
error!("{msg}");
|
||||
}
|
||||
|
||||
Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_send(
|
||||
&mut self,
|
||||
@@ -118,27 +171,11 @@ impl Handler {
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
|
||||
let connection_id = match lane {
|
||||
TransmissionLane::General
|
||||
| TransmissionLane::ReplySurbRequest
|
||||
| TransmissionLane::Retransmission
|
||||
| TransmissionLane::AdditionalReplySurbs => return None,
|
||||
TransmissionLane::ConnectionId(id) => id,
|
||||
let TransmissionLane::ConnectionId(connection_id) = lane else {
|
||||
return None
|
||||
};
|
||||
|
||||
// on receiving a send, we reply back the current lane queue length for that connection id.
|
||||
// Note that this does _NOT_ take into account the packets that have been received but not
|
||||
// yet reach `OutQueueControl`, so it might be a tad low.
|
||||
if let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() {
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
return Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!("Failed to get the lane queue length lock, not responding back with the current queue length");
|
||||
None
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
async fn handle_send_anonymous(
|
||||
@@ -165,27 +202,11 @@ impl Handler {
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
|
||||
let connection_id = match lane {
|
||||
TransmissionLane::General
|
||||
| TransmissionLane::ReplySurbRequest
|
||||
| TransmissionLane::Retransmission
|
||||
| TransmissionLane::AdditionalReplySurbs => return None,
|
||||
TransmissionLane::ConnectionId(id) => id,
|
||||
let TransmissionLane::ConnectionId(connection_id) = lane else {
|
||||
return None
|
||||
};
|
||||
|
||||
// on receiving a send, we reply back the current lane queue length for that connection id.
|
||||
// Note that this does _NOT_ take into account the packets that have been received but not
|
||||
// yet reach `OutQueueControl`, so it might be a tad low.
|
||||
if let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() {
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
return Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!("Failed to get the lane queue length lock, not responding back with the current queue length");
|
||||
None
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
async fn handle_reply(
|
||||
@@ -208,27 +229,11 @@ impl Handler {
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
|
||||
let connection_id = match lane {
|
||||
TransmissionLane::General
|
||||
| TransmissionLane::ReplySurbRequest
|
||||
| TransmissionLane::Retransmission
|
||||
| TransmissionLane::AdditionalReplySurbs => return None,
|
||||
TransmissionLane::ConnectionId(id) => id,
|
||||
let TransmissionLane::ConnectionId(connection_id) = lane else {
|
||||
return None
|
||||
};
|
||||
|
||||
// on receiving a send, we reply back the current lane queue length for that connection id.
|
||||
// Note that this does _NOT_ take into account the packets that have been received but not
|
||||
// yet reach `OutQueueControl`, so it might be a tad low.
|
||||
if let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() {
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
return Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!("Failed to get the lane queue length lock, not responding back with the current queue length");
|
||||
None
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
fn handle_self_address(&self) -> ServerResponse {
|
||||
@@ -242,20 +247,8 @@ impl Handler {
|
||||
None
|
||||
}
|
||||
|
||||
fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option<ServerResponse> {
|
||||
let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() else {
|
||||
log::warn!(
|
||||
"Failed to get the lane queue length lock, not responding back with the current queue length"
|
||||
);
|
||||
return None;
|
||||
};
|
||||
|
||||
let lane = TransmissionLane::ConnectionId(connection_id);
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
})
|
||||
async fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option<ServerResponse> {
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, request: ClientRequest) -> Option<ServerResponse> {
|
||||
@@ -284,7 +277,7 @@ impl Handler {
|
||||
|
||||
ClientRequest::SelfAddress => Some(self.handle_self_address()),
|
||||
ClientRequest::ClosedConnection(id) => self.handle_closed_connection(id),
|
||||
ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id),
|
||||
ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::handler::Handler;
|
||||
use super::handler::HandlerBuilder;
|
||||
use log::*;
|
||||
use std::{net::SocketAddr, process, sync::Arc};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
@@ -32,7 +32,7 @@ impl Listener {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self, handler: Handler, mut task_client: task::TaskClient) {
|
||||
pub(crate) async fn run(&mut self, handler: HandlerBuilder, mut task_client: task::TaskClient) {
|
||||
let tcp_listener = match tokio::net::TcpListener::bind(self.address).await {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => {
|
||||
@@ -100,7 +100,11 @@ impl Listener {
|
||||
log::debug!("Websocket listener: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self, handler: Handler, shutdown: task::TaskClient) -> JoinHandle<()> {
|
||||
pub(crate) fn start(
|
||||
mut self,
|
||||
handler: HandlerBuilder,
|
||||
shutdown: task::TaskClient,
|
||||
) -> JoinHandle<()> {
|
||||
info!("Running websocket on {:?}", self.address.to_string());
|
||||
|
||||
tokio::spawn(async move { self.run(handler, shutdown).await })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub(crate) use handler::Handler;
|
||||
pub(crate) use handler::HandlerBuilder;
|
||||
pub(crate) use listener::Listener;
|
||||
|
||||
pub(crate) mod handler;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
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"
|
||||
|
||||
@@ -143,6 +143,7 @@ pub struct Socks5 {
|
||||
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
||||
///
|
||||
/// Note that some service providers might not support this.
|
||||
#[serde(default)]
|
||||
send_anonymously: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ impl NymClient {
|
||||
let mut shutdown = self.start().await?;
|
||||
|
||||
// Listen to status messages from task, that we forward back to the caller
|
||||
shutdown.start_status_listener(sender);
|
||||
shutdown.start_status_listener(sender).await;
|
||||
|
||||
let res = tokio::select! {
|
||||
biased;
|
||||
|
||||
@@ -120,6 +120,10 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
if config.get_base_mut().set_empty_fields_to_defaults() {
|
||||
warn!("some of the core config options were left unset. the default values are going to get used instead.");
|
||||
}
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::socks::types::SocksProxyError;
|
||||
use client_core::client::replies::reply_storage::fs_backend;
|
||||
use client_core::error::ClientCoreError;
|
||||
use socks5_requests::ConnectionId;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Socks5ClientError {
|
||||
@@ -21,4 +22,10 @@ pub enum Socks5ClientError {
|
||||
|
||||
#[error("Fail to bind address")]
|
||||
FailToBindAddress,
|
||||
|
||||
#[error("Network requester: connection id {connection_id}: {error}")]
|
||||
NetworkRequesterError {
|
||||
connection_id: ConnectionId,
|
||||
error: String,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
|
||||
use socks5_requests::Message;
|
||||
use task::TaskClient;
|
||||
|
||||
use crate::error::Socks5ClientError;
|
||||
|
||||
pub(crate) struct MixnetResponseListener {
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
mix_response_receiver: ReconstructedMessagesReceiver,
|
||||
@@ -52,7 +54,10 @@ impl MixnetResponseListener {
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_message(&self, reconstructed_message: ReconstructedMessage) {
|
||||
fn on_message(
|
||||
&self,
|
||||
reconstructed_message: ReconstructedMessage,
|
||||
) -> Result<(), Socks5ClientError> {
|
||||
let raw_message = reconstructed_message.message;
|
||||
if reconstructed_message.sender_tag.is_some() {
|
||||
warn!("this message was sent anonymously - it couldn't have come from the service provider");
|
||||
@@ -61,11 +66,11 @@ impl MixnetResponseListener {
|
||||
let response = match Message::try_from_bytes(&raw_message) {
|
||||
Err(err) => {
|
||||
warn!("failed to parse received response - {err}");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
Ok(Message::Request(_)) => {
|
||||
warn!("unexpected request");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
Ok(Message::Response(data)) => data,
|
||||
Ok(Message::NetworkRequesterResponse(r)) => {
|
||||
@@ -73,7 +78,10 @@ impl MixnetResponseListener {
|
||||
"Network requester failed on connection id {} with error: {}",
|
||||
r.connection_id, r.network_requester_error
|
||||
);
|
||||
return;
|
||||
return Err(Socks5ClientError::NetworkRequesterError {
|
||||
connection_id: r.connection_id,
|
||||
error: r.network_requester_error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,18 +92,21 @@ impl MixnetResponseListener {
|
||||
response.is_closed,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
while !self.shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
received_responses = self.mix_response_receiver.next() => match received_responses {
|
||||
Some(received_responses) => {
|
||||
received_responses = self.mix_response_receiver.next() => {
|
||||
if let Some(received_responses) = received_responses {
|
||||
for reconstructed_message in received_responses {
|
||||
self.on_message(reconstructed_message).await;
|
||||
if let Err(err) = self.on_message(reconstructed_message) {
|
||||
self.shutdown.send_status_msg(Box::new(err));
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
} else {
|
||||
log::trace!("MixnetResponseListener: Stopping since channel closed");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type ConnectionId = u64;
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ async fn test_nymd_connection(
|
||||
log::debug!("Checking: nymd_url: {url}: {}: {}", "failed".red(), e);
|
||||
false
|
||||
}
|
||||
Ok(Err(NymdError::AbciError(code, log))) => {
|
||||
Ok(Err(NymdError::AbciError { code, log, .. })) => {
|
||||
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
|
||||
// for example on a pre-launch network.
|
||||
log::debug!(
|
||||
|
||||
@@ -9,10 +9,11 @@ use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
|
||||
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError,
|
||||
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
ComputeRewardEstParam, GatewayCoreStatusResponse, GatewayStatusReportResponse,
|
||||
GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
|
||||
MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, StakeSaturationResponse,
|
||||
UptimeResponse,
|
||||
};
|
||||
use reqwest::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -361,6 +362,25 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn compute_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
request_body: &ComputeRewardEstParam,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
&mix_id.to_string(),
|
||||
routes::COMPUTE_REWARD_ESTIMATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
|
||||
@@ -28,6 +28,7 @@ pub const STATUS: &str = "status";
|
||||
pub const REPORT: &str = "report";
|
||||
pub const HISTORY: &str = "history";
|
||||
pub const REWARD_ESTIMATION: &str = "reward-estimation";
|
||||
pub const COMPUTE_REWARD_ESTIMATION: &str = "compute-reward-estimation";
|
||||
pub const AVG_UPTIME: &str = "avg_uptime";
|
||||
pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd;
|
||||
use crate::nymd::coin::Coin;
|
||||
use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key};
|
||||
use crate::nymd::cosmwasm_client::types::{
|
||||
@@ -23,7 +24,6 @@ use cosmrs::rpc::endpoint::broadcast;
|
||||
use cosmrs::rpc::endpoint::tx::Response as TxResponse;
|
||||
use cosmrs::rpc::query::Query;
|
||||
use cosmrs::rpc::{self, HttpClient, Order};
|
||||
use cosmrs::tendermint::abci::Code as AbciCode;
|
||||
use cosmrs::tendermint::abci::Transaction;
|
||||
use cosmrs::tendermint::{abci, block, chain};
|
||||
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx};
|
||||
@@ -66,13 +66,9 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
req.encode(&mut buf)?;
|
||||
|
||||
let res = self.abci_query(path, buf, None, false).await?;
|
||||
let res_success = nymd::error::parse_abci_query_result(res)?;
|
||||
|
||||
match res.code {
|
||||
AbciCode::Err(code) => return Err(NymdError::AbciError(code, res.log)),
|
||||
AbciCode::Ok => (),
|
||||
}
|
||||
|
||||
Ok(Res::decode(res.value.as_ref())?)
|
||||
Ok(Res::decode(res_success.value.as_ref())?)
|
||||
}
|
||||
|
||||
async fn get_chain_id(&self) -> Result<chain::Id, NymdError> {
|
||||
|
||||
@@ -2,16 +2,23 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::cosmwasm_client::types::ContractCodeId;
|
||||
use cosmrs::tendermint::{abci, block};
|
||||
use cosmrs::{bip32, tx, AccountId};
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
use cosmrs::{
|
||||
bip32,
|
||||
rpc::endpoint::abci_query::AbciQuery,
|
||||
tendermint::{
|
||||
abci::{self, Code as AbciCode},
|
||||
block,
|
||||
},
|
||||
tx, AccountId,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
pub use cosmrs::rpc::error::{
|
||||
Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail,
|
||||
use std::{io, time::Duration};
|
||||
|
||||
pub use cosmrs::rpc::{
|
||||
error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail},
|
||||
response_error::{Code, ResponseError},
|
||||
};
|
||||
pub use cosmrs::rpc::response_error::{Code, ResponseError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum NymdError {
|
||||
@@ -110,8 +117,12 @@ pub enum NymdError {
|
||||
#[error("Failed to estimate gas price for the transaction")]
|
||||
GasEstimationFailure,
|
||||
|
||||
#[error("Abci query failed with code {0} - {1}")]
|
||||
AbciError(u32, abci::Log),
|
||||
#[error("Abci query failed with code {code} - {log}")]
|
||||
AbciError {
|
||||
code: u32,
|
||||
log: abci::Log,
|
||||
pretty_log: Option<String>,
|
||||
},
|
||||
|
||||
#[error("Unsupported account type: {type_url}")]
|
||||
UnsupportedAccountType { type_url: String },
|
||||
@@ -135,6 +146,32 @@ pub enum NymdError {
|
||||
UnexpectedBech32Prefix { got: String, expected: String },
|
||||
}
|
||||
|
||||
// The purpose of parsing the abci query result is that we want to generate the `pretty_log` if
|
||||
// possible.
|
||||
pub fn parse_abci_query_result(query_result: AbciQuery) -> Result<AbciQuery, NymdError> {
|
||||
match query_result.code {
|
||||
AbciCode::Ok => Ok(query_result),
|
||||
AbciCode::Err(code) => Err(NymdError::AbciError {
|
||||
code,
|
||||
log: query_result.log.clone(),
|
||||
pretty_log: try_parse_abci_log(&query_result.log),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Some of the error strings returned by the query are a bit too technical to present to the
|
||||
// enduser. So we special case some commonly encountered errors.
|
||||
fn try_parse_abci_log(log: &abci::Log) -> Option<String> {
|
||||
if log
|
||||
.value()
|
||||
.contains("Maximum amount of locked coins has already been pledged")
|
||||
{
|
||||
Some("Maximum amount of locked tokens has alredy been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl NymdError {
|
||||
pub fn is_tendermint_response_timeout(&self) -> bool {
|
||||
match &self {
|
||||
|
||||
@@ -8,13 +8,13 @@ use coconut_dkg_common::dealer::{
|
||||
DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
|
||||
};
|
||||
use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use cosmrs::AccountId;
|
||||
|
||||
#[async_trait]
|
||||
pub trait DkgQueryClient {
|
||||
async fn get_current_epoch_state(&self) -> Result<EpochState, NymdError>;
|
||||
async fn get_current_epoch(&self) -> Result<Epoch, NymdError>;
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NymdError>;
|
||||
async fn get_dealer_details(
|
||||
&self,
|
||||
@@ -49,7 +49,7 @@ impl<C> DkgQueryClient for NymdClient<C>
|
||||
where
|
||||
C: CosmWasmClient + Send + Sync,
|
||||
{
|
||||
async fn get_current_epoch_state(&self) -> Result<EpochState, NymdError> {
|
||||
async fn get_current_epoch(&self) -> Result<Epoch, NymdError> {
|
||||
let request = DkgQueryMsg::GetCurrentEpochState {};
|
||||
self.client
|
||||
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
|
||||
|
||||
@@ -12,6 +12,7 @@ use contracts_common::dealings::ContractSafeBytes;
|
||||
|
||||
#[async_trait]
|
||||
pub trait DkgSigningClient {
|
||||
async fn advance_dkg_epoch_state(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
@@ -37,6 +38,21 @@ impl<C> DkgSigningClient for NymdClient<C>
|
||||
where
|
||||
C: SigningCosmWasmClient + Send + Sync,
|
||||
{
|
||||
async fn advance_dkg_epoch_state(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
|
||||
let req = DkgExecuteMsg::AdvanceEpochState {};
|
||||
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.coconut_dkg_contract_address(),
|
||||
&req,
|
||||
fee.unwrap_or_default(),
|
||||
"advancing DKG state",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
|
||||
@@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct InstantiateMsg {
|
||||
pub group_addr: String,
|
||||
pub multisig_addr: String,
|
||||
pub admin: String,
|
||||
pub mix_denom: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,48 @@ use std::fmt::{Display, Formatter};
|
||||
|
||||
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
|
||||
pub use contracts_common::dealings::ContractSafeBytes;
|
||||
pub use cosmwasm_std::{Addr, Coin};
|
||||
pub use cosmwasm_std::{Addr, Coin, Timestamp};
|
||||
|
||||
pub type EncodedBTEPublicKeyWithProof = String;
|
||||
pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str;
|
||||
pub type NodeIndex = u64;
|
||||
|
||||
// The time sign-up is open for dealers to join (2 minutes)
|
||||
pub const PUBLIC_KEY_SUBMISSION_TIME_SECS: u64 = 60 * 2;
|
||||
pub const DEALING_EXCHANGE_TIME_SECS: u64 = 60 * 5;
|
||||
pub const VERIFICATION_KEY_SUBMISSION_TIME_SECS: u64 = 60 * 5;
|
||||
pub const VERIFICATION_KEY_VALIDATION_TIME_SECS: u64 = 60;
|
||||
pub const VERIFICATION_KEY_FINALIZATION_TIME_SECS: u64 = 60;
|
||||
// The time an epoch lasts (2 weeks)
|
||||
pub const IN_PROGRESS_TIME_SECS: u64 = 60 * 60 * 24 * 14;
|
||||
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct Epoch {
|
||||
pub state: EpochState,
|
||||
pub finish_timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
pub fn new(state: EpochState, current_timestamp: Timestamp) -> Self {
|
||||
let duration = match state {
|
||||
EpochState::PublicKeySubmission => PUBLIC_KEY_SUBMISSION_TIME_SECS,
|
||||
EpochState::DealingExchange => DEALING_EXCHANGE_TIME_SECS,
|
||||
EpochState::VerificationKeySubmission => VERIFICATION_KEY_SUBMISSION_TIME_SECS,
|
||||
EpochState::VerificationKeyValidation => VERIFICATION_KEY_VALIDATION_TIME_SECS,
|
||||
EpochState::VerificationKeyFinalization => VERIFICATION_KEY_FINALIZATION_TIME_SECS,
|
||||
EpochState::InProgress => IN_PROGRESS_TIME_SECS,
|
||||
};
|
||||
Epoch {
|
||||
state,
|
||||
finish_timestamp: current_timestamp.plus_seconds(duration),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints),
|
||||
// the epoch can be in the following states (in order):
|
||||
// 1. PublicKeySubmission -> potential dealers are submitting their BTE and ed25519 public keys to participate in dealing exchange
|
||||
|
||||
@@ -208,6 +208,11 @@ impl Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the size of payload contained in this `Fragment`.
|
||||
pub fn payload_size(&self) -> usize {
|
||||
self.payload.len()
|
||||
}
|
||||
|
||||
/// Extracts id of this `Fragment`.
|
||||
pub fn id(&self) -> i32 {
|
||||
self.header.id
|
||||
|
||||
@@ -63,7 +63,7 @@ impl NetworkRequesterResponse {
|
||||
self.connection_id
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.copied()
|
||||
.chain(self.network_requester_error.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@ enum TaskError {
|
||||
UnexpectedHalt,
|
||||
}
|
||||
|
||||
// TODO: possibly we should create a `Status` trait instead of reusing `Error`
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum TaskStatus {
|
||||
#[error("Ready")]
|
||||
Ready,
|
||||
}
|
||||
|
||||
/// Listens to status and error messages from tasks, as well as notifying them to gracefully
|
||||
/// shutdown. Keeps track of if task stop unexpectedly, such as in a panic.
|
||||
#[derive(Debug)]
|
||||
@@ -101,15 +108,21 @@ impl TaskManager {
|
||||
self.notify_tx.send(())
|
||||
}
|
||||
|
||||
pub fn start_status_listener(&mut self, mut sender: StatusSender) {
|
||||
pub async fn start_status_listener(&mut self, mut sender: StatusSender) {
|
||||
// Announce that we are operational. This means that in the application where this is used,
|
||||
// everything is up and running and ready to go.
|
||||
if let Err(msg) = sender.send(Box::new(TaskStatus::Ready)).await {
|
||||
log::error!("Error sending status message: {}", msg);
|
||||
};
|
||||
|
||||
if let Some(mut task_status_rx) = self.task_status_rx.take() {
|
||||
log::info!("Starting status message listener");
|
||||
crate::spawn::spawn(async move {
|
||||
loop {
|
||||
if let Some(msg) = task_status_rx.next().await {
|
||||
log::trace!("Got msg: {}", msg);
|
||||
if sender.send(msg).await.is_err() {
|
||||
log::error!("Error sending status message");
|
||||
if let Err(msg) = sender.send(msg).await {
|
||||
log::error!("Error sending status message: {}", msg);
|
||||
}
|
||||
} else {
|
||||
log::trace!("Stopping since channel closed");
|
||||
@@ -145,7 +158,7 @@ impl TaskManager {
|
||||
}
|
||||
|
||||
pub async fn wait_for_shutdown(&mut self) {
|
||||
log::info!("Waiting for shutdown");
|
||||
log::debug!("Waiting for shutdown");
|
||||
if let Some(notify_rx) = self.notify_rx.take() {
|
||||
drop(notify_rx);
|
||||
}
|
||||
@@ -256,7 +269,7 @@ impl TaskClient {
|
||||
pub async fn recv_with_delay(&mut self) {
|
||||
self.recv()
|
||||
.then(|msg| async move {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
msg
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -7,16 +7,16 @@ use crate::dealers::queries::{
|
||||
use crate::dealers::transactions::try_add_dealer;
|
||||
use crate::dealings::queries::query_dealings_paged;
|
||||
use crate::dealings::transactions::try_commit_dealings;
|
||||
use crate::epoch_state::queries::{query_current_epoch_state, query_current_epoch_threshold};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::epoch_state::queries::{query_current_epoch, query_current_epoch_threshold};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, ADMIN, MULTISIG, STATE};
|
||||
use crate::state::{State, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use crate::verification_key_shares::transactions::try_commit_verification_key_share;
|
||||
use crate::verification_key_shares::transactions::try_verify_verification_key_share;
|
||||
use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use coconut_dkg_common::types::{Epoch, EpochState};
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
|
||||
};
|
||||
@@ -30,13 +30,11 @@ use cw4::Cw4Contract;
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
mut deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
env: Env,
|
||||
_info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let admin_addr = deps.api.addr_validate(&msg.admin)?;
|
||||
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
|
||||
ADMIN.set(deps.branch(), Some(admin_addr))?;
|
||||
MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?;
|
||||
|
||||
let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
@@ -52,7 +50,10 @@ pub fn instantiate(
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
|
||||
CURRENT_EPOCH_STATE.save(deps.storage, &EpochState::default())?;
|
||||
CURRENT_EPOCH.save(
|
||||
deps.storage,
|
||||
&Epoch::new(EpochState::default(), env.block.time),
|
||||
)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
@@ -79,14 +80,14 @@ pub fn execute(
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner } => {
|
||||
try_verify_verification_key_share(deps, info, owner)
|
||||
}
|
||||
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, info),
|
||||
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env),
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let response = match msg {
|
||||
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch_state(deps.storage)?)?,
|
||||
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?,
|
||||
QueryMsg::GetCurrentEpochThreshold {} => {
|
||||
to_binary(&query_current_epoch_threshold(deps.storage)?)?
|
||||
}
|
||||
@@ -120,9 +121,8 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respon
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, TEST_MIX_DENOM};
|
||||
use crate::support::tests::fixtures::TEST_MIX_DENOM;
|
||||
use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::msg::ExecuteMsg::RegisterDealer;
|
||||
use coconut_dkg_common::types::NodeIndex;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
@@ -163,7 +163,6 @@ mod tests {
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
multisig_addr: MULTISIG_CONTRACT.to_string(),
|
||||
admin: Addr::unchecked(ADMIN_ADDRESS).to_string(),
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
app.instantiate_contract(
|
||||
@@ -198,7 +197,6 @@ mod tests {
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: "group_addr".to_string(),
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
admin: "admin".to_string(),
|
||||
mix_denom: "nym".to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
@@ -72,8 +72,7 @@ pub(crate) mod tests {
|
||||
use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT};
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
use cosmwasm_std::DepsMut;
|
||||
|
||||
fn fill_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) {
|
||||
for n in 0..size {
|
||||
@@ -95,8 +94,7 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn dealers_empty_on_init() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let deps = init_contract();
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap();
|
||||
@@ -107,8 +105,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealers_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let limit = 2;
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
@@ -124,7 +120,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealers_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
for mapping in [storage::current_dealers(), storage::past_dealers()] {
|
||||
fill_dealers(deps.as_mut(), &mapping, 1000);
|
||||
@@ -141,7 +136,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealers_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT;
|
||||
@@ -163,7 +157,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealers_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let per_page = 2;
|
||||
|
||||
|
||||
@@ -70,21 +70,21 @@ pub fn try_add_dealer(
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::ADMIN_ADDRESS;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use coconut_dkg_common::types::PUBLIC_KEY_SUBMISSION_TIME_SECS;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
|
||||
#[test]
|
||||
fn invalid_state() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let mut env = mock_env();
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealer_details = dealer_details_fixture(1);
|
||||
let bte_key_with_proof = String::from("bte_key_with_proof");
|
||||
let announce_address = String::from("localhost:8000");
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_add_dealer(
|
||||
deps.as_mut(),
|
||||
|
||||
@@ -50,7 +50,6 @@ pub(crate) mod tests {
|
||||
use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
|
||||
fn fill_dealings(deps: DepsMut<'_>, size: usize) {
|
||||
@@ -68,7 +67,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn empty_on_bad_idx() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in TOTAL_DEALINGS as u64..100 * TOTAL_DEALINGS as u64 {
|
||||
@@ -89,7 +87,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let limit = 2;
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
@@ -103,7 +100,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
@@ -117,7 +113,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
@@ -135,7 +130,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn dealings_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
fill_dealings(deps.as_mut(), 1);
|
||||
|
||||
|
||||
@@ -42,16 +42,16 @@ pub(crate) mod tests {
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::ADMIN_ADDRESS;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::TOTAL_DEALINGS;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use coconut_dkg_common::types::PUBLIC_KEY_SUBMISSION_TIME_SECS;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let mut env = mock_env();
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing_bytes = dealing_bytes_fixture();
|
||||
|
||||
@@ -65,7 +65,8 @@ pub(crate) mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH_STATE, THRESHOLD};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use cosmwasm_std::Storage;
|
||||
|
||||
pub(crate) fn query_current_epoch_state(
|
||||
storage: &dyn Storage,
|
||||
) -> Result<EpochState, ContractError> {
|
||||
CURRENT_EPOCH_STATE
|
||||
pub(crate) fn query_current_epoch(storage: &dyn Storage) -> Result<Epoch, ContractError> {
|
||||
CURRENT_EPOCH
|
||||
.load(storage)
|
||||
.map_err(|_| ContractError::EpochNotInitialised)
|
||||
}
|
||||
@@ -24,12 +22,21 @@ pub(crate) fn query_current_epoch_threshold(
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use coconut_dkg_common::types::{EpochState, PUBLIC_KEY_SUBMISSION_TIME_SECS};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
|
||||
#[test]
|
||||
fn query_state() {
|
||||
let mut deps = init_contract();
|
||||
let state = query_current_epoch_state(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(state, EpochState::PublicKeySubmission);
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
mock_env()
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use cw_storage_plus::Item;
|
||||
|
||||
pub(crate) const CURRENT_EPOCH_STATE: Item<'_, EpochState> = Item::new("current_epoch_state");
|
||||
pub(crate) const CURRENT_EPOCH: Item<'_, Epoch> = Item::new("current_epoch");
|
||||
pub const THRESHOLD: Item<u64> = Item::new("threshold");
|
||||
|
||||
@@ -2,25 +2,29 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage::current_dealers;
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH_STATE, THRESHOLD};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::ADMIN;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::{DepsMut, MessageInfo, Order, Response};
|
||||
use coconut_dkg_common::types::{Epoch, EpochState};
|
||||
use cosmwasm_std::{DepsMut, Env, Order, Response};
|
||||
|
||||
pub(crate) fn advance_epoch_state(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
let current_epoch_state =
|
||||
CURRENT_EPOCH_STATE.update::<_, ContractError>(deps.storage, |mut epoch_state| {
|
||||
// TODO: When defaulting to the first state, some action will probably need to be taken on the
|
||||
// rest of the contract, as we're starting with a new set of signers
|
||||
epoch_state = epoch_state.next().unwrap_or_default();
|
||||
Ok(epoch_state)
|
||||
})?;
|
||||
if current_epoch_state == EpochState::DealingExchange {
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if epoch.finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
epoch
|
||||
.finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
}
|
||||
|
||||
let current_epoch = CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |mut epoch| {
|
||||
// TODO: When defaulting to the first state, some action will probably need to be taken on the
|
||||
// rest of the contract, as we're starting with a new set of signers
|
||||
epoch = Epoch::new(epoch.state.next().unwrap_or_default(), env.block.time);
|
||||
Ok(epoch)
|
||||
})?;
|
||||
if current_epoch.state == EpochState::DealingExchange {
|
||||
let current_dealer_count = current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.count();
|
||||
@@ -34,64 +38,147 @@ pub(crate) fn advance_epoch_state(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
|
||||
use coconut_dkg_common::types::{DealerDetails, EpochState};
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use coconut_dkg_common::types::{
|
||||
DealerDetails, EpochState, DEALING_EXCHANGE_TIME_SECS, IN_PROGRESS_TIME_SECS,
|
||||
PUBLIC_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_FINALIZATION_TIME_SECS,
|
||||
VERIFICATION_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_VALIDATION_TIME_SECS,
|
||||
};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_controllers::AdminError;
|
||||
|
||||
#[test]
|
||||
fn advance_state() {
|
||||
let mut deps = init_contract();
|
||||
let info = mock_info("requester", &[]);
|
||||
let admin_info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
let mut env = mock_env();
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), info).unwrap_err(),
|
||||
ContractError::Admin(AdminError::NotAdmin {})
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS)
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS - 1);
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::DealingExchange
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::DealingExchange);
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::VerificationKeySubmission
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS)
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS - 2);
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::VerificationKeyValidation
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeySubmission);
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::VerificationKeyFinalization
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS)
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS - 2);
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::InProgress
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeyValidation);
|
||||
assert_eq!(
|
||||
CURRENT_EPOCH_STATE.load(deps.as_mut().storage).unwrap(),
|
||||
EpochState::PublicKeySubmission
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS - 3);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(3)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeyFinalization);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_FINALIZATION_TIME_SECS)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_FINALIZATION_TIME_SECS - 1);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(IN_PROGRESS_TIME_SECS)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(IN_PROGRESS_TIME_SECS - 100);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(100)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(50);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(50)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(100);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_threshold() {
|
||||
let mut deps = init_contract();
|
||||
let admin_info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
let mut env = mock_env();
|
||||
|
||||
assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none());
|
||||
|
||||
@@ -111,7 +198,8 @@ pub(crate) mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
advance_epoch_state(deps.as_mut(), admin_info.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
assert_eq!(
|
||||
THRESHOLD.may_load(deps.as_mut().storage).unwrap().unwrap(),
|
||||
67
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::Storage;
|
||||
@@ -10,7 +10,7 @@ pub(crate) fn check_epoch_state(
|
||||
storage: &dyn Storage,
|
||||
against: EpochState,
|
||||
) -> Result<(), ContractError> {
|
||||
let epoch_state = CURRENT_EPOCH_STATE.load(storage)?;
|
||||
let epoch_state = CURRENT_EPOCH.load(storage)?.state;
|
||||
if epoch_state != against {
|
||||
Err(ContractError::IncorrectEpochState {
|
||||
current_state: epoch_state.to_string(),
|
||||
@@ -25,14 +25,20 @@ pub(crate) fn check_epoch_state(
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
|
||||
#[test]
|
||||
pub fn check_state() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
for fixed_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
CURRENT_EPOCH_STATE
|
||||
.save(deps.as_mut().storage, &fixed_state)
|
||||
CURRENT_EPOCH
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&Epoch::new(fixed_state, env.block.time),
|
||||
)
|
||||
.unwrap();
|
||||
for against_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
let ret = check_epoch_state(deps.as_mut().storage, against_state);
|
||||
|
||||
@@ -23,6 +23,9 @@ pub enum ContractError {
|
||||
#[error("This sender is already a dealer for the epoch")]
|
||||
AlreadyADealer,
|
||||
|
||||
#[error("Too soon to advance epoch state. {0} more seconds until it can be advanced")]
|
||||
EarlyEpochStateAdvancement(u64),
|
||||
|
||||
#[error("Epoch hasn't been correctly initialised!")]
|
||||
EpochNotInitialised,
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
// unique items
|
||||
pub const STATE: Item<State> = Item::new("state");
|
||||
pub const ADMIN: Admin = Admin::new("admin");
|
||||
pub const MULTISIG: Admin = Admin::new("multisig");
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails};
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::ContractSafeBytes;
|
||||
use coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
@@ -17,7 +17,6 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: String::from(GROUP_CONTRACT),
|
||||
multisig_addr: String::from(MULTISIG_CONTRACT),
|
||||
admin: String::from(ADMIN_ADDRESS),
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
let env = mock_env();
|
||||
|
||||
@@ -45,7 +45,6 @@ pub(crate) mod tests {
|
||||
use crate::verification_key_shares::storage::{
|
||||
VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT, VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT,
|
||||
};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[test]
|
||||
@@ -58,7 +57,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn vk_shares_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let limit = 2;
|
||||
for n in 0..1000 {
|
||||
let vk_share = vk_share_fixture(n);
|
||||
@@ -75,7 +73,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn vk_shares_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
for n in 0..1000 {
|
||||
let vk_share = vk_share_fixture(n);
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
@@ -96,7 +93,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn vk_shares_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
for n in 0..1000 {
|
||||
let vk_share = vk_share_fixture(n);
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
@@ -117,7 +113,6 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn vk_shares_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let vk_share = vk_share_fixture(1);
|
||||
let sender = Addr::unchecked(format!("owner{}", 1));
|
||||
|
||||
@@ -73,20 +73,21 @@ pub fn try_verify_verification_key_share(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH_STATE;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use crate::support::tests::helpers::MULTISIG_CONTRACT;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use coconut_dkg_common::types::{
|
||||
EpochState, DEALING_EXCHANGE_TIME_SECS, PUBLIC_KEY_SUBMISSION_TIME_SECS,
|
||||
VERIFICATION_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_VALIDATION_TIME_SECS,
|
||||
};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Storage;
|
||||
use cw_controllers::AdminError;
|
||||
|
||||
#[test]
|
||||
fn commit_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut env = mock_env();
|
||||
let info = mock_info("requester", &[]);
|
||||
let share = "share".to_string();
|
||||
|
||||
@@ -104,8 +105,10 @@ mod tests {
|
||||
expected_state: EpochState::VerificationKeySubmission.to_string()
|
||||
}
|
||||
);
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
@@ -147,6 +150,7 @@ mod tests {
|
||||
#[test]
|
||||
fn invalid_verify_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let mut env = mock_env();
|
||||
let info = mock_info("requester", &[]);
|
||||
let owner = Addr::unchecked("owner");
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
@@ -161,10 +165,20 @@ mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), info, owner.clone()).unwrap_err();
|
||||
@@ -183,14 +197,16 @@ mod tests {
|
||||
#[test]
|
||||
fn verify_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut env = mock_env();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let info = mock_info(owner.as_ref(), &[]);
|
||||
let share = "share".to_string();
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
@@ -204,8 +220,16 @@ mod tests {
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone())
|
||||
.unwrap();
|
||||
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
advance_epoch_state(deps.as_mut(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone()).unwrap();
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ fn dkg_proposal() {
|
||||
let msg = DkgInstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
multisig_addr: multisig_contract_addr.to_string(),
|
||||
admin: Addr::unchecked(OWNER).to_string(),
|
||||
mix_denom: TEST_COIN_DENOM.to_string(),
|
||||
};
|
||||
let coconut_dkg_contract_addr = app
|
||||
@@ -109,6 +108,7 @@ fn dkg_proposal() {
|
||||
.unwrap();
|
||||
|
||||
for _ in 0..2 {
|
||||
app.update_block(|block| block.time = block.time.plus_seconds(1000));
|
||||
app.execute_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
@@ -175,6 +175,7 @@ fn dkg_proposal() {
|
||||
.unwrap();
|
||||
|
||||
for _ in 0..2 {
|
||||
app.update_block(|block| block.time = block.time.plus_seconds(1000));
|
||||
app.execute_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-validator-api"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -114,7 +114,7 @@ impl MixNodeBondAnnotated {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ComputeRewardEstParam {
|
||||
pub performance: Option<Performance>,
|
||||
pub active_in_rewarded_set: Option<bool>,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::coconut::error::Result;
|
||||
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cw3::ProposalResponse;
|
||||
@@ -22,7 +22,7 @@ pub trait Client {
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
) -> Result<SpendCredentialResponse>;
|
||||
async fn get_current_epoch_state(&self) -> Result<EpochState>;
|
||||
async fn get_current_epoch(&self) -> Result<Epoch>;
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<Threshold>>;
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse>;
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>>;
|
||||
@@ -31,6 +31,7 @@ pub trait Client {
|
||||
async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option<Fee>)
|
||||
-> Result<()>;
|
||||
async fn execute_proposal(&self, proposal_id: u64) -> Result<()>;
|
||||
async fn advance_epoch_state(&self) -> Result<()>;
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::coconut::client::Client;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState, NodeIndex};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, NodeIndex};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cw3::ProposalResponse;
|
||||
@@ -35,13 +35,13 @@ impl DkgClient {
|
||||
self.inner.address().await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_current_epoch_state(&self) -> Result<EpochState, CoconutError> {
|
||||
let mut ret = self.inner.get_current_epoch_state().await;
|
||||
pub(crate) async fn get_current_epoch(&self) -> Result<Epoch, CoconutError> {
|
||||
let mut ret = self.inner.get_current_epoch().await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
ret = self.inner.get_current_epoch_state().await;
|
||||
ret = self.inner.get_current_epoch().await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
@@ -86,6 +86,10 @@ impl DkgClient {
|
||||
self.inner.list_proposals().await
|
||||
}
|
||||
|
||||
pub(crate) async fn advance_epoch_state(&self) -> Result<(), CoconutError> {
|
||||
self.inner.advance_epoch_state().await
|
||||
}
|
||||
|
||||
pub(crate) async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
|
||||
@@ -18,7 +18,7 @@ use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use task::TaskClient;
|
||||
use tokio::time::interval;
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
@@ -83,16 +83,16 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_epoch_state(&mut self) {
|
||||
match self.dkg_client.get_current_epoch_state().await {
|
||||
match self.dkg_client.get_current_epoch().await {
|
||||
Err(e) => warn!("Could not get current epoch state {}", e),
|
||||
Ok(epoch_state) => {
|
||||
if let Err(e) = self.state.is_consistent(epoch_state).await {
|
||||
Ok(epoch) => {
|
||||
if let Err(e) = self.state.is_consistent(epoch.state).await {
|
||||
error!(
|
||||
"Epoch state is corrupted - {}, the process should be terminated",
|
||||
e
|
||||
);
|
||||
}
|
||||
let ret = match epoch_state {
|
||||
let ret = match epoch.state {
|
||||
EpochState::PublicKeySubmission => {
|
||||
public_key_submission(&self.dkg_client, &mut self.state).await
|
||||
}
|
||||
@@ -122,7 +122,7 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
};
|
||||
if let Err(e) = ret {
|
||||
warn!("Could not handle this iteration for the epoch state: {}", e);
|
||||
} else if epoch_state != EpochState::InProgress {
|
||||
} else if epoch.state != EpochState::InProgress {
|
||||
let persistent_state = PersistentState::from(&self.state);
|
||||
if let Err(e) =
|
||||
persistent_state.save_to_file(self.state.persistent_state_path())
|
||||
@@ -130,6 +130,15 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
warn!("Could not backup the state for this iteration: {}", e);
|
||||
}
|
||||
}
|
||||
if let Ok(current_timestamp) =
|
||||
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
|
||||
{
|
||||
if current_timestamp.as_secs() >= epoch.finish_timestamp.seconds() {
|
||||
// We try advancing the epoch state, on a best-effort basis
|
||||
info!("Trying to advance the epoch");
|
||||
self.dkg_client.advance_epoch_state().await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ use coconut_dkg_common::dealer::{
|
||||
ContractDealing, DealerDetails, DealerDetailsResponse, DealerType,
|
||||
};
|
||||
use coconut_dkg_common::event_attributes::{DKG_PROPOSAL_ID, NODE_INDEX};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState, TOTAL_DEALINGS};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, TOTAL_DEALINGS};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
@@ -66,7 +66,7 @@ pub(crate) struct DummyClient {
|
||||
proposal_db: Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
spent_credential_db: Arc<RwLock<HashMap<String, SpendCredentialResponse>>>,
|
||||
|
||||
epoch_state: Arc<RwLock<EpochState>>,
|
||||
epoch: Arc<RwLock<Epoch>>,
|
||||
dealer_details: Arc<RwLock<HashMap<String, DealerDetails>>>,
|
||||
threshold: Arc<RwLock<Option<Threshold>>>,
|
||||
dealings: Arc<RwLock<HashMap<String, Vec<ContractSafeBytes>>>>,
|
||||
@@ -80,7 +80,7 @@ impl DummyClient {
|
||||
tx_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
proposal_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
spent_credential_db: Arc::new(RwLock::new(HashMap::new())),
|
||||
epoch_state: Arc::new(RwLock::new(EpochState::default())),
|
||||
epoch: Arc::new(RwLock::new(Epoch::default())),
|
||||
dealer_details: Arc::new(RwLock::new(HashMap::new())),
|
||||
threshold: Arc::new(RwLock::new(None)),
|
||||
dealings: Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -109,8 +109,8 @@ impl DummyClient {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn _with_epoch_state(mut self, epoch_state: &Arc<RwLock<EpochState>>) -> Self {
|
||||
self.epoch_state = Arc::clone(epoch_state);
|
||||
pub fn _with_epoch(mut self, epoch: &Arc<RwLock<Epoch>>) -> Self {
|
||||
self.epoch = Arc::clone(epoch);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -188,8 +188,8 @@ impl super::client::Client for DummyClient {
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_current_epoch_state(&self) -> Result<EpochState> {
|
||||
Ok(*self.epoch_state.read().unwrap())
|
||||
async fn get_current_epoch(&self) -> Result<Epoch> {
|
||||
Ok(*self.epoch.read().unwrap())
|
||||
}
|
||||
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<Threshold>> {
|
||||
@@ -273,6 +273,10 @@ impl super::client::Client for DummyClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn advance_epoch_state(&self) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
@@ -282,7 +286,7 @@ impl super::client::Client for DummyClient {
|
||||
self.dealer_details.write().unwrap().insert(
|
||||
self.validator_address.to_string(),
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(&self.validator_address.to_string()),
|
||||
address: Addr::unchecked(self.validator_address.to_string()),
|
||||
bte_public_key_with_proof,
|
||||
announce_address,
|
||||
assigned_index,
|
||||
|
||||
@@ -30,7 +30,7 @@ use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch};
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -329,8 +329,8 @@ where
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn get_current_epoch_state(&self) -> crate::coconut::error::Result<EpochState> {
|
||||
Ok(self.0.read().await.nymd.get_current_epoch_state().await?)
|
||||
async fn get_current_epoch(&self) -> crate::coconut::error::Result<Epoch> {
|
||||
Ok(self.0.read().await.nymd.get_current_epoch().await?)
|
||||
}
|
||||
|
||||
async fn get_current_epoch_threshold(
|
||||
@@ -405,6 +405,16 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn advance_epoch_state(&self) -> crate::coconut::error::Result<()> {
|
||||
self.0
|
||||
.write()
|
||||
.await
|
||||
.nymd
|
||||
.advance_dkg_epoch_state(None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
|
||||
@@ -30,6 +30,19 @@ module.exports = {
|
||||
use: ['@svgr/webpack'],
|
||||
});
|
||||
|
||||
config.module.rules.unshift({
|
||||
test: /\.ya?ml$/,
|
||||
type: 'json',
|
||||
use: [
|
||||
{
|
||||
loader: 'yaml-loader',
|
||||
options: {
|
||||
asJSON: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
config.resolve.extensions = ['.tsx', '.ts', '.js'];
|
||||
config.resolve.plugins = [new TsconfigPathsPlugin()];
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* This is a mock for Tauri's API package (@tauri-apps/api/notification), to prevent stories from being excluded, because they either use
|
||||
* or import dependencies that use Tauri.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
isPermissionGranted: () => undefined,
|
||||
requestPermission: () => undefined,
|
||||
sendNotification: () => undefined,
|
||||
};
|
||||
@@ -1,10 +1,18 @@
|
||||
## UNRELEASED
|
||||
|
||||
## [nym-connect-v1.1.4](https://github.com/nymtech/nym/tree/nym-connect-v1.1.4) (2022-12-20)
|
||||
|
||||
This release contains the new opt-in Test & Earn program, and it uses a stress-tested directory of network requesters to improve reliability. It also has some bugfixes, performance improvements, and better error handling.
|
||||
|
||||
- nym-connect: send status messages from socks5 task to tauri backend by @octol in https://github.com/nymtech/nym/pull/1882
|
||||
- socks5: rework waiting in inbound.rs by @octol in https://github.com/nymtech/nym/pull/1880
|
||||
- Test&Earn by @mmsinclair in https://github.com/nymtech/nym/pull/2729
|
||||
|
||||
## [nym-connect-v1.1.3](https://github.com/nymtech/nym/tree/nym-connect-v1.1.3) (2022-12-13)
|
||||
|
||||
- socks5-client: added support for socks4a.
|
||||
|
||||
## [nym-connect-v1.1.2](https://github.com/nymtech/nym/tree/nym-connect-v1.1.2) (2022-11-29)
|
||||
## [nym-connect-v1.1.2](https://github.com/nymtech/nym/tree/nym-connect-v1.1.2) (2022-12-06)
|
||||
|
||||
- socks5-client: fix error with client failing and disconnecting unnecessarily.
|
||||
|
||||
|
||||
Generated
+230
-92
@@ -14,12 +14,6 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -469,12 +463,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cargo_toml"
|
||||
version = "0.11.6"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4419e9adae9fd7e231b60d50467481bf8181ddeef6ed54683b23ae925c74c9c"
|
||||
checksum = "aa0e3586af56b3bfa51fca452bd56e8dbbbd5d8d81cbf0b7e4e35b695b537eb8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"toml",
|
||||
]
|
||||
|
||||
@@ -639,7 +632,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "client-core"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"client-connections",
|
||||
@@ -754,6 +747,17 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
@@ -1316,13 +1320,14 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.7.20"
|
||||
name = "dbus"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4"
|
||||
checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"libdbus-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1471,6 +1476,12 @@ dependencies = [
|
||||
"dtoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.9"
|
||||
@@ -1632,6 +1643,16 @@ dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a"
|
||||
dependencies = [
|
||||
"colored 1.9.3",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.10.1"
|
||||
@@ -2620,12 +2641,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ico"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a4b3331534254a9b64095ae60d3dc2a8225a7a70229cd5888be127cdc1f6804"
|
||||
checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"png 0.11.0",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2702,15 +2723,6 @@ dependencies = [
|
||||
"cfb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflate"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5f9f47468e9a76a6452271efadc88fe865a82be91fe75e6c0c57b87ccea59d4"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
@@ -2749,9 +2761,9 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.4"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
@@ -2793,9 +2805,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.19.0"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
|
||||
checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"combine",
|
||||
@@ -2914,6 +2926,15 @@ version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.14.0+1.5.0"
|
||||
@@ -2974,6 +2995,12 @@ dependencies = [
|
||||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "lioness"
|
||||
version = "0.1.2"
|
||||
@@ -3003,6 +3030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3034,6 +3062,19 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "mac-notification-sys"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e72d50edb17756489e79d52eb146927bec8eba9dd48faadf9ef08bca3791ad5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"dirs-next",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
"time 0.3.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@@ -3255,6 +3296,17 @@ dependencies = [
|
||||
"wasm-timer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368e89ea58df747ce88be669ae44e79783c1d30bfd540ad0fc520b3f41f0b3b0"
|
||||
dependencies = [
|
||||
"dbus",
|
||||
"mac-notification-sys",
|
||||
"tauri-winrt-notification",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
@@ -3276,17 +3328,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
@@ -3357,21 +3398,28 @@ dependencies = [
|
||||
name = "nym-connect"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bip39",
|
||||
"chrono",
|
||||
"client-core",
|
||||
"config",
|
||||
"crypto",
|
||||
"dirs",
|
||||
"eyre",
|
||||
"fern",
|
||||
"fix-path-env",
|
||||
"futures",
|
||||
"itertools",
|
||||
"log",
|
||||
"logging",
|
||||
"nym-socks5-client",
|
||||
"pretty_env_logger",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tap",
|
||||
"task",
|
||||
"tauri",
|
||||
@@ -3385,11 +3433,12 @@ dependencies = [
|
||||
"topology",
|
||||
"ts-rs",
|
||||
"url",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"client-connections",
|
||||
@@ -3612,9 +3661,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.14.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
@@ -4065,18 +4114,6 @@ dependencies = [
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b0cabbbd20c2d7f06dbf015e06aad59b6ca3d9ed14848783e98af9aaf19925"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"deflate",
|
||||
"inflate",
|
||||
"num-iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.6"
|
||||
@@ -4221,6 +4258,15 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
@@ -4473,9 +4519,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -4535,6 +4581,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-socks",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
@@ -4604,6 +4651,41 @@ dependencies = [
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "6.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "7.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"sha2 0.10.6",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.3.3"
|
||||
@@ -5459,6 +5541,27 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb"
|
||||
dependencies = [
|
||||
"heck 0.3.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
@@ -5531,9 +5634,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.14.0"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43336f5d1793543ba96e2a1e75f3a5c7dcd592743be06a0ab3a190f4fcb4b934"
|
||||
checksum = "a1fa15735311b4816d030ff54da58560b047daca0970e1031aed5502e84231a8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cairo-rs",
|
||||
@@ -5566,12 +5669,12 @@ dependencies = [
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"paste",
|
||||
"png 0.17.6",
|
||||
"png",
|
||||
"raw-window-handle",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
"unicode-segmentation",
|
||||
"uuid 1.1.2",
|
||||
"uuid 1.2.2",
|
||||
"windows 0.39.0",
|
||||
"windows-implement",
|
||||
"x11-dl",
|
||||
@@ -5608,9 +5711,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.1.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbf22abd61d95ca9b2becd77f9db4c093892f73e8a07d21d8b0b2bf71a7bcea"
|
||||
checksum = "d8ea1d785ab2164373703817bff144c4610a69ad3f659becaca0e1ea004b98d8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"attohttpc",
|
||||
@@ -5628,6 +5731,7 @@ dependencies = [
|
||||
"http",
|
||||
"ignore",
|
||||
"minisign-verify",
|
||||
"notify-rust",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"open",
|
||||
@@ -5652,7 +5756,7 @@ dependencies = [
|
||||
"time 0.3.17",
|
||||
"tokio",
|
||||
"url",
|
||||
"uuid 1.1.2",
|
||||
"uuid 1.2.2",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows 0.39.0",
|
||||
@@ -5661,9 +5765,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "1.1.1"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0991fb306849897439dbd4a72e4cbed2413e2eb26cb4b3ba220b94edba8b4b88"
|
||||
checksum = "8807c85d656b2b93927c19fe5a5f1f1f348f96c2de8b90763b3c2d561511f9b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -5677,16 +5781,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "1.1.1"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356fa253e40ae4d6ff02075011f2f2bb4066f5c9d8c1e16ca6912d7b75903ba6"
|
||||
checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"brotli",
|
||||
"ico",
|
||||
"json-patch",
|
||||
"plist",
|
||||
"png 0.17.6",
|
||||
"png",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
@@ -5697,15 +5801,15 @@ dependencies = [
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
"uuid 1.1.2",
|
||||
"uuid 1.2.2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "1.1.1"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6051fd6940ddb22af452340d03c66a3e2f5d72e0788d4081d91e31528ccdc4d"
|
||||
checksum = "069319e5ecbe653a799b94b0690d9f9bf5d00f7b1d3989aa331c524d4e354075"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"proc-macro2",
|
||||
@@ -5717,30 +5821,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "0.11.1"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d49439a5ea47f474572b854972f42eda2e02a470be5ca9609cc83bb66945abe2"
|
||||
checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
"http-range",
|
||||
"infer",
|
||||
"rand 0.8.5",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"uuid 1.1.2",
|
||||
"uuid 1.2.2",
|
||||
"webview2-com",
|
||||
"windows 0.39.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "0.11.1"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dce920995fd49907aa9bea7249ed1771454f11f7611924c920a1f75fb614d4"
|
||||
checksum = "36b1c5764a41a13176a4599b5b7bd0881bea7d94dfe45e1e755f789b98317e30"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"gtk",
|
||||
@@ -5749,7 +5852,7 @@ dependencies = [
|
||||
"raw-window-handle",
|
||||
"tauri-runtime",
|
||||
"tauri-utils",
|
||||
"uuid 1.1.2",
|
||||
"uuid 1.2.2",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows 0.39.0",
|
||||
@@ -5758,15 +5861,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "1.1.1"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e8fdae6f29cef959809a3c3afef510c5b715a446a597ab8b791497585363f39"
|
||||
checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"ctor",
|
||||
"glob",
|
||||
"heck 0.4.0",
|
||||
"html5ever",
|
||||
"infer",
|
||||
"json-patch",
|
||||
"kuchiki",
|
||||
"memchr",
|
||||
@@ -5783,6 +5887,17 @@ dependencies = [
|
||||
"windows 0.39.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-winrt-notification"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
"strum",
|
||||
"windows 0.39.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
@@ -6055,6 +6170,18 @@ dependencies = [
|
||||
"webpki 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-socks"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"futures-util",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.10"
|
||||
@@ -6346,9 +6473,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.1.2"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.7",
|
||||
]
|
||||
@@ -6363,7 +6490,7 @@ dependencies = [
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-dkg-common",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"colored 2.0.0",
|
||||
"config",
|
||||
"contracts-common",
|
||||
"cosmrs",
|
||||
@@ -6634,9 +6761,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webkit2gtk"
|
||||
version = "0.18.0"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29952969fb5e10fe834a52eb29ad0814ccdfd8387159b0933edf1344a1c9cdcc"
|
||||
checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cairo-rs",
|
||||
@@ -7011,15 +7138,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.21.1"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff5c1352b4266fdf92c63479d2f58ab4cd29dc4e78fbc1b62011ed1227926945"
|
||||
checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"block",
|
||||
"cocoa",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dunce",
|
||||
"gdk",
|
||||
"gio",
|
||||
"glib",
|
||||
@@ -7035,6 +7163,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.6",
|
||||
"soup2",
|
||||
"tao",
|
||||
"thiserror",
|
||||
"url",
|
||||
@@ -7101,6 +7230,15 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
@@ -7124,9 +7262,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d"
|
||||
checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@nym/nym-connect",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prewebpack:dev": "yarn --cwd .. build",
|
||||
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
|
||||
"webpack:dev:onlyThis": "yarn webpack serve --config webpack.dev.js",
|
||||
"webpack:prod": "yarn webpack --progress --config webpack.prod.js",
|
||||
"tauri:dev": "RUST_DEBUG=1 yarn tauri dev",
|
||||
"tauri:build": "yarn tauri build",
|
||||
@@ -30,7 +31,7 @@
|
||||
"@mui/material": "^5.2.2",
|
||||
"@mui/styles": "^5.2.2",
|
||||
"@nymproject/react": "^1.0.0",
|
||||
"@tauri-apps/api": "^1.1.0",
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
|
||||
"clsx": "^1.1.1",
|
||||
"luxon": "^2.3.0",
|
||||
@@ -39,6 +40,7 @@
|
||||
"react-dom": "^17.0.2",
|
||||
"react-error-boundary": "^3.1.3",
|
||||
"react-hook-form": "^7.14.2",
|
||||
"react-markdown": "^8.0.4",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"semver": "^6.3.0",
|
||||
"yup": "^0.32.9"
|
||||
@@ -49,11 +51,12 @@
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@mdx-js/loader": "^2.1.5",
|
||||
"@nymproject/eslint-config-react-typescript": "^1.0.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
|
||||
"@storybook/react": "^6.5.8",
|
||||
"@svgr/webpack": "^6.1.1",
|
||||
"@tauri-apps/cli": "^1.1.0",
|
||||
"@tauri-apps/cli": "^1.2.2",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
@@ -105,6 +108,7 @@
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0"
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml-loader": "^0.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Nym Connect</title>
|
||||
</head>
|
||||
<body style="background: rgb(29, 33, 37);">
|
||||
<div id="root-growth"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>nym-connect</title>
|
||||
<title>Nym Connect</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Nym Wallet Logs</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root-log"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,33 +13,40 @@ rust-version = "1.58"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "^1.1.1", features = [] }
|
||||
tauri-build = { version = "^1.2.1", features = [] }
|
||||
|
||||
tauri-codegen = "^1.1.1"
|
||||
tauri-macros = "^1.1.1"
|
||||
tauri-codegen = "^1.2.1"
|
||||
tauri-macros = "^1.2.1"
|
||||
|
||||
[dependencies]
|
||||
|
||||
anyhow = "1.0"
|
||||
bip39 = "1.0"
|
||||
chrono = "0.4"
|
||||
dirs = "4.0"
|
||||
eyre = "0.6.5"
|
||||
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs", branch = "release"}
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
fern = { version = "0.6.1", features = ["colored"] }
|
||||
itertools = "0.10.5"
|
||||
log = { version = "0.4", features = ["serde"] }
|
||||
pretty_env_logger = "0.4.0"
|
||||
rand = "0.8"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
reqwest = { version = "0.11", features = ["json", "socks"] }
|
||||
rust-embed = { version = "6.4.2", features = ["include-exclude"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
tap = "1.0.1"
|
||||
tauri = { version = "^1.1.1", features = ["clipboard-write-text", "macos-private-api", "shell-open", "system-tray", "updater", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
tauri = { version = "^1.2.2", features = ["clipboard-write-text", "macos-private-api", "notification-all", "shell-open", "system-tray", "updater", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
tendermint-rpc = "0.23.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.21.2", features = ["sync", "time"] }
|
||||
url = "2.2"
|
||||
yaml-rust = "0.4"
|
||||
|
||||
client-core = { path = "../../clients/client-core" }
|
||||
config-common = { path = "../../common/config", package = "config" }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
logging = { path = "../../common/logging"}
|
||||
nym-socks5-client = { path = "../../clients/socks5" }
|
||||
task = { path = "../../common/task" }
|
||||
|
||||
@@ -44,6 +44,13 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn new_with_port<S: Into<String>>(id: S, provider_mix_address: S, port: u16) -> Self {
|
||||
Config {
|
||||
socks5: Socks5Config::new(id, provider_mix_address).with_port(port),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_socks5(&self) -> &Socks5Config {
|
||||
&self.socks5
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ pub enum BackendError {
|
||||
source: tauri::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
TauriApiError {
|
||||
#[from]
|
||||
source: tauri::api::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
SerdeJsonError {
|
||||
#[from]
|
||||
source: serde_json::Error,
|
||||
@@ -36,6 +41,11 @@ pub enum BackendError {
|
||||
#[from]
|
||||
source: ClientCoreError<fs_backend::Backend>,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ApiClientError {
|
||||
#[from]
|
||||
source: crate::operations::growth::api_client::ApiClientError,
|
||||
},
|
||||
|
||||
#[error("Could not send disconnect signal to the SOCKS5 client")]
|
||||
CoundNotSendDisconnectSignal,
|
||||
@@ -57,6 +67,8 @@ pub enum BackendError {
|
||||
CouldNotGetConfigFilename,
|
||||
#[error("Could not load existing gateway configuration")]
|
||||
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
|
||||
#[error("Unable to open a new window")]
|
||||
NewWindowError,
|
||||
}
|
||||
|
||||
impl Serialize for BackendError {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use fern::colors::ColoredLevelConfig;
|
||||
use serde::Serialize;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use tauri::Manager;
|
||||
|
||||
pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> {
|
||||
let colors = ColoredLevelConfig::new();
|
||||
let base_config = fern::Dispatch::new()
|
||||
.level(global_level())
|
||||
.filter_lowlevel_external_components()
|
||||
.show_operations();
|
||||
|
||||
let stdout_config = fern::Dispatch::new()
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{}[{}][{}] {}",
|
||||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
|
||||
record.target(),
|
||||
colors.color(record.level()),
|
||||
message,
|
||||
))
|
||||
})
|
||||
.chain(std::io::stdout());
|
||||
|
||||
let tauri_event_config = fern::Dispatch::new()
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{}[{}] {}",
|
||||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
|
||||
record.target(),
|
||||
message,
|
||||
))
|
||||
})
|
||||
.chain(fern::Output::call(move |record| {
|
||||
let msg = LogMessage {
|
||||
message: record.args().to_string(),
|
||||
level: record.level().into(),
|
||||
};
|
||||
app_handle.emit_all("log://log", msg).unwrap();
|
||||
}));
|
||||
|
||||
base_config
|
||||
.chain(stdout_config)
|
||||
.chain(tauri_event_config)
|
||||
.apply()
|
||||
}
|
||||
|
||||
trait FernExt {
|
||||
fn show_operations(self) -> Self;
|
||||
fn filter_lowlevel_external_components(self) -> Self;
|
||||
}
|
||||
|
||||
impl FernExt for fern::Dispatch {
|
||||
fn show_operations(self) -> Self {
|
||||
if ::std::env::var("RUST_TRACE_OPERATIONS").is_ok() {
|
||||
self.level_for("nym_connect::operations", log::LevelFilter::Trace)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_lowlevel_external_components(self) -> Self {
|
||||
self.level_for("hyper", log::LevelFilter::Warn)
|
||||
.level_for("tokio_reactor", log::LevelFilter::Warn)
|
||||
.level_for("reqwest", log::LevelFilter::Warn)
|
||||
.level_for("mio", log::LevelFilter::Warn)
|
||||
.level_for("want", log::LevelFilter::Warn)
|
||||
.level_for("sled", log::LevelFilter::Warn)
|
||||
.level_for("tungstenite", log::LevelFilter::Warn)
|
||||
.level_for("tokio_tungstenite", log::LevelFilter::Warn)
|
||||
.level_for("rustls", log::LevelFilter::Warn)
|
||||
.level_for("tokio_util", log::LevelFilter::Warn)
|
||||
}
|
||||
}
|
||||
|
||||
fn global_level() -> log::LevelFilter {
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
log::LevelFilter::from_str(&s).unwrap_or(log::LevelFilter::Info)
|
||||
} else {
|
||||
log::LevelFilter::Info
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
struct LogMessage {
|
||||
message: String,
|
||||
level: LogLevel,
|
||||
}
|
||||
|
||||
// Serialize to u16 instead of strings.
|
||||
#[derive(Debug, Clone, Deserialize_repr, Serialize_repr)]
|
||||
#[repr(u16)]
|
||||
enum LogLevel {
|
||||
Trace = 1,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl From<log::Level> for LogLevel {
|
||||
fn from(level: log::Level) -> Self {
|
||||
match level {
|
||||
log::Level::Trace => LogLevel::Trace,
|
||||
log::Level::Debug => LogLevel::Debug,
|
||||
log::Level::Info => LogLevel::Info,
|
||||
log::Level::Warn => LogLevel::Warn,
|
||||
log::Level::Error => LogLevel::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config_common::defaults::setup_env;
|
||||
use logging::setup_logging;
|
||||
use tauri::Menu;
|
||||
use tauri::{Manager, Menu};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::menu::AddDefaultSubmenus;
|
||||
@@ -17,6 +16,7 @@ use crate::window::window_toggle;
|
||||
|
||||
mod config;
|
||||
mod error;
|
||||
mod logging;
|
||||
mod menu;
|
||||
mod models;
|
||||
mod operations;
|
||||
@@ -25,7 +25,6 @@ mod tasks;
|
||||
mod window;
|
||||
|
||||
fn main() {
|
||||
setup_logging();
|
||||
setup_env(None);
|
||||
println!("Starting up...");
|
||||
|
||||
@@ -35,11 +34,13 @@ fn main() {
|
||||
log::warn!("Failed to fix PATH: {error}");
|
||||
}
|
||||
|
||||
let context = tauri::generate_context!();
|
||||
tauri::Builder::default()
|
||||
.manage(Arc::new(RwLock::new(State::new())))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
crate::config::get_config_file_location,
|
||||
crate::config::get_config_id,
|
||||
crate::operations::connection::status::get_connection_status,
|
||||
crate::operations::connection::connect::get_gateway,
|
||||
crate::operations::connection::connect::get_service_provider,
|
||||
crate::operations::connection::connect::set_gateway,
|
||||
@@ -49,10 +50,31 @@ fn main() {
|
||||
crate::operations::directory::get_services,
|
||||
crate::operations::export::export_keys,
|
||||
crate::operations::window::hide_window,
|
||||
crate::operations::growth::test_and_earn::growth_tne_get_client_id,
|
||||
crate::operations::growth::test_and_earn::growth_tne_take_part,
|
||||
crate::operations::growth::test_and_earn::growth_tne_get_draws,
|
||||
crate::operations::growth::test_and_earn::growth_tne_ping,
|
||||
crate::operations::growth::test_and_earn::growth_tne_submit_wallet_address,
|
||||
crate::operations::growth::test_and_earn::growth_tne_enter_draw,
|
||||
crate::operations::growth::test_and_earn::growth_tne_toggle_window,
|
||||
crate::operations::help::log::help_log_toggle_window,
|
||||
])
|
||||
.menu(Menu::new().add_default_app_submenu_if_macos())
|
||||
.menu(Menu::os_default(&context.package_info().name).add_default_app_submenus())
|
||||
.on_menu_event(|event| {
|
||||
if event.menu_item_id() == menu::SHOW_LOG_WINDOW {
|
||||
let _r = crate::operations::help::log::help_log_toggle_window(
|
||||
event.window().app_handle(),
|
||||
);
|
||||
}
|
||||
if event.menu_item_id() == menu::CLEAR_STORAGE {
|
||||
let _r = crate::operations::help::storage::help_clear_storage(
|
||||
event.window().app_handle(),
|
||||
);
|
||||
}
|
||||
})
|
||||
.setup(|app| Ok(crate::logging::setup_logging(app.app_handle())?))
|
||||
.system_tray(create_tray_menu())
|
||||
.on_system_tray_event(tray_menu_event_handler)
|
||||
.run(tauri::generate_context!())
|
||||
.run(context)
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
@@ -1,40 +1,24 @@
|
||||
use crate::window_toggle;
|
||||
use tauri::{
|
||||
AppHandle, CustomMenuItem, Menu, SystemTray, SystemTrayEvent, SystemTrayMenu,
|
||||
AppHandle, CustomMenuItem, Menu, Submenu, SystemTray, SystemTrayEvent, SystemTrayMenu,
|
||||
SystemTrayMenuItem, Wry,
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::{MenuItem, Submenu};
|
||||
|
||||
use crate::window_toggle;
|
||||
|
||||
pub const SHOW_LOG_WINDOW: &str = "show_log_window";
|
||||
pub const CLEAR_STORAGE: &str = "clear_storage";
|
||||
|
||||
pub trait AddDefaultSubmenus {
|
||||
fn add_default_app_submenu_if_macos(self) -> Self;
|
||||
fn add_default_app_submenus(self) -> Self;
|
||||
}
|
||||
|
||||
impl AddDefaultSubmenus for Menu {
|
||||
fn add_default_app_submenu_if_macos(self) -> Menu {
|
||||
#[cfg(target_os = "macos")]
|
||||
return self
|
||||
.add_submenu(Submenu::new(
|
||||
"File",
|
||||
Menu::new().add_native_item(MenuItem::Quit),
|
||||
))
|
||||
.add_submenu(Submenu::new(
|
||||
"Edit",
|
||||
Menu::new()
|
||||
.add_native_item(MenuItem::Copy)
|
||||
.add_native_item(MenuItem::Cut)
|
||||
.add_native_item(MenuItem::Paste)
|
||||
.add_native_item(MenuItem::SelectAll),
|
||||
))
|
||||
.add_submenu(Submenu::new(
|
||||
"Window",
|
||||
Menu::new()
|
||||
.add_native_item(MenuItem::Hide)
|
||||
.add_native_item(MenuItem::HideOthers)
|
||||
.add_native_item(MenuItem::ShowAll),
|
||||
));
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
return self;
|
||||
fn add_default_app_submenus(self) -> Self {
|
||||
let submenu = Submenu::new(
|
||||
"Help",
|
||||
Menu::new().add_item(CustomMenuItem::new(SHOW_LOG_WINDOW, "Show logs")),
|
||||
);
|
||||
self.add_submenu(submenu)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,21 +39,31 @@ impl fmt::Display for ConnectionStatusKind {
|
||||
pub const APP_EVENT_CONNECTION_STATUS_CHANGED: &str = "app:connection-status-changed";
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
#[derive(Clone, Serialize)]
|
||||
pub struct AppEventConnectionStatusChangedPayload {
|
||||
pub status: ConnectionStatusKind,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DirectoryService {
|
||||
pub id: String,
|
||||
pub description: String,
|
||||
pub items: Vec<DirectoryServiceProvider>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct HarbourMasterService {
|
||||
pub service_provider_client_id: String,
|
||||
pub gateway_identity_key: String,
|
||||
pub ip_address: String,
|
||||
pub last_successful_ping_utc: String,
|
||||
pub last_updated_utc: String,
|
||||
pub routing_score: f32,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DirectoryServiceProvider {
|
||||
pub id: String,
|
||||
pub description: String,
|
||||
@@ -63,3 +73,11 @@ pub struct DirectoryServiceProvider {
|
||||
/// Address of the gateway, e.g. 2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh
|
||||
pub gateway: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct PagedResult<T> {
|
||||
pub page: u32,
|
||||
pub size: u32,
|
||||
pub total: i32,
|
||||
pub items: Vec<T>,
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ pub async fn get_service_provider(state: tauri::State<'_, Arc<RwLock<State>>>) -
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn set_service_provider(
|
||||
service_provider: String,
|
||||
service_provider: Option<String>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<()> {
|
||||
log::trace!("Setting service_provider: {service_provider}");
|
||||
log::trace!("Setting service_provider: {:?}", &service_provider);
|
||||
let mut guard = state.write().await;
|
||||
guard.set_service_provider(service_provider);
|
||||
Ok(())
|
||||
@@ -57,10 +57,10 @@ pub async fn get_gateway(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn set_gateway(
|
||||
gateway: String,
|
||||
gateway: Option<String>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<()> {
|
||||
log::trace!("Setting gateway: {gateway}");
|
||||
log::trace!("Setting gateway: {:?}", &gateway);
|
||||
let mut guard = state.write().await;
|
||||
guard.set_gateway(gateway);
|
||||
Ok(())
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod connect;
|
||||
pub mod disconnect;
|
||||
pub mod status;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use crate::error::Result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::models::ConnectionStatusKind;
|
||||
use crate::state::State;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_connection_status(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ConnectionStatusKind> {
|
||||
let state = state.read().await;
|
||||
Ok(state.get_status())
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::models::DirectoryService;
|
||||
use crate::models::{DirectoryService, HarbourMasterService, PagedResult};
|
||||
|
||||
static SERVICE_PROVIDER_WELLKNOWN_URL: &str =
|
||||
"https://nymtech.net/.wellknown/connect/service-providers.json";
|
||||
|
||||
static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services/?size=100";
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_services() -> Result<Vec<DirectoryService>> {
|
||||
log::trace!("Fetching services");
|
||||
@@ -12,5 +16,36 @@ pub async fn get_services() -> Result<Vec<DirectoryService>> {
|
||||
.json::<Vec<DirectoryService>>()
|
||||
.await?;
|
||||
log::trace!("Received: {:#?}", res);
|
||||
Ok(res)
|
||||
|
||||
// TODO: get paged
|
||||
log::trace!("Fetching active services");
|
||||
let active_services = reqwest::get(HARBOUR_MASTER_URL)
|
||||
.await?
|
||||
.json::<PagedResult<HarbourMasterService>>()
|
||||
.await?;
|
||||
log::trace!("Active: {:#?}", active_services);
|
||||
|
||||
let mut filtered: Vec<DirectoryService> = vec![];
|
||||
|
||||
for service in &res {
|
||||
let items: _ = service
|
||||
.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|sp| {
|
||||
active_services
|
||||
.items
|
||||
.iter()
|
||||
.any(|active| active.service_provider_client_id == sp.address)
|
||||
})
|
||||
.collect_vec();
|
||||
log::trace!("service = {} has {} items", service.id, items.len());
|
||||
filtered.push(DirectoryService {
|
||||
id: service.id.clone(),
|
||||
description: service.description.clone(),
|
||||
items,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,24 @@ use crate::{
|
||||
error::{BackendError, Result},
|
||||
state::State,
|
||||
};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use crypto::asymmetric::identity;
|
||||
|
||||
pub async fn get_identity_key(
|
||||
state: &tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Arc<identity::KeyPair>> {
|
||||
let config = {
|
||||
let state = state.read().await;
|
||||
state.load_socks5_config()?
|
||||
};
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
let key_manager = KeyManager::load_keys(&pathfinder)?;
|
||||
let identity_keypair = key_manager.identity_keypair();
|
||||
|
||||
Ok(identity_keypair)
|
||||
}
|
||||
|
||||
/// Export the gateway keys as a JSON string blob
|
||||
#[tauri::command]
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ApiClientError {
|
||||
#[error("{source}")]
|
||||
Reqwest {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
SerdeJson {
|
||||
#[from]
|
||||
source: serde_json::Error,
|
||||
},
|
||||
#[error("{0}")]
|
||||
Status(String),
|
||||
}
|
||||
|
||||
const API_BASE_URL: &str = "https://growth-api.nymtech.net";
|
||||
|
||||
// For development mode, switch to this
|
||||
// const API_BASE_URL: &str = "http://localhost:8000";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GrowthApiClient {
|
||||
base_url: String,
|
||||
}
|
||||
|
||||
impl GrowthApiClient {
|
||||
pub fn new(resource_base: &str) -> Self {
|
||||
let base_url = std::env::var("API_BASE_URL").unwrap_or_else(|_| API_BASE_URL.to_string());
|
||||
GrowthApiClient {
|
||||
base_url: format!("{}{}", base_url, resource_base),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registrations() -> Registrations {
|
||||
Registrations::new(GrowthApiClient::new("/v1/tne"))
|
||||
}
|
||||
|
||||
pub fn daily_draws() -> DailyDraws {
|
||||
DailyDraws::new(GrowthApiClient::new("/v1/tne/daily_draw"))
|
||||
}
|
||||
|
||||
pub(crate) async fn get<T: DeserializeOwned>(&self, url: &str) -> Result<T, ApiClientError> {
|
||||
log::info!(">>> GET {}", url);
|
||||
let proxy = reqwest::Proxy::all("socks5h://127.0.0.1:1080")?;
|
||||
let client = reqwest::Client::builder()
|
||||
.proxy(proxy)
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()?;
|
||||
|
||||
match client.get(format!("{}{}", self.base_url, url)).send().await {
|
||||
Ok(res) => {
|
||||
if res.status().is_client_error() || res.status().is_server_error() {
|
||||
log::error!("<<< {}", res.status());
|
||||
return Err(ApiClientError::Status(res.status().to_string()));
|
||||
}
|
||||
match res.text().await {
|
||||
Ok(response_body) => {
|
||||
log::info!("<<< {}", response_body);
|
||||
match serde_json::from_str(&response_body) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
log::error!("<<< JSON parsing error: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("<<< Request error: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("<<< Response parsing error: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn post<REQ: Serialize + ?Sized, RESP: DeserializeOwned>(
|
||||
&self,
|
||||
url: &str,
|
||||
body: &REQ,
|
||||
) -> Result<RESP, ApiClientError> {
|
||||
log::info!(">>> POST {}", url);
|
||||
let proxy = reqwest::Proxy::all("socks5h://127.0.0.1:1080")?;
|
||||
let client = reqwest::Client::builder()
|
||||
.proxy(proxy)
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()?;
|
||||
|
||||
match client
|
||||
.post(format!("{}{}", self.base_url, url))
|
||||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(res) => {
|
||||
if res.status().is_client_error() || res.status().is_server_error() {
|
||||
log::error!("<<< {}", res.status());
|
||||
return Err(ApiClientError::Status(res.status().to_string()));
|
||||
}
|
||||
match res.text().await {
|
||||
Ok(response_body) => {
|
||||
log::info!("<<< {}", response_body);
|
||||
match serde_json::from_str(&response_body) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
log::error!("<<< JSON parsing error: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("<<< Request error: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("<<< Response parsing error: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ClientIdPartial {
|
||||
pub client_id: String,
|
||||
pub client_id_signature: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Registration {
|
||||
pub id: String,
|
||||
pub client_id: String,
|
||||
pub timestamp: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Ping {
|
||||
pub client_id: String,
|
||||
pub timestamp: String,
|
||||
}
|
||||
|
||||
pub struct Registrations {
|
||||
client: GrowthApiClient,
|
||||
}
|
||||
|
||||
impl Registrations {
|
||||
pub fn new(client: GrowthApiClient) -> Self {
|
||||
Registrations { client }
|
||||
}
|
||||
|
||||
pub async fn register(
|
||||
&self,
|
||||
registration: &ClientIdPartial,
|
||||
) -> Result<Registration, ApiClientError> {
|
||||
self.client.post("/register", ®istration).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn unregister(&self, registration: &ClientIdPartial) -> Result<(), ApiClientError> {
|
||||
self.client.post("/unregister", ®istration).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn status(&self, registration: &ClientIdPartial) -> Result<(), ApiClientError> {
|
||||
self.client.post("/status", ®istration).await
|
||||
}
|
||||
|
||||
pub async fn ping(&self, registration: &ClientIdPartial) -> Result<(), ApiClientError> {
|
||||
self.client.post("/ping", ®istration).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn health(&self) -> Result<(), ApiClientError> {
|
||||
self.client.get("/health").await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct DrawEntryPartial {
|
||||
pub draw_id: String,
|
||||
pub client_id: String,
|
||||
pub client_id_signature: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct DrawEntry {
|
||||
pub id: String,
|
||||
pub draw_id: String,
|
||||
pub timestamp: String,
|
||||
pub status: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct DrawWithWordOfTheDay {
|
||||
pub id: String,
|
||||
pub start_utc: String,
|
||||
pub end_utc: String,
|
||||
pub word_of_the_day: Option<String>,
|
||||
pub last_modified: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ClaimPartial {
|
||||
pub draw_id: String,
|
||||
pub registration_id: String,
|
||||
pub client_id: String,
|
||||
pub client_id_signature: String,
|
||||
pub wallet_address: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Winner {
|
||||
pub id: String,
|
||||
pub client_id: String,
|
||||
pub draw_id: String,
|
||||
pub timestamp: String,
|
||||
pub winner_reg_id: String,
|
||||
pub winner_wallet_address: Option<String>,
|
||||
pub winner_claim_timestamp: Option<String>,
|
||||
}
|
||||
|
||||
pub struct DailyDraws {
|
||||
client: GrowthApiClient,
|
||||
}
|
||||
|
||||
impl DailyDraws {
|
||||
pub fn new(client: GrowthApiClient) -> Self {
|
||||
DailyDraws { client }
|
||||
}
|
||||
|
||||
pub async fn current(&self) -> Result<DrawWithWordOfTheDay, ApiClientError> {
|
||||
self.client.get("/current").await
|
||||
}
|
||||
|
||||
pub async fn next(&self) -> Result<DrawWithWordOfTheDay, ApiClientError> {
|
||||
self.client.get("/next").await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn status(&self, draw_id: &str) -> Result<DrawWithWordOfTheDay, ApiClientError> {
|
||||
self.client
|
||||
.get(format!("/status/{}", draw_id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn enter(&self, entry: &DrawEntryPartial) -> Result<DrawEntry, ApiClientError> {
|
||||
self.client.post("/enter", entry).await
|
||||
}
|
||||
|
||||
pub async fn entries(
|
||||
&self,
|
||||
client_id: &ClientIdPartial,
|
||||
) -> Result<Vec<DrawEntry>, ApiClientError> {
|
||||
self.client.post("/entries", client_id).await
|
||||
}
|
||||
|
||||
pub async fn claim(&self, claim: &ClaimPartial) -> Result<Winner, ApiClientError> {
|
||||
self.client.post("/claim", claim).await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use rust_embed::RustEmbed;
|
||||
extern crate yaml_rust;
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../src/components/Growth/content/"]
|
||||
#[include = "*.yaml"]
|
||||
#[exclude = "*.mdx"]
|
||||
struct Asset;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NotificationContent {
|
||||
pub title: String,
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Notifications {
|
||||
pub you_are_in_draw: NotificationContent,
|
||||
pub take_part: NotificationContent,
|
||||
}
|
||||
|
||||
pub struct Content {}
|
||||
|
||||
const RESOURCE_ERROR: &str = "❌ RESOURCE ERROR";
|
||||
|
||||
fn get_as_string_or_error_message(value: &yaml_rust::Yaml) -> String {
|
||||
value.as_str().unwrap_or(RESOURCE_ERROR).to_string()
|
||||
}
|
||||
|
||||
impl Content {
|
||||
pub fn get_notifications() -> Notifications {
|
||||
let content = Asset::get("en.yaml").unwrap();
|
||||
let s = std::str::from_utf8(content.data.as_ref()).unwrap();
|
||||
let content = YamlLoader::load_from_str(s).unwrap();
|
||||
let content = &content[0];
|
||||
|
||||
Notifications {
|
||||
you_are_in_draw: NotificationContent {
|
||||
title: get_as_string_or_error_message(
|
||||
&content["testAndEarn"]["notifications"]["youAreInDraw"]["title"],
|
||||
),
|
||||
body: get_as_string_or_error_message(
|
||||
&content["testAndEarn"]["notifications"]["youAreInDraw"]["body"],
|
||||
),
|
||||
},
|
||||
take_part: NotificationContent {
|
||||
title: get_as_string_or_error_message(
|
||||
&content["testAndEarn"]["notifications"]["takePart"]["title"],
|
||||
),
|
||||
body: get_as_string_or_error_message(
|
||||
&content["testAndEarn"]["notifications"]["takePart"]["body"],
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod api_client;
|
||||
pub mod assets;
|
||||
pub mod test_and_earn;
|
||||
@@ -0,0 +1,156 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::operations::export::get_identity_key;
|
||||
use crate::operations::growth::api_client::{
|
||||
ClaimPartial, ClientIdPartial, DrawEntry, DrawEntryPartial, DrawWithWordOfTheDay,
|
||||
GrowthApiClient, Registration, Winner,
|
||||
};
|
||||
use crate::State;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tauri::api::notification::Notification;
|
||||
use tauri::Manager;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
async fn get_client_id(
|
||||
state: &tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ClientIdPartial, BackendError> {
|
||||
let keypair = get_identity_key(state).await?;
|
||||
let client_id = keypair.public_key().to_base58_string();
|
||||
let client_id_signature = keypair
|
||||
.private_key()
|
||||
.sign(client_id.as_bytes())
|
||||
.to_base58_string();
|
||||
Ok(ClientIdPartial {
|
||||
client_id,
|
||||
client_id_signature,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_get_client_id(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ClientIdPartial, BackendError> {
|
||||
get_client_id(&state).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_take_part(
|
||||
app_handle: tauri::AppHandle,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Registration, BackendError> {
|
||||
let notifications = super::assets::Content::get_notifications();
|
||||
|
||||
let client_id = get_client_id(&state).await?;
|
||||
let registration = GrowthApiClient::registrations()
|
||||
.register(&client_id)
|
||||
.await?;
|
||||
|
||||
log::info!("<<< Test&Earn: registration details: {:?}", registration);
|
||||
|
||||
if let Err(e) = Notification::new(&app_handle.config().tauri.bundle.identifier)
|
||||
.title(notifications.take_part.title)
|
||||
.body(notifications.take_part.body)
|
||||
.show()
|
||||
{
|
||||
log::error!("Could not show notification. Error = {:?}", e);
|
||||
}
|
||||
|
||||
Ok(registration)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Draws {
|
||||
pub current: Option<DrawWithWordOfTheDay>,
|
||||
pub next: Option<DrawWithWordOfTheDay>,
|
||||
pub draws: Vec<DrawEntry>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_get_draws(client_details: ClientIdPartial) -> Result<Draws, BackendError> {
|
||||
let draws_api = GrowthApiClient::daily_draws();
|
||||
|
||||
let current = draws_api.current().await.ok();
|
||||
let next = draws_api.next().await.ok();
|
||||
let draws = draws_api.entries(&client_details).await?;
|
||||
|
||||
Ok(Draws {
|
||||
current,
|
||||
next,
|
||||
draws,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_enter_draw(
|
||||
client_details: ClientIdPartial,
|
||||
draw_id: String,
|
||||
) -> Result<DrawEntry, BackendError> {
|
||||
Ok(GrowthApiClient::daily_draws()
|
||||
.enter(&DrawEntryPartial {
|
||||
draw_id,
|
||||
client_id: client_details.client_id,
|
||||
client_id_signature: client_details.client_id_signature,
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_submit_wallet_address(
|
||||
client_details: ClientIdPartial,
|
||||
draw_id: String,
|
||||
wallet_address: String,
|
||||
registration_id: String,
|
||||
) -> Result<Winner, BackendError> {
|
||||
Ok(GrowthApiClient::daily_draws()
|
||||
.claim(&ClaimPartial {
|
||||
draw_id,
|
||||
client_id: client_details.client_id,
|
||||
client_id_signature: client_details.client_id_signature,
|
||||
wallet_address,
|
||||
registration_id,
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_ping(client_details: ClientIdPartial) -> Result<(), BackendError> {
|
||||
log::info!("Test&Earn is sending a ping...");
|
||||
Ok(GrowthApiClient::registrations()
|
||||
.ping(&client_details)
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn growth_tne_toggle_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
window_title: Option<String>,
|
||||
) -> Result<(), BackendError> {
|
||||
if let Some(window) = app_handle.windows().get("growth") {
|
||||
log::info!("Closing growth window...");
|
||||
if let Err(e) = window.close() {
|
||||
log::error!("Unable to close growth window: {:?}", e);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!("Creating growth window...");
|
||||
match tauri::WindowBuilder::new(
|
||||
&app_handle,
|
||||
"growth",
|
||||
tauri::WindowUrl::App("growth.html".into()),
|
||||
)
|
||||
.title(window_title.unwrap_or_else(|| "NymConnect Test&Earn".to_string()))
|
||||
.build()
|
||||
{
|
||||
Ok(window) => {
|
||||
if let Err(e) = window.set_focus() {
|
||||
log::error!("Unable to focus growth window: {:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Unable to create growth window: {:?}", e);
|
||||
Err(BackendError::NewWindowError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::error::BackendError;
|
||||
use tauri::Manager;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> {
|
||||
if let Some(current_log_window) = app_handle.windows().get("log") {
|
||||
log::info!("Closing log window...");
|
||||
if let Err(e) = current_log_window.close() {
|
||||
log::error!("Unable to close log window: {:?}", e);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!("Creating log window...");
|
||||
match tauri::WindowBuilder::new(&app_handle, "log", tauri::WindowUrl::App("log.html".into()))
|
||||
.title("Nym Connect Logs")
|
||||
.build()
|
||||
{
|
||||
Ok(window) => {
|
||||
if let Err(e) = window.set_focus() {
|
||||
log::error!("Unable to focus log window: {:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Unable to create log window: {:?}", e);
|
||||
Err(BackendError::NewWindowError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod log;
|
||||
pub mod storage;
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::error::BackendError;
|
||||
use serde::Serialize;
|
||||
use tauri::Manager;
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
struct ClearStorageEvent {
|
||||
kind: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn help_clear_storage(app_handle: tauri::AppHandle) -> Result<(), BackendError> {
|
||||
log::info!("Sending event to clear local storage...");
|
||||
|
||||
let event = ClearStorageEvent {
|
||||
kind: "local_storage".to_string(),
|
||||
};
|
||||
app_handle.emit_all("help://clear-storage", event)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
pub mod connection;
|
||||
pub mod directory;
|
||||
pub mod export;
|
||||
pub mod growth;
|
||||
pub mod help;
|
||||
pub mod window;
|
||||
|
||||
@@ -64,16 +64,16 @@ impl State {
|
||||
&self.service_provider
|
||||
}
|
||||
|
||||
pub fn set_service_provider(&mut self, provider: String) {
|
||||
self.service_provider = Some(provider);
|
||||
pub fn set_service_provider(&mut self, provider: Option<String>) {
|
||||
self.service_provider = provider;
|
||||
}
|
||||
|
||||
pub fn get_gateway(&self) -> &Option<String> {
|
||||
&self.gateway
|
||||
}
|
||||
|
||||
pub fn set_gateway(&mut self, gateway: String) {
|
||||
self.gateway = Some(gateway);
|
||||
pub fn set_gateway(&mut self, gateway: Option<String>) {
|
||||
self.gateway = gateway;
|
||||
}
|
||||
|
||||
/// The effective config id is the static config id appended with the id of the gateway
|
||||
|
||||
@@ -100,7 +100,7 @@ pub fn start_status_listener(
|
||||
log::info!("SOCKS5 proxy sent status message: {}", msg);
|
||||
window
|
||||
.emit(
|
||||
"socks5-event",
|
||||
"socks5-status-event",
|
||||
Payload {
|
||||
title: "SOCKS5 update".into(),
|
||||
message: msg.to_string(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user