Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 75b9766d60 | |||
| 922941f2db | |||
| f334acab99 | |||
| 001c98e544 | |||
| d3eb238d5a | |||
| 23f0212a16 | |||
| cb58a62ff7 | |||
| cc8be3bce2 | |||
| a2c6abd3dd | |||
| d289c46e87 | |||
| a6aba3defd | |||
| 6557be3738 | |||
| 7134755073 | |||
| dd1420a65a | |||
| df1bc60464 | |||
| 865e809342 | |||
| 51f9c1ca29 | |||
| 303b014a59 | |||
| e1e20fb13e | |||
| 0c3c13ae88 | |||
| 8c8b7d71d0 | |||
| 3163c5f054 | |||
| 4a1794b2f1 | |||
| 1898b8ed96 | |||
| a23471859d | |||
| 9d8c9edf22 | |||
| 5ea7b24efc | |||
| a43a24faa8 | |||
| 39ee215005 | |||
| ef7961f58e | |||
| e628338b33 | |||
| 1bb137f87f | |||
| 773f9e5ead | |||
| 79f695f138 | |||
| 3aabbcf876 | |||
| d96b7408db | |||
| 728b0f4549 | |||
| 0a37c81709 | |||
| 957cbb45b0 | |||
| 8ec074cb1f | |||
| ab5740087f | |||
| 6af59c303e | |||
| 27b384e034 | |||
| 7f5ce3ffeb | |||
| 89b6667c75 | |||
| a94a9aeaf5 | |||
| 6bc8b88a20 | |||
| 21f3991714 | |||
| cd8eba988a | |||
| d2b3841bbd | |||
| de877fb337 | |||
| d4c2b9060f | |||
| 41ac866729 | |||
| a7afd2a1c7 | |||
| df03daf2cc | |||
| b3f5a4f496 | |||
| d080d661f7 | |||
| 6deb481e5d | |||
| 5b98e18a4e | |||
| 506a0da89c | |||
| c7fdcf0a79 | |||
| f7d38a7ec6 | |||
| 8edc762df9 | |||
| 4459aca933 | |||
| 5b84c58985 | |||
| 4301d91f6c | |||
| 03d28c115e | |||
| 7b15f350cd | |||
| 2b4917b8b1 | |||
| de78ca8d9b | |||
| 58d09e382a | |||
| 0cef12d05b | |||
| 30e73ee795 | |||
| d918b69664 | |||
| 921e558660 | |||
| b3b8d2ab46 | |||
| d62638b8e2 | |||
| 67130a1289 | |||
| 0dabff72bd | |||
| e8e2f195e6 | |||
| fa354016e0 | |||
| 935ee765e9 | |||
| 4c8e59e6fc | |||
| 067f3e6f1a | |||
| 6f09d46dce | |||
| bdef48331b | |||
| 51a6936e51 | |||
| fd456d2952 | |||
| eee1abe593 | |||
| fffad43937 | |||
| 3a79f43a8d | |||
| 2e495f87ab | |||
| 57a9f18f5a | |||
| 0c6a0a9cae | |||
| c80d8d354a | |||
| 3f544dbc69 | |||
| d1e1f15db0 | |||
| 651c314182 | |||
| a57545521d | |||
| da60606921 | |||
| 14f9bf7234 | |||
| c1fa92869a | |||
| c8533e3ec8 | |||
| 06c4dd601d | |||
| 4ff80bbab2 | |||
| d7220b1fec | |||
| d92df9ada3 |
@@ -0,0 +1,36 @@
|
||||
name: Daily security audit
|
||||
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
security_audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
notification:
|
||||
if: ${{ failure() }}
|
||||
needs: security_audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Nym daily audit"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "test"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -166,8 +166,8 @@ jobs:
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "test"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Publish Nym CLI binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
|
||||
jobs:
|
||||
publish-nym-cli:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Check the release tag starts with `nym-cli-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-cli-') == false && github.event_name != 'workflow_dispatch'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-cli-...')
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Build binary
|
||||
run: make build-nym-cli
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-cli-${{ matrix.platform }}
|
||||
path: |
|
||||
target/release/nym-cli*
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
target/release/nym-cli
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Publish Nym binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
@@ -18,7 +20,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Check the release tag starts with `nym-binaries-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false
|
||||
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false && github.event_name != 'workflow_dispatch'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
@@ -35,8 +37,24 @@ jobs:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: my-artifact
|
||||
path: |
|
||||
target/release/nym-client
|
||||
target/release/nym-gateway
|
||||
target/release/nym-mixnode
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-validator-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
target/release/nym-cli
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
target/release/nym-client
|
||||
@@ -45,3 +63,5 @@ jobs:
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-validator-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
target/release/nym-cli
|
||||
|
||||
+28
-254
@@ -2,8 +2,24 @@
|
||||
|
||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
|
||||
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- 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])
|
||||
|
||||
[#1541]: https://github.com/nymtech/nym/pull/1541
|
||||
[#1558]: https://github.com/nymtech/nym/pull/1558
|
||||
[#1585]: https://github.com/nymtech/nym/pull/1585
|
||||
|
||||
|
||||
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -19,12 +35,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- validator-api: add Swagger to document the REST API ([#1249]).
|
||||
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
|
||||
- validator-api: add `uptime`, `estimated_operator_apy`, `estimated_delegators_apy` to `/mixnodes/detailed` endpoint ([#1393])
|
||||
- validator-api: add node info cache storing simulated active set inclusion probabilities
|
||||
- network-statistics: a new mixnet service that aggregates and exposes anonymized data about mixnet services ([#1328])
|
||||
- mixnode: Added basic mixnode hardware reporting to the HTTP API ([#1308]).
|
||||
- validator-api: endpoint, in coconut mode, for returning the validator-api cosmos address ([#1404]).
|
||||
- validator-client: add `denom` argument and add simple test for querying an account balance
|
||||
- gateway, validator-api: Checks for coconut credential double spending attempts, taking the coconut bandwidth contract as source of truth ([#1457])
|
||||
- coconut-bandwidth-contract: Record the state of a coconut credential; create specific proposal for releasing funds ([#1457])
|
||||
- inclusion-probability: add simulator for active set inclusion probability
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -37,6 +55,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
|
||||
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
|
||||
- network-requester: fix filter for suffix-only domains ([#1487])
|
||||
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service; cleaner shutdown, without panics ([#1496], [#1573]).
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -51,6 +70,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- multisig-contract: Limit the proposal creating functionality to one address (coconut-bandwidth-contract address) ([#1457])
|
||||
- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463])
|
||||
- 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])
|
||||
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
@@ -77,90 +99,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#1478]: https://github.com/nymtech/nym/pull/1478
|
||||
[#1482]: https://github.com/nymtech/nym/pull/1482
|
||||
[#1487]: https://github.com/nymtech/nym/pull/1487
|
||||
|
||||
## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22)
|
||||
|
||||
### Added
|
||||
|
||||
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
|
||||
- nym-connect: add ability to select network requester and gateway ([#1427])
|
||||
- nym-connect: add ability to export gateway keys as JSON
|
||||
- nym-connect: add auto updater
|
||||
|
||||
### Changed
|
||||
|
||||
- nym-connect: reuse config id instead of creating a new id on each connection
|
||||
|
||||
[#1427]: https://github.com/nymtech/nym/pull/1427
|
||||
|
||||
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
||||
|
||||
- wallet: dark mode
|
||||
- wallet: when simulating gas costs, an automatic adjustment is being used ([#1388]).
|
||||
|
||||
[#1388]: https://github.com/nymtech/nym/pull/1388
|
||||
|
||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||
|
||||
### Added
|
||||
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- mixnet-contract: Added staking_supply field to ContractStateParams.
|
||||
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
|
||||
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
|
||||
|
||||
### Fixed
|
||||
|
||||
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
|
||||
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
|
||||
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
|
||||
|
||||
[#1255]: https://github.com/nymtech/nym/pull/1255
|
||||
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1331]: https://github.com/nymtech/nym/pull/1331
|
||||
[#1369]: https://github.com/nymtech/nym/pull/1369
|
||||
[#1373]: https://github.com/nymtech/nym/pull/1373
|
||||
|
||||
## [nym-wallet-v1.0.6](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.6) (2022-06-21)
|
||||
|
||||
- wallet: undelegating now uses either the mixnet or vesting contract, or both, depending on how delegations were made
|
||||
- wallet: redeeming and compounding now uses both the mixnet and vesting contract
|
||||
- wallet: the wallet backend learned how to archive wallet files
|
||||
- wallet: add ENABLE_QA_MODE environment variable to enable QA mode on built wallet
|
||||
|
||||
## [nym-wallet-v1.0.5](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.5) (2022-06-14)
|
||||
|
||||
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
|
||||
- wallet: require password to switch accounts
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- wallet: new delegation and rewards UI
|
||||
- wallet: show version in nav bar
|
||||
- wallet: contract admin route put back
|
||||
- wallet: staking_supply field to StateParams
|
||||
- wallet: show transaction hash for redeeming or compounding rewards
|
||||
|
||||
[#1265]: https://github.com/nymtech/nym/pull/1265
|
||||
[#1302]: https://github.com/nymtech/nym/pull/1302
|
||||
|
||||
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||
|
||||
### Changed
|
||||
|
||||
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
|
||||
[#1496]: https://github.com/nymtech/nym/pull/1496
|
||||
[#1503]: https://github.com/nymtech/nym/pull/1503
|
||||
[#1520]: https://github.com/nymtech/nym/pull/1520
|
||||
[#1573]: https://github.com/nymtech/nym/pull/1573
|
||||
[#1576]: https://github.com/nymtech/nym/pull/1576
|
||||
|
||||
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
|
||||
|
||||
@@ -193,77 +136,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.3...nym-binaries-1.0.0)
|
||||
|
||||
## [nym-wallet-v1.0.3](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.3) (2022-04-25)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.2...nym-wallet-v1.0.3)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[Issue\] Wallet 1.0.2 cannot send NYM tokens from a DelayedVestingAccount [\#1215](https://github.com/nymtech/nym/issues/1215)
|
||||
- Main README not showing properly with GitHub dark mode [\#1211](https://github.com/nymtech/nym/issues/1211)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Bugfix - wallet undelegation for vesting accounts [\#1220](https://github.com/nymtech/nym/pull/1220) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Bugfix/delegation reconcile [\#1219](https://github.com/nymtech/nym/pull/1219) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bugfix/query proxied pending delegations [\#1218](https://github.com/nymtech/nym/pull/1218) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Using custom gas multiplier in the wallet [\#1217](https://github.com/nymtech/nym/pull/1217) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/vesting accounts support [\#1216](https://github.com/nymtech/nym/pull/1216) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Release/1.0.0 rc.2 [\#1214](https://github.com/nymtech/nym/pull/1214) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- chore: fix dark mode rendering [\#1212](https://github.com/nymtech/nym/pull/1212) ([pwnfoo](https://github.com/pwnfoo))
|
||||
- Feature/spend coconut [\#1210](https://github.com/nymtech/nym/pull/1210) ([neacsu](https://github.com/neacsu))
|
||||
- Bugfix/unique sphinx key [\#1207](https://github.com/nymtech/nym/pull/1207) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add cache read and write timeouts [\#1206](https://github.com/nymtech/nym/pull/1206) ([durch](https://github.com/durch))
|
||||
- Additional, more informative routes [\#1204](https://github.com/nymtech/nym/pull/1204) ([durch](https://github.com/durch))
|
||||
- Feature/aggregated econ dynamics explorer endpoint [\#1203](https://github.com/nymtech/nym/pull/1203) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Debugging validator [\#1198](https://github.com/nymtech/nym/pull/1198) ([durch](https://github.com/durch))
|
||||
- wallet: expose additional validator configuration functionality to the frontend [\#1195](https://github.com/nymtech/nym/pull/1195) ([octol](https://github.com/octol))
|
||||
- Update rewarding validator address [\#1193](https://github.com/nymtech/nym/pull/1193) ([durch](https://github.com/durch))
|
||||
- Crypto part of the Groth's NIDKG [\#1182](https://github.com/nymtech/nym/pull/1182) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix unbond page [\#1180](https://github.com/nymtech/nym/pull/1180) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Type safe bounds [\#1179](https://github.com/nymtech/nym/pull/1179) ([durch](https://github.com/durch))
|
||||
- Fix delegation paging [\#1174](https://github.com/nymtech/nym/pull/1174) ([durch](https://github.com/durch))
|
||||
- Update binaries to rc version [\#1172](https://github.com/nymtech/nym/pull/1172) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Bump ansi-regex from 4.1.0 to 4.1.1 in /docker/typescript\_client/upload\_contract [\#1171](https://github.com/nymtech/nym/pull/1171) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
|
||||
## [nym-binaries-1.0.0-rc.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.2) (2022-04-15)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.2...nym-binaries-1.0.0-rc.2)
|
||||
|
||||
## [nym-wallet-v1.0.2](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.2) (2022-04-05)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.1...nym-wallet-v1.0.2)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Wallet 1.0.2 visual tweaks [\#1197](https://github.com/nymtech/nym/pull/1197) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Password for wallet with routes [\#1196](https://github.com/nymtech/nym/pull/1196) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add auto-updater to Nym Wallet [\#1194](https://github.com/nymtech/nym/pull/1194) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Fix clippy warnings for beta toolchain [\#1191](https://github.com/nymtech/nym/pull/1191) ([octol](https://github.com/octol))
|
||||
- wallet: expose validator urls to the frontend [\#1190](https://github.com/nymtech/nym/pull/1190) ([octol](https://github.com/octol))
|
||||
- wallet: add test for decrypting stored wallet file [\#1189](https://github.com/nymtech/nym/pull/1189) ([octol](https://github.com/octol))
|
||||
- Fix clippy warnings [\#1188](https://github.com/nymtech/nym/pull/1188) ([octol](https://github.com/octol))
|
||||
- Password for wallet with routes [\#1187](https://github.com/nymtech/nym/pull/1187) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: add validate\_mnemonic [\#1186](https://github.com/nymtech/nym/pull/1186) ([octol](https://github.com/octol))
|
||||
- wallet: support removing accounts from the wallet file [\#1185](https://github.com/nymtech/nym/pull/1185) ([octol](https://github.com/octol))
|
||||
- Feature/adding discord [\#1184](https://github.com/nymtech/nym/pull/1184) ([gala1234](https://github.com/gala1234))
|
||||
- wallet: config backend for validator selection [\#1183](https://github.com/nymtech/nym/pull/1183) ([octol](https://github.com/octol))
|
||||
- Add storybook to wallet [\#1178](https://github.com/nymtech/nym/pull/1178) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: connection test nymd and api urls independently [\#1170](https://github.com/nymtech/nym/pull/1170) ([octol](https://github.com/octol))
|
||||
- wallet: wire up account storage [\#1153](https://github.com/nymtech/nym/pull/1153) ([octol](https://github.com/octol))
|
||||
- Feature/signature on deposit [\#1151](https://github.com/nymtech/nym/pull/1151) ([neacsu](https://github.com/neacsu))
|
||||
|
||||
## [nym-wallet-v1.0.1](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.1) (2022-04-05)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.1...nym-wallet-v1.0.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Check enabling bbbc simultaneously with open access. Estimate what it would take to make this the default compilation target. [\#1175](https://github.com/nymtech/nym/issues/1175)
|
||||
- Get coconut credential for deposited tokens [\#1138](https://github.com/nymtech/nym/issues/1138)
|
||||
- Make payments lazy [\#1135](https://github.com/nymtech/nym/issues/1135)
|
||||
- Uptime on node selection for sets [\#1049](https://github.com/nymtech/nym/issues/1049)
|
||||
|
||||
## [nym-binaries-1.0.0-rc.1](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.1) (2022-03-28)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.0...nym-binaries-1.0.0-rc.1)
|
||||
@@ -342,108 +218,6 @@ 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))
|
||||
|
||||
## [nym-wallet-v1.0.0](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.0) (2022-02-03)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...nym-wallet-v1.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- \[Feature Request\] Please enable registration without need for Telegram account [\#1016](https://github.com/nymtech/nym/issues/1016)
|
||||
- Fast mixnode launch with a pre-built ISO + VM software [\#1001](https://github.com/nymtech/nym/issues/1001)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[Issue\] [\#1000](https://github.com/nymtech/nym/issues/1000)
|
||||
- \[Issue\] `nym-client` requires multiple attempts to run a server [\#869](https://github.com/nymtech/nym/issues/869)
|
||||
- De-'float'-ing `Interval` \(`Display` impl + `serde`\) [\#1065](https://github.com/nymtech/nym/pull/1065) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- display client address on wallet creation [\#1058](https://github.com/nymtech/nym/pull/1058) ([fmtabbara](https://github.com/fmtabbara))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Rewarded set inclusion probability API endpoint [\#1037](https://github.com/nymtech/nym/issues/1037)
|
||||
- Update cw-storage-plus to 0.11 [\#1032](https://github.com/nymtech/nym/issues/1032)
|
||||
- Change `u128` fields in `RewardEstimationResponse` to `u64` [\#1029](https://github.com/nymtech/nym/issues/1029)
|
||||
- Test out the mainnet Gravity Bridge [\#1006](https://github.com/nymtech/nym/issues/1006)
|
||||
- Add vesting contract interface to nym-wallet [\#959](https://github.com/nymtech/nym/issues/959)
|
||||
- Mixnode crash [\#486](https://github.com/nymtech/nym/issues/486)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- create custom urls for mainnet [\#1095](https://github.com/nymtech/nym/pull/1095) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Wallet signing on MacOS [\#1093](https://github.com/nymtech/nym/pull/1093) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Fix rust 2018 idioms warnings [\#1092](https://github.com/nymtech/nym/pull/1092) ([octol](https://github.com/octol))
|
||||
- Prevent contract overwriting [\#1090](https://github.com/nymtech/nym/pull/1090) ([durch](https://github.com/durch))
|
||||
- Logout operation [\#1087](https://github.com/nymtech/nym/pull/1087) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Update to rust edition 2021 everywhere [\#1086](https://github.com/nymtech/nym/pull/1086) ([octol](https://github.com/octol))
|
||||
- Tag contract errors, and print out lines for easier QA [\#1084](https://github.com/nymtech/nym/pull/1084) ([durch](https://github.com/durch))
|
||||
- Feature/flexible vesting + utility queries [\#1083](https://github.com/nymtech/nym/pull/1083) ([durch](https://github.com/durch))
|
||||
- Bump @openzeppelin/contracts from 4.3.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1082](https://github.com/nymtech/nym/pull/1082) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nth-check from 2.0.0 to 2.0.1 in /clients/native/examples/js-examples/websocket [\#1081](https://github.com/nymtech/nym/pull/1081) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump url-parse from 1.5.1 to 1.5.4 in /clients/native/examples/js-examples/websocket [\#1080](https://github.com/nymtech/nym/pull/1080) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump follow-redirects from 1.14.1 to 1.14.7 in /clients/native/examples/js-examples/websocket [\#1079](https://github.com/nymtech/nym/pull/1079) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nanoid from 3.1.23 to 3.2.0 in /clients/native/examples/js-examples/websocket [\#1078](https://github.com/nymtech/nym/pull/1078) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Setup basic test for mixnode stats reporting [\#1077](https://github.com/nymtech/nym/pull/1077) ([octol](https://github.com/octol))
|
||||
- Make wallet\_address mandatory for mixnode init [\#1076](https://github.com/nymtech/nym/pull/1076) ([octol](https://github.com/octol))
|
||||
- Tidy nym-mixnode module visibility [\#1075](https://github.com/nymtech/nym/pull/1075) ([octol](https://github.com/octol))
|
||||
- Feature/wallet login with password [\#1074](https://github.com/nymtech/nym/pull/1074) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add trait to mock client dependency in DelayForwarder [\#1073](https://github.com/nymtech/nym/pull/1073) ([octol](https://github.com/octol))
|
||||
- Bump rust-version to latest stable for nym-mixnode [\#1072](https://github.com/nymtech/nym/pull/1072) ([octol](https://github.com/octol))
|
||||
- Fixes CI for our wasm build [\#1069](https://github.com/nymtech/nym/pull/1069) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add @octol as codeowner [\#1068](https://github.com/nymtech/nym/pull/1068) ([octol](https://github.com/octol))
|
||||
- set-up inclusion probability [\#1067](https://github.com/nymtech/nym/pull/1067) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/wasm client [\#1066](https://github.com/nymtech/nym/pull/1066) ([neacsu](https://github.com/neacsu))
|
||||
- Changed bech32\_prefix from punk to nymt [\#1064](https://github.com/nymtech/nym/pull/1064) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet [\#1063](https://github.com/nymtech/nym/pull/1063) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet [\#1062](https://github.com/nymtech/nym/pull/1062) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Rework vesting contract storage [\#1061](https://github.com/nymtech/nym/pull/1061) ([durch](https://github.com/durch))
|
||||
- Mixnet Contract constants extraction [\#1060](https://github.com/nymtech/nym/pull/1060) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix: make explorer footer year dynamic [\#1059](https://github.com/nymtech/nym/pull/1059) ([martinyung](https://github.com/martinyung))
|
||||
- Add mnemonic just on creation, to display it [\#1057](https://github.com/nymtech/nym/pull/1057) ([neacsu](https://github.com/neacsu))
|
||||
- Network Explorer: updates to API and UI to show the active set [\#1056](https://github.com/nymtech/nym/pull/1056) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Made contract addresses for query NymdClient construction optional [\#1055](https://github.com/nymtech/nym/pull/1055) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Introduced RPC query for total token supply [\#1053](https://github.com/nymtech/nym/pull/1053) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/tokio console [\#1052](https://github.com/nymtech/nym/pull/1052) ([durch](https://github.com/durch))
|
||||
- Implemented beta clippy lint recommendations [\#1051](https://github.com/nymtech/nym/pull/1051) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add new function to update profit percentage [\#1050](https://github.com/nymtech/nym/pull/1050) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Upgrade Clap and use declarative argument parsing for nym-mixnode [\#1047](https://github.com/nymtech/nym/pull/1047) ([octol](https://github.com/octol))
|
||||
- Feature/additional bond validation [\#1046](https://github.com/nymtech/nym/pull/1046) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Fix clippy on relevant lints [\#1044](https://github.com/nymtech/nym/pull/1044) ([neacsu](https://github.com/neacsu))
|
||||
- Bump shelljs from 0.8.4 to 0.8.5 in /contracts/basic-bandwidth-generation [\#1043](https://github.com/nymtech/nym/pull/1043) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Endpoint for rewarded set inclusion probabilities [\#1042](https://github.com/nymtech/nym/pull/1042) ([durch](https://github.com/durch))
|
||||
- Bump follow-redirects from 1.14.4 to 1.14.7 in /contracts/basic-bandwidth-generation [\#1041](https://github.com/nymtech/nym/pull/1041) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet [\#1040](https://github.com/nymtech/nym/pull/1040) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/node settings update [\#1036](https://github.com/nymtech/nym/pull/1036) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Migrate to cw-storage-plus 0.11.1 [\#1035](https://github.com/nymtech/nym/pull/1035) ([durch](https://github.com/durch))
|
||||
- Bump @openzeppelin/contracts from 4.4.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1034](https://github.com/nymtech/nym/pull/1034) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/configurable wallet [\#1033](https://github.com/nymtech/nym/pull/1033) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/downcast reward estimation [\#1031](https://github.com/nymtech/nym/pull/1031) ([durch](https://github.com/durch))
|
||||
- Wallet UI updates [\#1028](https://github.com/nymtech/nym/pull/1028) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Remove migration code [\#1027](https://github.com/nymtech/nym/pull/1027) ([neacsu](https://github.com/neacsu))
|
||||
- Chore/stricter dependency requirements [\#1025](https://github.com/nymtech/nym/pull/1025) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/validator api client endpoints [\#1024](https://github.com/nymtech/nym/pull/1024) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Updated cosmrs to 0.4.1 [\#1023](https://github.com/nymtech/nym/pull/1023) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/testnet deploy scripts [\#1022](https://github.com/nymtech/nym/pull/1022) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Changed wallet's client to a full validator client [\#1021](https://github.com/nymtech/nym/pull/1021) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Fix 404 link [\#1020](https://github.com/nymtech/nym/pull/1020) ([RiccardoMasutti](https://github.com/RiccardoMasutti))
|
||||
- Feature/additional mixnode endpoints [\#1019](https://github.com/nymtech/nym/pull/1019) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Introduced denom check when trying to withdraw vested coins [\#1018](https://github.com/nymtech/nym/pull/1018) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add network defaults for qa [\#1017](https://github.com/nymtech/nym/pull/1017) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/expanded events [\#1015](https://github.com/nymtech/nym/pull/1015) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- update frontend to use new profit update api [\#1014](https://github.com/nymtech/nym/pull/1014) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/node state endpoint [\#1013](https://github.com/nymtech/nym/pull/1013) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/hourly set updates [\#1012](https://github.com/nymtech/nym/pull/1012) ([durch](https://github.com/durch))
|
||||
- Feature/remove unused profit margin [\#1011](https://github.com/nymtech/nym/pull/1011) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/explorer node status [\#1010](https://github.com/nymtech/nym/pull/1010) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Use serial integer instead of random [\#1009](https://github.com/nymtech/nym/pull/1009) ([durch](https://github.com/durch))
|
||||
- Feature/configure profit [\#1008](https://github.com/nymtech/nym/pull/1008) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/fix gateway sign [\#1004](https://github.com/nymtech/nym/pull/1004) ([neacsu](https://github.com/neacsu))
|
||||
- Fix clippy [\#1003](https://github.com/nymtech/nym/pull/1003) ([neacsu](https://github.com/neacsu))
|
||||
- Update wallet version [\#998](https://github.com/nymtech/nym/pull/998) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Fix wallet build instructions [\#997](https://github.com/nymtech/nym/pull/997) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Make the separation between testnet-mode and erc20 bandwidth mode clearer [\#994](https://github.com/nymtech/nym/pull/994) ([neacsu](https://github.com/neacsu))
|
||||
- Bump @openzeppelin/contracts from 3.4.0 to 4.4.1 in /contracts/basic-bandwidth-generation [\#983](https://github.com/nymtech/nym/pull/983) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/implicit runtime [\#973](https://github.com/nymtech/nym/pull/973) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Differentiate staking and ownership [\#961](https://github.com/nymtech/nym/pull/961) ([durch](https://github.com/durch))
|
||||
|
||||
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
|
||||
|
||||
|
||||
Generated
+138
-110
@@ -894,6 +894,7 @@ dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"clap 3.2.8",
|
||||
"coconut-interface",
|
||||
"config",
|
||||
"credential-storage",
|
||||
"credentials",
|
||||
"crypto",
|
||||
@@ -1127,7 +1128,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8"
|
||||
dependencies = [
|
||||
"sct",
|
||||
"sct 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1903,10 +1904,6 @@ name = "futures-timer"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||
dependencies = [
|
||||
"gloo-timers",
|
||||
"send_wrapper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
@@ -1957,8 +1954,8 @@ dependencies = [
|
||||
"secp256k1",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tungstenite",
|
||||
"tokio-tungstenite 0.17.2",
|
||||
"tungstenite 0.17.3",
|
||||
"url",
|
||||
"validator-client",
|
||||
"wasm-bindgen",
|
||||
@@ -1983,7 +1980,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tungstenite",
|
||||
"tungstenite 0.17.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2091,18 +2088,6 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.10.0"
|
||||
@@ -2395,7 +2380,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2408,12 +2393,12 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"log",
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
"webpki 0.21.4",
|
||||
"webpki-roots 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2496,6 +2481,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inclusion-probability"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
@@ -2743,9 +2737,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.16"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
@@ -3056,7 +3050,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap 3.2.8",
|
||||
"client-core",
|
||||
@@ -3079,7 +3073,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sled",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.17.2",
|
||||
"topology",
|
||||
"url",
|
||||
"validator-client",
|
||||
@@ -3090,7 +3084,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3126,7 +3120,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.17.2",
|
||||
"tokio-util 0.7.3",
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
@@ -3138,7 +3132,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -3178,7 +3172,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap 2.34.0",
|
||||
@@ -3200,13 +3194,13 @@ dependencies = [
|
||||
"statistics-common",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.14.0",
|
||||
"websocket-requests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "0.1.0"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -3217,12 +3211,12 @@ dependencies = [
|
||||
"statistics-common",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap 3.2.8",
|
||||
"client-core",
|
||||
@@ -3283,7 +3277,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-validator-api"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3306,6 +3300,7 @@ dependencies = [
|
||||
"gateway-client",
|
||||
"getset",
|
||||
"humantime-serde",
|
||||
"inclusion-probability",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"multisig-contract-common",
|
||||
@@ -3325,6 +3320,8 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"task",
|
||||
"thiserror",
|
||||
"time 0.3.9",
|
||||
"tokio",
|
||||
@@ -4717,8 +4714,20 @@ dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
"sct 0.6.1",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct 0.7.0",
|
||||
"webpki 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4728,7 +4737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
@@ -4820,6 +4829,16 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.2.1"
|
||||
@@ -4913,12 +4932,6 @@ dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
@@ -4980,9 +4993,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.79"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
dependencies = [
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
@@ -5198,6 +5211,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"nymsphinx-addressing",
|
||||
"ordered-buffer",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5316,7 +5330,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"sha2 0.9.9",
|
||||
"smallvec 1.8.0",
|
||||
"sqlformat",
|
||||
@@ -5325,8 +5339,8 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
"webpki 0.21.4",
|
||||
"webpki-roots 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5397,15 +5411,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "streaming-stats"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0d670ce4e348a2081843569e0f79b21c99c91bb9028b3b3ecb0f050306de547"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.2"
|
||||
@@ -5660,18 +5665,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5794,9 +5799,9 @@ version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"tokio",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5833,7 +5838,19 @@ dependencies = [
|
||||
"log",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
"tungstenite 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite 0.17.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6101,6 +6118,28 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rustls 0.20.6",
|
||||
"sha-1 0.10.0",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
"webpki 0.22.0",
|
||||
"webpki-roots 0.22.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
@@ -6293,29 +6332,6 @@ dependencies = [
|
||||
"vesting-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-client-scripts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 3.2.8",
|
||||
"csv",
|
||||
"dotenv",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"network-defaults",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"streaming-stats",
|
||||
"tokio",
|
||||
"url",
|
||||
"validator-client",
|
||||
"vesting-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
@@ -6431,25 +6447,23 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -6470,9 +6484,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -6480,9 +6494,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6493,9 +6507,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-utils"
|
||||
@@ -6503,7 +6517,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
"tungstenite",
|
||||
"tungstenite 0.17.3",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
@@ -6533,15 +6547,12 @@ dependencies = [
|
||||
"ethereum-types",
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"getrandom 0.2.6",
|
||||
"headers",
|
||||
"hex",
|
||||
"js-sys",
|
||||
"jsonrpc-core",
|
||||
"log",
|
||||
"parking_lot 0.11.2",
|
||||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"rlp",
|
||||
"secp256k1",
|
||||
@@ -6553,8 +6564,6 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
"tokio-util 0.6.9",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web3-async-native-tls",
|
||||
]
|
||||
|
||||
@@ -6580,13 +6589,32 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
|
||||
dependencies = [
|
||||
"webpki 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+10
-9
@@ -22,22 +22,23 @@ members = [
|
||||
"clients/native",
|
||||
"clients/native/websocket-requests",
|
||||
"clients/socks5",
|
||||
"common/bandwidth-claim-contract",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
"common/credential-storage",
|
||||
"common/coconut-interface",
|
||||
"common/config",
|
||||
"common/credentials",
|
||||
"common/crypto",
|
||||
"common/crypto/dkg",
|
||||
"common/execute",
|
||||
"common/bandwidth-claim-contract",
|
||||
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
"common/credential-storage",
|
||||
"common/credentials",
|
||||
"common/crypto",
|
||||
"common/crypto/dkg",
|
||||
"common/execute",
|
||||
"common/inclusion-probability",
|
||||
"common/mixnode-common",
|
||||
"common/network-defaults",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
@@ -53,9 +54,9 @@ members = [
|
||||
"common/nymsphinx/params",
|
||||
"common/nymsphinx/types",
|
||||
"common/pemstore",
|
||||
"common/statistics",
|
||||
"common/socks5/proxy-helpers",
|
||||
"common/socks5/requests",
|
||||
"common/statistics",
|
||||
"common/task",
|
||||
"common/topology",
|
||||
"common/types",
|
||||
@@ -68,8 +69,7 @@ members = [
|
||||
"service-providers/network-statistics",
|
||||
"validator-api",
|
||||
"validator-api/validator-api-requests",
|
||||
"tools/ts-rs-cli",
|
||||
"tools/validator-client-scripts"
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -77,6 +77,7 @@ default-members = [
|
||||
"clients/socks5",
|
||||
"gateway",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"mixnode",
|
||||
"validator-api",
|
||||
"explorer-api",
|
||||
|
||||
-1
@@ -133,7 +133,6 @@ where
|
||||
let prepared_fragment = self
|
||||
.message_preparer
|
||||
.prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
real_messages.push(RealMessage::new(
|
||||
|
||||
-1
@@ -83,7 +83,6 @@ where
|
||||
let prepared_fragment = self
|
||||
.message_preparer
|
||||
.prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, packet_recipient)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// if we have the ONLY strong reference to the ack data, it means it was removed from the
|
||||
|
||||
@@ -18,6 +18,7 @@ url = "2.2"
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||
|
||||
coconut-interface = { path = "../../common/coconut-interface" }
|
||||
config = { path = "../../common/config" }
|
||||
credentials = { path = "../../common/credentials" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::{MNEMONIC, NYMD_URL};
|
||||
use bip39::Mnemonic;
|
||||
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
||||
use std::str::FromStr;
|
||||
@@ -17,9 +16,9 @@ pub(crate) struct Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new() -> Self {
|
||||
let nymd_url = Url::from_str(NYMD_URL).unwrap();
|
||||
let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
|
||||
pub fn new(nymd_url: &str, mnemonic: &str) -> Self {
|
||||
let nymd_url = Url::from_str(nymd_url).unwrap();
|
||||
let mnemonic = Mnemonic::from_str(mnemonic).unwrap();
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = nymd::Config::try_from_nym_network_details(&network_details)
|
||||
.expect("failed to construct valid validator client config with the provided network");
|
||||
|
||||
@@ -6,7 +6,6 @@ use clap::{Args, Subcommand};
|
||||
use pickledb::PickleDb;
|
||||
use rand::rngs::OsRng;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters};
|
||||
use credential_storage::storage::Storage;
|
||||
@@ -20,7 +19,6 @@ use validator_client::nymd::tx::Hash;
|
||||
use crate::client::Client;
|
||||
use crate::error::{CredentialClientError, Result};
|
||||
use crate::state::{KeyPair, RequestData, State};
|
||||
use crate::SIGNER_AUTHORITIES;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Commands {
|
||||
@@ -39,6 +37,12 @@ pub(crate) trait Execute {
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Deposit {
|
||||
/// The nymd URL that should be used
|
||||
#[clap(long)]
|
||||
nymd_url: String,
|
||||
/// A mnemonic for the account that does the deposit
|
||||
#[clap(long)]
|
||||
mnemonic: String,
|
||||
/// The amount that needs to be deposited
|
||||
#[clap(long)]
|
||||
amount: u64,
|
||||
@@ -51,7 +55,7 @@ impl Execute for Deposit {
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
|
||||
let client = Client::new();
|
||||
let client = Client::new(&self.nymd_url, &self.mnemonic);
|
||||
let tx_hash = client
|
||||
.deposit(
|
||||
self.amount,
|
||||
@@ -96,6 +100,10 @@ pub(crate) struct GetCredential {
|
||||
/// The hash of a successful deposit transaction
|
||||
#[clap(long)]
|
||||
tx_hash: String,
|
||||
/// The URLs to the validator-api endpoints the are run as coconut signer authorities, separated
|
||||
/// by comma (,)
|
||||
#[clap(long)]
|
||||
signer_authorities: String,
|
||||
/// If we want to get the signature without attaching a blind sign request; it is expected that
|
||||
/// there is already a signature stored on the signer
|
||||
#[clap(long, parse(from_flag))]
|
||||
@@ -108,7 +116,8 @@ impl Execute for GetCredential {
|
||||
let mut state = db
|
||||
.get::<State>(&self.tx_hash)
|
||||
.ok_or(CredentialClientError::NoDeposit)?;
|
||||
let urls = SIGNER_AUTHORITIES.map(|addr| Url::from_str(addr).unwrap());
|
||||
|
||||
let urls = config::parse_validators(&self.signer_authorities);
|
||||
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let bandwidth_credential_attributes = if self.__no_request {
|
||||
|
||||
@@ -11,20 +11,24 @@ cfg_if::cfg_if! {
|
||||
|
||||
use commands::{Commands, Execute};
|
||||
use error::Result;
|
||||
use network_defaults::setup_env;
|
||||
|
||||
use clap::Parser;
|
||||
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
|
||||
|
||||
pub const MNEMONIC: &str = "jazz fatigue diagram account outer wrist slide cherry mother grid network pause wolf pig round answer mail junior better hair dismiss toward access end";
|
||||
pub const NYMD_URL: &str = "http://127.0.0.1:26657";
|
||||
pub const CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||
pub const SIGNER_AUTHORITIES: [&str; 1] = [
|
||||
"http://127.0.0.1:8080",
|
||||
];
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, about)]
|
||||
struct Cli {
|
||||
/// Path pointing to an env file that configures the client.
|
||||
#[clap(long)]
|
||||
pub(crate) config_env_file: Option<std::path::PathBuf>,
|
||||
|
||||
/// Path where the sqlite credental database will be located.
|
||||
/// It should point to a $HOME/$CLIENT_ID/data/db.sqlite file of
|
||||
/// the client that is supposed to use the credential.
|
||||
#[clap(long)]
|
||||
pub(crate) credential_db_path: std::path::PathBuf,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
@@ -32,8 +36,9 @@ cfg_if::cfg_if! {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
setup_env(args.config_env_file.clone());
|
||||
|
||||
let shared_storage = credential_storage::initialise_storage(std::path::PathBuf::from("/tmp/credential.db")).await;
|
||||
let shared_storage = credential_storage::initialise_storage(args.credential_db_path.clone()).await;
|
||||
let mut db = match PickleDb::load(
|
||||
"credential.db",
|
||||
PickleDbDumpPolicy::AutoDump,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
@@ -28,7 +28,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits +
|
||||
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
|
||||
sled = "0.34" # for storage of replySURB decryption keys
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = "0.14" # websocket
|
||||
tokio-tungstenite = "0.17.2" # websocket
|
||||
|
||||
## internal
|
||||
client-core = { path = "../client-core" }
|
||||
|
||||
@@ -50,6 +50,10 @@ impl NymConfig for Config {
|
||||
.join("clients")
|
||||
}
|
||||
|
||||
fn try_default_root_directory() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf {
|
||||
self.base.get_nym_root_directory()
|
||||
}
|
||||
|
||||
@@ -199,9 +199,9 @@ impl NymClient {
|
||||
Some(bandwidth_controller),
|
||||
);
|
||||
|
||||
if self.config.get_base().get_disabled_credentials_mode() {
|
||||
gateway_client.set_disabled_credentials_mode(true)
|
||||
}
|
||||
gateway_client
|
||||
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
|
||||
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
|
||||
@@ -46,10 +46,9 @@ pub(crate) struct Init {
|
||||
fastmode: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private_key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
@@ -79,7 +78,7 @@ impl From<Init> for OverrideConfig {
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use clap::{Parser, Subcommand};
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
@@ -79,7 +78,7 @@ pub(crate) struct OverrideConfig {
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
@@ -97,28 +96,17 @@ pub(crate) async fn execute(args: &Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
.set_custom_validator_apis(config::parse_validators(&raw_validators));
|
||||
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
|
||||
let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR)
|
||||
.expect("api validator not set");
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
.set_custom_validator_apis(config::parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if args.disable_socket {
|
||||
@@ -138,12 +126,15 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,9 @@ pub(crate) struct Run {
|
||||
port: Option<u16>,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private-key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
@@ -62,7 +61,7 @@ impl From<Run> for OverrideConfig {
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
@@ -82,7 +81,7 @@ fn version_check(cfg: &Config) -> bool {
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
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"
|
||||
|
||||
@@ -33,6 +33,10 @@ impl NymConfig for Config {
|
||||
.join("socks5-clients")
|
||||
}
|
||||
|
||||
fn try_default_root_directory() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf {
|
||||
self.base.get_nym_root_directory()
|
||||
}
|
||||
|
||||
@@ -198,9 +198,9 @@ impl NymClient {
|
||||
Some(bandwidth_controller),
|
||||
);
|
||||
|
||||
if self.config.get_base().get_disabled_credentials_mode() {
|
||||
gateway_client.set_disabled_credentials_mode(true)
|
||||
}
|
||||
gateway_client
|
||||
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
|
||||
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
|
||||
@@ -46,10 +46,9 @@ pub(crate) struct Init {
|
||||
fastmode: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private_key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
@@ -78,7 +77,7 @@ impl From<Init> for OverrideConfig {
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::client::config::Config;
|
||||
use clap::{Parser, Subcommand};
|
||||
use url::Url;
|
||||
use config::parse_validators;
|
||||
|
||||
pub mod init;
|
||||
pub(crate) mod run;
|
||||
@@ -78,7 +78,7 @@ pub(crate) struct OverrideConfig {
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
@@ -96,17 +96,6 @@ pub(crate) async fn execute(args: &Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config
|
||||
@@ -132,11 +121,14 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,9 @@ pub(crate) struct Run {
|
||||
port: Option<u16>,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private-key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
@@ -65,7 +64,7 @@ impl From<Run> for OverrideConfig {
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
|
||||
@@ -2,9 +2,4 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client;
|
||||
// This is only used as we reach into the init functions in nym-connect. We need to refactor the
|
||||
// init functions so that nym-connect can just call the same init function as the regular socks5
|
||||
// client.
|
||||
#[allow(unused)]
|
||||
pub mod commands;
|
||||
pub mod socks;
|
||||
|
||||
@@ -54,6 +54,13 @@ impl MixnetResponseListener {
|
||||
return;
|
||||
}
|
||||
Ok(Message::Response(data)) => data,
|
||||
Ok(Message::NetworkRequesterResponse(r)) => {
|
||||
error!(
|
||||
"Network requester failed on connection id {} with error: {}",
|
||||
r.connection_id, r.network_requester_error
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.controller_sender
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||
license = "Apache-2.0"
|
||||
@@ -20,7 +20,8 @@ coconut = ["coconut-interface", "credentials", "gateway-client/coconut"]
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen = { version = "=0.2.78", features = ["serde-serialize"] }
|
||||
serde-wasm-bindgen = "0.4"
|
||||
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
js-sys = "0.3"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
@@ -32,7 +33,7 @@ credentials = { path = "../../common/credentials", optional = true }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
topology = { path = "../../common/topology" }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||
wasm-utils = { path = "../../common/wasm-utils" }
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 318 B |
@@ -25,7 +25,7 @@ async function main() {
|
||||
set_panic_hook();
|
||||
|
||||
// validator server we will use to get topology from
|
||||
const validator = "https://sandbox-validator.nymtech.net/api"; //"http://localhost:8081";
|
||||
const validator = "https://validator.nymtech.net/api"; //"http://localhost:8081";
|
||||
|
||||
client = new NymClient(validator);
|
||||
|
||||
|
||||
@@ -132,9 +132,7 @@ impl NymClient {
|
||||
bandwidth_controller,
|
||||
);
|
||||
|
||||
if disabled_credentials_mode {
|
||||
gateway_client.set_disabled_credentials_mode(true)
|
||||
}
|
||||
gateway_client.set_disabled_credentials_mode(disabled_credentials_mode);
|
||||
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
@@ -199,7 +197,6 @@ impl NymClient {
|
||||
// don't bother with acks etc. for time being
|
||||
let prepared_fragment = message_preparer
|
||||
.prepare_chunk_for_sending(message_chunk, topology, &self.ack_key, &recipient)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
console_warn!("packet is going to have round trip time of {:?}, but we're not going to do anything for acks anyway ", prepared_fragment.total_delay);
|
||||
@@ -214,7 +211,7 @@ impl NymClient {
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn choose_gateway(&self) -> &gateway::Node {
|
||||
pub(crate) fn choose_gateway(&self) -> gateway::Node {
|
||||
let topology = self
|
||||
.topology
|
||||
.as_ref()
|
||||
@@ -222,7 +219,21 @@ impl NymClient {
|
||||
|
||||
// choose the first one available
|
||||
assert!(!topology.gateways().is_empty());
|
||||
topology.gateways().first().unwrap()
|
||||
topology.gateways().first().unwrap();
|
||||
|
||||
console_log!("picking nym gateway");
|
||||
|
||||
gateway::Node {
|
||||
owner: "n1kymvkx6vsq7pvn6hfurkpg06h3j4gxj4em7tlg".to_string(),
|
||||
stake: 100000000,
|
||||
location: "PRIVACY HQ".to_string(),
|
||||
host: "gateway1.nymtech.net".parse().unwrap(),
|
||||
mix_host: "213.219.38.119:1789".parse().unwrap(),
|
||||
clients_port: 443,
|
||||
identity_key: identity::PublicKey::from_base58_string("E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM").unwrap(),
|
||||
sphinx_key: encryption::PublicKey::from_base58_string("CYcrjoJ8GT7Dp54zViUyyRUfegeRCyPifWQZHRgMZrfX").unwrap(),
|
||||
version: "1.0.0-rc.2".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// Right now it's impossible to have async exported functions to take `&mut self` rather than mut self
|
||||
|
||||
@@ -167,7 +167,7 @@ impl ReceivedMessagesProcessor {
|
||||
mix_msgs = fused_mixnet_messages_receiver.next() => {
|
||||
for mix_msg in mix_msgs.unwrap() {
|
||||
if let Some(processed) = self.process_received_fragment(mix_msg) {
|
||||
let arg1 = JsValue::from_serde(&processed).unwrap();
|
||||
let arg1 = serde_wasm_bindgen::to_value(&processed).unwrap();
|
||||
on_message.call1(&this, &arg1).expect("on message failed!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ log = "0.4"
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
secp256k1 = "0.20.3"
|
||||
web3 = { version = "0.17.0", default-features = false }
|
||||
secp256k1 = { version = "0.20.3", optional = true }
|
||||
web3 = { version = "0.17.0", default-features = false, optional = true }
|
||||
async-trait = { version = "0.1.51" }
|
||||
|
||||
# internal
|
||||
@@ -30,8 +30,8 @@ network-defaults = { path = "../../network-defaults" }
|
||||
validator-client = { path = "../validator-client", optional = true }
|
||||
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13"
|
||||
default-features = false
|
||||
version = "0.17.3"
|
||||
features = ["rustls-tls-webpki-roots"]
|
||||
|
||||
# non-wasm-only dependencies
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
@@ -39,7 +39,7 @@ version = "1.19.1"
|
||||
features = ["macros", "rt", "net", "sync", "time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
version = "0.17.2"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
|
||||
path = "../../credential-storage"
|
||||
@@ -73,5 +73,5 @@ features = ["js"]
|
||||
|
||||
[features]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
|
||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||
default = ["web3/default"]
|
||||
wasm = []
|
||||
default = ["web3/default", "secp256k1"]
|
||||
|
||||
@@ -23,7 +23,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
use {
|
||||
credentials::token::bandwidth::TokenCredential,
|
||||
crypto::asymmetric::identity,
|
||||
@@ -45,7 +45,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
@@ -58,7 +58,7 @@ pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
@@ -76,11 +76,11 @@ pub struct BandwidthController<St: Storage> {
|
||||
storage: St,
|
||||
#[cfg(feature = "coconut")]
|
||||
validator_endpoints: Vec<url::Url>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
erc20_contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
eth_private_key: SecretKey,
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
pub fn new(
|
||||
storage: St,
|
||||
eth_endpoint: String,
|
||||
@@ -120,7 +120,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
async fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
||||
self.storage
|
||||
.insert_erc20_credential(
|
||||
@@ -132,7 +132,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
async fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
|
||||
let data = self.storage.get_next_erc20_credential().await?;
|
||||
let public_key = identity::PublicKey::from_base58_string(data.public_key).unwrap();
|
||||
@@ -141,7 +141,7 @@ where
|
||||
Ok(identity::KeyPair::from_keys(private_key, public_key))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
async fn mark_keypair_as_spent(
|
||||
&self,
|
||||
keypair: &identity::KeyPair,
|
||||
@@ -180,7 +180,7 @@ where
|
||||
)?)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
pub async fn prepare_token_credential(
|
||||
&self,
|
||||
gateway_identity: identity::PublicKey,
|
||||
@@ -219,7 +219,7 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
pub async fn buy_token_credential(
|
||||
&self,
|
||||
verification_key: identity::PublicKey,
|
||||
@@ -348,7 +348,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use network_defaults::ETH_EVENT_NAME;
|
||||
|
||||
@@ -30,7 +30,7 @@ use rand::rngs::OsRng;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tungstenite::protocol::Message;
|
||||
use tungstenite::Message;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
@@ -449,7 +449,7 @@ impl GatewayClient {
|
||||
.derive_destination_address();
|
||||
let encrypted_address = EncryptedAddressBytes::new(&self_address, shared_key, &iv);
|
||||
|
||||
let msg =
|
||||
let msg: tungstenite::Message =
|
||||
ClientControlRequest::new_authenticate(self_address, encrypted_address, iv).into();
|
||||
|
||||
match self.send_websocket_message(msg).await? {
|
||||
@@ -733,6 +733,8 @@ impl GatewayClient {
|
||||
}
|
||||
|
||||
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError> {
|
||||
println!("About to establish connection to {}", self.gateway_address);
|
||||
|
||||
if !self.connection.is_established() {
|
||||
self.establish_connection().await?;
|
||||
}
|
||||
|
||||
@@ -162,12 +162,10 @@ impl PartiallyDelegated {
|
||||
.expect("stream sender was somehow dropped without sending anything!");
|
||||
|
||||
if let Some(res) = receive_res {
|
||||
if let Err(err) = res {
|
||||
// the receiver got an error. most likely a network one.
|
||||
return Err(err);
|
||||
} else {
|
||||
panic!("This should have NEVER happened - returned a stream before receiving notification")
|
||||
}
|
||||
let _res = res?;
|
||||
panic!(
|
||||
"This should have NEVER happened - returned a stream before receiving notification"
|
||||
)
|
||||
}
|
||||
|
||||
// this call failing is incredibly unlikely, but not impossible.
|
||||
|
||||
@@ -24,7 +24,7 @@ use mixnet_contract_common::{
|
||||
PagedMixDelegationsResponse, PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg,
|
||||
RewardedSetUpdateDetails,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::time::SystemTime;
|
||||
use vesting_contract_common::ExecuteMsg as VestingExecuteMsg;
|
||||
@@ -282,6 +282,30 @@ impl<C> NymdClient<C> {
|
||||
self.simulated_gas_multiplier = multiplier;
|
||||
}
|
||||
|
||||
pub async fn query_contract_smart<M, T>(
|
||||
&self,
|
||||
contract: &AccountId,
|
||||
query_msg: &M,
|
||||
) -> Result<T, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
M: ?Sized + Serialize + Sync,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
{
|
||||
self.client.query_contract_smart(contract, query_msg).await
|
||||
}
|
||||
|
||||
pub async fn query_contract_raw(
|
||||
&self,
|
||||
contract: &AccountId,
|
||||
query_data: Vec<u8>,
|
||||
) -> Result<Vec<u8>, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
self.client.query_contract_raw(contract, query_data).await
|
||||
}
|
||||
|
||||
pub fn wrap_contract_execute_message<M>(
|
||||
&self,
|
||||
contract_address: &AccountId,
|
||||
@@ -893,7 +917,7 @@ impl<C> NymdClient<C> {
|
||||
&self,
|
||||
contract_address: &AccountId,
|
||||
msg: &M,
|
||||
fee: Fee,
|
||||
fee: Option<Fee>,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
@@ -901,6 +925,7 @@ impl<C> NymdClient<C> {
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
M: ?Sized + Serialize + Sync,
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
self.client
|
||||
.execute(self.address(), contract_address, msg, fee, memo, funds)
|
||||
.await
|
||||
@@ -910,7 +935,7 @@ impl<C> NymdClient<C> {
|
||||
&self,
|
||||
contract_address: &AccountId,
|
||||
msgs: I,
|
||||
fee: Fee,
|
||||
fee: Option<Fee>,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
@@ -918,6 +943,7 @@ impl<C> NymdClient<C> {
|
||||
I: IntoIterator<Item = (M, Vec<Coin>)> + Send,
|
||||
M: Serialize,
|
||||
{
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
self.client
|
||||
.execute_multiple(self.address(), contract_address, msgs, fee, memo)
|
||||
.await
|
||||
|
||||
@@ -7,9 +7,11 @@ use crate::nymd::error::NymdError;
|
||||
use crate::nymd::NymdClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use vesting_contract::vesting::Account;
|
||||
use vesting_contract_common::{
|
||||
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
|
||||
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
|
||||
OriginalVestingResponse, Period, PledgeData, VestingDelegation,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -70,6 +72,37 @@ pub trait VestingQueryClient {
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Period, NymdError>;
|
||||
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_identity: String,
|
||||
) -> Result<DelegationTimesResponse, NymdError>;
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, IdentityKey, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NymdError>;
|
||||
|
||||
async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NymdError> {
|
||||
let mut delegations = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.get_all_vesting_delegations_paged(start_after.take(), None)
|
||||
.await?;
|
||||
delegations.append(&mut paged_response.delegations);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -232,4 +265,29 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_identity: String,
|
||||
) -> Result<DelegationTimesResponse, NymdError> {
|
||||
let request = VestingQueryMsg::GetDelegationTimes {
|
||||
address: address.to_string(),
|
||||
mix_identity,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, IdentityKey, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NymdError> {
|
||||
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,30 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
||||
Self::default_config_directory(id).join(Self::config_file_name())
|
||||
}
|
||||
|
||||
// We provide a second set of functions that tries to not panic.
|
||||
|
||||
fn try_default_root_directory() -> Option<PathBuf>;
|
||||
|
||||
fn try_default_config_directory(id: Option<&str>) -> Option<PathBuf> {
|
||||
if let Some(id) = id {
|
||||
Self::try_default_root_directory().map(|d| d.join(id).join("config"))
|
||||
} else {
|
||||
Self::try_default_root_directory().map(|d| d.join("config"))
|
||||
}
|
||||
}
|
||||
|
||||
fn try_default_data_directory(id: Option<&str>) -> Option<PathBuf> {
|
||||
if let Some(id) = id {
|
||||
Self::try_default_root_directory().map(|d| d.join(id).join("data"))
|
||||
} else {
|
||||
Self::try_default_root_directory().map(|d| d.join("data"))
|
||||
}
|
||||
}
|
||||
|
||||
fn try_default_config_file_path(id: Option<&str>) -> Option<PathBuf> {
|
||||
Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf;
|
||||
fn config_directory(&self) -> PathBuf;
|
||||
fn data_directory(&self) -> PathBuf;
|
||||
@@ -88,3 +112,14 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_validators(raw: &str) -> Vec<url::Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl SpendCredentialData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub enum SpendCredentialStatus {
|
||||
InProgress,
|
||||
Spent,
|
||||
|
||||
@@ -213,11 +213,11 @@ pub enum QueryMsg {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub mixnet_denom: String,
|
||||
nodes_to_remove: Option<Vec<NodeToRemove>>,
|
||||
pub nodes_to_remove: Option<Vec<NodeToRemove>>,
|
||||
}
|
||||
|
||||
impl MigrateMsg {
|
||||
@@ -226,7 +226,7 @@ impl MigrateMsg {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct NodeToRemove {
|
||||
owner: String,
|
||||
proxy: Option<String>,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use cosmwasm_std::{Coin, Timestamp};
|
||||
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
|
||||
pub mod events;
|
||||
pub mod messages;
|
||||
@@ -73,3 +74,35 @@ impl OriginalVestingResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct VestingDelegation {
|
||||
pub account_id: u32,
|
||||
pub mix_identity: IdentityKey,
|
||||
pub block_timestamp: u64,
|
||||
pub amount: Uint128,
|
||||
}
|
||||
|
||||
impl VestingDelegation {
|
||||
pub fn storage_key(&self) -> (u32, IdentityKey, u64) {
|
||||
(
|
||||
self.account_id,
|
||||
self.mix_identity.clone(),
|
||||
self.block_timestamp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct DelegationTimesResponse {
|
||||
pub owner: Addr,
|
||||
pub account_id: u32,
|
||||
pub mix_identity: IdentityKey,
|
||||
pub delegation_timestamps: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct AllDelegationsResponse {
|
||||
pub delegations: Vec<VestingDelegation>,
|
||||
pub start_next_after: Option<(u32, IdentityKey, u64)>,
|
||||
}
|
||||
|
||||
@@ -170,4 +170,12 @@ pub enum QueryMsg {
|
||||
address: String,
|
||||
},
|
||||
GetLockedPledgeCap {},
|
||||
GetDelegationTimes {
|
||||
address: String,
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
GetAllDelegations {
|
||||
start_after: Option<(u32, IdentityKey, u64)>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "inclusion-probability"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.17"
|
||||
rand = "0.8.5"
|
||||
thiserror = "1.0.32"
|
||||
@@ -0,0 +1,11 @@
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("The list of cumulative stake was unexpectedly empty")]
|
||||
EmptyListCumulStake,
|
||||
#[error("Sample point was unexpectedly out of bounds")]
|
||||
SamplePointOutOfBounds,
|
||||
#[error("Norm computation failed on different size arrays")]
|
||||
NormDifferenceSizeArrays,
|
||||
#[error("Computed probabilities are fewer than input number of nodes")]
|
||||
ResultsShorterThanInput,
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
//! Active set inclusion probability simulator
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use error::Error;
|
||||
use rand::Rng;
|
||||
|
||||
mod error;
|
||||
|
||||
const TOLERANCE_L2_NORM: f64 = 1e-4;
|
||||
const TOLERANCE_MAX_NORM: f64 = 1e-4;
|
||||
|
||||
pub struct SelectionProbability {
|
||||
pub active_set_probability: Vec<f64>,
|
||||
pub reserve_set_probability: Vec<f64>,
|
||||
pub samples: u64,
|
||||
pub time: Duration,
|
||||
pub delta_l2: f64,
|
||||
pub delta_max: f64,
|
||||
}
|
||||
|
||||
pub fn simulate_selection_probability_mixnodes<R>(
|
||||
list_stake_for_mixnodes: &[u128],
|
||||
active_set_size: usize,
|
||||
reserve_set_size: usize,
|
||||
max_samples: u64,
|
||||
max_time: Duration,
|
||||
rng: &mut R,
|
||||
) -> Result<SelectionProbability, Error>
|
||||
where
|
||||
R: Rng + ?Sized,
|
||||
{
|
||||
log::trace!("Simulating mixnode active set selection probability");
|
||||
|
||||
// In case the active set size is larger than the number of bonded mixnodes, they all have 100%
|
||||
// chance we don't have to go through with the simulation
|
||||
if list_stake_for_mixnodes.len() <= active_set_size {
|
||||
return Ok(SelectionProbability {
|
||||
active_set_probability: vec![1.0; list_stake_for_mixnodes.len()],
|
||||
reserve_set_probability: vec![0.0; list_stake_for_mixnodes.len()],
|
||||
samples: 0,
|
||||
time: Duration::ZERO,
|
||||
delta_l2: 0.0,
|
||||
delta_max: 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
// Total number of existing (registered) nodes
|
||||
let num_mixnodes = list_stake_for_mixnodes.len();
|
||||
|
||||
// Cumulative stake ordered by node index
|
||||
let list_cumul = cumul_sum(list_stake_for_mixnodes);
|
||||
|
||||
// The computed probabilities
|
||||
let mut active_set_probability = vec![0.0; num_mixnodes];
|
||||
let mut reserve_set_probability = vec![0.0; num_mixnodes];
|
||||
|
||||
// Number sufficiently large to have a good approximation of selection probability
|
||||
let mut samples = 0;
|
||||
let mut delta_l2;
|
||||
let mut delta_max;
|
||||
|
||||
// Make sure we bound the time we allow it to run
|
||||
let start_time = Instant::now();
|
||||
|
||||
loop {
|
||||
samples += 1;
|
||||
let mut sample_active_mixnodes = Vec::new();
|
||||
let mut sample_reserve_mixnodes = Vec::new();
|
||||
let mut list_cumul_temp = list_cumul.clone();
|
||||
|
||||
let active_set_probability_previous = active_set_probability.clone();
|
||||
|
||||
// Select the active nodes for the epoch (hour)
|
||||
while sample_active_mixnodes.len() < active_set_size
|
||||
&& sample_active_mixnodes.len() < list_cumul_temp.len()
|
||||
{
|
||||
let candidate = sample_candidate(&list_cumul_temp, rng)?;
|
||||
|
||||
if !sample_active_mixnodes.contains(&candidate) {
|
||||
sample_active_mixnodes.push(candidate);
|
||||
remove_mixnode_from_cumul_stake(candidate, &mut list_cumul_temp);
|
||||
}
|
||||
}
|
||||
|
||||
// Select the reserve nodes for the epoch (hour)
|
||||
while sample_reserve_mixnodes.len() < reserve_set_size
|
||||
&& sample_reserve_mixnodes.len() + sample_active_mixnodes.len() < list_cumul_temp.len()
|
||||
{
|
||||
let candidate = sample_candidate(&list_cumul_temp, rng)?;
|
||||
|
||||
if !sample_reserve_mixnodes.contains(&candidate)
|
||||
&& !sample_active_mixnodes.contains(&candidate)
|
||||
{
|
||||
sample_reserve_mixnodes.push(candidate);
|
||||
remove_mixnode_from_cumul_stake(candidate, &mut list_cumul_temp);
|
||||
}
|
||||
}
|
||||
|
||||
// Sum up nodes being in active or reserve set
|
||||
for active_mixnodes in sample_active_mixnodes {
|
||||
active_set_probability[active_mixnodes] += 1.0;
|
||||
}
|
||||
for reserve_mixnodes in sample_reserve_mixnodes {
|
||||
reserve_set_probability[reserve_mixnodes] += 1.0;
|
||||
}
|
||||
|
||||
// Convergence critera only on active set.
|
||||
// We devide by samples to get the average, that is not really part of the delta
|
||||
// computation.
|
||||
delta_l2 =
|
||||
l2_diff(&active_set_probability, &active_set_probability_previous)? / (samples as f64);
|
||||
delta_max =
|
||||
max_diff(&active_set_probability, &active_set_probability_previous)? / (samples as f64);
|
||||
if samples > 10 && delta_l2 < TOLERANCE_L2_NORM && delta_max < TOLERANCE_MAX_NORM
|
||||
|| samples >= max_samples
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop if we run out of time
|
||||
if start_time.elapsed() > max_time {
|
||||
log::debug!("Simulation ran out of time, stopping");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Divide occurrences with the number of samples once we're done to get the probabilities.
|
||||
active_set_probability
|
||||
.iter_mut()
|
||||
.for_each(|x| *x /= samples as f64);
|
||||
reserve_set_probability
|
||||
.iter_mut()
|
||||
.for_each(|x| *x /= samples as f64);
|
||||
|
||||
// Some sanity checks of the output
|
||||
if active_set_probability.len() != num_mixnodes || reserve_set_probability.len() != num_mixnodes
|
||||
{
|
||||
return Err(Error::ResultsShorterThanInput);
|
||||
}
|
||||
|
||||
Ok(SelectionProbability {
|
||||
active_set_probability,
|
||||
reserve_set_probability,
|
||||
samples,
|
||||
time: start_time.elapsed(),
|
||||
delta_l2,
|
||||
delta_max,
|
||||
})
|
||||
}
|
||||
|
||||
// Compute the cumulative sum
|
||||
fn cumul_sum<'a>(list: impl IntoIterator<Item = &'a u128>) -> Vec<u128> {
|
||||
let mut list_cumul = Vec::new();
|
||||
let mut cumul = 0;
|
||||
for entry in list {
|
||||
cumul += entry;
|
||||
list_cumul.push(cumul);
|
||||
}
|
||||
list_cumul
|
||||
}
|
||||
|
||||
fn sample_candidate<R>(list_cumul: &[u128], rng: &mut R) -> Result<usize, Error>
|
||||
where
|
||||
R: Rng + ?Sized,
|
||||
{
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
let uniform = Uniform::from(0..*list_cumul.last().ok_or(Error::EmptyListCumulStake)?);
|
||||
let r = uniform.sample(rng);
|
||||
|
||||
let candidate = list_cumul
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, x)| *x >= &r)
|
||||
.ok_or(Error::SamplePointOutOfBounds)?
|
||||
.0;
|
||||
|
||||
Ok(candidate)
|
||||
}
|
||||
|
||||
// Update list of cumulative stake to reflect eliminating the picked node
|
||||
fn remove_mixnode_from_cumul_stake(candidate: usize, list_cumul_stake: &mut [u128]) {
|
||||
let prob_candidate = if candidate == 0 {
|
||||
list_cumul_stake[0]
|
||||
} else {
|
||||
list_cumul_stake[candidate] - list_cumul_stake[candidate - 1]
|
||||
};
|
||||
|
||||
for cumul in list_cumul_stake.iter_mut().skip(candidate) {
|
||||
*cumul -= prob_candidate;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the difference in l2-norm
|
||||
fn l2_diff(v1: &[f64], v2: &[f64]) -> Result<f64, Error> {
|
||||
if v1.len() != v2.len() {
|
||||
return Err(Error::NormDifferenceSizeArrays);
|
||||
}
|
||||
Ok(v1
|
||||
.iter()
|
||||
.zip(v2)
|
||||
.map(|(&i1, &i2)| (i1 - i2).powi(2))
|
||||
.sum::<f64>()
|
||||
.sqrt())
|
||||
}
|
||||
|
||||
// Compute the difference in max-norm
|
||||
fn max_diff(v1: &[f64], v2: &[f64]) -> Result<f64, Error> {
|
||||
if v1.len() != v2.len() {
|
||||
return Err(Error::NormDifferenceSizeArrays);
|
||||
}
|
||||
Ok(v1
|
||||
.iter()
|
||||
.zip(v2)
|
||||
.map(|(x, y)| (x - y).abs())
|
||||
.fold(f64::NEG_INFINITY, f64::max))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn test_rng() -> StdRng {
|
||||
StdRng::seed_from_u64(42)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compute_cumul_sum() {
|
||||
let v = cumul_sum(&vec![1, 2, 3]);
|
||||
assert_eq!(v, &[1, 3, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_mixnode_from_cumul() {
|
||||
let mut cumul_stake = vec![1, 2, 3, 4, 5, 6];
|
||||
remove_mixnode_from_cumul_stake(3, &mut cumul_stake);
|
||||
assert_eq!(cumul_stake, &[1, 2, 3, 3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_norm() {
|
||||
let v1 = vec![1.0, 2.0, 3.0];
|
||||
let v2 = vec![2.0, 4.0, -6.0];
|
||||
assert!((max_diff(&v1, &v2).unwrap() - 9.0).abs() < f64::EPSILON);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ls_norm() {
|
||||
let v1 = vec![1.0, 2.0, 3.0];
|
||||
let v2 = vec![2.0, 3.0, -2.0];
|
||||
assert!((l2_diff(&v1, &v2).unwrap() - 5.196_152_422_706_632).abs() < 1e2 * f64::EPSILON);
|
||||
}
|
||||
|
||||
// Replicate the results from the Python simulation code in https://github.com/nymtech/team-core/issues/114
|
||||
#[test]
|
||||
fn replicate_python_simulation() {
|
||||
let active_set_size = 4;
|
||||
let standby_set_size = 1;
|
||||
|
||||
// this has to contain the total stake per node
|
||||
let list_mix = vec![
|
||||
100, 100, 3000, 500_000, 100, 10, 10, 10, 10, 10, 30000, 500, 200, 52345,
|
||||
];
|
||||
|
||||
let max_samples = 100_000;
|
||||
let max_time = Duration::from_secs(10);
|
||||
let mut rng = test_rng();
|
||||
|
||||
let SelectionProbability {
|
||||
active_set_probability,
|
||||
reserve_set_probability,
|
||||
samples,
|
||||
time,
|
||||
delta_l2,
|
||||
delta_max,
|
||||
} = simulate_selection_probability_mixnodes(
|
||||
&list_mix,
|
||||
active_set_size,
|
||||
standby_set_size,
|
||||
max_samples,
|
||||
max_time,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Check that any possible test failure wasn't because we ran it on 1970s hardware, and the
|
||||
// sampling aborted prematurely due to hitting `max_time`.
|
||||
assert!(time < max_time);
|
||||
|
||||
// These values comes from running the python simulator for a very long time
|
||||
let expected_active_set_probability = vec![
|
||||
0.025_070_8,
|
||||
0.025_073_2,
|
||||
0.744_117,
|
||||
0.999_999,
|
||||
0.025_000_2,
|
||||
0.002_524_4,
|
||||
0.002_527_8,
|
||||
0.002_528_6,
|
||||
0.002_569_6,
|
||||
0.002_513_6,
|
||||
0.994,
|
||||
0.125_482_8,
|
||||
0.050_279_8,
|
||||
0.998_313_2,
|
||||
];
|
||||
// The same check is used in the convergence criterion, and hence should be reflected in
|
||||
// `delta_max` too.
|
||||
assert!(
|
||||
max_diff(&active_set_probability, &expected_active_set_probability).unwrap() < 1e-2
|
||||
);
|
||||
|
||||
let expected_reserve_set_probability = vec![
|
||||
0.076_392_4,
|
||||
0.076_499,
|
||||
0.204_893_6,
|
||||
1e-06,
|
||||
0.076_278_8,
|
||||
0.007_720_6,
|
||||
0.007_673,
|
||||
0.007_700_2,
|
||||
0.007_669_4,
|
||||
0.007_731_2,
|
||||
0.005_789_4,
|
||||
0.368_465_6,
|
||||
0.151_537_2,
|
||||
0.001_648_6,
|
||||
];
|
||||
assert!(
|
||||
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap() < 1e-2
|
||||
);
|
||||
|
||||
// We converge around 20_000, add another 500 for some slack due to random values
|
||||
assert_eq!(samples, 20_001);
|
||||
assert!(delta_l2 < TOLERANCE_L2_NORM);
|
||||
assert!(delta_max < TOLERANCE_MAX_NORM);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fewer_nodes_than_active_set_size() {
|
||||
let active_set_size = 10;
|
||||
let standby_set_size = 3;
|
||||
let list_mix = vec![100, 100, 3000];
|
||||
let max_samples = 100_000;
|
||||
let max_time = Duration::from_secs(10);
|
||||
let mut rng = test_rng();
|
||||
|
||||
let SelectionProbability {
|
||||
active_set_probability,
|
||||
reserve_set_probability,
|
||||
samples,
|
||||
time: _,
|
||||
delta_l2,
|
||||
delta_max,
|
||||
} = simulate_selection_probability_mixnodes(
|
||||
&list_mix,
|
||||
active_set_size,
|
||||
standby_set_size,
|
||||
max_samples,
|
||||
max_time,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// These values comes from running the python simulator for a very long time
|
||||
let expected_active_set_probability = vec![1.0, 1.0, 1.0];
|
||||
let expected_reserve_set_probability = vec![0.0, 0.0, 0.0];
|
||||
assert!(
|
||||
max_diff(&active_set_probability, &expected_active_set_probability).unwrap()
|
||||
< 1e1 * f64::EPSILON
|
||||
);
|
||||
assert!(
|
||||
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap()
|
||||
< 1e1 * f64::EPSILON
|
||||
);
|
||||
|
||||
// We converge around 20_000, add another 500 for some slack due to random values
|
||||
assert_eq!(samples, 0);
|
||||
assert!(delta_l2 < f64::EPSILON);
|
||||
assert!(delta_max < f64::EPSILON);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fewer_nodes_than_reward_set_size() {
|
||||
let active_set_size = 4;
|
||||
let standby_set_size = 3;
|
||||
let list_mix = vec![100, 100, 3000, 342, 3_498_234];
|
||||
let max_samples = 100_000_000;
|
||||
let max_time = Duration::from_secs(10);
|
||||
let mut rng = test_rng();
|
||||
|
||||
let SelectionProbability {
|
||||
active_set_probability,
|
||||
reserve_set_probability,
|
||||
samples,
|
||||
time: _,
|
||||
delta_l2,
|
||||
delta_max,
|
||||
} = simulate_selection_probability_mixnodes(
|
||||
&list_mix,
|
||||
active_set_size,
|
||||
standby_set_size,
|
||||
max_samples,
|
||||
max_time,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// These values comes from running the python simulator for a very long time
|
||||
let expected_active_set_probability = vec![0.546, 0.538, 0.999, 0.915, 1.0];
|
||||
let expected_reserve_set_probability = vec![0.453, 0.461, 0.0005, 0.084, 0.0];
|
||||
assert!(
|
||||
max_diff(&active_set_probability, &expected_active_set_probability).unwrap() < 1e-2,
|
||||
);
|
||||
assert!(
|
||||
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap() < 1e-2,
|
||||
);
|
||||
|
||||
// We converge around 20_000, add another 500 for some slack due to random values
|
||||
assert_eq!(samples, 20_001);
|
||||
assert!(delta_l2 < TOLERANCE_L2_NORM);
|
||||
assert!(delta_max < TOLERANCE_MAX_NORM);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://127.0.0.1:8090";
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
|
||||
pub const NYMD_VALIDATOR: &str = "https://rpc.nyx.nodes.guru/";
|
||||
pub const API_VALIDATOR: &str = "https://validator.nymtech.net/api/";
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
|
||||
@@ -42,7 +42,7 @@ pub enum CoconutError {
|
||||
)]
|
||||
DeserializationMinLength { min: usize, actual: usize },
|
||||
|
||||
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
|
||||
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {object} or {modulus_target} % {modulus} == 0")]
|
||||
DeserializationInvalidLength {
|
||||
actual: usize,
|
||||
target: usize,
|
||||
|
||||
@@ -213,7 +213,7 @@ where
|
||||
/// - compute vk_b = g^x || v_b
|
||||
/// - compute sphinx_plaintext = SURB_ACK || g^x || v_b
|
||||
/// - compute sphinx_packet = Sphinx(recipient, sphinx_plaintext)
|
||||
pub async fn prepare_chunk_for_sending(
|
||||
pub fn prepare_chunk_for_sending(
|
||||
&mut self,
|
||||
fragment: Fragment,
|
||||
topology: &NymTopology,
|
||||
@@ -222,8 +222,7 @@ where
|
||||
) -> Result<PreparedFragment, NymTopologyError> {
|
||||
// create an ack
|
||||
let (ack_delay, surb_ack_bytes) = self
|
||||
.generate_surb_ack(fragment.fragment_identifier(), topology, ack_key)
|
||||
.await?
|
||||
.generate_surb_ack(fragment.fragment_identifier(), topology, ack_key)?
|
||||
.prepare_for_sending();
|
||||
|
||||
// TODO:
|
||||
@@ -294,7 +293,7 @@ where
|
||||
}
|
||||
|
||||
/// Construct an acknowledgement SURB for the given [`FragmentIdentifier`]
|
||||
async fn generate_surb_ack(
|
||||
fn generate_surb_ack(
|
||||
&mut self,
|
||||
fragment_id: FragmentIdentifier,
|
||||
topology: &NymTopology,
|
||||
@@ -357,8 +356,7 @@ where
|
||||
// gateways could not distinguish reply packets from normal messages due to lack of said acks
|
||||
// note: the ack delay is irrelevant since we do not know the delay of actual surb
|
||||
let (_, surb_ack_bytes) = self
|
||||
.generate_surb_ack(reply_id, topology, ack_key)
|
||||
.await?
|
||||
.generate_surb_ack(reply_id, topology, ack_key)?
|
||||
.prepare_for_sending();
|
||||
|
||||
let zero_pad_len = self.packet_size.plaintext_size()
|
||||
|
||||
@@ -9,3 +9,4 @@ edition = "2021"
|
||||
[dependencies]
|
||||
nymsphinx-addressing = { path = "../../../common/nymsphinx/addressing" }
|
||||
ordered-buffer = {path = "../ordered-buffer"}
|
||||
thiserror = "1"
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod msg;
|
||||
pub mod network_requester_response;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
pub use msg::*;
|
||||
pub use network_requester_response::*;
|
||||
pub use request::*;
|
||||
pub use response::*;
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::network_requester_response::{Error as NrError, NetworkRequesterResponse};
|
||||
use crate::request::{Request, RequestError};
|
||||
use crate::response::{Response, ResponseError};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MessageError {
|
||||
#[error("{0}")]
|
||||
Request(RequestError),
|
||||
Response(ResponseError),
|
||||
NoData,
|
||||
UnknownMessageType,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
MessageError::Request(r) => write!(f, "{}", r),
|
||||
MessageError::Response(r) => write!(f, "{:?}", r),
|
||||
MessageError::NoData => write!(f, "no data provided"),
|
||||
MessageError::UnknownMessageType => write!(f, "unknown message type received"),
|
||||
}
|
||||
}
|
||||
#[error("{0:?}")]
|
||||
Response(ResponseError),
|
||||
|
||||
#[error("{0}")]
|
||||
NetworkRequesterResponseError(NrError),
|
||||
|
||||
#[error("no data")]
|
||||
NoData,
|
||||
|
||||
#[error("unknown message type received")]
|
||||
UnknownMessageType,
|
||||
}
|
||||
|
||||
pub enum Message {
|
||||
Request(Request),
|
||||
Response(Response),
|
||||
NetworkRequesterResponse(NetworkRequesterResponse),
|
||||
}
|
||||
|
||||
impl Message {
|
||||
const REQUEST_FLAG: u8 = 0;
|
||||
const RESPONSE_FLAG: u8 = 1;
|
||||
const NR_RESPONSE_FLAG: u8 = 2;
|
||||
|
||||
pub fn conn_id(&self) -> u64 {
|
||||
match self {
|
||||
@@ -39,6 +43,7 @@ impl Message {
|
||||
Request::Send(conn_id, _, _) => *conn_id,
|
||||
},
|
||||
Message::Response(resp) => resp.connection_id,
|
||||
Message::NetworkRequesterResponse(resp) => resp.connection_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +54,7 @@ impl Message {
|
||||
Request::Send(_, data, _) => data.len(),
|
||||
},
|
||||
Message::Response(resp) => resp.data.len(),
|
||||
Message::NetworkRequesterResponse(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +71,10 @@ impl Message {
|
||||
Response::try_from_bytes(&b[1..])
|
||||
.map(Message::Response)
|
||||
.map_err(MessageError::Response)
|
||||
} else if b[0] == Self::NR_RESPONSE_FLAG {
|
||||
NetworkRequesterResponse::try_from_bytes(&b[1..])
|
||||
.map(Message::NetworkRequesterResponse)
|
||||
.map_err(MessageError::NetworkRequesterResponseError)
|
||||
} else {
|
||||
Err(MessageError::UnknownMessageType)
|
||||
}
|
||||
@@ -78,6 +88,9 @@ impl Message {
|
||||
Self::Response(r) => std::iter::once(Self::RESPONSE_FLAG)
|
||||
.chain(r.into_bytes().iter().cloned())
|
||||
.collect(),
|
||||
Self::NetworkRequesterResponse(r) => std::iter::once(Self::NR_RESPONSE_FLAG)
|
||||
.chain(r.into_bytes().iter().cloned())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ConnectionId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NetworkRequesterResponse {
|
||||
pub connection_id: ConnectionId,
|
||||
pub network_requester_error: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("no data provided")]
|
||||
NoData,
|
||||
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
|
||||
#[error("message is not utf8 encoded")]
|
||||
MalformedErrorMessage(#[from] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
impl NetworkRequesterResponse {
|
||||
pub fn new(connection_id: ConnectionId, network_requester_error: String) -> Self {
|
||||
NetworkRequesterResponse {
|
||||
connection_id,
|
||||
network_requester_error,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<NetworkRequesterResponse, Error> {
|
||||
if b.is_empty() {
|
||||
return Err(Error::NoData);
|
||||
}
|
||||
|
||||
if b.len() < 8 {
|
||||
return Err(Error::ConnectionIdTooShort);
|
||||
}
|
||||
|
||||
let mut connection_id_bytes = b.to_vec();
|
||||
let network_requester_error_bytes = connection_id_bytes.split_off(8);
|
||||
|
||||
let connection_id = u64::from_be_bytes([
|
||||
connection_id_bytes[0],
|
||||
connection_id_bytes[1],
|
||||
connection_id_bytes[2],
|
||||
connection_id_bytes[3],
|
||||
connection_id_bytes[4],
|
||||
connection_id_bytes[5],
|
||||
connection_id_bytes[6],
|
||||
connection_id_bytes[7],
|
||||
]);
|
||||
let network_requester_error = String::from_utf8(network_requester_error_bytes)?;
|
||||
|
||||
Ok(NetworkRequesterResponse {
|
||||
connection_id,
|
||||
network_requester_error,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.connection_id
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.network_requester_error.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod network_requester_response_serde_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_serde() {
|
||||
let conn_id = 42;
|
||||
let network_requester_error = String::from("This is a test msg");
|
||||
let response = NetworkRequesterResponse::new(conn_id, network_requester_error.clone());
|
||||
let bytes = response.into_bytes();
|
||||
let deserialized_response = NetworkRequesterResponse::try_from_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(conn_id, deserialized_response.connection_id);
|
||||
assert_eq!(
|
||||
network_requester_error,
|
||||
deserialized_response.network_requester_error
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_errors() {
|
||||
let err = NetworkRequesterResponse::try_from_bytes(&[]).err().unwrap();
|
||||
assert_eq!(err, Error::NoData);
|
||||
|
||||
let bytes: [u8; 5] = [1, 2, 3, 4, 5];
|
||||
let err = NetworkRequesterResponse::try_from_bytes(&bytes)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(err, Error::ConnectionIdTooShort);
|
||||
|
||||
let bytes: Vec<u8> = 42u64
|
||||
.to_be_bytes()
|
||||
.into_iter()
|
||||
.chain([0, 159, 146, 150].into_iter())
|
||||
.collect();
|
||||
let err = NetworkRequesterResponse::try_from_bytes(&bytes)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(matches!(err, Error::MalformedErrorMessage(_)));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nymsphinx_addressing::clients::{Recipient, RecipientFormattingError};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type ConnectionId = u64;
|
||||
pub type RemoteAddress = String;
|
||||
@@ -12,39 +15,30 @@ pub enum RequestFlag {
|
||||
Send = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RequestError {
|
||||
#[error("not enough bytes to recover the length of the address")]
|
||||
AddressLengthTooShort,
|
||||
|
||||
#[error("not enough bytes to recover the address")]
|
||||
AddressTooShort,
|
||||
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
|
||||
#[error("no data provided")]
|
||||
NoData,
|
||||
|
||||
#[error("request of unknown type")]
|
||||
UnknownRequestFlag,
|
||||
|
||||
#[error("too short return address")]
|
||||
ReturnAddressTooShort,
|
||||
|
||||
#[error("malformed return address - {0}")]
|
||||
MalformedReturnAddress(RecipientFormattingError),
|
||||
}
|
||||
|
||||
impl fmt::Display for RequestError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RequestError::AddressLengthTooShort => {
|
||||
write!(f, "not enough bytes to recover the length of the address")
|
||||
}
|
||||
RequestError::AddressTooShort => write!(f, "not enough bytes to recover the address"),
|
||||
RequestError::ConnectionIdTooShort => {
|
||||
write!(f, "not enough bytes to recover the connection id")
|
||||
}
|
||||
RequestError::NoData => write!(f, "no data provided"),
|
||||
RequestError::UnknownRequestFlag => write!(f, "request of unknown type"),
|
||||
RequestError::ReturnAddressTooShort => write!(f, "too short return address"),
|
||||
RequestError::MalformedReturnAddress(recipient_err) => {
|
||||
write!(f, "malformed return address - {}", recipient_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RequestError {}
|
||||
|
||||
impl RequestError {
|
||||
pub fn is_malformed_return(&self) -> bool {
|
||||
matches!(self, RequestError::MalformedReturnAddress(_))
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::ConnectionId;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum ResponseError {
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
#[error("no data provided")]
|
||||
NoData,
|
||||
}
|
||||
/// A remote network response retrieved by the Socks5 service provider. This
|
||||
|
||||
@@ -5,13 +5,14 @@ use std::time::Duration;
|
||||
|
||||
use tokio::sync::watch::{self, error::SendError};
|
||||
|
||||
const SHUTDOWN_TIMER_SECS: u64 = 5;
|
||||
const DEFAULT_SHUTDOWN_TIMER_SECS: u64 = 5;
|
||||
|
||||
/// Used to notify other tasks to gracefully shutdown
|
||||
#[derive(Debug)]
|
||||
pub struct ShutdownNotifier {
|
||||
notify_tx: watch::Sender<()>,
|
||||
notify_rx: Option<watch::Receiver<()>>,
|
||||
shutdown_timer_secs: u64,
|
||||
}
|
||||
|
||||
impl Default for ShutdownNotifier {
|
||||
@@ -20,11 +21,19 @@ impl Default for ShutdownNotifier {
|
||||
Self {
|
||||
notify_tx,
|
||||
notify_rx: Some(notify_rx),
|
||||
shutdown_timer_secs: DEFAULT_SHUTDOWN_TIMER_SECS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ShutdownNotifier {
|
||||
pub fn new(shutdown_timer_secs: u64) -> Self {
|
||||
Self {
|
||||
shutdown_timer_secs,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe(&self) -> ShutdownListener {
|
||||
ShutdownListener::new(
|
||||
self.notify_rx
|
||||
@@ -50,7 +59,7 @@ impl ShutdownNotifier {
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
log::info!("Forcing shutdown");
|
||||
}
|
||||
_ = tokio::time::sleep(Duration::from_secs(SHUTDOWN_TIMER_SECS)) => {
|
||||
_ = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs)) => {
|
||||
log::info!("Timout reached, forcing shutdown");
|
||||
},
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ impl Node {
|
||||
}
|
||||
|
||||
pub fn clients_address(&self) -> String {
|
||||
format!("ws://{}:{}", self.host, self.clients_port)
|
||||
format!("wss://{}:{}", self.host, self.clients_port)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -507,7 +507,7 @@ mod test {
|
||||
|
||||
for (expected, raw_display) in values {
|
||||
let coin = DecCoin {
|
||||
denom: Network::MAINNET.mix_denom().display.into(),
|
||||
denom: Network::MAINNET.mix_denom().display,
|
||||
amount: raw_display.parse().unwrap(),
|
||||
};
|
||||
let base = reg.attempt_convert_to_base_coin(coin).unwrap();
|
||||
|
||||
@@ -9,12 +9,12 @@ edition = "2021"
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
js-sys = "^0.3.51"
|
||||
wasm-bindgen = "=0.2.78"
|
||||
wasm-bindgen = "=0.2.83"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
|
||||
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13"
|
||||
version = "0.17.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.web-sys]
|
||||
|
||||
@@ -85,6 +85,11 @@ pub struct JSWebsocket {
|
||||
|
||||
impl JSWebsocket {
|
||||
pub fn new(url: &str) -> Result<Self, JsValue> {
|
||||
console_log!(
|
||||
"Attempting to establish wasm websocket connection to {}",
|
||||
url
|
||||
);
|
||||
|
||||
let ws = WebSocket::new(url)?;
|
||||
// we don't want to ever have to deal with blobs
|
||||
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- vesting-contract: added queries for delegation timestamps and paged query for all vesting delegations in the contract ([#1569])
|
||||
|
||||
### Changed
|
||||
|
||||
- mixnet-contract: compounding delegator rewards now happens instantaneously as opposed to having to wait for the current epoch to finish ([#1571])
|
||||
|
||||
### Fixed
|
||||
|
||||
- vesting-contract: the contract now correctly stores delegations with their timestamp as opposed to using block height ([#1544])
|
||||
- mixnet-contract: compounding delegator rewards is now possible even if the associated mixnode had already unbonded ([#1571])
|
||||
|
||||
[#1544]: https://github.com/nymtech/nym/pull/1544
|
||||
[#1569]: https://github.com/nymtech/nym/pull/1569
|
||||
[#1569]: https://github.com/nymtech/nym/pull/1571
|
||||
|
||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||
|
||||
### Added
|
||||
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- mixnet-contract: Added staking_supply field to ContractStateParams.
|
||||
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
|
||||
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
|
||||
|
||||
### Fixed
|
||||
|
||||
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
|
||||
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
|
||||
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
|
||||
|
||||
[#1255]: https://github.com/nymtech/nym/pull/1255
|
||||
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1331]: https://github.com/nymtech/nym/pull/1331
|
||||
[#1369]: https://github.com/nymtech/nym/pull/1369
|
||||
[#1373]: https://github.com/nymtech/nym/pull/1373
|
||||
@@ -18,7 +18,6 @@ pub fn migrate_config_from_env(
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct OldContractState {
|
||||
pub owner: Addr,
|
||||
pub mix_denom: String,
|
||||
pub rewarding_validator_address: Addr,
|
||||
pub params: ContractStateParams,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use super::storage::{
|
||||
use crate::constants;
|
||||
use crate::contract::debug_with_visibility;
|
||||
use crate::delegations::storage as delegations_storage;
|
||||
use crate::delegations::transactions::_try_delegate_to_mixnode;
|
||||
use crate::error::ContractError;
|
||||
use crate::mixnet_contract_settings::storage::mix_denom;
|
||||
use crate::mixnodes::storage::mixnodes;
|
||||
@@ -18,7 +17,7 @@ use crate::rewards::helpers;
|
||||
use crate::support::helpers::{is_authorized, operator_cost_at_epoch};
|
||||
use cosmwasm_std::{
|
||||
coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response,
|
||||
Storage, Uint128,
|
||||
StdResult, Storage, Uint128,
|
||||
};
|
||||
use cw_storage_plus::Bound;
|
||||
use mixnet_contract_common::events::{
|
||||
@@ -450,18 +449,15 @@ pub fn try_compound_delegator_reward(
|
||||
|
||||
pub fn _try_compound_delegator_reward(
|
||||
block_height: u64,
|
||||
mut deps: DepsMut<'_>,
|
||||
deps: DepsMut<'_>,
|
||||
owner_address: &str,
|
||||
mix_identity: &str,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
let delegation_map = crate::delegations::storage::delegations();
|
||||
let mix_denom = mix_denom(deps.storage)?;
|
||||
|
||||
let key = mixnet_contract_common::delegation::generate_storage_key(
|
||||
&deps.api.addr_validate(owner_address)?,
|
||||
proxy.as_ref(),
|
||||
);
|
||||
let owner = deps.api.addr_validate(owner_address)?;
|
||||
let key = mixnet_contract_common::delegation::generate_storage_key(&owner, proxy.as_ref());
|
||||
let reward = calculate_delegator_reward(deps.storage, deps.api, key.clone(), mix_identity)?;
|
||||
let mut total_delegation_delegate = Uint128::zero();
|
||||
|
||||
@@ -469,8 +465,7 @@ pub fn _try_compound_delegator_reward(
|
||||
let delegation_heights = delegation_map
|
||||
.prefix((mix_identity.to_string(), key.clone()))
|
||||
.keys(deps.storage, None, None, cosmwasm_std::Order::Ascending)
|
||||
.filter_map(|v| v.ok())
|
||||
.collect::<Vec<u64>>();
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
for h in delegation_heights {
|
||||
let delegation =
|
||||
@@ -494,30 +489,36 @@ pub fn _try_compound_delegator_reward(
|
||||
// since we know that the target node exists and because the total_delegation bucket
|
||||
// entry is created whenever the node itself is added, the unwrap here is fine
|
||||
// as the entry MUST exist
|
||||
Ok(total_delegation
|
||||
.unwrap()
|
||||
.saturating_sub(total_delegation_delegate))
|
||||
Ok(total_delegation.unwrap() + reward)
|
||||
},
|
||||
)?;
|
||||
|
||||
_try_delegate_to_mixnode(
|
||||
deps.branch(),
|
||||
block_height,
|
||||
mix_identity,
|
||||
owner_address,
|
||||
// let's simplify the entire procedure. Rather than creating a fresh delegation on the mixnode
|
||||
// via `_try_delegate_to_mixnode` and then waiting for reconcile to happen,
|
||||
// just save it directly to the storage right now.
|
||||
// my reasoning for that is simple: `_try_delegate_to_mixnode` could fail if the node the
|
||||
// delegator has delegated to no longer exists.
|
||||
let delegation = Delegation::new(
|
||||
owner,
|
||||
mix_identity.into(),
|
||||
Coin {
|
||||
amount: compounded_delegation,
|
||||
denom: mix_denom,
|
||||
},
|
||||
block_height,
|
||||
proxy,
|
||||
);
|
||||
|
||||
delegation_map.save(
|
||||
deps.storage,
|
||||
(mix_identity.into(), key.clone(), block_height),
|
||||
&delegation,
|
||||
)?;
|
||||
}
|
||||
|
||||
{
|
||||
if let Some(mut bond) = mixnodes().may_load(deps.storage, mix_identity)? {
|
||||
bond.accumulated_rewards = Some(bond.accumulated_rewards().saturating_sub(reward));
|
||||
mixnodes().save(deps.storage, mix_identity, &bond, block_height)?;
|
||||
}
|
||||
if let Some(mut bond) = mixnodes().may_load(deps.storage, mix_identity)? {
|
||||
bond.accumulated_rewards = Some(bond.accumulated_rewards().saturating_sub(reward));
|
||||
mixnodes().save(deps.storage, mix_identity, &bond, block_height)?;
|
||||
}
|
||||
|
||||
DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
use crate::errors::ContractError;
|
||||
use crate::queued_migrations::migrate_config_from_env;
|
||||
use crate::storage::{
|
||||
account_from_address, locked_pledge_cap, update_locked_pledge_cap, ADMIN,
|
||||
MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
|
||||
account_from_address, locked_pledge_cap, update_locked_pledge_cap, BlockTimestampSecs, ADMIN,
|
||||
DELEGATIONS, MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
|
||||
};
|
||||
use crate::traits::{
|
||||
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
|
||||
};
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use cosmwasm_std::{
|
||||
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
|
||||
Response, Timestamp, Uint128,
|
||||
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
|
||||
QueryResponse, Response, StdResult, Timestamp, Uint128,
|
||||
};
|
||||
use cw_storage_plus::Bound;
|
||||
use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
|
||||
use vesting_contract_common::events::{
|
||||
new_ownership_transfer_event, new_periodic_vesting_account_event,
|
||||
@@ -22,7 +23,10 @@ use vesting_contract_common::events::{
|
||||
use vesting_contract_common::messages::{
|
||||
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
|
||||
};
|
||||
use vesting_contract_common::{OriginalVestingResponse, Period, PledgeData};
|
||||
use vesting_contract_common::{
|
||||
AllDelegationsResponse, DelegationTimesResponse, OriginalVestingResponse, Period, PledgeData,
|
||||
VestingDelegation,
|
||||
};
|
||||
|
||||
pub const INITIAL_LOCKED_PLEDGE_CAP: Uint128 = Uint128::new(100_000_000_000);
|
||||
|
||||
@@ -518,6 +522,13 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
|
||||
QueryMsg::GetCurrentVestingPeriod { address } => {
|
||||
to_binary(&try_get_current_vesting_period(&address, deps, env)?)
|
||||
}
|
||||
QueryMsg::GetDelegationTimes {
|
||||
address,
|
||||
mix_identity,
|
||||
} => to_binary(&try_get_delegation_times(deps, &address, mix_identity)?),
|
||||
QueryMsg::GetAllDelegations { start_after, limit } => {
|
||||
to_binary(&try_get_all_delegations(deps, start_after, limit)?)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(query_res?)
|
||||
@@ -634,6 +645,63 @@ pub fn try_get_delegated_vesting(
|
||||
account.get_delegated_vesting(block_time, &env, deps.storage)
|
||||
}
|
||||
|
||||
pub fn try_get_delegation_times(
|
||||
deps: Deps<'_>,
|
||||
vesting_account_address: &str,
|
||||
mix_identity: String,
|
||||
) -> Result<DelegationTimesResponse, ContractError> {
|
||||
let owner = deps.api.addr_validate(vesting_account_address)?;
|
||||
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
|
||||
|
||||
let delegation_timestamps = DELEGATIONS
|
||||
.prefix((account.storage_key(), mix_identity.clone()))
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
Ok(DelegationTimesResponse {
|
||||
owner,
|
||||
account_id: account.storage_key(),
|
||||
mix_identity,
|
||||
delegation_timestamps,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_get_all_delegations(
|
||||
deps: Deps<'_>,
|
||||
start_after: Option<(u32, IdentityKey, BlockTimestampSecs)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, ContractError> {
|
||||
let limit = limit.unwrap_or(100).min(200) as usize;
|
||||
|
||||
let start = start_after.map(Bound::exclusive);
|
||||
let delegations = DELEGATIONS
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.map(|kv| {
|
||||
kv.map(
|
||||
|((account_id, mix_identity, block_timestamp), amount)| VestingDelegation {
|
||||
account_id,
|
||||
mix_identity,
|
||||
block_timestamp,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = if delegations.len() < limit {
|
||||
None
|
||||
} else {
|
||||
delegations
|
||||
.last()
|
||||
.map(|delegation| delegation.storage_key())
|
||||
};
|
||||
|
||||
Ok(AllDelegationsResponse {
|
||||
delegations,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_funds(funds: &[Coin], mix_denom: String) -> Result<Coin, ContractError> {
|
||||
if funds.is_empty() || funds[0].amount.is_zero() {
|
||||
return Err(ContractError::EmptyFunds);
|
||||
|
||||
@@ -5,7 +5,7 @@ use cw_storage_plus::{Item, Map};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use vesting_contract_common::PledgeData;
|
||||
|
||||
type BlockHeight = u64;
|
||||
pub(crate) type BlockTimestampSecs = u64;
|
||||
|
||||
pub const KEY: Item<'_, u32> = Item::new("key");
|
||||
const ACCOUNTS: Map<'_, String, Account> = Map::new("acc");
|
||||
@@ -14,7 +14,7 @@ const BALANCES: Map<'_, u32, Uint128> = Map::new("blc");
|
||||
const WITHDRAWNS: Map<'_, u32, Uint128> = Map::new("wthd");
|
||||
const BOND_PLEDGES: Map<'_, u32, PledgeData> = Map::new("bnd");
|
||||
const GATEWAY_PLEDGES: Map<'_, u32, PledgeData> = Map::new("gtw");
|
||||
pub const DELEGATIONS: Map<'_, (u32, IdentityKey, BlockHeight), Uint128> = Map::new("dlg");
|
||||
pub const DELEGATIONS: Map<'_, (u32, IdentityKey, BlockTimestampSecs), Uint128> = Map::new("dlg");
|
||||
pub const ADMIN: Item<'_, String> = Item::new("adm");
|
||||
pub const MIXNET_CONTRACT_ADDRESS: Item<'_, String> = Item::new("mix");
|
||||
pub const MIX_DENOM: Item<'_, String> = Item::new("den");
|
||||
@@ -35,7 +35,7 @@ pub fn update_locked_pledge_cap(
|
||||
}
|
||||
|
||||
pub fn save_delegation(
|
||||
key: (u32, IdentityKey, BlockHeight),
|
||||
key: (u32, IdentityKey, BlockTimestampSecs),
|
||||
amount: Uint128,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
@@ -44,7 +44,7 @@ pub fn save_delegation(
|
||||
}
|
||||
|
||||
pub fn remove_delegation(
|
||||
key: (u32, IdentityKey, BlockHeight),
|
||||
key: (u32, IdentityKey, BlockTimestampSecs),
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
DELEGATIONS.remove(storage, key);
|
||||
|
||||
@@ -88,7 +88,7 @@ impl DelegatingAccount for Account {
|
||||
vec![coin.clone()],
|
||||
)?;
|
||||
self.track_delegation(
|
||||
env.block.height,
|
||||
env.block.time.seconds(),
|
||||
mix_identity,
|
||||
current_balance,
|
||||
coin,
|
||||
@@ -129,14 +129,14 @@ impl DelegatingAccount for Account {
|
||||
|
||||
fn track_delegation(
|
||||
&self,
|
||||
block_height: u64,
|
||||
block_timestamp_secs: u64,
|
||||
mix_identity: IdentityKey,
|
||||
current_balance: Uint128,
|
||||
delegation: Coin,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
save_delegation(
|
||||
(self.storage_key(), mix_identity, block_height),
|
||||
(self.storage_key(), mix_identity, block_timestamp_secs),
|
||||
delegation.amount,
|
||||
storage,
|
||||
)?;
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::errors::ContractError;
|
||||
use crate::storage::{
|
||||
load_balance, load_bond_pledge, load_gateway_pledge, load_withdrawn, remove_bond_pledge,
|
||||
remove_delegation, remove_gateway_pledge, save_account, save_balance, save_bond_pledge,
|
||||
save_gateway_pledge, save_withdrawn, DELEGATIONS, KEY,
|
||||
save_gateway_pledge, save_withdrawn, BlockTimestampSecs, DELEGATIONS, KEY,
|
||||
};
|
||||
use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128};
|
||||
use cw_storage_plus::Bound;
|
||||
@@ -101,10 +101,9 @@ impl Account {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
|
||||
/// In case vesting is over it will always return NUM_VESTING_PERIODS.
|
||||
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
|
||||
// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
|
||||
// In case vesting is over it will always return NUM_VESTING_PERIODS.
|
||||
|
||||
if block_time.seconds() < self.periods.first().unwrap().start_time {
|
||||
Period::Before
|
||||
} else if self.periods.last().unwrap().end_time() < block_time {
|
||||
@@ -262,4 +261,19 @@ impl Account {
|
||||
.filter_map(|x| x.ok())
|
||||
.fold(Uint128::zero(), |acc, (_key, val)| acc + val))
|
||||
}
|
||||
|
||||
pub fn total_delegations_at_timestamp(
|
||||
&self,
|
||||
storage: &dyn Storage,
|
||||
start_time: BlockTimestampSecs,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
Ok(DELEGATIONS
|
||||
.sub_prefix(self.storage_key())
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.filter_map(|x| x.ok())
|
||||
.filter(|((_mix, block_time), _amount)| *block_time <= start_time)
|
||||
.fold(Uint128::zero(), |acc, ((_mix, _block_time), amount)| {
|
||||
acc + amount
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::{delete_account, save_account, DELEGATIONS, MIX_DENOM};
|
||||
use crate::storage::{delete_account, save_account, MIX_DENOM};
|
||||
use crate::traits::VestingAccount;
|
||||
use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128};
|
||||
use cosmwasm_std::{Addr, Coin, Env, Storage, Timestamp, Uint128};
|
||||
use vesting_contract_common::{OriginalVestingResponse, Period};
|
||||
|
||||
use super::Account;
|
||||
@@ -16,13 +16,6 @@ impl VestingAccount for Account {
|
||||
+ self.get_pledged_vesting(None, env, storage)?.amount)
|
||||
}
|
||||
|
||||
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
let current_balance = self.load_balance(storage)?;
|
||||
let new_balance = current_balance + amount.amount;
|
||||
self.save_balance(new_balance, storage)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn locked_coins(
|
||||
&self,
|
||||
block_time: Option<Timestamp>,
|
||||
@@ -141,14 +134,7 @@ impl VestingAccount for Account {
|
||||
Period::In(idx) => self.periods[idx as usize].start_time,
|
||||
};
|
||||
|
||||
let coin = DELEGATIONS
|
||||
.sub_prefix(self.storage_key())
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.filter_map(|x| x.ok())
|
||||
.filter(|((_mix, block_time), _amount)| *block_time < start_time)
|
||||
.fold(Uint128::zero(), |acc, ((_mix, _block_time), amount)| {
|
||||
acc + amount
|
||||
});
|
||||
let coin = self.total_delegations_at_timestamp(storage, start_time)?;
|
||||
|
||||
let amount = Uint128::new(coin.u128().min(max_available.u128()));
|
||||
|
||||
@@ -158,6 +144,7 @@ impl VestingAccount for Account {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: why do we allow querying for block times in the past? - just use env.block.time all the time
|
||||
fn get_delegated_vesting(
|
||||
&self,
|
||||
block_time: Option<Timestamp>,
|
||||
@@ -166,9 +153,18 @@ impl VestingAccount for Account {
|
||||
) -> Result<Coin, ContractError> {
|
||||
let block_time = block_time.unwrap_or(env.block.time);
|
||||
let delegated_free = self.get_delegated_free(Some(block_time), env, storage)?;
|
||||
let total_delegations = self.total_delegations(storage)?;
|
||||
|
||||
let amount = total_delegations - delegated_free.amount;
|
||||
let period = self.get_current_vesting_period(block_time);
|
||||
let start_time = match period {
|
||||
Period::Before => 0,
|
||||
Period::After => u64::MAX,
|
||||
Period::In(idx) => self.periods[idx as usize].start_time,
|
||||
};
|
||||
|
||||
let delegations_before_start_time =
|
||||
self.total_delegations_at_timestamp(storage, start_time)?;
|
||||
|
||||
let amount = delegations_before_start_time - delegated_free.amount;
|
||||
|
||||
Ok(Coin {
|
||||
amount,
|
||||
@@ -261,4 +257,11 @@ impl VestingAccount for Account {
|
||||
save_account(self, storage)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
let current_balance = self.load_balance(storage)?;
|
||||
let new_balance = current_balance + amount.amount;
|
||||
self.save_balance(new_balance, storage)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,11 @@ mod tests {
|
||||
use crate::traits::DelegatingAccount;
|
||||
use crate::traits::VestingAccount;
|
||||
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128};
|
||||
use mixnet_contract_common::{Gateway, MixNode};
|
||||
use vesting_contract_common::messages::ExecuteMsg;
|
||||
use vesting_contract_common::messages::{ExecuteMsg, VestingSpecification};
|
||||
use vesting_contract_common::Period;
|
||||
|
||||
#[test]
|
||||
@@ -757,4 +758,171 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(Uint128::zero(), bonded_vesting.amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delegated_free() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
|
||||
let vesting_period_length_secs = 3600;
|
||||
|
||||
let account_creation_timestamp = 1650000000;
|
||||
let account_creation_blockheight = 12345;
|
||||
|
||||
// this value is completely arbitrary, I just wanted to keep consistent
|
||||
// (and make sure that if block timestamp increases so does the block height)
|
||||
let blocks_per_period = 100;
|
||||
|
||||
env.block.height = account_creation_blockheight;
|
||||
env.block.time = Timestamp::from_seconds(account_creation_timestamp);
|
||||
|
||||
// lets define some helper timestamps
|
||||
|
||||
// our account is set to be created after 2 vesting periods already passed
|
||||
let vesting_start_blockheight = account_creation_blockheight - 2 * blocks_per_period;
|
||||
let vesting_start_timestamp = account_creation_timestamp - 2 * vesting_period_length_secs;
|
||||
|
||||
let vesting_period2_start_blockheight = vesting_start_blockheight + blocks_per_period;
|
||||
let vesting_period2_start_timestamp = vesting_start_timestamp + vesting_period_length_secs;
|
||||
|
||||
// this vesting period is currently in progress!
|
||||
let vesting_period3_start_blockheight =
|
||||
vesting_period2_start_blockheight + blocks_per_period;
|
||||
let vesting_period3_start_timestamp =
|
||||
vesting_period2_start_timestamp + vesting_period_length_secs;
|
||||
|
||||
// and this one is in the future! (in relation to account creation)
|
||||
let vesting_period4_start_blockheight =
|
||||
vesting_period3_start_blockheight + blocks_per_period;
|
||||
let vesting_period4_start_timestamp =
|
||||
vesting_period3_start_timestamp + vesting_period_length_secs;
|
||||
|
||||
// lets create our vesting account
|
||||
let periods = populate_vesting_periods(
|
||||
vesting_start_timestamp,
|
||||
VestingSpecification::new(None, Some(vesting_period_length_secs), None),
|
||||
);
|
||||
|
||||
let vesting_account = Account::new(
|
||||
Addr::unchecked("owner"),
|
||||
Some(Addr::unchecked("staking")),
|
||||
Coin {
|
||||
amount: Uint128::new(1_000_000_000_000),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
},
|
||||
Timestamp::from_seconds(account_creation_timestamp),
|
||||
periods,
|
||||
deps.as_mut().storage,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// time for some delegations
|
||||
|
||||
let mix_identity = "alice".to_string();
|
||||
|
||||
let delegation = Coin {
|
||||
amount: Uint128::new(90_000_000_000),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
};
|
||||
|
||||
// delegate explicitly at the time the account was created
|
||||
// (i.e. after 2 vesting periods already elapsed)
|
||||
env.block.height = account_creation_blockheight;
|
||||
env.block.time = Timestamp::from_seconds(account_creation_timestamp);
|
||||
let ok = vesting_account.try_delegate_to_mixnode(
|
||||
mix_identity.clone(),
|
||||
delegation.clone(),
|
||||
&env,
|
||||
&mut deps.storage,
|
||||
);
|
||||
assert!(ok.is_ok());
|
||||
|
||||
let vested_coins = vesting_account
|
||||
.get_vested_coins(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
let vesting_coins = vesting_account
|
||||
.get_vesting_coins(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000));
|
||||
assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000));
|
||||
let delegated_free = vesting_account
|
||||
.get_delegated_free(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
let delegated_vesting = vesting_account
|
||||
.get_delegated_vesting(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
|
||||
// all good so far
|
||||
assert_eq!(delegated_free.amount, Uint128::new(90_000_000_000));
|
||||
assert_eq!(delegated_vesting.amount, Uint128::zero());
|
||||
|
||||
// some time passes, and we're now into the next vesting period, more of our coins got unlocked!
|
||||
env.block.height = vesting_period4_start_blockheight;
|
||||
env.block.time = Timestamp::from_seconds(vesting_period4_start_timestamp);
|
||||
|
||||
let vested_coins = vesting_account
|
||||
.get_vested_coins(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
let vesting_coins = vesting_account
|
||||
.get_vesting_coins(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
assert_eq!(vested_coins.amount, Uint128::new(375_000_000_000));
|
||||
assert_eq!(vesting_coins.amount, Uint128::new(625_000_000_000));
|
||||
|
||||
// and nothing about our existing delegation changed
|
||||
let delegated_free = vesting_account
|
||||
.get_delegated_free(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
let delegated_vesting = vesting_account
|
||||
.get_delegated_vesting(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
assert_eq!(delegated_free.amount, Uint128::new(90_000_000_000));
|
||||
assert_eq!(delegated_vesting.amount, Uint128::zero());
|
||||
|
||||
// however, create a new delegation now in this brand new vesting period
|
||||
let delegation = Coin {
|
||||
amount: Uint128::new(50_000_000_000),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
};
|
||||
let ok = vesting_account.try_delegate_to_mixnode(
|
||||
mix_identity.clone(),
|
||||
delegation.clone(),
|
||||
&env,
|
||||
&mut deps.storage,
|
||||
);
|
||||
assert!(ok.is_ok());
|
||||
|
||||
// we're still good here, we have delegated in total 140M from our vested tokens!
|
||||
let delegated_free = vesting_account
|
||||
.get_delegated_free(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
let delegated_vesting = vesting_account
|
||||
.get_delegated_vesting(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
assert_eq!(delegated_free.amount, Uint128::new(140_000_000_000));
|
||||
assert_eq!(delegated_vesting.amount, Uint128::zero());
|
||||
|
||||
// but let's ask now a different question:
|
||||
// how many vested tokens have I had delegated during vesting period3? (i.e. after account creation)
|
||||
let delegated_free = vesting_account
|
||||
.get_delegated_free(
|
||||
Some(Timestamp::from_seconds(vesting_period3_start_timestamp)),
|
||||
&env,
|
||||
&deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
let delegated_vesting = vesting_account
|
||||
.get_delegated_vesting(
|
||||
Some(Timestamp::from_seconds(vesting_period3_start_timestamp)),
|
||||
&env,
|
||||
&deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// returns 90M as the 50M delegation didn't exist at this point of time
|
||||
assert_eq!(delegated_free.amount, Uint128::new(90_000_000_000));
|
||||
|
||||
// the 50M delegation wasn't a thing here for VESTING tokens either
|
||||
assert_eq!(delegated_vesting.amount, Uint128::zero());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,6 +15,6 @@ BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090"
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
||||
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
|
||||
API_VALIDATOR="https://validator.nymtech.net/api/"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
EXPLORER_API_URL=https://explorer.nymtech.net/api/v1
|
||||
VALIDATOR_API_URL=https://validator.nymtech.net
|
||||
VALIDATOR_URL=https://rpc.nyx.nodes.guru
|
||||
BIG_DIPPER_URL=https://blocks.nymtech.net
|
||||
CURRENCY_DENOM=unym
|
||||
CURRENCY_STAKING_DENOM=unyx
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
EXPLORER_API_URL=https://qa-explorer.nymtech.net/api/v1
|
||||
VALIDATOR_API_URL=https://qa-validator.nymtech.net
|
||||
VALIDATOR_API_URL=https://qa-validator-api.nymtech.net
|
||||
VALIDATOR_URL=https://qa-validator.nymtech.net
|
||||
BIG_DIPPER_URL=https://qa-blocks.nymtech.net
|
||||
CURRENCY_DENOM=unymt
|
||||
CURRENCY_STAKING_DENOM=unyxt
|
||||
CURRENCY_DENOM=unym
|
||||
CURRENCY_STAKING_DENOM=unyx
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// master APIs
|
||||
export const API_BASE_URL = process.env.EXPLORER_API_URL;
|
||||
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
|
||||
export const VALIDATOR_URL = process.env.VALIDATOR_URL;
|
||||
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
|
||||
|
||||
// specific API routes
|
||||
@@ -9,7 +10,7 @@ export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
||||
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
||||
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
||||
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_URL}/validators`;
|
||||
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
|
||||
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
||||
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
Slider,
|
||||
Typography,
|
||||
Box,
|
||||
@@ -25,7 +24,9 @@ import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
const FilterItem = ({
|
||||
label,
|
||||
id,
|
||||
tooltipInfo,
|
||||
value,
|
||||
isSmooth,
|
||||
marks,
|
||||
scale,
|
||||
min,
|
||||
@@ -36,12 +37,13 @@ const FilterItem = ({
|
||||
}) => (
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography gutterBottom>{label}</Typography>
|
||||
<Typography fontSize={12}>{tooltipInfo}</Typography>
|
||||
<Slider
|
||||
value={value}
|
||||
onChange={(e: Event, newValue: number | number[]) => onChange(id, newValue as number[])}
|
||||
valueLabelDisplay="off"
|
||||
valueLabelDisplay={isSmooth ? 'auto' : 'off'}
|
||||
marks={marks}
|
||||
step={null}
|
||||
step={isSmooth ? 1 : null}
|
||||
scale={scale}
|
||||
min={min}
|
||||
max={max}
|
||||
@@ -50,7 +52,7 @@ const FilterItem = ({
|
||||
);
|
||||
|
||||
export const Filters = () => {
|
||||
const { filterMixnodes, fetchMixnodes } = useMainContext();
|
||||
const { filterMixnodes, fetchMixnodes, mixnodes } = useMainContext();
|
||||
const { status } = useParams<{ status: MixnodeStatusWithAll | undefined }>();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@@ -127,19 +129,26 @@ export const Filters = () => {
|
||||
<Alert
|
||||
severity="info"
|
||||
variant={isMobile ? 'standard' : 'outlined'}
|
||||
sx={{ color: (t) => t.palette.info.light }}
|
||||
action={
|
||||
<Button size="small" onClick={onClearFilters}>
|
||||
Clear
|
||||
CLEAR FILTERS
|
||||
</Button>
|
||||
}
|
||||
sx={{ width: 300 }}
|
||||
>
|
||||
Filters applied
|
||||
{mixnodes?.data?.length} mixnodes matched your criteria
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<IconButton size="large" onClick={handleToggleShowFilters}>
|
||||
<Tune />
|
||||
</IconButton>
|
||||
<Button
|
||||
size="large"
|
||||
variant="text"
|
||||
color="inherit"
|
||||
endIcon={<Tune />}
|
||||
onClick={handleToggleShowFilters}
|
||||
sx={{ textTransform: 'none' }}
|
||||
>
|
||||
Advanced filters
|
||||
</Button>
|
||||
<Dialog open={showFilters} onClose={handleToggleShowFilters} maxWidth="md" fullWidth>
|
||||
<DialogTitle>Mixnode filters</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
|
||||
@@ -5,6 +5,7 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
|
||||
label: 'Profit margin (%)',
|
||||
id: EnumFilterKey.profitMargin,
|
||||
value: [0, 100],
|
||||
isSmooth: true,
|
||||
marks: [
|
||||
{ label: '0', value: 0 },
|
||||
{ label: '10', value: 10 },
|
||||
@@ -18,6 +19,8 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
|
||||
{ label: '90', value: 90 },
|
||||
{ label: '100', value: 100 },
|
||||
],
|
||||
tooltipInfo:
|
||||
'As a delegator you want to chose nodes with lower profit margin, meaning more payout for their delegators',
|
||||
},
|
||||
stakeSaturation: {
|
||||
label: 'Stake saturation (%)',
|
||||
@@ -43,65 +46,29 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
|
||||
},
|
||||
],
|
||||
max: upperSaturationValue,
|
||||
tooltipInfo: "Select nodes with <100% saturation. Any additional stake above 100% saturation won't get rewards",
|
||||
},
|
||||
stake: {
|
||||
label: 'Stake (NYM)',
|
||||
id: EnumFilterKey.stake,
|
||||
value: [20, 90],
|
||||
min: 20,
|
||||
max: 90,
|
||||
routingScore: {
|
||||
label: 'Routing score (%)',
|
||||
id: EnumFilterKey.routingScore,
|
||||
value: [0, 100],
|
||||
marks: [
|
||||
{
|
||||
value: 0,
|
||||
label: '1',
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
label: '10',
|
||||
},
|
||||
{
|
||||
value: 20,
|
||||
label: '100',
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: '1k',
|
||||
},
|
||||
{
|
||||
value: 40,
|
||||
label: '10k',
|
||||
},
|
||||
{
|
||||
value: 50,
|
||||
label: '100k',
|
||||
},
|
||||
{
|
||||
value: 60,
|
||||
label: '1M',
|
||||
},
|
||||
{
|
||||
value: 70,
|
||||
label: '10M',
|
||||
},
|
||||
{
|
||||
value: 80,
|
||||
label: '100M',
|
||||
},
|
||||
{
|
||||
value: 90,
|
||||
label: '1B',
|
||||
},
|
||||
{ label: '0', value: 0 },
|
||||
{ label: '10', value: 10 },
|
||||
{ label: '20', value: 20 },
|
||||
{ label: '30', value: 30 },
|
||||
{ label: '40', value: 40 },
|
||||
{ label: '50', value: 50 },
|
||||
{ label: '60', value: 60 },
|
||||
{ label: '70', value: 70 },
|
||||
{ label: '80', value: 80 },
|
||||
{ label: '90', value: 90 },
|
||||
{ label: '100', value: 100 },
|
||||
],
|
||||
tooltipInfo: 'The higher the routing score the better the performance of the node and so its rewards',
|
||||
},
|
||||
});
|
||||
|
||||
const formatStakeValuesToMinorDenom = ([value_1, value_2]: number[]) => {
|
||||
const lowerValue = 10 ** (value_1 / 10) * 1_000_000;
|
||||
const upperValue = 10 ** (value_2 / 10) * 1_000_000;
|
||||
|
||||
return [lowerValue, upperValue];
|
||||
};
|
||||
|
||||
const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
|
||||
const lowerValue = value_1 / 100;
|
||||
const upperValue = value_2 / 100;
|
||||
@@ -110,7 +77,7 @@ const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
|
||||
};
|
||||
|
||||
export const formatOnSave = (filters: TFilters) => ({
|
||||
stake: formatStakeValuesToMinorDenom(filters.stake.value),
|
||||
routingScore: filters.routingScore.value,
|
||||
profitMargin: filters.profitMargin.value,
|
||||
stakeSaturation: formatStakeSaturationValues(filters.stakeSaturation.value),
|
||||
});
|
||||
|
||||
@@ -102,8 +102,8 @@ export const MainContextProvider: React.FC = ({ children }) => {
|
||||
m.mix_node.profit_margin_percent <= filters.profitMargin[1] &&
|
||||
m.stake_saturation >= filters.stakeSaturation[0] &&
|
||||
m.stake_saturation <= filters.stakeSaturation[1] &&
|
||||
+m.pledge_amount.amount + +m.total_delegation.amount >= filters.stake[0] &&
|
||||
+m.pledge_amount.amount + +m.total_delegation.amount <= filters.stake[1],
|
||||
m.avg_uptime >= filters.routingScore[0] &&
|
||||
m.avg_uptime <= filters.routingScore[1],
|
||||
);
|
||||
setMixnodes({ data: filtered, isLoading: false });
|
||||
};
|
||||
|
||||
@@ -4,17 +4,19 @@ import { Mark } from '@mui/base';
|
||||
export enum EnumFilterKey {
|
||||
profitMargin = 'profitMargin',
|
||||
stakeSaturation = 'stakeSaturation',
|
||||
stake = 'stake',
|
||||
routingScore = 'routingScore',
|
||||
}
|
||||
|
||||
export type TFilterItem = {
|
||||
label: string;
|
||||
id: EnumFilterKey;
|
||||
value: number[];
|
||||
isSmooth?: boolean;
|
||||
marks: Mark[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
scale?: (value: number) => number;
|
||||
tooltipInfo?: string;
|
||||
};
|
||||
|
||||
export type TFilters = { [key in EnumFilterKey]: TFilterItem };
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Mixnet Gateway"
|
||||
edition = "2021"
|
||||
@@ -33,7 +33,7 @@ subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.19.1", features = [ "rt-multi-thread", "net", "signal", "fs" ] }
|
||||
tokio-stream = { version = "0.1.9", features = [ "fs" ] }
|
||||
tokio-tungstenite = "0.14"
|
||||
tokio-tungstenite = "0.17"
|
||||
tokio-util = { version = "0.7.3", features = [ "codec" ] }
|
||||
url = { version = "2.2", features = [ "serde" ] }
|
||||
web3 = "0.17.0"
|
||||
|
||||
@@ -29,6 +29,6 @@ credentials = { path = "../../common/credentials" }
|
||||
coconut = ["coconut-interface", "credentials/coconut"]
|
||||
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13.0"
|
||||
version = "0.17.3"
|
||||
default-features = false
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ pub use self::shared_key::{SharedKeySize, SharedKeys};
|
||||
use crypto::asymmetric::identity;
|
||||
use futures::{Sink, Stream};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use tungstenite::{Error as WsError, Message as WsMessage};
|
||||
use tungstenite::error::Error as WsError;
|
||||
use tungstenite::protocol::Message as WsMessage;
|
||||
|
||||
pub(crate) type WsItem = Result<WsMessage, WsError>;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::{self, Error, Formatter},
|
||||
};
|
||||
use tungstenite::protocol::Message;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::Credential;
|
||||
@@ -192,12 +191,12 @@ impl ClientControlRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientControlRequest> for Message {
|
||||
impl From<ClientControlRequest> for tungstenite::Message {
|
||||
fn from(req: ClientControlRequest) -> Self {
|
||||
// it should be safe to call `unwrap` here as the message is generated by the server
|
||||
// so if it fails (and consequently panics) it's a bug that should be resolved
|
||||
let str_req = serde_json::to_string(&req).unwrap();
|
||||
Message::Text(str_req)
|
||||
tungstenite::Message::Text(str_req)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,12 +257,12 @@ impl ServerResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerResponse> for Message {
|
||||
impl From<ServerResponse> for tungstenite::Message {
|
||||
fn from(res: ServerResponse) -> Self {
|
||||
// it should be safe to call `unwrap` here as the message is generated by the server
|
||||
// so if it fails (and consequently panics) it's a bug that should be resolved
|
||||
let str_res = serde_json::to_string(&res).unwrap();
|
||||
Message::Text(str_res)
|
||||
tungstenite::Message::Text(str_res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,8 +313,8 @@ impl BinaryRequest {
|
||||
BinaryRequest::ForwardSphinx(mix_packet)
|
||||
}
|
||||
|
||||
pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message {
|
||||
Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
|
||||
pub fn into_ws_message(self, shared_key: &SharedKeys) -> tungstenite::Message {
|
||||
tungstenite::Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,8 +366,8 @@ impl BinaryResponse {
|
||||
BinaryResponse::PushedMixMessage(msg)
|
||||
}
|
||||
|
||||
pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message {
|
||||
Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
|
||||
pub fn into_ws_message(self, shared_key: &SharedKeys) -> tungstenite::Message {
|
||||
tungstenite::Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ pub struct Init {
|
||||
mnemonic: Option<String>,
|
||||
|
||||
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
@@ -83,7 +83,7 @@ impl From<Init> for OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
mnemonic: init_config.mnemonic,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
@@ -177,7 +177,7 @@ mod tests {
|
||||
mnemonic: None,
|
||||
statistics_service_url: None,
|
||||
enabled_statistics: None,
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: None,
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: "".to_string(),
|
||||
|
||||
@@ -6,11 +6,11 @@ use std::{process, str::FromStr};
|
||||
use crate::{config::Config, Cli};
|
||||
use clap::Subcommand;
|
||||
use colored::Colorize;
|
||||
use config::parse_validators;
|
||||
use crypto::bech32_address_validation;
|
||||
use network_defaults::var_names::{
|
||||
API_VALIDATOR, BECH32_PREFIX, CONFIGURED, NYMD_VALIDATOR, STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod node_details;
|
||||
@@ -52,7 +52,7 @@ pub(crate) struct OverrideConfig {
|
||||
validators: Option<String>,
|
||||
mnemonic: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
@@ -69,17 +69,6 @@ pub(crate) async fn execute(args: Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
let mut was_host_overridden = false;
|
||||
if let Some(host) = args.host {
|
||||
@@ -129,8 +118,8 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.with_custom_validator_apis(parse_validators(&raw_validators))
|
||||
}
|
||||
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config = config.with_custom_validator_nymd(parse_validators(&raw_validators));
|
||||
if let Some(ref raw_validators) = args.validators {
|
||||
config = config.with_custom_validator_nymd(parse_validators(raw_validators));
|
||||
} else if std::env::var(CONFIGURED).is_ok() {
|
||||
let raw_validators = std::env::var(NYMD_VALIDATOR).expect("nymd validator not set");
|
||||
config = config.with_custom_validator_nymd(parse_validators(&raw_validators))
|
||||
@@ -157,18 +146,15 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.with_eth_endpoint(String::from(DEFAULT_ETH_ENDPOINT));
|
||||
}
|
||||
|
||||
// We set the disabled credentials mode flag if we either compile without 'eth', or if there is a flag we
|
||||
// can read from, which is when we build with 'eth' (and without 'coconut').
|
||||
if cfg!(not(feature = "eth")) {
|
||||
config = config.with_disabled_credentials_mode(true);
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
{
|
||||
if let Some(enabled_credentials_mode) = args.enabled_credentials_mode {
|
||||
config = config.with_disabled_credentials_mode(!enabled_credentials_mode);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(enabled_credentials_mode) = args.enabled_credentials_mode {
|
||||
config = config.with_disabled_credentials_mode(enabled_credentials_mode);
|
||||
}
|
||||
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config = config.with_custom_validator_nymd(parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ pub struct Run {
|
||||
mnemonic: Option<String>,
|
||||
|
||||
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
@@ -83,7 +83,7 @@ impl From<Run> for OverrideConfig {
|
||||
validators: run_config.validators,
|
||||
mnemonic: run_config.mnemonic,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
|
||||
@@ -66,6 +66,10 @@ impl NymConfig for Config {
|
||||
.join("gateways")
|
||||
}
|
||||
|
||||
fn try_default_root_directory() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(".nym").join("gateways"))
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf {
|
||||
self.gateway.nym_root_directory.clone()
|
||||
}
|
||||
@@ -123,6 +127,7 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
|
||||
self.gateway.disabled_credentials_mode = disabled_credentials_mode;
|
||||
self
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -6,9 +6,11 @@ use std::process;
|
||||
use crate::{config::Config, Cli};
|
||||
use clap::Subcommand;
|
||||
use colored::Colorize;
|
||||
use config::defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED};
|
||||
use config::{
|
||||
defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED},
|
||||
parse_validators,
|
||||
};
|
||||
use crypto::bech32_address_validation;
|
||||
use url::Url;
|
||||
|
||||
mod describe;
|
||||
mod init;
|
||||
@@ -61,17 +63,6 @@ pub(crate) async fn execute(args: Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
let mut was_host_overridden = false;
|
||||
if let Some(host) = args.host {
|
||||
|
||||
@@ -96,6 +96,10 @@ impl NymConfig for Config {
|
||||
.join("mixnodes")
|
||||
}
|
||||
|
||||
fn try_default_root_directory() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(".nym").join("mixnodes"))
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf {
|
||||
self.mixnode.nym_root_directory.clone()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
## [nym-connect-v1.0.2](https://github.com/nymtech/nym/tree/nym-connect-v1.0.2) (2022-08-18)
|
||||
|
||||
### Changed
|
||||
|
||||
- nym-connect: "load balance" the service providers by picking a random Service Provider for each Service and storing in local storage so it remains sticky for the user ([#1540])
|
||||
- nym-connect: the ServiceProviderSelector only displays the available Services, and picks a random Service Provider for Services the user has never used before ([#1540])
|
||||
- nym-connect: add `local-forage` for storing user settings ([#1540])
|
||||
|
||||
[#1540]: https://github.com/nymtech/nym/pull/1540
|
||||
|
||||
|
||||
## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22)
|
||||
|
||||
### Added
|
||||
|
||||
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
|
||||
- nym-connect: add ability to select network requester and gateway ([#1427])
|
||||
- nym-connect: add ability to export gateway keys as JSON
|
||||
- nym-connect: add auto updater
|
||||
|
||||
### Changed
|
||||
|
||||
- nym-connect: reuse config id instead of creating a new id on each connection
|
||||
|
||||
[#1427]: https://github.com/nymtech/nym/pull/1427
|
||||
Generated
+77
-43
@@ -1079,7 +1079,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8"
|
||||
dependencies = [
|
||||
"sct",
|
||||
"sct 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2570,7 +2570,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2583,12 +2583,12 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"log",
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
"webpki 0.21.4",
|
||||
"webpki-roots 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2745,15 +2745,6 @@ dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "input_buffer"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -3404,7 +3395,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-connect"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"client-core",
|
||||
@@ -3436,7 +3427,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"client-core",
|
||||
@@ -4709,8 +4700,20 @@ dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
"sct 0.6.1",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"sct 0.7.0",
|
||||
"webpki 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4720,7 +4723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
@@ -4809,6 +4812,16 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.2.1"
|
||||
@@ -5227,6 +5240,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"nymsphinx-addressing",
|
||||
"ordered-buffer",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5373,7 +5387,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"sha2 0.10.2",
|
||||
"smallvec 1.9.0",
|
||||
"sqlformat",
|
||||
@@ -5382,8 +5396,8 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
"webpki 0.21.4",
|
||||
"webpki-roots 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6055,9 +6069,9 @@ version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.19.1",
|
||||
"tokio",
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6073,13 +6087,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.14.0"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2"
|
||||
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
@@ -6245,22 +6258,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.13.0"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093"
|
||||
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"input_buffer",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha-1 0.9.8",
|
||||
"rustls 0.20.6",
|
||||
"sha-1 0.10.0",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
"webpki 0.22.0",
|
||||
"webpki-roots 0.22.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6592,9 +6607,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -6602,13 +6617,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -6629,9 +6644,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -6639,9 +6654,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6652,9 +6667,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.78"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-utils"
|
||||
@@ -6781,13 +6796,32 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
|
||||
dependencies = [
|
||||
"webpki 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"react-hook-form": "^7.14.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"semver": "^6.3.0",
|
||||
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
|
||||
"yup": "^0.32.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-connect"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
description = "nym-connect"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
|
||||
@@ -7,7 +7,7 @@ use tokio::sync::RwLock;
|
||||
|
||||
use client_core::config::Config as BaseConfig;
|
||||
use config_common::NymConfig;
|
||||
use nym_socks5::{client::config::Config as Socks5Config, commands::parse_validators};
|
||||
use nym_socks5::client::config::Config as Socks5Config;
|
||||
|
||||
use crate::{
|
||||
error::{BackendError, Result},
|
||||
@@ -37,9 +37,7 @@ pub async fn get_config_file_location(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<String> {
|
||||
let id = get_config_id(state).await?;
|
||||
Ok(Config::config_file_location(&id)
|
||||
.to_string_lossy()
|
||||
.to_string())
|
||||
Config::config_file_location(&id).map(|d| d.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -94,8 +92,9 @@ impl Config {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn config_file_location(id: &str) -> PathBuf {
|
||||
Socks5Config::default_config_file_path(Some(id))
|
||||
pub fn config_file_location(id: &str) -> Result<PathBuf> {
|
||||
Socks5Config::try_default_config_file_path(Some(id))
|
||||
.ok_or(BackendError::CouldNotGetFilename)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +106,9 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
|
||||
log::debug!(
|
||||
"Attempting to use config file location: {}",
|
||||
Config::config_file_location(&id).to_string_lossy(),
|
||||
Config::config_file_location(&id)?.to_string_lossy(),
|
||||
);
|
||||
let already_init = Config::config_file_location(&id).exists();
|
||||
let already_init = Config::config_file_location(&id)?.exists();
|
||||
if already_init {
|
||||
log::info!(
|
||||
"SOCKS5 client \"{}\" was already initialised before! \
|
||||
@@ -134,10 +133,11 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||
|
||||
if let Ok(raw_validators) = std::env::var(config_common::defaults::var_names::API_VALIDATOR) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
.set_custom_validator_apis(config_common::parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
let gateway = setup_gateway(
|
||||
@@ -146,12 +146,12 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
Some(&chosen_gateway_id),
|
||||
config.get_socks5(),
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
let config_save_location = config.get_socks5().get_config_file_save_location();
|
||||
config.get_socks5().save_to_file(None).tap_err(|_| {
|
||||
log::warn!("Failed to save the config file");
|
||||
log::error!("Failed to save the config file");
|
||||
})?;
|
||||
|
||||
log::info!("Saved configuration file to {:?}", config_save_location);
|
||||
@@ -182,7 +182,7 @@ async fn setup_gateway(
|
||||
register: bool,
|
||||
user_chosen_gateway_id: Option<&str>,
|
||||
config: &Socks5Config,
|
||||
) -> GatewayEndpoint {
|
||||
) -> Result<GatewayEndpoint> {
|
||||
if register {
|
||||
// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
// the chosen one if it's among the available ones.
|
||||
@@ -200,7 +200,7 @@ async fn setup_gateway(
|
||||
.await;
|
||||
println!("Saved all generated keys");
|
||||
|
||||
gateway.into()
|
||||
Ok(gateway.into())
|
||||
} else if user_chosen_gateway_id.is_some() {
|
||||
// Just set the config, don't register or create any keys
|
||||
// This assumes that the user knows what they are doing, and that the existing keys are
|
||||
@@ -212,19 +212,20 @@ async fn setup_gateway(
|
||||
)
|
||||
.await;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
gateway.into()
|
||||
Ok(gateway.into())
|
||||
} else {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
match Socks5Config::load_from_file(Some(id)) {
|
||||
Ok(existing_config) => existing_config.get_base().get_gateway_endpoint().clone(),
|
||||
Ok(existing_config) => Ok(existing_config.get_base().get_gateway_endpoint().clone()),
|
||||
Err(err) => {
|
||||
panic!(
|
||||
log::error!(
|
||||
"Unable to configure gateway: {err}. \n
|
||||
Seems like the client was already initialized but it was not possible to read \
|
||||
the existing configuration file. \n
|
||||
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
|
||||
removing the existing configuration and starting over."
|
||||
)
|
||||
);
|
||||
Err(BackendError::CouldNotLoadExistingGatewayConfiguration(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,10 @@ pub enum BackendError {
|
||||
CouldNotInitWithoutServiceProvider,
|
||||
#[error("Could not get file name")]
|
||||
CouldNotGetFilename,
|
||||
#[error("Could not get config file location")]
|
||||
CouldNotGetConfigFilename,
|
||||
#[error("Could not load existing gateway configuration")]
|
||||
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
|
||||
}
|
||||
|
||||
impl Serialize for BackendError {
|
||||
|
||||
@@ -101,7 +101,7 @@ impl State {
|
||||
|
||||
// Setup configuration by writing to file
|
||||
if let Err(err) = self.init_config().await {
|
||||
log::warn!("Failed to initialize: {}", err);
|
||||
log::error!("Failed to initialize: {}", err);
|
||||
|
||||
// Wait a little to give the user some rudimentary feedback that the click actually
|
||||
// registered.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-connect",
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
@@ -65,9 +65,9 @@
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"title": "Nym Connect",
|
||||
"title": "NymConnect",
|
||||
"width": 240,
|
||||
"height": 480,
|
||||
"height": 500,
|
||||
"resizable": false
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,6 +1,56 @@
|
||||
import React from 'react';
|
||||
import { ConnectionStatusKind } from '../types';
|
||||
|
||||
const getBusyFillColor = (color: string): string => {
|
||||
if (color === '#60D6EF') {
|
||||
return '#21D072';
|
||||
}
|
||||
return '#60D6EF';
|
||||
};
|
||||
|
||||
const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => {
|
||||
if (isError && hover) {
|
||||
return '#21D072';
|
||||
}
|
||||
if (isError) {
|
||||
return '#40475C';
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
if (hover) {
|
||||
return '#21D072';
|
||||
}
|
||||
return '#60D6EF';
|
||||
case ConnectionStatusKind.connecting:
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
return '#60D6EF';
|
||||
default:
|
||||
// connected
|
||||
if (hover) {
|
||||
return '#DA465B';
|
||||
}
|
||||
return '#21D072';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: ConnectionStatusKind, hover: boolean): string => {
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
return 'Connect';
|
||||
case ConnectionStatusKind.connecting:
|
||||
return 'Connecting';
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
return 'Connected';
|
||||
default:
|
||||
// connected
|
||||
if (hover) {
|
||||
return 'Disconnect';
|
||||
}
|
||||
return 'Connected';
|
||||
}
|
||||
};
|
||||
|
||||
export const ConnectionButton: React.FC<{
|
||||
status: ConnectionStatusKind;
|
||||
disabled?: boolean;
|
||||
@@ -130,53 +180,3 @@ export const ConnectionButton: React.FC<{
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const getBusyFillColor = (color: string): string => {
|
||||
if (color === '#60D6EF') {
|
||||
return '#21D072';
|
||||
}
|
||||
return '#60D6EF';
|
||||
};
|
||||
|
||||
const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => {
|
||||
if (isError && hover) {
|
||||
return '#21D072';
|
||||
}
|
||||
if (isError) {
|
||||
return '#40475C';
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
if (hover) {
|
||||
return '#21D072';
|
||||
}
|
||||
return '#60D6EF';
|
||||
case ConnectionStatusKind.connecting:
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
return '#60D6EF';
|
||||
default:
|
||||
// connected
|
||||
if (hover) {
|
||||
return '#DA465B';
|
||||
}
|
||||
return '#21D072';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: ConnectionStatusKind, hover: boolean): string => {
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
return 'Connect';
|
||||
case ConnectionStatusKind.connecting:
|
||||
return 'Connecting';
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
return 'Connected';
|
||||
default:
|
||||
// connected
|
||||
if (hover) {
|
||||
return 'Disconnect';
|
||||
}
|
||||
return 'Connected';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,24 +1,53 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import ArrowDropDownCircleIcon from '@mui/icons-material/ArrowDropDownCircle';
|
||||
import { Box, CircularProgress, Stack, Tooltip, Typography } from '@mui/material';
|
||||
import { ServiceProvider, Services } from '../types/directory';
|
||||
import { ServiceProvider, Service, Services } from '../types/directory';
|
||||
|
||||
type ServiceWithRandomSp = {
|
||||
id: string;
|
||||
description: string;
|
||||
sp: ServiceProvider;
|
||||
};
|
||||
|
||||
export const ServiceProviderSelector: React.FC<{
|
||||
onChange?: (serviceProvider: ServiceProvider) => void;
|
||||
services?: Services;
|
||||
}> = ({ services, onChange }) => {
|
||||
const [serviceProvider, setServiceProvider] = React.useState<ServiceProvider | undefined>();
|
||||
currentSp?: ServiceProvider;
|
||||
}> = ({ services, currentSp, onChange }) => {
|
||||
const [service, setService] = React.useState<Service>();
|
||||
const [serviceProvider, setServiceProvider] = React.useState<ServiceProvider | undefined>(currentSp);
|
||||
const textEl = React.useRef<null | HTMLElement>(null);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
useEffect(() => {
|
||||
if (!serviceProvider && currentSp) {
|
||||
setServiceProvider(currentSp);
|
||||
}
|
||||
}, [currentSp]);
|
||||
|
||||
useEffect(() => {
|
||||
if (services && serviceProvider) {
|
||||
// retrieve the service corresponding to this service provider
|
||||
setService(
|
||||
services.find((s) =>
|
||||
s.items.some(
|
||||
({ id, address, gateway }) =>
|
||||
id === serviceProvider.id && address === serviceProvider.address && gateway === serviceProvider.gateway,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}, [serviceProvider, services]);
|
||||
|
||||
const handleClick = () => {
|
||||
setAnchorEl(textEl.current);
|
||||
};
|
||||
const handleClose = (newServiceProvider?: ServiceProvider) => {
|
||||
if (newServiceProvider) {
|
||||
if (newServiceProvider && newServiceProvider !== currentSp) {
|
||||
setServiceProvider(newServiceProvider);
|
||||
onChange?.(newServiceProvider);
|
||||
}
|
||||
@@ -39,6 +68,16 @@ export const ServiceProviderSelector: React.FC<{
|
||||
);
|
||||
}
|
||||
|
||||
const servicesWithRandomSp: ServiceWithRandomSp[] = useMemo(
|
||||
() =>
|
||||
services.map(({ id, items, description }) => ({
|
||||
id,
|
||||
description,
|
||||
sp: items[Math.floor(Math.random() * items.length)],
|
||||
})),
|
||||
[services],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between" sx={{ mt: 3 }}>
|
||||
@@ -48,7 +87,7 @@ export const ServiceProviderSelector: React.FC<{
|
||||
fontWeight={700}
|
||||
color={(theme) => (serviceProvider ? undefined : theme.palette.primary.main)}
|
||||
>
|
||||
{serviceProvider ? serviceProvider.description : 'Select a service'}
|
||||
{service ? service.description : 'Select a service'}
|
||||
</Typography>
|
||||
<IconButton
|
||||
id="service-provider-button"
|
||||
@@ -65,44 +104,46 @@ export const ServiceProviderSelector: React.FC<{
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={() => handleClose()}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'service-provider-button',
|
||||
sx: {
|
||||
minWidth: 160,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{services.map((service) => (
|
||||
<>
|
||||
<MenuItem disabled dense sx={{ fontSize: 'small', fontWeight: 'bold', mb: -1 }}>
|
||||
{service.description}
|
||||
</MenuItem>
|
||||
{service.items.map((sp) => (
|
||||
<MenuItem dense sx={{ fontSize: 'small', ml: 2, height: 'auto' }} onClick={() => handleClose(sp)}>
|
||||
<Tooltip
|
||||
title={
|
||||
<Stack direction="column">
|
||||
<Typography fontSize="inherit">
|
||||
<code>{sp.id}</code>
|
||||
</Typography>
|
||||
<Typography fontSize="inherit" fontWeight={700}>
|
||||
{sp.description}
|
||||
</Typography>
|
||||
<Typography fontSize="inherit">
|
||||
Gateway <code>{sp.gateway.slice(0, 10)}...</code>
|
||||
</Typography>
|
||||
<Typography fontSize="inherit">
|
||||
Provider <code>{sp.address.slice(0, 10)}...</code>
|
||||
</Typography>
|
||||
</Stack>
|
||||
}
|
||||
arrow
|
||||
placement="top"
|
||||
>
|
||||
<Typography fontSize="inherit" noWrap>
|
||||
{servicesWithRandomSp.map(({ id, description, sp }) => (
|
||||
<MenuItem dense key={id} sx={{ fontSize: 'small', fontWeight: 'bold' }} onClick={() => handleClose(sp)}>
|
||||
<Tooltip
|
||||
title={
|
||||
<Stack direction="column">
|
||||
<Typography fontSize="inherit">
|
||||
<code>{sp.id}</code>
|
||||
</Typography>
|
||||
<Typography fontSize="inherit" fontWeight={700}>
|
||||
{sp.description}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</MenuItem>
|
||||
))}
|
||||
</>
|
||||
<Typography fontSize="inherit">
|
||||
Gateway <code>{sp.gateway.slice(0, 10)}...</code>
|
||||
</Typography>
|
||||
<Typography fontSize="inherit">
|
||||
Provider <code>{sp.address.slice(0, 10)}...</code>
|
||||
</Typography>
|
||||
</Stack>
|
||||
}
|
||||
arrow
|
||||
placement="top"
|
||||
>
|
||||
<Typography>{description}</Typography>
|
||||
</Tooltip>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { forage } from '@tauri-apps/tauri-forage';
|
||||
import { ConnectionStatusKind } from '../types';
|
||||
import { ConnectionStatsItem } from '../components/ConnectionStats';
|
||||
import { ServiceProvider, Services } from '../types/directory';
|
||||
@@ -36,7 +37,7 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
|
||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatusKind>(ConnectionStatusKind.disconnected);
|
||||
const [connectionStats, setConnectionStats] = useState<ConnectionStatsItem[]>();
|
||||
const [connectedSince, setConnectedSince] = useState<DateTime>();
|
||||
const [services, setServices] = React.useState<Services>();
|
||||
const [services, setServices] = React.useState<Services>([]);
|
||||
const [serviceProvider, setRawServiceProvider] = React.useState<ServiceProvider>();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -72,33 +73,71 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
|
||||
await invoke('start_disconnecting');
|
||||
}, []);
|
||||
|
||||
const setSpInStorage = async (sp: ServiceProvider) => {
|
||||
await forage.setItem({
|
||||
key: 'nym-connect-sp',
|
||||
value: sp,
|
||||
} as any)();
|
||||
};
|
||||
|
||||
const setServiceProvider = useCallback(async (newServiceProvider: ServiceProvider) => {
|
||||
await invoke('set_gateway', { gateway: newServiceProvider.gateway });
|
||||
await invoke('set_service_provider', { serviceProvider: newServiceProvider.address });
|
||||
await setSpInStorage(newServiceProvider);
|
||||
setRawServiceProvider(newServiceProvider);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ClientContext.Provider
|
||||
value={{
|
||||
mode,
|
||||
setMode,
|
||||
connectionStatus,
|
||||
setConnectionStatus,
|
||||
connectionStats,
|
||||
setConnectionStats,
|
||||
connectedSince,
|
||||
setConnectedSince,
|
||||
startConnecting,
|
||||
startDisconnecting,
|
||||
services,
|
||||
serviceProvider,
|
||||
setServiceProvider,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ClientContext.Provider>
|
||||
const getSpFromStorage = async () => {
|
||||
try {
|
||||
const spFromStorage = await forage.getItem({ key: 'nym-connect-sp' })();
|
||||
if (spFromStorage) {
|
||||
setRawServiceProvider(spFromStorage);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const validityCheck = async () => {
|
||||
if (services.length > 0 && serviceProvider) {
|
||||
const isValid = services.some(({ items }) => items.some(({ id }) => id === serviceProvider.id));
|
||||
if (!isValid) {
|
||||
console.warn('invalid SP, cleaning local storage');
|
||||
await forage.removeItem({
|
||||
key: 'nym-connect-sp',
|
||||
})();
|
||||
setRawServiceProvider(undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
validityCheck();
|
||||
}, [services, serviceProvider]);
|
||||
|
||||
useEffect(() => {
|
||||
getSpFromStorage();
|
||||
}, []);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
mode,
|
||||
setMode,
|
||||
connectionStatus,
|
||||
setConnectionStatus,
|
||||
connectionStats,
|
||||
setConnectionStats,
|
||||
connectedSince,
|
||||
setConnectedSince,
|
||||
startConnecting,
|
||||
startDisconnecting,
|
||||
services,
|
||||
serviceProvider,
|
||||
setServiceProvider,
|
||||
}),
|
||||
[mode, connectedSince, connectionStatus, connectionStats, connectedSince, services, serviceProvider],
|
||||
);
|
||||
|
||||
return <ClientContext.Provider value={contextValue}>{children}</ClientContext.Provider>;
|
||||
};
|
||||
|
||||
export const useClientContext = () => useContext(ClientContext);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ConnectionStatusKind } from '../types';
|
||||
import { NeedHelp } from '../components/NeedHelp';
|
||||
import { ServiceProviderSelector } from '../components/ServiceProviderSelector';
|
||||
import { ServiceProvider, Services } from '../types/directory';
|
||||
import { useClientContext } from '../context/main';
|
||||
|
||||
export const DefaultLayout: React.FC<{
|
||||
status: ConnectionStatusKind;
|
||||
@@ -20,6 +21,8 @@ export const DefaultLayout: React.FC<{
|
||||
setServiceProvider(newServiceProvider);
|
||||
onServiceProviderChange?.(newServiceProvider);
|
||||
};
|
||||
const { serviceProvider: currentSp } = useClientContext();
|
||||
|
||||
return (
|
||||
<AppWindowFrame>
|
||||
<Typography fontWeight="400" fontSize="12px" textAlign="center" sx={{ opacity: 0.6 }}>
|
||||
@@ -31,10 +34,10 @@ export const DefaultLayout: React.FC<{
|
||||
<br />
|
||||
Nym mixnet for privacy.
|
||||
</Typography>
|
||||
<ServiceProviderSelector services={services} onChange={handleServiceProviderChange} />
|
||||
<ServiceProviderSelector services={services} onChange={handleServiceProviderChange} currentSp={currentSp} />
|
||||
<ConnectionButton
|
||||
status={status}
|
||||
disabled={serviceProvider === undefined}
|
||||
disabled={serviceProvider === undefined && currentSp === undefined}
|
||||
busy={busy}
|
||||
isError={isError}
|
||||
onClick={onConnectClick}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user