diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index f43a1feb77..00b13da72b 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -5,7 +5,7 @@ on: - cron: '5 9 * * *' jobs: cargo-deny: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Checkout repository code uses: actions/checkout@v2 @@ -26,7 +26,7 @@ jobs: path: .github/workflows/support-files/notifications/deny.message notification: needs: cargo-deny - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Check out repository code uses: actions/checkout@v2 diff --git a/.github/workflows/build_matrix_includes.json b/.github/workflows/build_matrix_includes.json index a75ca76530..8d8f8297bc 100644 --- a/.github/workflows/build_matrix_includes.json +++ b/.github/workflows/build_matrix_includes.json @@ -1,6 +1,6 @@ [ { - "os":"ubuntu-latest", + "os":"ubuntu-20.04", "rust":"stable", "runOnEvent":"always" }, diff --git a/.github/workflows/contracts-build.yml b/.github/workflows/contracts-build.yml index 3921bd3a6d..9ecc6b49d9 100644 --- a/.github/workflows/contracts-build.yml +++ b/.github/workflows/contracts-build.yml @@ -6,7 +6,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 2c1eaf40b2..6d51356a80 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -10,7 +10,7 @@ on: jobs: matrix_prep: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -24,7 +24,7 @@ jobs: contracts: # since it's going to be compiled into wasm, there's absolutely # no point in running CI on different OS-es - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 continue-on-error: ${{ matrix.rust == 'nightly' }} needs: matrix_prep strategy: diff --git a/.github/workflows/network-explorer-api.yml b/.github/workflows/network-explorer-api.yml index 2ad184be72..5c60038347 100644 --- a/.github/workflows/network-explorer-api.yml +++ b/.github/workflows/network-explorer-api.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/nightly_build.yml b/.github/workflows/nightly_build.yml index 8475b83b8b..d103b66278 100644 --- a/.github/workflows/nightly_build.yml +++ b/.github/workflows/nightly_build.yml @@ -5,7 +5,7 @@ on: - cron: '14 1 * * *' jobs: matrix_prep: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -25,7 +25,7 @@ jobs: steps: - name: Install Dependencies (Linux) run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' - name: Check out repository code uses: actions/checkout@v2 @@ -96,7 +96,7 @@ jobs: - name: Reclaim some disk space uses: actions-rs/cargo@v1 - if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }} with: command: clean @@ -160,7 +160,7 @@ jobs: notification: needs: build - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Collect jobs status uses: technote-space/workflow-conclusion-action@v2 diff --git a/.github/workflows/nightly_build_matrix_includes.json b/.github/workflows/nightly_build_matrix_includes.json index a979c90a62..172944df28 100644 --- a/.github/workflows/nightly_build_matrix_includes.json +++ b/.github/workflows/nightly_build_matrix_includes.json @@ -1,6 +1,6 @@ [ { - "os":"ubuntu-latest", + "os":"ubuntu-20.04", "rust":"stable", "runOnEvent":"schedule" }, @@ -17,7 +17,7 @@ }, { - "os":"ubuntu-latest", + "os":"ubuntu-20.04", "rust":"beta", "runOnEvent":"schedule" }, @@ -33,7 +33,7 @@ }, { - "os":"ubuntu-latest", + "os":"ubuntu-20.04", "rust":"nightly", "runOnEvent":"schedule" }, diff --git a/.github/workflows/nightly_build_release.yml b/.github/workflows/nightly_build_release.yml index 4474b399b6..30e43d7f90 100644 --- a/.github/workflows/nightly_build_release.yml +++ b/.github/workflows/nightly_build_release.yml @@ -5,7 +5,7 @@ on: - cron: '14 2 * * *' jobs: matrix_prep: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -17,7 +17,7 @@ jobs: inputFile: '.github/workflows/nightly_build_matrix_includes.json' filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]' get_release: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 needs: matrix_prep outputs: output1: ${{ steps.step2.outputs.latest_release }} @@ -38,7 +38,7 @@ jobs: steps: - name: Install Dependencies (Linux) run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' - name: Check out latest release branch uses: actions/checkout@v3 @@ -111,7 +111,7 @@ jobs: - name: Reclaim some disk space uses: actions-rs/cargo@v1 - if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }} with: command: clean @@ -175,7 +175,7 @@ jobs: notification: needs: [build,get_release] - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Collect jobs status uses: technote-space/workflow-conclusion-action@v2 @@ -192,7 +192,7 @@ jobs: NYM_PROJECT_NAME: "Nym nightly build on latest release" GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" - GIT_BRANCH: "https://github.com/nymtech/nym/tree/${{needs.get_release.outputs.output1}}" + GIT_BRANCH_NAME: "${{needs.get_release.outputs.output1}}" KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}" KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}" KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}" diff --git a/.github/workflows/nightly_build_release2.yml b/.github/workflows/nightly_build_release2.yml index 71f89948c9..6ff5368c56 100644 --- a/.github/workflows/nightly_build_release2.yml +++ b/.github/workflows/nightly_build_release2.yml @@ -5,7 +5,7 @@ on: - cron: '24 2 * * *' jobs: matrix_prep: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -17,7 +17,7 @@ jobs: inputFile: '.github/workflows/nightly_build_matrix_includes.json' filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]' get_release: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 needs: matrix_prep outputs: output1: ${{ steps.step2.outputs.latest_release }} @@ -38,7 +38,7 @@ jobs: steps: - name: Install Dependencies (Linux) run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' - name: Check out latest release branch uses: actions/checkout@v3 @@ -111,7 +111,7 @@ jobs: - name: Reclaim some disk space uses: actions-rs/cargo@v1 - if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }} with: command: clean @@ -175,7 +175,7 @@ jobs: notification: needs: [build,get_release] - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Collect jobs status uses: technote-space/workflow-conclusion-action@v2 @@ -192,7 +192,7 @@ jobs: NYM_PROJECT_NAME: "Nym nightly build on latest release" GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" - GIT_BRANCH: "https://github.com/nymtech/nym/tree/${{needs.get_release.outputs.output1}}" + GIT_BRANCH_NAME: "${{needs.get_release.outputs.output1}}" KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}" KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}" KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}" diff --git a/.github/workflows/nym-cli-publish.yml b/.github/workflows/nym-cli-publish.yml index 065e9f1658..5fd532c68b 100644 --- a/.github/workflows/nym-cli-publish.yml +++ b/.github/workflows/nym-cli-publish.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest, windows-latest, macos-latest] + platform: [ubuntu-20.04, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/nym-connect-publish-ubuntu.yml b/.github/workflows/nym-connect-publish-ubuntu.yml index 52c6ed9c5e..a4eb77cae1 100644 --- a/.github/workflows/nym-connect-publish-ubuntu.yml +++ b/.github/workflows/nym-connect-publish-ubuntu.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/nym-release-publish.yml b/.github/workflows/nym-release-publish.yml index 700f1d697f..979781652f 100644 --- a/.github/workflows/nym-release-publish.yml +++ b/.github/workflows/nym-release-publish.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/nym-wallet-publish-ubuntu.yml b/.github/workflows/nym-wallet-publish-ubuntu.yml index 46ed4be076..04ff746851 100644 --- a/.github/workflows/nym-wallet-publish-ubuntu.yml +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/nym-wallet-release.yml b/.github/workflows/nym-wallet-release.yml index 4e157903e6..34883144f3 100644 --- a/.github/workflows/nym-wallet-release.yml +++ b/.github/workflows/nym-wallet-release.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/nym_wallet.yml b/.github/workflows/nym_wallet.yml index bbc981d0f4..49b2381ed0 100644 --- a/.github/workflows/nym_wallet.yml +++ b/.github/workflows/nym_wallet.yml @@ -12,7 +12,7 @@ defaults: jobs: test: name: wallet tests - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/support-files/nightly/index.js b/.github/workflows/support-files/nightly/index.js index f913539235..3bf2516092 100644 --- a/.github/workflows/support-files/nightly/index.js +++ b/.github/workflows/support-files/nightly/index.js @@ -85,7 +85,7 @@ async function getMessageBody(context) { ... ], check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182940024', - labels: [ 'ubuntu-latest' ], + labels: [ 'ubuntu-20.04' ], runner_id: 1, runner_name: 'Hosted Agent', runner_group_id: 2, diff --git a/.github/workflows/wasm_client_build.yml b/.github/workflows/wasm_client_build.yml index 2761aabcd2..e1da3b04b0 100644 --- a/.github/workflows/wasm_client_build.yml +++ b/.github/workflows/wasm_client_build.yml @@ -7,7 +7,7 @@ on: jobs: wasm: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index f102743734..6d81c53530 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ validator-config *.patch validator-api-config.toml dist -storybook-static \ No newline at end of file +storybook-static +envs/qwerty.env +Cargo.lock +nym-connect/Cargo.lock diff --git a/.wellknown/wallet/validators.json b/.wellknown/wallet/validators.json index f4987aa3dc..ff849e16d1 100644 --- a/.wellknown/wallet/validators.json +++ b/.wellknown/wallet/validators.json @@ -1,6 +1,8 @@ { - "mainnet":[{ - "nymd_url":"https://rpc.nyx.nodes.guru/", - "api_url":"https://api.nyx.nodes.guru/" - }] -} + "mainnet": [ + { + "nyxd_url": "https://rpc.nyx.nodes.guru/", + "api_url": "https://api.nyx.nodes.guru/" + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f3274b4e1..9a372b8376 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,81 @@ 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] + +### Added + +- socks5: send status message for service ready, and network-requester error response + +### Changed + +- renamed all references to validator_api to nym_api +- renamed all references to nymd to nyxd +- all-binaries: improved error logging ([#2686]) +- native client: bring shutdown logic up to the same level as socks5-client +- nym-api, coconut-dkg contract: automatic, time-based dkg epoch state advancement ([#2670]) +- all-binaries: standarised argument names (note: old names should still be accepted) ([#2762] + +### Fixed + +- nym-api: should now correctly use `rewarding.enabled` config flag ([#2753]) + +[#2686]: https://github.com/nymtech/nym/pull/2686 +[#2670]: https://github.com/nymtech/nym/pull/2670 +[#2753]: https://github.com/nymtech/nym/pull/2753 +[#2762]: https://github.com/nymtech/nym/pull/2762 + +## [v1.1.4] (2022-12-20) + +This release adds multiple Single Use Reply Blocks (SURBs) to allow arbitrarily-sized anonymized replies. +At the moment this is turned off by default, but available for use by application developers. +We will need to wait for network-requesters to upgrade to this new release, after which multi-SURB anonymization will become the default setting for the SOCKS proxy clients. + +The release also include some additional work for distributed key generation in the Coconut signing authority nodes. + +### Changed + +- Feature/dkg contract threshold by @neacsu in https://github.com/nymtech/nym/pull/1885 +- Multi-surbs by @jstuczyn in https://github.com/nymtech/nym/pull/2667 +- Fix multi-surb backwards compatibility in pre 1.1.4 client config files by @jstuczyn in https://github.com/nymtech/nym/pull/2703 +- fix: ignore corrupted surb storage and instead create fresh one by @jstuczyn in https://github.com/nymtech/nym/pull/2711 +- socks5: rework waiting in inbound.rs by @octol in https://github.com/nymtech/nym/pull/1880 + +## [v1.1.3] (2022-12-13) + +### Changed + +- validator-api: can recover from shutdown during DKG process ([#1872]) +- clients: deduplicate gateway initialization, part of work towards a rust-sdk +- clients: keep all transmission lanes going at all times by making priority probabilistic +- clients: ability to use multi-reply SURBs to send arbitrarily long messages fully anonymously whilst requesting additional reply blocks whenever they're about to run out ([#1796], [#1801], [#1804], [#1835], [#1858], [#1883])) + +### Fixed + +- network-requester: fix bug where websocket connection disconnect resulted in success error code +- clients: fix a few panics handling the gateway-client +- mixnode, gateway, validator-api: Use mainnet values as defaults for URLs and mixnet contract ([#1884]) +- socks5: fixed bug where connections sometimes where closed too early +- clients: improve message logging when received message fails to get reconstructed ([#1803]) + +[#1796]: https://github.com/nymtech/nym/pull/1796 +[#1801]: https://github.com/nymtech/nym/pull/1801 +[#1803]: https://github.com/nymtech/nym/pull/1803 +[#1804]: https://github.com/nymtech/nym/pull/1804 +[#1835]: https://github.com/nymtech/nym/pull/1835 +[#1858]: https://github.com/nymtech/nym/pull/1858 +[#1872]: https://github.com/nymtech/nym/pull/1872 +[#1883]: https://github.com/nymtech/nym/pull/1883 +[#1884]: https://github.com/nymtech/nym/pull/1884 + +## [v1.1.2] + +### Changed + +- gateway: Renamed flag from `enabled/disabled_credentials_mode` to `only-coconut-credentials` +- "Family" feature for node families + layers +- Initial coconut functionality including credentials and distributed key generation + ## [v1.1.1](https://github.com/nymtech/nym/tree/v1.1.1) (2022-11-29) ### Added @@ -36,7 +111,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1786]: https://github.com/nymtech/nym/pull/1786 [#1805]: https://github.com/nymtech/nym/pull/1805 - ## [v1.1.0](https://github.com/nymtech/nym/tree/v1.1.0) (2022-11-09) ### Added @@ -48,7 +122,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - native-client/socks5-client/wasm-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671]) - network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611]) - validator-api: add `interval_operating_cost` and `profit_margin_percent` to compute reward estimation endpoint -- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558]) +- validator-client: added `query_contract_smart` and `query_contract_raw` on `NyxdClient` ([#1558]) - wasm-client: uses updated wasm-compatible `client-core` so that it's now capable of packet retransmission, cover traffic and poisson delay (among other things!) ([#1673]) ### Fixed @@ -63,7 +137,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669]) - moved `Percent` struct to `contracts-common`, change affects explorer-api - socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591]) -- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681]) +- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681]) - validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541]) - wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585]) - validator-api: changes to internal SQL schema due to the mixnet contract revamp ([#1472]) @@ -91,7 +165,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1724]: https://github.com/nymtech/nym/pull/1724 [#1725]: https://github.com/nymtech/nym/pull/1725 - ## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2) ### Added @@ -144,9 +217,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - 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]) - +- validator-api: rewarder set update fails without panicking on possible nyxd 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 [#1256]: https://github.com/nymtech/nym/pull/1256 @@ -240,9 +312,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Bump minimist from 1.2.5 to 1.2.6 in /clients/tauri-client [\#1163](https://github.com/nymtech/nym/pull/1163) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump minimist from 1.2.5 to 1.2.6 in /clients/webassembly/js-example [\#1162](https://github.com/nymtech/nym/pull/1162) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump minimist from 1.2.5 to 1.2.6 in /clients/native/examples/js-examples/websocket [\#1160](https://github.com/nymtech/nym/pull/1160) ([dependabot[bot]](https://github.com/apps/dependabot)) -- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript\_client/upload\_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript_client/upload_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot)) - Feature/vesting full [\#1158](https://github.com/nymtech/nym/pull/1158) ([fmtabbara](https://github.com/fmtabbara)) -- get\_current\_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch)) +- get_current_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch)) - Cleanup [\#1155](https://github.com/nymtech/nym/pull/1155) ([durch](https://github.com/durch)) - Feature flag reward payments [\#1154](https://github.com/nymtech/nym/pull/1154) ([durch](https://github.com/durch)) - Add Query endpoints for calculating rewards [\#1152](https://github.com/nymtech/nym/pull/1152) ([durch](https://github.com/durch)) @@ -251,7 +323,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - wallet: use Urls rather than Strings for validator urls [\#1148](https://github.com/nymtech/nym/pull/1148) ([octol](https://github.com/octol)) - Change accumulated reward to Option, migrate delegations [\#1147](https://github.com/nymtech/nym/pull/1147) ([durch](https://github.com/durch)) - wallet: fetch validators url remotely if available [\#1146](https://github.com/nymtech/nym/pull/1146) ([octol](https://github.com/octol)) -- Fix delegated\_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch)) +- Fix delegated_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch)) - Update Nym wallet dependencies to use `ts-packages` [\#1144](https://github.com/nymtech/nym/pull/1144) ([mmsinclair](https://github.com/mmsinclair)) - wallet: try validators one by one if available [\#1143](https://github.com/nymtech/nym/pull/1143) ([octol](https://github.com/octol)) - Update Network Explorer Packages and add mix node identity key copy [\#1142](https://github.com/nymtech/nym/pull/1142) ([mmsinclair](https://github.com/mmsinclair)) @@ -291,14 +363,13 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary)) - Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu)) - ## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23) [Full Changelog](https://github.com/nymtech/nym/compare/v0.12.0...v0.12.1) **Implemented enhancements:** -- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967) +- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967) **Fixed bugs:** @@ -328,7 +399,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Bugfix/remove mixnode bonding overwrite [\#917](https://github.com/nymtech/nym/pull/917) ([jstuczyn](https://github.com/jstuczyn)) - Fixes crash condition in validator API when calculating last day uptime [\#909](https://github.com/nymtech/nym/pull/909) ([jstuczyn](https://github.com/jstuczyn)) - Bugfix/monitor initial values wait [\#907](https://github.com/nymtech/nym/pull/907) ([jstuczyn](https://github.com/jstuczyn)) -- Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions [\#806](https://github.com/nymtech/nym/pull/806) ([mmsinclair](https://github.com/mmsinclair)) +- Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions [\#806](https://github.com/nymtech/nym/pull/806) ([mmsinclair](https://github.com/mmsinclair)) - Explorer API: port test now split out address resolution and add units tests [\#755](https://github.com/nymtech/nym/pull/755) ([mmsinclair](https://github.com/mmsinclair)) **Closed issues:** @@ -343,7 +414,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - help!!! [\#712](https://github.com/nymtech/nym/issues/712) - UX feature request: show all delegated nodes in wallet [\#711](https://github.com/nymtech/nym/issues/711) - UX feature request: add current balance on wallet pages [\#710](https://github.com/nymtech/nym/issues/710) -- got sign issue from bot [\#709](https://github.com/nymtech/nym/issues/709) +- got sign issue from bot [\#709](https://github.com/nymtech/nym/issues/709) - As a wallet user, I would like to be able to log out of the wallet [\#706](https://github.com/nymtech/nym/issues/706) - As a wallet user, I would like to have a "receive" page where I can see my own wallet address [\#705](https://github.com/nymtech/nym/issues/705) - Update native client/socks client/mixnode/gateway `upgrade` command [\#689](https://github.com/nymtech/nym/issues/689) @@ -353,7 +424,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - nym-socks5-client crash after opening Keybase team "Browse all channels" [\#494](https://github.com/nymtech/nym/issues/494) - Mixed Content problem [\#400](https://github.com/nymtech/nym/issues/400) - Gateway disk quota [\#137](https://github.com/nymtech/nym/issues/137) -- Simplify message encapsulation with regards to topology [\#127](https://github.com/nymtech/nym/issues/127) +- Simplify message encapsulation with regards to topology [\#127](https://github.com/nymtech/nym/issues/127) - Create constants for cli argument names [\#115](https://github.com/nymtech/nym/issues/115) - Using Blake3 as a hash function [\#103](https://github.com/nymtech/nym/issues/103) - Validator should decide which layer a node is in [\#86](https://github.com/nymtech/nym/issues/86) @@ -409,10 +480,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Feature/pre cosmrs updates [\#935](https://github.com/nymtech/nym/pull/935) ([jstuczyn](https://github.com/jstuczyn)) - Feature/client on behalf [\#934](https://github.com/nymtech/nym/pull/934) ([neacsu](https://github.com/neacsu)) - Webpack wallet prod configuration [\#933](https://github.com/nymtech/nym/pull/933) ([tommyv1987](https://github.com/tommyv1987)) -- Adding tx\_hash to wallet response [\#932](https://github.com/nymtech/nym/pull/932) ([futurechimp](https://github.com/futurechimp)) +- Adding tx_hash to wallet response [\#932](https://github.com/nymtech/nym/pull/932) ([futurechimp](https://github.com/futurechimp)) - Release/1.0.0 pre1 [\#931](https://github.com/nymtech/nym/pull/931) ([durch](https://github.com/durch)) - Feature/identity verification [\#930](https://github.com/nymtech/nym/pull/930) ([jstuczyn](https://github.com/jstuczyn)) -- Move cleaned up smart contracts to main code repo [\#929](https://github.com/nymtech/nym/pull/929) ([mfahampshire](https://github.com/mfahampshire)) +- Move cleaned up smart contracts to main code repo [\#929](https://github.com/nymtech/nym/pull/929) ([mfahampshire](https://github.com/mfahampshire)) - Feature/mixnet contract further adjustments [\#928](https://github.com/nymtech/nym/pull/928) ([jstuczyn](https://github.com/jstuczyn)) - typo copy change for nodemap [\#926](https://github.com/nymtech/nym/pull/926) ([Aid19801](https://github.com/Aid19801)) - Feature/UI enhancements for Desktop Wallet [\#925](https://github.com/nymtech/nym/pull/925) ([fmtabbara](https://github.com/fmtabbara)) @@ -425,7 +496,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Feature/faucet page react [\#911](https://github.com/nymtech/nym/pull/911) ([fmtabbara](https://github.com/fmtabbara)) - Feature/mixnet contract refactor [\#910](https://github.com/nymtech/nym/pull/910) ([futurechimp](https://github.com/futurechimp)) - Update README.md [\#905](https://github.com/nymtech/nym/pull/905) ([tommyv1987](https://github.com/tommyv1987)) -- BUG: Bond cell denom [\#904](https://github.com/nymtech/nym/pull/904) ([Aid19801](https://github.com/Aid19801)) +- BUG: Bond cell denom [\#904](https://github.com/nymtech/nym/pull/904) ([Aid19801](https://github.com/Aid19801)) - Explorer UI tests missing data-testid [\#903](https://github.com/nymtech/nym/pull/903) ([tommyv1987](https://github.com/tommyv1987)) - Fix up Nym-Wallet README.md [\#899](https://github.com/nymtech/nym/pull/899) ([tommyv1987](https://github.com/tommyv1987)) - Feature/batch delegator rewarding [\#898](https://github.com/nymtech/nym/pull/898) ([jstuczyn](https://github.com/jstuczyn)) @@ -443,7 +514,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Reverted gateway registration handshake to its 0.11.0 version [\#882](https://github.com/nymtech/nym/pull/882) ([jstuczyn](https://github.com/jstuczyn)) - Network Explorer [\#881](https://github.com/nymtech/nym/pull/881) ([mmsinclair](https://github.com/mmsinclair)) - Feature/rewarding interval updates [\#880](https://github.com/nymtech/nym/pull/880) ([jstuczyn](https://github.com/jstuczyn)) -- Put client\_address and id in the correct order [\#875](https://github.com/nymtech/nym/pull/875) ([neacsu](https://github.com/neacsu)) +- Put client_address and id in the correct order [\#875](https://github.com/nymtech/nym/pull/875) ([neacsu](https://github.com/neacsu)) - remove gateway selection on delegation and undelegation pages [\#873](https://github.com/nymtech/nym/pull/873) ([fmtabbara](https://github.com/fmtabbara)) - Set MSRV on all binaries to 1.56 [\#872](https://github.com/nymtech/nym/pull/872) ([jstuczyn](https://github.com/jstuczyn)) - add native window items \(copy/paste\) via tauri [\#871](https://github.com/nymtech/nym/pull/871) ([fmtabbara](https://github.com/fmtabbara)) @@ -459,7 +530,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Overflow checks in release [\#846](https://github.com/nymtech/nym/pull/846) ([jstuczyn](https://github.com/jstuczyn)) - fix delegate success overflow [\#842](https://github.com/nymtech/nym/pull/842) ([fmtabbara](https://github.com/fmtabbara)) - Feature NYM wallet webdriverio test [\#841](https://github.com/nymtech/nym/pull/841) ([tommyv1987](https://github.com/tommyv1987)) -- Update nym\_wallet.yml [\#840](https://github.com/nymtech/nym/pull/840) ([tommyv1987](https://github.com/tommyv1987)) +- Update nym_wallet.yml [\#840](https://github.com/nymtech/nym/pull/840) ([tommyv1987](https://github.com/tommyv1987)) - Feature/vouchers [\#837](https://github.com/nymtech/nym/pull/837) ([aniampio](https://github.com/aniampio)) - Apply readable ids to elements on Nym Wallet [\#836](https://github.com/nymtech/nym/pull/836) ([tommyv1987](https://github.com/tommyv1987)) - Feature/removal of monitor good nodes [\#833](https://github.com/nymtech/nym/pull/833) ([jstuczyn](https://github.com/jstuczyn)) @@ -483,8 +554,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Created getters for AccountData [\#787](https://github.com/nymtech/nym/pull/787) ([jstuczyn](https://github.com/jstuczyn)) - Feature/migrate hidden delegations [\#786](https://github.com/nymtech/nym/pull/786) ([neacsu](https://github.com/neacsu)) - Feature/persistent gateway storage [\#784](https://github.com/nymtech/nym/pull/784) ([jstuczyn](https://github.com/jstuczyn)) -- Replaced unwrap\_or\_else with unwrap\_or\_default [\#780](https://github.com/nymtech/nym/pull/780) ([jstuczyn](https://github.com/jstuczyn)) -- Add block\_height method to Delegation [\#778](https://github.com/nymtech/nym/pull/778) ([durch](https://github.com/durch)) +- Replaced unwrap_or_else with unwrap_or_default [\#780](https://github.com/nymtech/nym/pull/780) ([jstuczyn](https://github.com/jstuczyn)) +- Add block_height method to Delegation [\#778](https://github.com/nymtech/nym/pull/778) ([durch](https://github.com/durch)) - Make fee helpers public [\#777](https://github.com/nymtech/nym/pull/777) ([durch](https://github.com/durch)) - re-enable bonding [\#776](https://github.com/nymtech/nym/pull/776) ([fmtabbara](https://github.com/fmtabbara)) - Explorer-api: add API resource to show the delegations for each mix node [\#774](https://github.com/nymtech/nym/pull/774) ([mmsinclair](https://github.com/mmsinclair)) @@ -493,14 +564,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Adding deps for building the Tauri wallet under Ubuntu [\#770](https://github.com/nymtech/nym/pull/770) ([futurechimp](https://github.com/futurechimp)) - remove alert [\#767](https://github.com/nymtech/nym/pull/767) ([fmtabbara](https://github.com/fmtabbara)) - Feature/consumable bandwidth [\#766](https://github.com/nymtech/nym/pull/766) ([neacsu](https://github.com/neacsu)) -- Update coconut-rs and use hash\_to\_scalar from there [\#765](https://github.com/nymtech/nym/pull/765) ([neacsu](https://github.com/neacsu)) +- Update coconut-rs and use hash_to_scalar from there [\#765](https://github.com/nymtech/nym/pull/765) ([neacsu](https://github.com/neacsu)) - Feature/active sets [\#764](https://github.com/nymtech/nym/pull/764) ([jstuczyn](https://github.com/jstuczyn)) - add app alert banner [\#762](https://github.com/nymtech/nym/pull/762) ([fmtabbara](https://github.com/fmtabbara)) - Updated cosmos-sdk [\#761](https://github.com/nymtech/nym/pull/761) ([jstuczyn](https://github.com/jstuczyn)) - Feature/bond blockstamp [\#760](https://github.com/nymtech/nym/pull/760) ([neacsu](https://github.com/neacsu)) - Feature/revert migration code [\#759](https://github.com/nymtech/nym/pull/759) ([neacsu](https://github.com/neacsu)) - Bump next from 11.1.0 to 11.1.1 in /wallet-web [\#758](https://github.com/nymtech/nym/pull/758) ([dependabot[bot]](https://github.com/apps/dependabot)) -- Add block\_height in the Delegation structure as well [\#757](https://github.com/nymtech/nym/pull/757) ([neacsu](https://github.com/neacsu)) +- Add block_height in the Delegation structure as well [\#757](https://github.com/nymtech/nym/pull/757) ([neacsu](https://github.com/neacsu)) - Feature/add blockstamp [\#756](https://github.com/nymtech/nym/pull/756) ([neacsu](https://github.com/neacsu)) - NetworkMonitorBuilder - starting the monitor after rocket has launched [\#754](https://github.com/nymtech/nym/pull/754) ([jstuczyn](https://github.com/jstuczyn)) - Enabled validators api argument [\#753](https://github.com/nymtech/nym/pull/753) ([jstuczyn](https://github.com/jstuczyn)) @@ -512,21 +583,21 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Feature/more reliable uptime calculation [\#747](https://github.com/nymtech/nym/pull/747) ([jstuczyn](https://github.com/jstuczyn)) - Update template toml key [\#746](https://github.com/nymtech/nym/pull/746) ([neacsu](https://github.com/neacsu)) - Feature/cred after handshake [\#745](https://github.com/nymtech/nym/pull/745) ([neacsu](https://github.com/neacsu)) -- Reinstate the POST method blind\_sign [\#744](https://github.com/nymtech/nym/pull/744) ([neacsu](https://github.com/neacsu)) +- Reinstate the POST method blind_sign [\#744](https://github.com/nymtech/nym/pull/744) ([neacsu](https://github.com/neacsu)) - explorer-api: add pending field to port check response [\#742](https://github.com/nymtech/nym/pull/742) ([mmsinclair](https://github.com/mmsinclair)) - Feature/use delegation rates [\#741](https://github.com/nymtech/nym/pull/741) ([neacsu](https://github.com/neacsu)) - Feature/copy to clipboard [\#740](https://github.com/nymtech/nym/pull/740) ([fmtabbara](https://github.com/fmtabbara)) - Feature/update wallet with stake rates [\#739](https://github.com/nymtech/nym/pull/739) ([neacsu](https://github.com/neacsu)) - Add stake reward rates and bump version of client [\#738](https://github.com/nymtech/nym/pull/738) ([neacsu](https://github.com/neacsu)) - Bump next from 10.1.3 to 11.1.0 in /wallet-web [\#737](https://github.com/nymtech/nym/pull/737) ([dependabot[bot]](https://github.com/apps/dependabot)) -- Feature/nymd client integration [\#736](https://github.com/nymtech/nym/pull/736) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/nyxd client integration [\#736](https://github.com/nymtech/nym/pull/736) ([jstuczyn](https://github.com/jstuczyn)) - Bug/fix parking lot on wasm [\#735](https://github.com/nymtech/nym/pull/735) ([neacsu](https://github.com/neacsu)) - Explorer API: add new HTTP resource to decorate mix nodes with geoip locations [\#734](https://github.com/nymtech/nym/pull/734) ([mmsinclair](https://github.com/mmsinclair)) -- Feature/completing nymd client api [\#732](https://github.com/nymtech/nym/pull/732) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/completing nyxd client api [\#732](https://github.com/nymtech/nym/pull/732) ([jstuczyn](https://github.com/jstuczyn)) - Explorer API - add port check and node description/stats proxy [\#731](https://github.com/nymtech/nym/pull/731) ([mmsinclair](https://github.com/mmsinclair)) -- Feature/nymd client fee handling [\#730](https://github.com/nymtech/nym/pull/730) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/nyxd client fee handling [\#730](https://github.com/nymtech/nym/pull/730) ([jstuczyn](https://github.com/jstuczyn)) - Update DelegationCheck.tsx [\#725](https://github.com/nymtech/nym/pull/725) ([jessgess](https://github.com/jessgess)) -- Rust nymd/cosmwasm client [\#724](https://github.com/nymtech/nym/pull/724) ([jstuczyn](https://github.com/jstuczyn)) +- Rust nyxd/cosmwasm client [\#724](https://github.com/nymtech/nym/pull/724) ([jstuczyn](https://github.com/jstuczyn)) - Removed wasm feature bypassing cyclic dependencies [\#723](https://github.com/nymtech/nym/pull/723) ([jstuczyn](https://github.com/jstuczyn)) - Updated used sphinx dependency to the most recent revision [\#722](https://github.com/nymtech/nym/pull/722) ([jstuczyn](https://github.com/jstuczyn)) - update state management and validation [\#721](https://github.com/nymtech/nym/pull/721) ([fmtabbara](https://github.com/fmtabbara)) @@ -545,10 +616,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - Bond and delegation alerts [\#698](https://github.com/nymtech/nym/pull/698) ([fmtabbara](https://github.com/fmtabbara)) - Bugfix/network monitor version check [\#697](https://github.com/nymtech/nym/pull/697) ([jstuczyn](https://github.com/jstuczyn)) - Feature/other containers [\#692](https://github.com/nymtech/nym/pull/692) ([neacsu](https://github.com/neacsu)) -- Using validator API instead of nymd [\#690](https://github.com/nymtech/nym/pull/690) ([futurechimp](https://github.com/futurechimp)) +- Using validator API instead of nyxd [\#690](https://github.com/nymtech/nym/pull/690) ([futurechimp](https://github.com/futurechimp)) - Hang coconut issuance off the validator-api [\#679](https://github.com/nymtech/nym/pull/679) ([durch](https://github.com/durch)) - Update hmac and blake3 [\#673](https://github.com/nymtech/nym/pull/673) ([durch](https://github.com/durch)) - - -\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* +\* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_ diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index b86923d884..0000000000 --- a/Cargo.lock +++ /dev/null @@ -1,6980 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if 1.0.0", - "cipher 0.3.0", - "cpufeatures", - "ctr 0.8.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" -dependencies = [ - "cfg-if 1.0.0", - "cipher 0.4.3", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes 0.7.5", - "cipher 0.3.0", - "ctr 0.8.0", - "ghash", - "subtle 2.4.1", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.6", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "async-stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atoi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atoi" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bandwidth-claim-contract" -version = "0.1.0" -dependencies = [ - "schemars", - "serde", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "base64ct" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" - -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bip32" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" -dependencies = [ - "bs58", - "hmac 0.11.0", - "k256", - "once_cell", - "pbkdf2", - "rand_core 0.6.3", - "ripemd160", - "sha2 0.9.9", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "bip39" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f" -dependencies = [ - "bitcoin_hashes", - "rand 0.6.5", - "rand_core 0.4.2", - "serde", - "unicode-normalization", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -dependencies = [ - "byte-tools", - "crypto-mac 0.7.0", - "digest 0.8.1", - "opaque-debug 0.2.3", -] - -[[package]] -name = "blake3" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if 1.0.0", - "constant_time_eq", - "digest 0.10.3", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding 0.2.1", - "generic-array 0.14.5", -] - -[[package]] -name = "block-buffer" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "bls12_381" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397" -dependencies = [ - "digest 0.9.0", - "ff 0.10.1", - "group 0.10.0", - "pairing 0.20.0", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "bls12_381" -version = "0.6.0" -source = "git+https://github.com/jstuczyn/bls12_381?branch=gt-serialisation#10fb6f700bfda17c8475af3bfd31e3fec15f2278" -dependencies = [ - "digest 0.9.0", - "ff 0.11.0", - "group 0.11.0", - "pairing 0.21.0", - "rand_core 0.6.3", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -dependencies = [ - "sha2 0.9.9", -] - -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cast" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" -dependencies = [ - "rustc_version 0.4.0", -] - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" -dependencies = [ - "byteorder", - "keystream", -] - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "serde", - "time 0.1.44", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "cipher" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "bitflags", - "textwrap 0.11.0", - "unicode-width", -] - -[[package]] -name = "clap" -version = "3.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap 0.15.0", -] - -[[package]] -name = "clap_complete" -version = "3.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4179da71abd56c26b54dd0c248cc081c1f43b0a1a7e8448e28e57a29baa993d" -dependencies = [ - "clap 3.2.8", -] - -[[package]] -name = "clap_complete_fig" -version = "3.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed37b4c0c1214673eba6ad8ea31666626bf72be98ffb323067d973c48b4964b9" -dependencies = [ - "clap 3.2.8", - "clap_complete", -] - -[[package]] -name = "clap_derive" -version = "3.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" -dependencies = [ - "heck 0.4.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "client-connections" -version = "0.1.0" -dependencies = [ - "futures", - "log", -] - -[[package]] -name = "client-core" -version = "1.1.1" -dependencies = [ - "client-connections", - "config", - "crypto", - "dirs", - "futures", - "gateway-client", - "gateway-requests", - "gloo-timers", - "humantime-serde", - "log", - "nonexhaustive-delayqueue", - "nymsphinx", - "pemstore", - "rand 0.7.3", - "serde", - "sled", - "tap", - "task", - "tempfile", - "thiserror", - "tokio", - "tokio-stream", - "topology", - "url", - "validator-client", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "coconut-dkg-common" -version = "0.1.0" -dependencies = [ - "contracts-common", - "cosmwasm-std", - "cw-utils", - "multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "coconut-interface" -version = "0.1.0" -dependencies = [ - "bs58", - "getset", - "nymcoconut", - "serde", - "thiserror", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "comfy-table" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121d8a5b0346092c18a4b2fd6f620d7a06f0eb7ac0a45860939a0884bc579c56" -dependencies = [ - "crossterm", - "strum 0.24.1", - "strum_macros 0.24.3", - "unicode-width", -] - -[[package]] -name = "completions" -version = "0.1.0" -dependencies = [ - "clap 3.2.8", - "clap_complete", - "clap_complete_fig", -] - -[[package]] -name = "config" -version = "0.1.0" -dependencies = [ - "cfg-if 1.0.0", - "handlebars", - "log", - "network-defaults", - "serde", - "toml", - "url", -] - -[[package]] -name = "console-api" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a" -dependencies = [ - "prost 0.9.0", - "prost-types 0.9.0", - "tonic", - "tonic-build", - "tracing-core", -] - -[[package]] -name = "console-subscriber" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565a7dfea2d10dd0e5c57cc394d5d441b1910960d8c9211ed14135e0e6ec3a20" -dependencies = [ - "console-api", - "crossbeam-channel", - "crossbeam-utils", - "futures", - "hdrhistogram", - "humantime 2.1.0", - "prost-types 0.9.0", - "serde", - "serde_json", - "thread_local", - "tokio", - "tokio-stream", - "tonic", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "contracts-common" -version = "0.1.0" -dependencies = [ - "bs58", - "cosmwasm-std", - "dkg", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cookie" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" -dependencies = [ - "aes-gcm", - "base64", - "hkdf 0.12.3", - "hmac 0.12.1", - "percent-encoding", - "rand 0.8.5", - "sha2 0.10.2", - "subtle 2.4.1", - "time 0.3.14", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" -dependencies = [ - "prost 0.10.3", - "prost-types 0.10.1", - "tendermint-proto", -] - -[[package]] -name = "cosmrs" -version = "0.7.1" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" -dependencies = [ - "bip32", - "cosmos-sdk-proto", - "ecdsa", - "eyre", - "getrandom 0.2.6", - "k256", - "prost 0.10.3", - "prost-types 0.10.1", - "rand_core 0.6.3", - "serde", - "serde_json", - "subtle-encoding", - "tendermint", - "tendermint-rpc", - "thiserror", -] - -[[package]] -name = "cosmwasm-crypto" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" -dependencies = [ - "digest 0.9.0", - "ed25519-zebra", - "k256", - "rand_core 0.6.3", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" -dependencies = [ - "syn", -] - -[[package]] -name = "cosmwasm-std" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" -dependencies = [ - "base64", - "cosmwasm-crypto", - "cosmwasm-derive", - "forward_ref", - "schemars", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" -dependencies = [ - "crc-catalog 1.1.1", -] - -[[package]] -name = "crc" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" -dependencies = [ - "crc-catalog 2.1.0", -] - -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - -[[package]] -name = "crc-catalog" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "credential" -version = "0.1.0" -dependencies = [ - "async-trait", - "bip39", - "cfg-if 0.1.10", - "clap 3.2.8", - "coconut-interface", - "completions", - "config", - "credential-storage", - "credentials", - "crypto", - "network-defaults", - "pemstore", - "pickledb", - "rand 0.7.3", - "serde", - "thiserror", - "tokio", - "url", - "validator-client", -] - -[[package]] -name = "credential-storage" -version = "0.1.0" -dependencies = [ - "async-trait", - "log", - "sqlx 0.5.11", - "thiserror", - "tokio", -] - -[[package]] -name = "credentials" -version = "0.1.0" -dependencies = [ - "bls12_381 0.5.0", - "coconut-interface", - "cosmrs", - "crypto", - "rand 0.7.3", - "thiserror", - "validator-api-requests", - "validator-client", -] - -[[package]] -name = "criterion" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" -dependencies = [ - "atty", - "cast", - "clap 2.34.0", - "criterion-plot", - "csv", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" -dependencies = [ - "autocfg 1.1.0", - "cfg-if 1.0.0", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] - -[[package]] -name = "crossterm" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" -dependencies = [ - "bitflags", - "crossterm_winapi", - "libc", - "mio", - "parking_lot 0.12.0", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" -dependencies = [ - "winapi", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto" -version = "0.1.0" -dependencies = [ - "aes 0.8.1", - "blake3", - "bs58", - "cipher 0.4.3", - "ctr 0.9.1", - "digest 0.10.3", - "ed25519-dalek", - "generic-array 0.14.5", - "hkdf 0.12.3", - "hmac 0.12.1", - "nymsphinx-types", - "pemstore", - "rand 0.7.3", - "rand_chacha 0.2.2", - "serde", - "serde_bytes", - "subtle-encoding", - "x25519-dalek", -] - -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array 0.14.5", - "rand_core 0.6.3", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" -dependencies = [ - "generic-array 0.14.5", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.5", - "subtle 2.4.1", -] - -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct 0.6.1", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher 0.3.0", -] - -[[package]] -name = "ctr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d14f329cfbaf5d0e06b5e87fff7e265d2673c5ea7d2c27691a2c107db1442a0" -dependencies = [ - "cipher 0.4.3", -] - -[[package]] -name = "cupid" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bad352a84b567cc38a5854e3aa8ee903cb8519a25d0b799b739bafffd1f91a1" -dependencies = [ - "gcc", - "rustc_version 0.2.3", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "serde", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "cw-storage-plus" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw2" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "cw3" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "schemars", - "serde", -] - -[[package]] -name = "cw3-fixed-multisig" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "cw2", - "cw3", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw4" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if 1.0.0", - "num_cpus", -] - -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", -] - -[[package]] -name = "devise" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c7580b072f1c8476148f16e0a0d5dedddab787da98d86c5082c5e9ed8ab595" -dependencies = [ - "devise_codegen", - "devise_core", -] - -[[package]] -name = "devise_codegen" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" -dependencies = [ - "devise_core", - "quote", -] - -[[package]] -name = "devise_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" -dependencies = [ - "bitflags", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "digest" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" -dependencies = [ - "block-buffer 0.10.2", - "crypto-common", - "subtle 2.4.1", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dkg" -version = "0.1.0" -dependencies = [ - "bitvec", - "bls12_381 0.6.0", - "bs58", - "criterion", - "ff 0.11.0", - "group 0.11.0", - "lazy_static", - "pemstore", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_core 0.6.3", - "serde", - "serde_derive", - "sha2 0.9.9", - "thiserror", - "zeroize", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dotenvy" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" -dependencies = [ - "dirs", -] - -[[package]] -name = "dyn-clone" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" - -[[package]] -name = "ecdsa" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" -dependencies = [ - "serde", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "serde_bytes", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "ed25519-zebra" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" -dependencies = [ - "curve25519-dalek", - "hex", - "rand_core 0.6.3", - "serde", - "sha2 0.9.9", - "thiserror", - "zeroize", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "elliptic-curve" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff 0.11.0", - "generic-array 0.14.5", - "group 0.11.0", - "rand_core 0.6.3", - "sec1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "execute" -version = "0.1.0" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "explorer-api" -version = "1.1.1" -dependencies = [ - "chrono", - "clap 3.2.8", - "contracts-common", - "dotenv", - "humantime-serde", - "isocountry", - "itertools", - "log", - "logging", - "maxminddb", - "mixnet-contract-common", - "network-defaults", - "okapi", - "pretty_env_logger", - "reqwest", - "rocket", - "rocket_cors", - "rocket_okapi", - "schemars", - "serde", - "serde_json", - "task", - "thiserror", - "tokio", - "validator-client", -] - -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - -[[package]] -name = "ff" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" -dependencies = [ - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "ff" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" -dependencies = [ - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "figment" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df" -dependencies = [ - "atomic", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] -name = "fixedbitset" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if 1.0.0", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "flex-error" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" -dependencies = [ - "eyre", - "paste", -] - -[[package]] -name = "flume" -version = "0.10.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843c03199d0c0ca54bc1ea90ac0d507274c28abcc4f691ae8b4eaa375087c76a" -dependencies = [ - "futures-core", - "futures-sink", - "pin-project", - "spin 0.9.2", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" - -[[package]] -name = "futures-executor" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot 0.11.2", -] - -[[package]] -name = "futures-io" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" - -[[package]] -name = "futures-macro" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" - -[[package]] -name = "futures-task" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" - -[[package]] -name = "futures-util" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gateway-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "coconut-interface", - "credential-storage", - "credentials", - "crypto", - "futures", - "gateway-requests", - "getrandom 0.2.6", - "log", - "network-defaults", - "nymsphinx", - "pemstore", - "rand 0.7.3", - "task", - "thiserror", - "tokio", - "tokio-stream", - "tokio-tungstenite 0.14.0", - "tungstenite 0.13.0", - "url", - "validator-client", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", - "wasm-utils", -] - -[[package]] -name = "gateway-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "coconut-interface", - "credentials", - "crypto", - "futures", - "log", - "nymsphinx", - "pemstore", - "rand 0.7.3", - "serde", - "serde_json", - "thiserror", - "tungstenite 0.13.0", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - -[[package]] -name = "generator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1d9279ca822891c1a4dae06d185612cf8fc6acfe5dff37781b41297811b12ee" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "winapi", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getset" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug 0.3.0", - "polyval", -] - -[[package]] -name = "git2" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3826a6e0e2215d7a41c2bfc7c9244123969273f3476b939a226aac0ab56e9e3c" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "url", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "gloo-timers" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" -dependencies = [ - "byteorder", - "ff 0.10.1", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "group" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" -dependencies = [ - "byteorder", - "ff 0.11.0", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "h2" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util 0.7.3", - "tracing", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "handlebars" -version = "3.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" -dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error 2.0.1", - "serde", - "serde_json", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - -[[package]] -name = "hashlink" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "hdrhistogram" -version = "7.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31672b7011be2c4f7456c4ddbcb40e7e9a4a9fad8efe49a6ebaf5f307d0109c0" -dependencies = [ - "base64", - "byteorder", - "flate2", - "nom", - "num-traits", -] - -[[package]] -name = "headers" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" -dependencies = [ - "base64", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha-1 0.10.0", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "hidapi" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d26e1151deaab68f34fbfd16d491a2a0170cf98d69d3efa23873b567a4199e1" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "hkdf" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" -dependencies = [ - "digest 0.9.0", - "hmac 0.11.0", -] - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.3", -] - -[[package]] -name = "http" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.1", -] - -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime 2.1.0", - "serde", -] - -[[package]] -name = "hyper" -version = "0.14.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 1.0.1", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-proxy" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" -dependencies = [ - "bytes", - "futures", - "headers", - "http", - "hyper", - "hyper-rustls", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "tower-service", - "webpki 0.21.4", -] - -[[package]] -name = "hyper-rustls" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" -dependencies = [ - "ct-logs", - "futures-util", - "hyper", - "log", - "rustls 0.19.1", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "inclusion-probability" -version = "0.1.0" -dependencies = [ - "log", - "rand 0.8.5", - "thiserror", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" -dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.11.2", - "serde", -] - -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "ipnet" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" - -[[package]] -name = "ipnetwork" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4088d739b183546b239688ddbc79891831df421773df95e236daf7867866d355" -dependencies = [ - "serde", -] - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", -] - -[[package]] -name = "isocountry" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea1dc4bf0fb4904ba83ffdb98af3d9c325274e92e6e295e4151e86c96363e04" -dependencies = [ - "serde", - "thiserror", -] - -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" -dependencies = [ - "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" - -[[package]] -name = "keystream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "ledger" -version = "0.1.0" -dependencies = [ - "bip32", - "k256", - "ledger-transport", - "ledger-transport-hid", - "thiserror", -] - -[[package]] -name = "ledger-apdu" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe435806c197dfeaa5efcded5e623c4b8230fd28fdf1e91e7a86e40ef2acbf90" -dependencies = [ - "arrayref", - "no-std-compat", - "snafu 0.7.1", -] - -[[package]] -name = "ledger-transport" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1117f2143d92c157197785bf57711d7b02f2cfa101e162f8ca7900fb7f976321" -dependencies = [ - "async-trait", - "ledger-apdu", -] - -[[package]] -name = "ledger-transport-hid" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ba81a1f5f24396b37211478aff7fbcd605dd4544df8dbed07b9da3c2057aee" -dependencies = [ - "byteorder", - "cfg-if 1.0.0", - "hex", - "hidapi", - "ledger-transport", - "libc", - "log", - "thiserror", -] - -[[package]] -name = "libc" -version = "0.2.122" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" - -[[package]] -name = "libgit2-sys" -version = "0.13.2+1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" - -[[package]] -name = "libsqlite3-sys" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "lioness" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" -dependencies = [ - "arrayref", - "blake2", - "chacha", - "keystream", -] - -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg 1.1.0", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "logging" -version = "0.1.0" -dependencies = [ - "log", - "pretty_env_logger", -] - -[[package]] -name = "loom" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309" -dependencies = [ - "cfg-if 1.0.0", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "maxminddb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe2ba61113f9f7a9f0e87c519682d39c43a6f3f79c2cc42c3ba3dda83b1fa334" -dependencies = [ - "ipnetwork 0.18.0", - "log", - "memchr", - "serde", -] - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg 1.1.0", -] - -[[package]] -name = "mio" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", -] - -[[package]] -name = "mixnet-client" -version = "0.1.0" -dependencies = [ - "futures", - "log", - "nymsphinx", - "tokio", - "tokio-util 0.7.3", -] - -[[package]] -name = "mixnet-contract-common" -version = "0.1.0" -dependencies = [ - "bs58", - "contracts-common", - "cosmwasm-std", - "humantime-serde", - "log", - "rand_chacha 0.3.1", - "schemars", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "time 0.3.14", - "ts-rs", -] - -[[package]] -name = "mixnode-common" -version = "0.1.0" -dependencies = [ - "bytes", - "crypto", - "futures", - "humantime-serde", - "log", - "nymsphinx-acknowledgements", - "nymsphinx-addressing", - "nymsphinx-forwarding", - "nymsphinx-framing", - "nymsphinx-params", - "nymsphinx-types", - "rand 0.8.5", - "serde", - "task", - "tokio", - "tokio-util 0.7.3", - "url", - "validator-client", - "version-checker", -] - -[[package]] -name = "multer" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8f35e687561d5c1667590911e6698a8cb714a134a7505718a182e7bc9d3836" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "log", - "memchr", - "mime", - "spin 0.9.2", - "tokio", - "tokio-util 0.6.9", - "version_check", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "multisig-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "cw3", - "cw3-fixed-multisig", - "cw4", - "schemars", - "serde", -] - -[[package]] -name = "native-tls" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "network-defaults" -version = "0.1.0" -dependencies = [ - "cfg-if 1.0.0", - "dotenv", - "hex-literal", - "once_cell", - "serde", - "thiserror", - "url", -] - -[[package]] -name = "no-std-compat" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nonexhaustive-delayqueue" -version = "0.1.0" -dependencies = [ - "futures-core", - "slab", - "tokio", - "tokio-stream", - "tokio-util 0.7.3", - "wasm-timer", -] - -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg 1.1.0", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg 1.1.0", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" -dependencies = [ - "libc", -] - -[[package]] -name = "nym-bity-integration" -version = "0.1.0" -dependencies = [ - "anyhow", - "cosmrs", - "eyre", - "k256", - "nym-cli-commands", - "serde", - "serde_json", - "thiserror", - "validator-client", -] - -[[package]] -name = "nym-cli" -version = "1.1.1" -dependencies = [ - "anyhow", - "base64", - "bip39", - "bs58", - "clap 3.2.8", - "clap_complete", - "clap_complete_fig", - "dotenv", - "log", - "logging", - "network-defaults", - "nym-cli-commands", - "pretty_env_logger", - "serde", - "serde_json", - "tap", - "tokio", - "validator-client", -] - -[[package]] -name = "nym-cli-commands" -version = "1.0.0" -dependencies = [ - "base64", - "bip39", - "bs58", - "cfg-if 1.0.0", - "clap 3.2.8", - "coconut-bandwidth-contract-common", - "coconut-dkg-common", - "comfy-table", - "cosmrs", - "cosmwasm-std", - "cw-utils", - "handlebars", - "humantime-serde", - "k256", - "log", - "mixnet-contract-common", - "multisig-contract-common", - "network-defaults", - "rand 0.6.5", - "serde", - "serde_json", - "tap", - "thiserror", - "time 0.3.14", - "toml", - "url", - "validator-client", - "vesting-contract-common", -] - -[[package]] -name = "nym-client" -version = "1.1.1" -dependencies = [ - "clap 3.2.8", - "client-connections", - "client-core", - "coconut-interface", - "completions", - "config", - "credential-storage", - "credentials", - "crypto", - "dirs", - "futures", - "gateway-client", - "gateway-requests", - "log", - "logging", - "network-defaults", - "nymsphinx", - "pemstore", - "pretty_env_logger", - "rand 0.7.3", - "serde", - "serde_json", - "sled", - "tap", - "task", - "thiserror", - "tokio", - "tokio-tungstenite 0.14.0", - "topology", - "url", - "validator-client", - "vergen 5.1.17", - "version-checker", - "websocket-requests", -] - -[[package]] -name = "nym-gateway" -version = "1.1.1" -dependencies = [ - "anyhow", - "async-trait", - "bip39", - "bs58", - "clap 3.2.8", - "coconut-interface", - "colored", - "completions", - "config", - "credentials", - "crypto", - "dashmap", - "dirs", - "dotenv", - "futures", - "gateway-requests", - "humantime-serde", - "log", - "logging", - "mixnet-client", - "mixnode-common", - "network-defaults", - "nymsphinx", - "once_cell", - "pemstore", - "pretty_env_logger", - "rand 0.7.3", - "serde", - "sqlx 0.5.11", - "statistics-common", - "subtle-encoding", - "thiserror", - "tokio", - "tokio-stream", - "tokio-tungstenite 0.14.0", - "tokio-util 0.7.3", - "url", - "validator-api-requests", - "validator-client", - "vergen 5.1.17", - "version-checker", -] - -[[package]] -name = "nym-mixnode" -version = "1.1.1" -dependencies = [ - "anyhow", - "bs58", - "clap 3.2.8", - "colored", - "completions", - "config", - "crypto", - "cupid", - "dirs", - "dotenv", - "futures", - "humantime-serde", - "lazy_static", - "log", - "logging", - "mixnet-client", - "mixnode-common", - "nonexhaustive-delayqueue", - "nymsphinx", - "nymsphinx-params", - "nymsphinx-types", - "pemstore", - "pretty_env_logger", - "rand 0.7.3", - "rocket", - "serde", - "sysinfo", - "task", - "tokio", - "tokio-util 0.7.3", - "toml", - "topology", - "url", - "validator-client", - "vergen 5.1.17", - "version-checker", -] - -[[package]] -name = "nym-network-requester" -version = "1.1.1" -dependencies = [ - "async-trait", - "clap 3.2.8", - "client-connections", - "completions", - "dirs", - "futures", - "ipnetwork 0.20.0", - "log", - "logging", - "network-defaults", - "nymsphinx", - "ordered-buffer", - "pretty_env_logger", - "proxy-helpers", - "publicsuffix", - "rand 0.7.3", - "reqwest", - "serde", - "socks5-requests", - "sqlx 0.6.2", - "statistics-common", - "task", - "thiserror", - "tokio", - "tokio-tungstenite 0.17.2", - "websocket-requests", -] - -[[package]] -name = "nym-network-statistics" -version = "1.0.2" -dependencies = [ - "dirs", - "log", - "logging", - "pretty_env_logger", - "rocket", - "serde", - "sqlx 0.5.11", - "statistics-common", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-socks5-client" -version = "1.1.1" -dependencies = [ - "clap 3.2.8", - "client-connections", - "client-core", - "coconut-interface", - "completions", - "config", - "credential-storage", - "credentials", - "crypto", - "dirs", - "futures", - "gateway-client", - "gateway-requests", - "log", - "logging", - "network-defaults", - "nymsphinx", - "ordered-buffer", - "pemstore", - "pin-project", - "pretty_env_logger", - "proxy-helpers", - "rand 0.7.3", - "serde", - "snafu 0.6.10", - "socks5-requests", - "tap", - "task", - "thiserror", - "tokio", - "topology", - "url", - "validator-client", - "vergen 5.1.17", - "version-checker", -] - -[[package]] -name = "nym-types" -version = "1.0.0" -dependencies = [ - "coconut-interface", - "config", - "cosmrs", - "cosmwasm-std", - "eyre", - "itertools", - "log", - "mixnet-contract-common", - "reqwest", - "schemars", - "serde", - "serde_json", - "strum 0.23.0", - "tempfile", - "thiserror", - "ts-rs", - "url", - "validator-client", - "vesting-contract", - "vesting-contract-common", -] - -[[package]] -name = "nym-validator-api" -version = "1.1.1" -dependencies = [ - "anyhow", - "async-trait", - "bs58", - "cfg-if 1.0.0", - "clap 3.2.8", - "coconut-bandwidth-contract-common", - "coconut-dkg-common", - "coconut-interface", - "config", - "console-subscriber", - "contracts-common", - "cosmwasm-std", - "credential-storage", - "credentials", - "crypto", - "cw-utils", - "cw3", - "dirs", - "dkg", - "dotenv", - "futures", - "gateway-client", - "getset", - "humantime-serde", - "inclusion-probability", - "log", - "logging", - "mixnet-contract-common", - "multisig-contract-common", - "nymcoconut", - "nymsphinx", - "okapi", - "pemstore", - "pin-project", - "pretty_env_logger", - "rand 0.7.3", - "rand 0.8.5", - "reqwest", - "rocket", - "rocket_cors", - "rocket_okapi", - "schemars", - "serde", - "serde_json", - "sqlx 0.6.2", - "tap", - "task", - "thiserror", - "time 0.3.14", - "tokio", - "tokio-stream", - "topology", - "ts-rs", - "url", - "validator-api-requests", - "validator-client", - "vergen 7.2.1", - "version-checker", -] - -[[package]] -name = "nym-wallet-types" -version = "1.0.0" -dependencies = [ - "config", - "cosmrs", - "cosmwasm-std", - "hex-literal", - "mixnet-contract-common", - "network-defaults", - "nym-types", - "serde", - "serde_json", - "strum 0.23.0", - "ts-rs", - "validator-client", - "vesting-contract", - "vesting-contract-common", -] - -[[package]] -name = "nymcoconut" -version = "0.5.0" -dependencies = [ - "bincode", - "bls12_381 0.6.0", - "bs58", - "criterion", - "digest 0.9.0", - "dkg", - "doc-comment", - "ff 0.11.0", - "getrandom 0.2.6", - "group 0.11.0", - "itertools", - "pemstore", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "serde_derive", - "sha2 0.9.9", - "thiserror", -] - -[[package]] -name = "nymsphinx" -version = "0.1.0" -dependencies = [ - "crypto", - "mixnet-contract-common", - "nymsphinx-acknowledgements", - "nymsphinx-addressing", - "nymsphinx-anonymous-replies", - "nymsphinx-chunking", - "nymsphinx-cover", - "nymsphinx-forwarding", - "nymsphinx-framing", - "nymsphinx-params", - "nymsphinx-types", - "rand 0.7.3", - "rand_distr", - "tokio", - "topology", -] - -[[package]] -name = "nymsphinx-acknowledgements" -version = "0.1.0" -dependencies = [ - "crypto", - "nymsphinx-addressing", - "nymsphinx-params", - "nymsphinx-types", - "pemstore", - "rand 0.7.3", - "topology", -] - -[[package]] -name = "nymsphinx-addressing" -version = "0.1.0" -dependencies = [ - "crypto", - "nymsphinx-types", - "rand 0.7.3", - "serde", -] - -[[package]] -name = "nymsphinx-anonymous-replies" -version = "0.1.0" -dependencies = [ - "bs58", - "crypto", - "nymsphinx-addressing", - "nymsphinx-params", - "nymsphinx-types", - "rand 0.7.3", - "serde", - "topology", -] - -[[package]] -name = "nymsphinx-chunking" -version = "0.1.0" -dependencies = [ - "log", - "nymsphinx-addressing", - "nymsphinx-params", - "nymsphinx-types", - "rand 0.7.3", -] - -[[package]] -name = "nymsphinx-cover" -version = "0.1.0" -dependencies = [ - "crypto", - "nymsphinx-acknowledgements", - "nymsphinx-addressing", - "nymsphinx-chunking", - "nymsphinx-forwarding", - "nymsphinx-params", - "nymsphinx-types", - "rand 0.7.3", - "topology", -] - -[[package]] -name = "nymsphinx-forwarding" -version = "0.1.0" -dependencies = [ - "nymsphinx-addressing", - "nymsphinx-params", - "nymsphinx-types", -] - -[[package]] -name = "nymsphinx-framing" -version = "0.1.0" -dependencies = [ - "bytes", - "nymsphinx-params", - "nymsphinx-types", - "tokio-util 0.7.3", -] - -[[package]] -name = "nymsphinx-params" -version = "0.1.0" -dependencies = [ - "crypto", - "nymsphinx-types", -] - -[[package]] -name = "nymsphinx-types" -version = "0.1.0" -dependencies = [ - "sphinx", -] - -[[package]] -name = "okapi" -version = "0.7.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce66b6366e049880a35c378123fddb630b1a1a3c37fa1ca70caaf4a09f6e2893" -dependencies = [ - "log", - "schemars", - "serde", - "serde_json", -] - -[[package]] -name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" -dependencies = [ - "autocfg 1.1.0", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-buffer" -version = "0.1.0" -dependencies = [ - "log", -] - -[[package]] -name = "os_str_bytes" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" - -[[package]] -name = "pairing" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3" -dependencies = [ - "group 0.10.0", -] - -[[package]] -name = "pairing" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" -dependencies = [ - "group 0.11.0", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.5", -] - -[[package]] -name = "parking_lot" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.2", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec 1.8.0", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "smallvec 1.8.0", - "windows-sys 0.34.0", -] - -[[package]] -name = "paste" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac 0.11.1", -] - -[[package]] -name = "pear" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "peg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64", - "once_cell", - "regex", -] - -[[package]] -name = "pemstore" -version = "0.1.0" -dependencies = [ - "pem", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" -dependencies = [ - "maplit", - "pest", - "sha-1 0.8.2", -] - -[[package]] -name = "petgraph" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "pickledb" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9161694d67f6c5163519d42be942ae36bbdb55f439460144f105bc4f9f7d1d61" -dependencies = [ - "bincode", - "serde", - "serde_cbor", - "serde_json", - "serde_yaml", -] - -[[package]] -name = "pin-project" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "plotters" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" - -[[package]] -name = "plotters-svg" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - -[[package]] -name = "prost" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" -dependencies = [ - "bytes", - "prost-derive 0.9.0", -] - -[[package]] -name = "prost" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc03e116981ff7d8da8e5c220e374587b98d294af7ba7dd7fda761158f00086f" -dependencies = [ - "bytes", - "prost-derive 0.10.1", -] - -[[package]] -name = "prost-build" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" -dependencies = [ - "bytes", - "heck 0.3.3", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prost 0.9.0", - "prost-types 0.9.0", - "regex", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-derive" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-types" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" -dependencies = [ - "bytes", - "prost 0.9.0", -] - -[[package]] -name = "prost-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" -dependencies = [ - "bytes", - "prost 0.10.3", -] - -[[package]] -name = "proxy-helpers" -version = "0.1.0" -dependencies = [ - "bytes", - "client-connections", - "futures", - "log", - "ordered-buffer", - "socks5-requests", - "task", - "tokio", - "tokio-test", - "tokio-util 0.7.3", -] - -[[package]] -name = "publicsuffix" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" -dependencies = [ - "error-chain", - "idna", - "lazy_static", - "native-tls", - "regex", - "url", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quote" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.6", -] - -[[package]] -name = "rand_distr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56" -dependencies = [ - "num-traits", - "rand 0.7.3", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg 1.1.0", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.6", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "ref-cast" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "regex" -version = "1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" -dependencies = [ - "crypto-bigint", - "hmac 0.11.0", - "zeroize", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "rocket" -version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317" -dependencies = [ - "async-stream", - "async-trait", - "atomic", - "atty", - "binascii", - "bytes", - "either", - "figment", - "futures", - "indexmap", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot 0.12.0", - "pin-project-lite", - "rand 0.8.5", - "ref-cast", - "rocket_codegen", - "rocket_http", - "serde", - "serde_json", - "state", - "tempfile", - "time 0.3.14", - "tokio", - "tokio-stream", - "tokio-util 0.7.3", - "ubyte", - "version_check", - "yansi", -] - -[[package]] -name = "rocket_codegen" -version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" -dependencies = [ - "devise", - "glob", - "indexmap", - "proc-macro2", - "quote", - "rocket_http", - "syn", - "unicode-xid", -] - -[[package]] -name = "rocket_cors" -version = "0.5.2" -source = "git+https://github.com/lawliet89/rocket_cors?rev=dfd3662c49e2f6fc37df35091cb94d82f7fb5915#dfd3662c49e2f6fc37df35091cb94d82f7fb5915" -dependencies = [ - "log", - "regex", - "rocket", - "serde", - "serde_derive", - "unicase", - "unicase_serde", - "url", -] - -[[package]] -name = "rocket_http" -version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2" -dependencies = [ - "cookie", - "either", - "futures", - "http", - "hyper", - "indexmap", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", - "serde", - "smallvec 1.8.0", - "stable-pattern", - "state", - "time 0.3.14", - "tokio", - "uncased", -] - -[[package]] -name = "rocket_okapi" -version = "0.8.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f4f5b120762f7974e65b919fc462d0660fd8b839026d8985b850fe5acccb0" -dependencies = [ - "either", - "log", - "okapi", - "rocket", - "rocket_okapi_codegen", - "schemars", - "serde", - "serde_json", -] - -[[package]] -name = "rocket_okapi_codegen" -version = "0.8.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f94d1ffe41472e08463d7a2674f1db04dc4df745285e8369b33d3cfd6b0308" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "rocket_http", - "syn", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.7", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64", - "log", - "ring", - "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]] -name = "rustls-native-certs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" -dependencies = [ - "openssl-probe", - "rustls 0.19.1", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" -dependencies = [ - "base64", -] - -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - -[[package]] -name = "schemars" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" -dependencies = [ - "dyn-clone", - "indexmap", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" -dependencies = [ - "der", - "generic-array 0.14.5", - "pkcs8", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", -] - -[[package]] -name = "semver" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" -dependencies = [ - "itoa 1.0.1", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.1", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" -dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", -] - -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.3", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.3", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" -dependencies = [ - "digest 0.9.0", - "rand_core 0.6.3", -] - -[[package]] -name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - -[[package]] -name = "sled" -version = "0.34.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" -dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", - "libc", - "log", - "parking_lot 0.11.2", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" - -[[package]] -name = "snafu" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" -dependencies = [ - "doc-comment", - "snafu-derive 0.6.10", -] - -[[package]] -name = "snafu" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2" -dependencies = [ - "doc-comment", - "snafu-derive 0.7.1", -] - -[[package]] -name = "snafu-derive" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "snafu-derive" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5" -dependencies = [ - "heck 0.4.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socks5-requests" -version = "0.1.0" -dependencies = [ - "nymsphinx-addressing", - "thiserror", -] - -[[package]] -name = "sphinx" -version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=c494250f2a78bed33a618d470792418eee932859#c494250f2a78bed33a618d470792418eee932859" -dependencies = [ - "aes 0.7.5", - "arrayref", - "blake2", - "bs58", - "byteorder", - "chacha", - "curve25519-dalek", - "digest 0.9.0", - "hkdf 0.11.0", - "hmac 0.11.0", - "lioness", - "log", - "rand 0.7.3", - "rand_distr", - "sha2 0.9.9", - "subtle 2.4.1", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlformat" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlformat" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826" -dependencies = [ - "sqlx-core 0.5.13", - "sqlx-macros 0.5.11", -] - -[[package]] -name = "sqlx" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" -dependencies = [ - "sqlx-core 0.6.2", - "sqlx-macros 0.6.2", -] - -[[package]] -name = "sqlx-core" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" -dependencies = [ - "ahash", - "atoi 0.4.0", - "bitflags", - "byteorder", - "bytes", - "chrono", - "crc 2.1.0", - "crossbeam-queue", - "either", - "event-listener", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "hashlink 0.7.0", - "hex", - "indexmap", - "itoa 1.0.1", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.19.1", - "sha2 0.10.2", - "smallvec 1.8.0", - "sqlformat 0.1.8", - "sqlx-rt 0.5.13", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "sqlx-core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" -dependencies = [ - "ahash", - "atoi 1.0.0", - "bitflags", - "byteorder", - "bytes", - "chrono", - "crc 3.0.0", - "crossbeam-queue", - "dotenvy", - "either", - "event-listener", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "hashlink 0.8.1", - "hex", - "indexmap", - "itoa 1.0.1", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.20.6", - "rustls-pemfile", - "sha2 0.10.2", - "smallvec 1.8.0", - "sqlformat 0.2.0", - "sqlx-rt 0.6.2", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "webpki-roots 0.22.4", -] - -[[package]] -name = "sqlx-macros" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee35713129561f5e55c554bba1c378e2a7e67f81257b7311183de98c50e6f94" -dependencies = [ - "dotenv", - "either", - "heck 0.3.3", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.9.9", - "sqlx-core 0.5.13", - "sqlx-rt 0.5.13", - "syn", - "url", -] - -[[package]] -name = "sqlx-macros" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" -dependencies = [ - "dotenvy", - "either", - "heck 0.4.0", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.10.2", - "sqlx-core 0.6.2", - "sqlx-rt 0.6.2", - "syn", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls 0.22.0", -] - -[[package]] -name = "sqlx-rt" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls 0.23.4", -] - -[[package]] -name = "stable-pattern" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" -dependencies = [ - "memchr", -] - -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "statistics-common" -version = "1.0.1" -dependencies = [ - "async-trait", - "log", - "reqwest", - "serde", - "serde_json", - "sqlx 0.5.11", - "thiserror", - "tokio", -] - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" -dependencies = [ - "strum_macros 0.23.1", -] - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.0", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-encoding" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" -dependencies = [ - "zeroize", -] - -[[package]] -name = "syn" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "sysinfo" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a8e71535da31837213ac114531d31def75d7aebd133264e420a3451fa7f703" -dependencies = [ - "cfg-if 1.0.0", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "winapi", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "task" -version = "0.1.0" -dependencies = [ - "futures", - "log", - "thiserror", - "tokio", -] - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "tendermint" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" -dependencies = [ - "async-trait", - "bytes", - "ed25519", - "ed25519-dalek", - "flex-error", - "futures", - "k256", - "num-traits", - "once_cell", - "prost 0.10.3", - "prost-types 0.10.1", - "ripemd160", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.9.9", - "signature", - "subtle 2.4.1", - "subtle-encoding", - "tendermint-proto", - "time 0.3.14", - "zeroize", -] - -[[package]] -name = "tendermint-config" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" -dependencies = [ - "flex-error", - "serde", - "serde_json", - "tendermint", - "toml", - "url", -] - -[[package]] -name = "tendermint-proto" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost 0.10.3", - "prost-types 0.10.1", - "serde", - "serde_bytes", - "subtle-encoding", - "time 0.3.14", -] - -[[package]] -name = "tendermint-rpc" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" -dependencies = [ - "async-trait", - "bytes", - "flex-error", - "futures", - "getrandom 0.2.6", - "http", - "hyper", - "hyper-proxy", - "hyper-rustls", - "peg", - "pin-project", - "serde", - "serde_bytes", - "serde_json", - "subtle-encoding", - "tendermint", - "tendermint-config", - "tendermint-proto", - "thiserror", - "time 0.3.14", - "tokio", - "tracing", - "url", - "uuid", - "walkdir", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - -[[package]] -name = "thiserror" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" -dependencies = [ - "itoa 1.0.1", - "libc", - "num_threads", - "serde", - "time-macros", -] - -[[package]] -name = "time-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tokio" -version = "1.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" -dependencies = [ - "autocfg 1.1.0", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "parking_lot 0.12.0", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "tracing", - "winapi", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls 0.19.1", - "tokio", - "webpki 0.21.4", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.6", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-stream" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util 0.7.3", -] - -[[package]] -name = "tokio-test" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3" -dependencies = [ - "async-stream", - "bytes", - "futures-core", - "tokio", - "tokio-stream", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2" -dependencies = [ - "futures-util", - "log", - "pin-project", - "tokio", - "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]] -name = "tokio-util" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "slab", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tonic" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" -dependencies = [ - "async-stream", - "async-trait", - "base64", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost 0.9.0", - "prost-derive 0.9.0", - "tokio", - "tokio-stream", - "tokio-util 0.6.9", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", -] - -[[package]] -name = "tonic-build" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" -dependencies = [ - "proc-macro2", - "prost-build", - "quote", - "syn", -] - -[[package]] -name = "topology" -version = "0.1.0" -dependencies = [ - "bs58", - "crypto", - "log", - "mixnet-contract-common", - "nymsphinx-addressing", - "nymsphinx-types", - "rand 0.7.3", - "version-checker", -] - -[[package]] -name = "tower" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" -dependencies = [ - "futures-core", - "futures-util", - "indexmap", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util 0.7.3", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b9fa4360528139bc96100c160b7ae879f5567f49f1782b0b02035b0358ebf3" -dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" -dependencies = [ - "lazy_static", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596" -dependencies = [ - "ansi_term", - "lazy_static", - "matchers", - "regex", - "sharded-slab", - "smallvec 1.8.0", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "ts-rs" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d26cba30c9b3a2f537f765cf754126f11c983b7426d280ae0b4cef2374cab98" -dependencies = [ - "thiserror", - "ts-rs-macros", -] - -[[package]] -name = "ts-rs-cli" -version = "0.1.0" -dependencies = [ - "anyhow", - "mixnet-contract-common", - "nym-types", - "nym-wallet-types", - "ts-rs", - "validator-api-requests", - "validator-client", - "vesting-contract-common", - "walkdir", -] - -[[package]] -name = "ts-rs-macros" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8e302fbcf5b60dfa1df443535967511442c5ce4eac8b4c9fa811f2274280a4" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn", - "termcolor", -] - -[[package]] -name = "tungstenite" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" -dependencies = [ - "base64", - "byteorder", - "bytes", - "http", - "httparse", - "input_buffer", - "log", - "rand 0.8.5", - "sha-1 0.9.8", - "thiserror", - "url", - "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", - "sha-1 0.10.0", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "ubyte" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42756bb9e708855de2f8a98195643dff31a97f0485d90d8467b39dc24be9e8fe" -dependencies = [ - "serde", -] - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "uncased" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" -dependencies = [ - "serde", - "version_check", -] - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicase_serde" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1" -dependencies = [ - "serde", - "unicase", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" - -[[package]] -name = "unicode-normalization" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" -dependencies = [ - "smallvec 0.6.14", -] - -[[package]] -name = "unicode-segmentation" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array 0.14.5", - "subtle 2.4.1", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - -[[package]] -name = "validator-api-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "coconut-interface", - "cosmrs", - "cosmwasm-std", - "getset", - "mixnet-contract-common", - "schemars", - "serde", - "ts-rs", -] - -[[package]] -name = "validator-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "base64", - "bip39", - "coconut-bandwidth-contract-common", - "coconut-dkg-common", - "coconut-interface", - "colored", - "config", - "contracts-common", - "cosmrs", - "cosmwasm-std", - "cw3", - "execute", - "flate2", - "futures", - "itertools", - "log", - "mixnet-contract-common", - "multisig-contract-common", - "network-defaults", - "prost 0.10.3", - "reqwest", - "serde", - "serde_json", - "sha2 0.9.9", - "thiserror", - "tokio", - "ts-rs", - "url", - "validator-api-requests", - "vesting-contract", - "vesting-contract-common", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vergen" -version = "5.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e" -dependencies = [ - "anyhow", - "cfg-if 1.0.0", - "chrono", - "enum-iterator", - "getset", - "git2", - "rustc_version 0.4.0", - "rustversion", - "thiserror", -] - -[[package]] -name = "vergen" -version = "7.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f44ef1afcf5979e34748c12595f9589f3dc4e34abf156fb6d95f9b835568dc" -dependencies = [ - "anyhow", - "cfg-if 1.0.0", - "enum-iterator", - "getset", - "git2", - "rustc_version 0.4.0", - "rustversion", - "thiserror", - "time 0.3.14", -] - -[[package]] -name = "version-checker" -version = "0.1.0" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vesting-contract" -version = "1.1.0" -dependencies = [ - "contracts-common", - "cosmwasm-std", - "cw-storage-plus", - "mixnet-contract-common", - "schemars", - "serde", - "thiserror", - "vergen 5.1.17", - "vesting-contract-common", -] - -[[package]] -name = "vesting-contract-common" -version = "0.1.0" -dependencies = [ - "contracts-common", - "cosmwasm-std", - "mixnet-contract-common", - "schemars", - "serde", - "ts-rs", -] - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "git+https://github.com/mmsinclair/wasm-timer?rev=b9d1a54ad514c2f230a026afe0dde341e98cd7b6#b9d1a54ad514c2f230a026afe0dde341e98cd7b6" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-utils" -version = "0.1.0" -dependencies = [ - "futures", - "js-sys", - "tungstenite 0.13.0", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "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 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]] -name = "websocket-requests" -version = "0.1.0" -dependencies = [ - "nymsphinx", - "serde", - "serde_json", -] - -[[package]] -name = "which" -version = "4.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" -dependencies = [ - "either", - "lazy_static", - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" -dependencies = [ - "windows_aarch64_msvc 0.34.0", - "windows_i686_gnu 0.34.0", - "windows_i686_msvc 0.34.0", - "windows_x86_64_gnu 0.34.0", - "windows_x86_64_msvc 0.34.0", -] - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "serde", - "zeroize", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zeroize" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] diff --git a/Cargo.toml b/Cargo.toml index e03bd46aeb..e756fc7bf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ "common/coconut-interface", "common/commands", "common/config", + "common/build-information", "common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/contracts-common", @@ -74,8 +75,9 @@ members = [ "mixnode", "service-providers/network-requester", "service-providers/network-statistics", - "validator-api", - "validator-api/validator-api-requests", + "nym-api", + "nym-api/nym-api-requests", + "nym-outfox", "tools/nym-cli", "tools/ts-rs-cli" ] @@ -87,7 +89,7 @@ default-members = [ "service-providers/network-requester", "service-providers/network-statistics", "mixnode", - "validator-api", + "nym-api", "explorer-api", ] diff --git a/Makefile b/Makefile index 8f5aba2678..883be67453 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ clippy-coconut: cargo clippy --workspace --features coconut -- -D warnings clippy-wasm: - cargo clippy --workspace --features wasm -- -D warnings + cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings clippy-all-contracts: @@ -49,9 +49,6 @@ test-main: test-coconut: cargo test --workspace --features coconut -test-wasm: - cargo test --workspace --features wasm - test-main-expensive: cargo test --workspace -- --ignored diff --git a/clients/client-core/Cargo.toml b/clients/client-core/Cargo.toml index 3d84983358..2c36fb892b 100644 --- a/clients/client-core/Cargo.toml +++ b/clients/client-core/Cargo.toml @@ -1,23 +1,27 @@ [package] name = "client-core" -version = "1.1.1" +version = "1.1.4" authors = ["Dave Hrycyszyn "] edition = "2021" +rust-version = "1.66" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = { version = "0.1.58" } dirs = "4.0" +dashmap = "5.4.0" futures = "0.3" humantime-serde = "1.0" log = "0.4" rand = { version = "0.7.3", features = ["wasm-bindgen"] } serde = { version = "1.0", features = ["derive"] } -sled = { version = "0.34", optional = true } +serde_json = "1.0.89" tap = "1.0.1" thiserror = "1.0.34" url = { version ="2.2", features = ["serde"] } -tokio = { version = "1.21.2", features = ["time", "macros"]} +tokio = { version = "1.21.2", features = ["macros"]} +time = "0.3.17" # internal config = { path = "../../common/config" } @@ -37,6 +41,15 @@ task = { path = "../../common/task" } version = "0.1.9" features = ["time"] +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] +version = "1.21.2" +features = ["time"] + +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx] +version = "0.6.2" +features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] +optional = true + [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures] version = "0.4" @@ -51,15 +64,23 @@ rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6" version = "0.2.4" features = ["futures"] -#[target."cfg(not(target_arch = \"wasm32\"))".dependencies.task] -#path = "../../common/task" +[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils] +path = "../../common/wasm-utils" + +[target."cfg(target_arch = \"wasm32\")".dependencies.time] +version = "0.3.17" +features = ["wasm-bindgen"] [dev-dependencies] tempfile = "3.1.0" +[build-dependencies] +tokio = { version = "1.21.2", features = ["rt-multi-thread", "macros"] } +sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } + [features] -default = ["reply-surb"] +default = [] +fs-surb-storage = ["sqlx"] wasm = ["gateway-client/wasm"] coconut = ["gateway-client/coconut", "gateway-requests/coconut"] -reply-surb = ["sled"] diff --git a/clients/client-core/build.rs b/clients/client-core/build.rs new file mode 100644 index 0000000000..bac1b20c53 --- /dev/null +++ b/clients/client-core/build.rs @@ -0,0 +1,31 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[tokio::main] +async fn main() { + #[cfg(feature = "fs-surb-storage")] + { + use sqlx::{Connection, SqliteConnection}; + use std::env; + + let out_dir = env::var("OUT_DIR").unwrap(); + let database_path = format!("{}/fs-surbs-example.sqlite", out_dir); + + let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path)) + .await + .expect("Failed to create SQLx database connection"); + + sqlx::migrate!("./fs_surbs_migrations") + .run(&mut conn) + .await + .expect("Failed to perform SQLx migrations"); + + #[cfg(target_family = "unix")] + println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path); + + #[cfg(target_family = "windows")] + // for some strange reason we need to add a leading `/` to the windows path even though it's + // not a valid windows path... but hey, it works... + println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path); + } +} diff --git a/clients/client-core/fs_surbs_migrations/20221130120000_create_initial_tables.sql b/clients/client-core/fs_surbs_migrations/20221130120000_create_initial_tables.sql new file mode 100644 index 0000000000..0143475a37 --- /dev/null +++ b/clients/client-core/fs_surbs_migrations/20221130120000_create_initial_tables.sql @@ -0,0 +1,40 @@ +CREATE TABLE status +( + flush_in_progress INTEGER NOT NULL, + previous_flush_timestamp INTEGER NOT NULL, + client_in_use INTEGER NOT NULL +); + +CREATE TABLE reply_surb_storage_metadata +( + min_reply_surb_threshold INTEGER NOT NULL, + max_reply_surb_threshold INTEGER NOT NULL +); + +CREATE TABLE sender_tag +( + recipient BLOB NOT NULL UNIQUE, + tag BLOB NOT NULL UNIQUE +); + +CREATE TABLE reply_key +( + key_digest BLOB NOT NULL UNIQUE, + reply_key BLOB NOT NULL UNIQUE, + sent_at_timestamp INTEGER NOT NULL +); + +CREATE TABLE reply_surb_sender +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + last_sent_timestamp INTEGER NOT NULL, + tag BLOB NOT NULL UNIQUE +); + +CREATE TABLE reply_surb +( + reply_surb_sender_id INTEGER NOT NULL, + reply_surb BLOB NOT NULL, + + FOREIGN KEY (reply_surb_sender_id) REFERENCES reply_surb_sender (id) +); \ No newline at end of file diff --git a/clients/client-core/src/client/base_client/mod.rs b/clients/client-core/src/client/base_client/mod.rs index 9788cb26f8..4757c3bf2c 100644 --- a/clients/client-core/src/client/base_client/mod.rs +++ b/clients/client-core/src/client/base_client/mod.rs @@ -10,41 +10,45 @@ use crate::client::real_messages_control::RealMessagesController; use crate::client::received_buffer::{ ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController, }; +use crate::client::replies::reply_controller; +use crate::client::replies::reply_controller::{ReplyControllerReceiver, ReplyControllerSender}; +use crate::client::replies::reply_storage::{ + CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys, +}; use crate::client::topology_control::{ TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, }; use crate::config::{Config, DebugConfig, GatewayEndpointConfig}; use crate::error::ClientCoreError; +use crate::spawn_future; use client_connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; -use crypto::asymmetric::identity; +use crypto::asymmetric::{encryption, identity}; use futures::channel::mpsc; use gateway_client::bandwidth::BandwidthController; use gateway_client::{ AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver, MixnetMessageSender, }; -use log::info; +use log::{debug, info}; +use nymsphinx::acknowledgements::AckKey; use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::nodes::NodeIdentity; -#[cfg(feature = "reply-surb")] -use std::path::PathBuf; -#[cfg(feature = "reply-surb")] +use std::sync::Arc; +use std::time::Duration; use tap::TapFallible; -use task::{ShutdownListener, ShutdownNotifier}; +use task::{TaskClient, TaskManager}; use url::Url; -// it's fine to do this disgusting compilation flag business here as this problem -// is going to go away in 1.2.0 -#[cfg(feature = "reply-surb")] -use crate::client::reply_key_storage::ReplyKeyStorage; +#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] +pub mod non_wasm_helpers; pub struct ClientInput { - pub shared_lane_queue_lengths: LaneQueueLengths, pub connection_command_sender: ConnectionCommandSender, pub input_sender: InputMessageSender, } pub struct ClientOutput { + pub shared_lane_queue_lengths: LaneQueueLengths, pub received_buffer_request_sender: ReceivedBufferRequestSender, } @@ -76,35 +80,36 @@ impl ClientOutputStatus { } } -pub struct BaseClientBuilder<'a> { +pub struct BaseClientBuilder<'a, B> { // due to wasm limitations I had to split it like this : ( gateway_config: &'a GatewayEndpointConfig, debug_config: &'a DebugConfig, disabled_credentials: bool, - validator_api_endpoints: Vec, - - #[cfg(feature = "reply-surb")] - reply_surb_keys_store_path: PathBuf, + nym_api_endpoints: Vec, + reply_storage_backend: B, bandwidth_controller: Option, key_manager: KeyManager, } -impl<'a> BaseClientBuilder<'a> { +impl<'a, B> BaseClientBuilder<'a, B> +where + B: ReplyStorageBackend + Send + Sync + 'static, +{ pub fn new_from_base_config( base_config: &'a Config, key_manager: KeyManager, bandwidth_controller: Option, - ) -> BaseClientBuilder<'a> { + reply_storage_backend: B, + ) -> BaseClientBuilder<'a, B> { BaseClientBuilder { gateway_config: base_config.get_gateway_endpoint_config(), debug_config: base_config.get_debug_config(), disabled_credentials: base_config.get_disabled_credentials_mode(), - validator_api_endpoints: base_config.get_validator_api_endpoints(), + nym_api_endpoints: base_config.get_nym_api_endpoints(), bandwidth_controller, + reply_storage_backend, key_manager, - #[cfg(feature = "reply-surb")] - reply_surb_keys_store_path: base_config.get_reply_encryption_key_store_path(), } } @@ -113,19 +118,18 @@ impl<'a> BaseClientBuilder<'a> { debug_config: &'a DebugConfig, key_manager: KeyManager, bandwidth_controller: Option, + reply_storage_backend: B, disabled_credentials: bool, - validator_api_endpoints: Vec, - #[cfg(feature = "reply-surb")] reply_surb_keys_store_path: PathBuf, - ) -> BaseClientBuilder<'a> { + nym_api_endpoints: Vec, + ) -> BaseClientBuilder<'a, B> { BaseClientBuilder { gateway_config, debug_config, disabled_credentials, - validator_api_endpoints, + nym_api_endpoints, + reply_storage_backend, bandwidth_controller, key_manager, - #[cfg(feature = "reply-surb")] - reply_surb_keys_store_path, } } @@ -142,24 +146,26 @@ impl<'a> BaseClientBuilder<'a> { // future constantly pumping loop cover traffic at some specified average rate // the pumped traffic goes to the MixTrafficController fn start_cover_traffic_stream( - &self, + debug_config: &DebugConfig, + ack_key: Arc, + self_address: Recipient, topology_accessor: TopologyAccessor, mix_tx: BatchMixMessageSender, - shutdown: ShutdownListener, + shutdown: TaskClient, ) { info!("Starting loop cover traffic stream..."); let mut stream = LoopCoverTrafficStream::new( - self.key_manager.ack_key(), - self.debug_config.average_ack_delay, - self.debug_config.average_packet_delay, - self.debug_config.loop_cover_traffic_average_delay, + ack_key, + debug_config.average_ack_delay, + debug_config.average_packet_delay, + debug_config.loop_cover_traffic_average_delay, mix_tx, - self.as_mix_recipient(), + self_address, topology_accessor, ); - if let Some(size) = self.debug_config.use_extended_packet_size { + if let Some(size) = debug_config.use_extended_packet_size { log::debug!("Setting extended packet size: {:?}", size); stream.set_custom_packet_size(size.into()); } @@ -169,32 +175,18 @@ impl<'a> BaseClientBuilder<'a> { #[allow(clippy::too_many_arguments)] fn start_real_traffic_controller( - &self, + controller_config: real_messages_control::Config, topology_accessor: TopologyAccessor, ack_receiver: AcknowledgementReceiver, input_receiver: InputMessageReceiver, mix_sender: BatchMixMessageSender, + reply_storage: CombinedReplyStorage, + reply_controller_sender: ReplyControllerSender, + reply_controller_receiver: ReplyControllerReceiver, lane_queue_lengths: LaneQueueLengths, client_connection_rx: ConnectionCommandReceiver, - shutdown: ShutdownListener, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, + shutdown: TaskClient, ) { - let mut controller_config = real_messages_control::Config::new( - self.key_manager.ack_key(), - self.debug_config.ack_wait_multiplier, - self.debug_config.ack_wait_addition, - self.debug_config.average_ack_delay, - self.debug_config.message_sending_average_delay, - self.debug_config.average_packet_delay, - self.debug_config.disable_main_poisson_packet_distribution, - self.as_mix_recipient(), - ); - - if let Some(size) = self.debug_config.use_extended_packet_size { - log::debug!("Setting extended packet size: {:?}", size); - controller_config.set_custom_packet_size(size.into()); - } - info!("Starting real traffic stream..."); RealMessagesController::new( @@ -203,10 +195,11 @@ impl<'a> BaseClientBuilder<'a> { input_receiver, mix_sender, topology_accessor, + reply_storage, + reply_controller_sender, + reply_controller_receiver, lane_queue_lengths, client_connection_rx, - #[cfg(feature = "reply-surb")] - reply_key_storage, ) .start_with_shutdown(shutdown); } @@ -214,19 +207,20 @@ impl<'a> BaseClientBuilder<'a> { // buffer controlling all messages fetched from provider // required so that other components would be able to use them (say the websocket) fn start_received_messages_buffer_controller( - &self, + local_encryption_keypair: Arc, query_receiver: ReceivedBufferRequestReceiver, mixnet_receiver: MixnetMessageReceiver, - shutdown: ShutdownListener, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, + reply_key_storage: SentReplyKeys, + reply_controller_sender: ReplyControllerSender, + shutdown: TaskClient, ) { info!("Starting received messages buffer controller..."); ReceivedMessagesBufferController::new( - self.key_manager.encryption_keypair(), + local_encryption_keypair, query_receiver, mixnet_receiver, - #[cfg(feature = "reply-surb")] reply_key_storage, + reply_controller_sender, ) .start_with_shutdown(shutdown) } @@ -235,23 +229,23 @@ impl<'a> BaseClientBuilder<'a> { &mut self, mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, - shutdown: ShutdownListener, - ) -> GatewayClient { + shutdown: TaskClient, + ) -> Result { let gateway_id = self.gateway_config.gateway_id.clone(); if gateway_id.is_empty() { - panic!("The identity of the gateway is unknown - did you run `nym-client` init?") + return Err(ClientCoreError::GatewayIdUnknown); } let gateway_owner = self.gateway_config.gateway_owner.clone(); if gateway_owner.is_empty() { - panic!("The owner of the gateway is unknown - did you run `nym-client` init?") + return Err(ClientCoreError::GatewayOwnerUnknown); } let gateway_address = self.gateway_config.gateway_listener.clone(); if gateway_address.is_empty() { - panic!("The address of the gateway is unknown - did you run `nym-client` init?") + return Err(ClientCoreError::GatwayAddressUnknown); } let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) - .expect("provided gateway id is invalid!"); + .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; // disgusting wasm workaround since there's no key persistence there (nor `client init`) let shared_key = if self.key_manager.gateway_key_set() { @@ -270,7 +264,7 @@ impl<'a> BaseClientBuilder<'a> { ack_sender, self.debug_config.gateway_response_timeout, self.bandwidth_controller.take(), - Some(shutdown), + shutdown, ); gateway_client.set_disabled_credentials_mode(self.disabled_credentials); @@ -278,21 +272,23 @@ impl<'a> BaseClientBuilder<'a> { gateway_client .authenticate_and_start() .await - .expect("could not authenticate and start up the gateway connection"); - - gateway_client + .tap_err(|err| { + log::error!("Could not authenticate and start up the gateway connection - {err}") + })?; + Ok(gateway_client) } // future responsible for periodically polling directory server and updating // the current global view of topology async fn start_topology_refresher( - &mut self, + nym_api_urls: Vec, + refresh_rate: Duration, topology_accessor: TopologyAccessor, - shutdown: ShutdownListener, + shutdown: TaskClient, ) -> Result<(), ClientCoreError> { let topology_refresher_config = TopologyRefresherConfig::new( - self.validator_api_endpoints.clone(), - self.debug_config.topology_refresh_rate, + nym_api_urls, + refresh_rate, env!("CARGO_PKG_VERSION").to_string(), ); let mut topology_refresher = @@ -302,13 +298,12 @@ impl<'a> BaseClientBuilder<'a> { info!("Obtaining initial network topology"); topology_refresher.refresh().await; - // TODO: a slightly more graceful termination here - if !topology_refresher.is_topology_routable().await { + if let Err(err) = topology_refresher.ensure_topology_is_routable().await { log::error!( "The current network topology seem to be insufficient to route any packets through \ - - check if enough nodes and a gateway are online" + - check if enough nodes and a gateway are online - source: {err}" ); - return Err(ClientCoreError::InsufficientNetworkTopology); + return Err(ClientCoreError::InsufficientNetworkTopology(err)); } info!("Starting topology refresher..."); @@ -322,7 +317,7 @@ impl<'a> BaseClientBuilder<'a> { // requests? fn start_mix_traffic_controller( gateway_client: GatewayClient, - shutdown: ShutdownListener, + shutdown: TaskClient, ) -> BatchMixMessageSender { info!("Starting mix traffic controller..."); let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client); @@ -330,7 +325,35 @@ impl<'a> BaseClientBuilder<'a> { mix_tx } - pub async fn start_base(mut self) -> Result { + async fn setup_persistent_reply_storage( + backend: B, + shutdown: TaskClient, + ) -> Result + where + ::StorageError: Sync + Send, + { + let persistent_storage = PersistentReplyStorage::new(backend); + let mem_store = persistent_storage + .load_state_from_backend() + .await + .map_err(|err| ClientCoreError::SurbStorageError { + source: Box::new(err), + })?; + + let store_clone = mem_store.clone(); + spawn_future(async move { + persistent_storage + .flush_on_shutdown(store_clone, shutdown) + .await + }); + + Ok(mem_store) + } + + pub async fn start_base(mut self) -> Result + where + ::StorageError: Sync + Send, + { info!("Starting nym client"); // channels for inter-component communication // TODO: make the channels be internally created by the relevant components @@ -351,38 +374,50 @@ impl<'a> BaseClientBuilder<'a> { let (ack_sender, ack_receiver) = mpsc::unbounded(); let shared_topology_accessor = TopologyAccessor::new(); - #[cfg(feature = "reply-surb")] - let reply_key_storage = - ReplyKeyStorage::load(&self.reply_surb_keys_store_path).tap_err(|err| { - log::error!("Failed to load reply key storage - is it perhaps already in use?"); - log::error!("{:?}", err); - })?; - // Shutdown notifier for signalling tasks to stop - let shutdown = ShutdownNotifier::default(); + let task_manager = TaskManager::default(); + + // channels responsible for dealing with reply-related fun + let (reply_controller_sender, reply_controller_receiver) = + reply_controller::requests::new_control_channels(); + + let self_address = self.as_mix_recipient(); // the components are started in very specific order. Unless you know what you are doing, // do not change that. - self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe()) + let gateway_client = self + .start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe()) .await?; - self.start_received_messages_buffer_controller( + + let reply_storage = Self::setup_persistent_reply_storage( + self.reply_storage_backend, + task_manager.subscribe(), + ) + .await?; + + Self::start_topology_refresher( + self.nym_api_endpoints.clone(), + self.debug_config.topology_refresh_rate, + shared_topology_accessor.clone(), + task_manager.subscribe(), + ) + .await?; + + Self::start_received_messages_buffer_controller( + self.key_manager.encryption_keypair(), received_buffer_request_receiver, mixnet_messages_receiver, - shutdown.subscribe(), - #[cfg(feature = "reply-surb")] - reply_key_storage.clone(), + reply_storage.key_storage(), + reply_controller_sender.clone(), + task_manager.subscribe(), ); - let gateway_client = self - .start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe()) - .await; - // The sphinx_message_sender is the transmitter for any component generating sphinx packets // that are to be sent to the mixnet. They are used by cover traffic stream and real // traffic stream. // The MixTrafficController then sends the actual traffic let sphinx_message_sender = - Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe()); + Self::start_mix_traffic_controller(gateway_client, task_manager.subscribe()); // Channels that the websocket listener can use to signal downstream to the real traffic // controller that connections are closed. @@ -392,43 +427,60 @@ impl<'a> BaseClientBuilder<'a> { // primarily to throttle incoming connections (e.g socks5 for attached network-requesters) let shared_lane_queue_lengths = LaneQueueLengths::new(); - self.start_real_traffic_controller( + let mut controller_config = real_messages_control::Config::new( + self.debug_config, + self.key_manager.ack_key(), + self_address, + ); + + if let Some(size) = self.debug_config.use_extended_packet_size { + log::debug!("Setting extended packet size: {:?}", size); + controller_config.set_custom_packet_size(size.into()); + } + + Self::start_real_traffic_controller( + controller_config, shared_topology_accessor.clone(), ack_receiver, input_receiver, sphinx_message_sender.clone(), + reply_storage, + reply_controller_sender.clone(), + reply_controller_receiver, shared_lane_queue_lengths.clone(), client_connection_rx, - shutdown.subscribe(), - #[cfg(feature = "reply-surb")] - reply_key_storage, + task_manager.subscribe(), ); if !self.debug_config.disable_loop_cover_traffic_stream { - self.start_cover_traffic_stream( + Self::start_cover_traffic_stream( + self.debug_config, + self.key_manager.ack_key(), + self_address, shared_topology_accessor, sphinx_message_sender, - shutdown.subscribe(), + task_manager.subscribe(), ); } - info!("Client startup finished!"); - info!("The address of this client is: {}", self.as_mix_recipient()); + debug!("Core client startup finished!"); + debug!("The address of this client is: {self_address}"); Ok(BaseClient { client_input: ClientInputStatus::AwaitingProducer { client_input: ClientInput { - shared_lane_queue_lengths, connection_command_sender: client_connection_tx, input_sender, }, }, client_output: ClientOutputStatus::AwaitingConsumer { client_output: ClientOutput { + shared_lane_queue_lengths, received_buffer_request_sender, }, }, - shutdown_notifier: shutdown, + reply_controller_sender, + task_manager, }) } } @@ -437,5 +489,8 @@ pub struct BaseClient { pub client_input: ClientInputStatus, pub client_output: ClientOutputStatus, - pub shutdown_notifier: ShutdownNotifier, + // it feels very wrong to put this channel here, but I can't think of any other way of passing it to the native client + pub reply_controller_sender: ReplyControllerSender, + + pub task_manager: TaskManager, } diff --git a/clients/client-core/src/client/base_client/non_wasm_helpers.rs b/clients/client-core/src/client/base_client/non_wasm_helpers.rs new file mode 100644 index 0000000000..996dcc8fc3 --- /dev/null +++ b/clients/client-core/src/client/base_client/non_wasm_helpers.rs @@ -0,0 +1,87 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::{ + fs_backend, CombinedReplyStorage, ReplyStorageBackend, +}; +use crate::config::DebugConfig; +use crate::error::ClientCoreError; +use log::{error, info}; +use std::path::Path; +use std::{fs, io}; +use time::OffsetDateTime; + +async fn setup_fresh_backend>( + db_path: P, + debug_config: &DebugConfig, +) -> Result { + info!("creating fresh surb database"); + let mut storage_backend = match fs_backend::Backend::init(db_path).await { + Ok(backend) => backend, + Err(err) => { + error!("failed to setup persistent storage backend for our reply needs: {err}"); + return Err(ClientCoreError::SurbStorageError { + source: Box::new(err), + }); + } + }; + + // while I kinda hate that we're going to be creating `CombinedReplyStorage` twice, + // it will only be happening on the very first run and in practice won't incur huge + // costs since the storage is going to be empty + let mem_store = CombinedReplyStorage::new( + debug_config.minimum_reply_surb_storage_threshold, + debug_config.maximum_reply_surb_storage_threshold, + ); + storage_backend + .init_fresh(&mem_store) + .await + .map_err(|err| ClientCoreError::SurbStorageError { + source: Box::new(err), + })?; + + Ok(storage_backend) +} + +fn archive_corrupted_database>(db_path: P) -> io::Result<()> { + let db_path = db_path.as_ref(); + debug_assert!(db_path.exists()); + + let now = OffsetDateTime::now_utc().unix_timestamp(); + + let suffix = format!("_{now}.corrupted"); + + let new_extension = + if let Some(existing_extension) = db_path.extension().and_then(|ext| ext.to_str()) { + format!("{existing_extension}.{}", suffix) + } else { + suffix + }; + + let mut renamed = db_path.to_owned(); + renamed.set_extension(new_extension); + + fs::rename(db_path, renamed) +} + +pub async fn setup_fs_reply_surb_backend>( + db_path: P, + debug_config: &DebugConfig, +) -> Result { + // if the database file doesnt exist, initialise fresh storage, otherwise attempt to load the existing one + let db_path = db_path.as_ref(); + if db_path.exists() { + info!("loading existing surb database"); + match fs_backend::Backend::try_load(db_path).await { + Ok(backend) => Ok(backend), + Err(err) => { + error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future"); + + archive_corrupted_database(db_path)?; + setup_fresh_backend(db_path, debug_config).await + } + } + } else { + setup_fresh_backend(db_path, debug_config).await + } +} diff --git a/clients/client-core/src/client/cover_traffic_stream.rs b/clients/client-core/src/client/cover_traffic_stream.rs index 061e5f0b98..a8ecad86b2 100644 --- a/clients/client-core/src/client/cover_traffic_stream.rs +++ b/clients/client-core/src/client/cover_traffic_stream.rs @@ -161,15 +161,16 @@ impl LoopCoverTrafficStream { // poisson delay, but is it really a problem? let topology_permit = self.topology_access.get_read_permit().await; // the ack is sent back to ourselves (and then ignored) - let topology_ref_option = topology_permit.try_get_valid_topology_ref( + let topology_ref = match topology_permit.try_get_valid_topology_ref( &self.our_full_destination, Some(&self.our_full_destination), - ); - if topology_ref_option.is_none() { - warn!("No valid topology detected - won't send any loop cover message this time"); - return; - } - let topology_ref = topology_ref_option.unwrap(); + ) { + Ok(topology) => topology, + Err(err) => { + warn!("We're not going to send any loop cover message this time, as the current topology seem to be invalid - {err}"); + return; + } + }; let cover_message = generate_loop_cover_packet( &mut self.rng, @@ -191,7 +192,7 @@ impl LoopCoverTrafficStream { // However it's still useful to alert the user that the gateway or the link to // the gateway can't keep up. Either due to insufficient bandwidth on the // client side, or that the gateway is overloaded. - log::warn!("Failed to send: gateway appears to not keep up"); + log::warn!("Failed to send sphinx packet - gateway or connection to gatway can't keep up"); } TrySendError::Closed(_) => { log::warn!("Failed to send cover message - channel closed"); @@ -212,7 +213,7 @@ impl LoopCoverTrafficStream { tokio::task::yield_now().await; } - pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) { + pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) { // we should set initial delay only when we actually start the stream let sampled = sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay); @@ -241,19 +242,4 @@ impl LoopCoverTrafficStream { log::debug!("LoopCoverTrafficStream: Exiting"); }) } - - pub fn start(mut self) { - // we should set initial delay only when we actually start the stream - let sampled = - sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay); - self.set_next_delay(sampled); - - spawn_future(async move { - debug!("Started LoopCoverTrafficStream without graceful shutdown support"); - - while self.next().await.is_some() { - self.on_new_message().await; - } - }) - } } diff --git a/clients/client-core/src/client/helpers/mod.rs b/clients/client-core/src/client/helpers/mod.rs new file mode 100644 index 0000000000..ffffea0313 --- /dev/null +++ b/clients/client-core/src/client/helpers/mod.rs @@ -0,0 +1,14 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(not(target_arch = "wasm32"))] +mod non_wasm; + +#[cfg(target_arch = "wasm32")] +mod wasm; + +#[cfg(not(target_arch = "wasm32"))] +pub use non_wasm::*; + +#[cfg(target_arch = "wasm32")] +pub use wasm::*; diff --git a/clients/client-core/src/client/helpers/non_wasm.rs b/clients/client-core/src/client/helpers/non_wasm.rs new file mode 100644 index 0000000000..ff5c9c0047 --- /dev/null +++ b/clients/client-core/src/client/helpers/non_wasm.rs @@ -0,0 +1,13 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub use tokio::time::*; +pub type IntervalStream = tokio_stream::wrappers::IntervalStream; + +pub(crate) fn get_time_now() -> Instant { + Instant::now() +} + +pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream { + tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate)) +} diff --git a/clients/client-core/src/client/helpers/wasm.rs b/clients/client-core/src/client/helpers/wasm.rs new file mode 100644 index 0000000000..f04dd64028 --- /dev/null +++ b/clients/client-core/src/client/helpers/wasm.rs @@ -0,0 +1,16 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::time::Duration; +use wasm_timer; + +pub use wasm_timer::*; +pub type IntervalStream = gloo_timers::future::IntervalStream; + +pub(crate) fn get_time_now() -> Instant { + wasm_timer::Instant::now() +} + +pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream { + gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32) +} diff --git a/clients/client-core/src/client/inbound_messages.rs b/clients/client-core/src/client/inbound_messages.rs index 909e3720f9..890787aa60 100644 --- a/clients/client-core/src/client/inbound_messages.rs +++ b/clients/client-core/src/client/inbound_messages.rs @@ -1,40 +1,80 @@ use client_connections::TransmissionLane; use nymsphinx::addressing::clients::Recipient; -use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; pub type InputMessageSender = tokio::sync::mpsc::Sender; pub type InputMessageReceiver = tokio::sync::mpsc::Receiver; #[derive(Debug)] pub enum InputMessage { - Fresh { + /// The simplest message variant where no additional information is attached. + /// You're simply sending your `data` to specified `recipient` without any tagging. + /// + /// Ends up with `NymMessage::Plain` variant + Regular { recipient: Recipient, data: Vec, - with_reply_surb: bool, lane: TransmissionLane, }, - Reply { - reply_surb: ReplySurb, + + /// Creates a message used for a duplex anonymous communication where the recipient + /// will never learn of our true identity. This is achieved by carefully sending `reply_surbs`. + /// + /// Note that if reply_surbs is set to zero then + /// this variant requires the client having sent some reply_surbs in the past + /// (and thus the recipient also knowing our sender tag). + /// + /// Ends up with `NymMessage::Repliable` variant + Anonymous { + recipient: Recipient, data: Vec, + reply_surbs: u32, + lane: TransmissionLane, + }, + + /// Attempt to use our internally received and stored `ReplySurb` to send the message back + /// to specified recipient whilst not knowing its full identity (or even gateway). + /// + /// Ends up with `NymMessage::Reply` variant + Reply { + recipient_tag: AnonymousSenderTag, + data: Vec, + lane: TransmissionLane, }, } impl InputMessage { - pub fn new_fresh( - recipient: Recipient, - data: Vec, - with_reply_surb: bool, - lane: TransmissionLane, - ) -> Self { - InputMessage::Fresh { + pub fn new_regular(recipient: Recipient, data: Vec, lane: TransmissionLane) -> Self { + InputMessage::Regular { recipient, data, - with_reply_surb, lane, } } - pub fn new_reply(reply_surb: ReplySurb, data: Vec) -> Self { - InputMessage::Reply { reply_surb, data } + pub fn new_anonymous( + recipient: Recipient, + data: Vec, + reply_surbs: u32, + lane: TransmissionLane, + ) -> Self { + InputMessage::Anonymous { + recipient, + data, + reply_surbs, + lane, + } + } + + pub fn new_reply( + recipient_tag: AnonymousSenderTag, + data: Vec, + lane: TransmissionLane, + ) -> Self { + InputMessage::Reply { + recipient_tag, + data, + lane, + } } } diff --git a/clients/client-core/src/client/mix_traffic.rs b/clients/client-core/src/client/mix_traffic.rs index 0d3aab9513..94944cb481 100644 --- a/clients/client-core/src/client/mix_traffic.rs +++ b/clients/client-core/src/client/mix_traffic.rs @@ -51,8 +51,8 @@ impl MixTrafficController { }; match result { - Err(e) => { - error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e); + Err(err) => { + error!("Failed to send sphinx packet(s) to the gateway! - {err}"); self.consecutive_gateway_failure_count += 1; if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT { // todo: in the future this should initiate a 'graceful' shutdown or try @@ -67,11 +67,11 @@ impl MixTrafficController { } } - pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) { + pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) { spawn_future(async move { debug!("Started MixTrafficController with graceful shutdown support"); - while !shutdown.is_shutdown() { + loop { tokio::select! { mix_packets = self.mix_rx.recv() => match mix_packets { Some(mix_packets) => { @@ -82,8 +82,9 @@ impl MixTrafficController { break; } }, - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("MixTrafficController: Received shutdown"); + break; } } } @@ -91,14 +92,4 @@ impl MixTrafficController { log::debug!("MixTrafficController: Exiting"); }) } - - pub fn start(mut self) { - spawn_future(async move { - debug!("Started MixTrafficController without graceful shutdown support"); - - while let Some(mix_packets) = self.mix_rx.recv().await { - self.on_messages(mix_packets).await; - } - }) - } } diff --git a/clients/client-core/src/client/mod.rs b/clients/client-core/src/client/mod.rs index 8c09544ce6..789dd300de 100644 --- a/clients/client-core/src/client/mod.rs +++ b/clients/client-core/src/client/mod.rs @@ -3,11 +3,12 @@ pub mod base_client; pub mod cover_traffic_stream; +pub(crate) mod helpers; pub mod inbound_messages; pub mod key_manager; pub mod mix_traffic; pub mod real_messages_control; pub mod received_buffer; -#[cfg(feature = "reply-surb")] -pub mod reply_key_storage; +pub mod replies; pub mod topology_control; +pub(crate) mod transmission_buffer; diff --git a/clients/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs b/clients/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs index bd7b34e93d..8febbfa85d 100644 --- a/clients/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs +++ b/clients/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::action_controller::{Action, ActionSender}; +use super::action_controller::{AckActionSender, Action}; use futures::StreamExt; use gateway_client::AcknowledgementReceiver; use log::*; @@ -16,14 +16,14 @@ use std::sync::Arc; pub(super) struct AcknowledgementListener { ack_key: Arc, ack_receiver: AcknowledgementReceiver, - action_sender: ActionSender, + action_sender: AckActionSender, } impl AcknowledgementListener { pub(super) fn new( ack_key: Arc, ack_receiver: AcknowledgementReceiver, - action_sender: ActionSender, + action_sender: AckActionSender, ) -> Self { AcknowledgementListener { ack_key, @@ -49,11 +49,6 @@ impl AcknowledgementListener { if frag_id == COVER_FRAG_ID { trace!("Received an ack for a cover message - no need to do anything"); return; - } else if frag_id.is_reply() { - info!("Received an ack for a reply message - no need to do anything! (don't know what to do!)"); - // TODO: probably there will need to be some extra procedure here, something to notify - // user that his reply reached the recipient (since we got an ack) - return; } trace!("Received {} from the mix network", frag_id); @@ -70,7 +65,7 @@ impl AcknowledgementListener { } } - pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started AcknowledgementListener with graceful shutdown support"); while !shutdown.is_shutdown() { @@ -82,7 +77,7 @@ impl AcknowledgementListener { break; } }, - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("AcknowledgementListener: Received shutdown"); } } @@ -90,14 +85,4 @@ impl AcknowledgementListener { shutdown.recv_timeout().await; log::debug!("AcknowledgementListener: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) async fn run(&mut self) { - debug!("Started AcknowledgementListener without graceful shutdown support"); - - while let Some(acks) = self.ack_receiver.next().await { - self.handle_ack_receiver_item(acks).await - } - } } diff --git a/clients/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs b/clients/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs index 09cf48185a..c65592b2f6 100644 --- a/clients/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs +++ b/clients/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs @@ -3,7 +3,7 @@ use super::PendingAcknowledgement; use crate::client::real_messages_control::acknowledgement_control::RetransmissionRequestSender; -use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use futures::channel::mpsc; use futures::StreamExt; use log::*; use nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey}; @@ -13,7 +13,8 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -pub(crate) type ActionSender = UnboundedSender; +pub(crate) type AckActionSender = mpsc::UnboundedSender; +pub(crate) type AckActionReceiver = mpsc::UnboundedReceiver; // The actual data being sent off as well as potential key to the delay queue type PendingAckEntry = (Arc, Option); @@ -95,7 +96,7 @@ pub(super) struct ActionController { pending_acks_timers: NonExhaustiveDelayQueue, /// Channel for receiving `Action`s from other modules. - incoming_actions: UnboundedReceiver, + incoming_actions: AckActionReceiver, /// Channel for notifying `RetransmissionRequestListener` about expired acknowledgements. retransmission_sender: RetransmissionRequestSender, @@ -105,18 +106,15 @@ impl ActionController { pub(super) fn new( config: Config, retransmission_sender: RetransmissionRequestSender, - ) -> (Self, ActionSender) { - let (sender, receiver) = mpsc::unbounded(); - ( - ActionController { - config, - pending_acks_data: HashMap::new(), - pending_acks_timers: NonExhaustiveDelayQueue::new(), - incoming_actions: receiver, - retransmission_sender, - }, - sender, - ) + incoming_actions: AckActionReceiver, + ) -> Self { + ActionController { + config, + pending_acks_data: HashMap::new(), + pending_acks_timers: NonExhaustiveDelayQueue::new(), + incoming_actions, + retransmission_sender, + } } fn handle_insert(&mut self, pending_acks: Vec) { @@ -138,13 +136,18 @@ impl ActionController { trace!("{} is starting its timer", frag_id); if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) { - if queue_key.is_some() { - // this branch should be IMPOSSIBLE under ANY condition. It would imply starting - // timer TWICE for the SAME PendingAcknowledgement - panic!("Tried to start an already started ack timer!") - } - let timeout = (pending_ack_data.delay.clone() * self.config.ack_wait_multiplier) - .to_duration() + // the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole + // retransmission procedure + // + // (it can happen as timer is started when ack expires to make sure it's not stuck in memory + // and the second instance can be fired when we finally get reply surbs for data we failed to retransmit) + + // if queue_key.is_some() { + // // this branch should be IMPOSSIBLE under ANY condition. It would imply starting + // // timer TWICE for the SAME PendingAcknowledgement + // panic!("Tried to start an already started ack timer!") + // } + let timeout = (pending_ack_data.delay * self.config.ack_wait_multiplier).to_duration() + self.config.ack_wait_addition; let new_queue_key = self.pending_acks_timers.insert(frag_id, timeout); @@ -192,7 +195,8 @@ impl ActionController { trace!("{} is updating its delay", frag_id); // TODO: is it possible to solve this without either locking or temporarily removing the value? if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) { - // this Action is triggered by `RetransmissionRequestListener` which held the other potential + // this Action is triggered by `RetransmissionRequestListener` (for 'normal' packets) + // or `ReplyController` (for 'reply' packets) which held the other potential // reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference // was dropped hence this unwrap is safe. let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap(); @@ -245,7 +249,7 @@ impl ActionController { } } - pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started ActionController with graceful shutdown support"); while !shutdown.is_shutdown() { @@ -266,7 +270,7 @@ impl ActionController { break; } }, - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("ActionController: Received shutdown"); } } @@ -277,17 +281,4 @@ impl ActionController { .expect("Task stopped without shutdown called"); log::debug!("ActionController: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) async fn run(&mut self) { - debug!("Started ActionController without graceful shutdown support"); - - loop { - tokio::select! { - action = self.incoming_actions.next() => self.process_action(action.unwrap()), - expired_ack = self.pending_acks_timers.next() => self.handle_expired_ack_timer(expired_ack.unwrap()) - } - } - } } diff --git a/clients/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs b/clients/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs index dec2d153d6..f492de1aae 100644 --- a/clients/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs +++ b/clients/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs @@ -1,23 +1,14 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::action_controller::{Action, ActionSender}; -use super::PendingAcknowledgement; -use crate::client::{ - inbound_messages::{InputMessage, InputMessageReceiver}, - real_messages_control::real_traffic_stream::{BatchRealMessageSender, RealMessage}, - topology_control::TopologyAccessor, -}; +use crate::client::inbound_messages::{InputMessage, InputMessageReceiver}; +use crate::client::real_messages_control::message_handler::MessageHandler; +use crate::client::replies::reply_controller::ReplyControllerSender; use client_connections::TransmissionLane; use log::*; -use nymsphinx::anonymous_replies::ReplySurb; -use nymsphinx::preparer::MessagePreparer; -use nymsphinx::{acknowledgements::AckKey, addressing::clients::Recipient}; +use nymsphinx::addressing::clients::Recipient; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use rand::{CryptoRng, Rng}; -use std::sync::Arc; - -#[cfg(feature = "reply-surb")] -use crate::client::reply_key_storage::ReplyKeyStorage; /// Module responsible for dealing with the received messages: splitting them, creating acknowledgements, /// putting everything into sphinx packets, etc. @@ -26,15 +17,9 @@ pub(super) struct InputMessageListener where R: CryptoRng + Rng, { - ack_key: Arc, - ack_recipient: Recipient, input_receiver: InputMessageReceiver, - message_preparer: MessagePreparer, - action_sender: ActionSender, - real_message_sender: BatchRealMessageSender, - topology_access: TopologyAccessor, - #[cfg(feature = "reply-surb")] - reply_key_storage: ReplyKeyStorage, + message_handler: MessageHandler, + reply_controller_sender: ReplyControllerSender, } impl InputMessageListener @@ -45,156 +30,86 @@ where // some considerable refactoring #[allow(clippy::too_many_arguments)] pub(super) fn new( - ack_key: Arc, - ack_recipient: Recipient, input_receiver: InputMessageReceiver, - message_preparer: MessagePreparer, - action_sender: ActionSender, - real_message_sender: BatchRealMessageSender, - topology_access: TopologyAccessor, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, + message_handler: MessageHandler, + reply_controller_sender: ReplyControllerSender, ) -> Self { InputMessageListener { - ack_key, - ack_recipient, input_receiver, - message_preparer, - action_sender, - real_message_sender, - topology_access, - #[cfg(feature = "reply-surb")] - reply_key_storage, + message_handler, + reply_controller_sender, } } - // we require topology for replies to generate surb_acks - async fn handle_reply(&mut self, reply_surb: ReplySurb, data: Vec) -> Option { - let topology_permit = self.topology_access.get_read_permit().await; - let topology = match topology_permit.try_get_valid_topology_ref(&self.ack_recipient, None) { - Some(topology_ref) => topology_ref, - None => { - warn!("Could not process the message - the network topology is invalid"); - return None; - } - }; - - match self - .message_preparer - .prepare_reply_for_use(data, reply_surb, topology, &self.ack_key) - .await - { - Ok((mix_packet, reply_id)) => { - // TODO: later probably write pending ack here - // and deal with them.... - // ... somehow - Some(RealMessage::new(mix_packet, reply_id)) - } - Err(err) => { - // TODO: should we have some mechanism to indicate to the user that the `reply_surb` - // could be reused since technically it wasn't used up here? - warn!("failed to deal with received reply surb - {:?}", err); - None - } - } + async fn handle_reply( + &mut self, + recipient_tag: AnonymousSenderTag, + data: Vec, + lane: TransmissionLane, + ) { + // offload reply handling to the dedicated task + self.reply_controller_sender + .send_reply(recipient_tag, data, lane) } - async fn handle_fresh_message( + async fn handle_plain_message( &mut self, recipient: Recipient, content: Vec, - with_reply_surb: bool, - ) -> Option> { - log::trace!("handling msg size: {}", content.len()); - let topology_permit = self.topology_access.get_read_permit().await; - let topology = match topology_permit - .try_get_valid_topology_ref(&self.ack_recipient, Some(&recipient)) + lane: TransmissionLane, + ) { + if let Err(err) = self + .message_handler + .try_send_plain_message(recipient, content, lane) + .await { - Some(topology_ref) => topology_ref, - None => { - warn!("Could not process the message - the network topology is invalid"); - return None; - } - }; - - // split the message, attach optional reply surb - let (split_message, reply_key) = self - .message_preparer - .prepare_and_split_message(content, with_reply_surb, topology) - .expect("somehow the topology was invalid after all!"); - - #[cfg(feature = "reply-surb")] - if let Some(reply_key) = reply_key { - self.reply_key_storage - .insert_encryption_key(reply_key) - .expect("Failed to insert surb reply key to the store!") + warn!("failed to send a plain message - {err}") } + } - #[cfg(not(feature = "reply-surb"))] - let _reply_key = reply_key; - - // encrypt chunks, put them inside sphinx packets and generate acks - let mut pending_acks = Vec::with_capacity(split_message.len()); - let mut real_messages = Vec::with_capacity(split_message.len()); - for message_chunk in split_message { - // we need to clone it because we need to keep it in memory in case we had to retransmit - // it. And then we'd need to recreate entire ACK again. - let chunk_clone = message_chunk.clone(); - let prepared_fragment = self - .message_preparer - .prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient) - .unwrap(); - - real_messages.push(RealMessage::new( - prepared_fragment.mix_packet, - message_chunk.fragment_identifier(), - )); - - pending_acks.push(PendingAcknowledgement::new( - message_chunk, - prepared_fragment.total_delay, - recipient, - )); + async fn handle_repliable_message( + &mut self, + recipient: Recipient, + content: Vec, + reply_surbs: u32, + lane: TransmissionLane, + ) { + if let Err(err) = self + .message_handler + .try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane) + .await + { + warn!("failed to send a repliable message - {err}") } - - // tells the controller to put this into the hashmap - self.action_sender - .unbounded_send(Action::new_insert(pending_acks)) - .unwrap(); - - Some(real_messages) } async fn on_input_message(&mut self, msg: InputMessage) { - let (real_messages, lane) = match msg { - InputMessage::Fresh { + match msg { + InputMessage::Regular { recipient, data, - with_reply_surb, lane, - } => ( - self.handle_fresh_message(recipient, data, with_reply_surb) - .await, + } => self.handle_plain_message(recipient, data, lane).await, + InputMessage::Anonymous { + recipient, + data, + reply_surbs, lane, - ), - InputMessage::Reply { reply_surb, data } => ( - self.handle_reply(reply_surb, data) + } => { + self.handle_repliable_message(recipient, data, reply_surbs, lane) .await - .map(|message| vec![message]), - TransmissionLane::Reply, - ), + } + InputMessage::Reply { + recipient_tag, + data, + lane, + } => { + self.handle_reply(recipient_tag, data, lane).await; + } }; - - // there's no point in trying to send nothing - if let Some(real_messages) = real_messages { - // tells real message sender (with the poisson timer) to send this to the mix network - self.real_message_sender - .send((real_messages, lane)) - .await - .expect("BatchRealMessageReceiver has stopped receiving!"); - } } - pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started InputMessageListener with graceful shutdown support"); while !shutdown.is_shutdown() { @@ -208,7 +123,7 @@ where break; } }, - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("InputMessageListener: Received shutdown"); } } @@ -216,13 +131,4 @@ where shutdown.recv_timeout().await; log::debug!("InputMessageListener: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) async fn run(&mut self) { - debug!("Started InputMessageListener without graceful shutdown support"); - while let Some(input_msg) = self.input_receiver.recv().await { - self.on_input_message(input_msg).await; - } - } } diff --git a/clients/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs b/clients/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs index 5bf2331555..befc24b86a 100644 --- a/clients/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs +++ b/clients/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs @@ -7,18 +7,20 @@ use self::{ retransmission_request_listener::RetransmissionRequestListener, sent_notification_listener::SentNotificationListener, }; -use super::real_traffic_stream::BatchRealMessageSender; -use crate::client::{inbound_messages::InputMessageReceiver, topology_control::TopologyAccessor}; +use crate::client::inbound_messages::InputMessageReceiver; +use crate::client::real_messages_control::message_handler::MessageHandler; +use crate::client::replies::reply_controller::ReplyControllerSender; use crate::spawn_future; +use action_controller::AckActionReceiver; use futures::channel::mpsc; use gateway_client::AcknowledgementReceiver; use log::*; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use nymsphinx::params::PacketSize; use nymsphinx::{ acknowledgements::AckKey, addressing::clients::Recipient, chunking::fragment::{Fragment, FragmentIdentifier}, - preparer::MessagePreparer, Delay as SphinxDelay, }; use rand::{CryptoRng, Rng}; @@ -27,8 +29,7 @@ use std::{ time::Duration, }; -#[cfg(feature = "reply-surb")] -use crate::client::reply_key_storage::ReplyKeyStorage; +pub(crate) use action_controller::{AckActionSender, Action}; mod acknowledgement_listener; mod action_controller; @@ -50,24 +51,64 @@ pub(super) type SentPacketNotificationSender = mpsc::UnboundedSender; +#[derive(Debug)] +pub(crate) enum PacketDestination { + Anonymous { + recipient_tag: AnonymousSenderTag, + // special flag to indicate whether this was an ack for requesting additional surbs, + // in that case we have to do everything we can to get it through, even if it means going + // below our stored reply surb threshold + extra_surb_request: bool, + }, + KnownRecipient(Box), +} + /// Structure representing a data `Fragment` that is on-route to the specified `Recipient` #[derive(Debug)] pub(crate) struct PendingAcknowledgement { message_chunk: Fragment, delay: SphinxDelay, - recipient: Recipient, + destination: PacketDestination, } impl PendingAcknowledgement { /// Creates new instance of `PendingAcknowledgement` using the provided data. - fn new(message_chunk: Fragment, delay: SphinxDelay, recipient: Recipient) -> Self { + pub(crate) fn new_known( + message_chunk: Fragment, + delay: SphinxDelay, + recipient: Recipient, + ) -> Self { PendingAcknowledgement { message_chunk, delay, - recipient, + destination: PacketDestination::KnownRecipient(recipient.into()), } } + pub(crate) fn new_anonymous( + message_chunk: Fragment, + delay: SphinxDelay, + recipient_tag: AnonymousSenderTag, + extra_surb_request: bool, + ) -> Self { + PendingAcknowledgement { + message_chunk, + delay, + destination: PacketDestination::Anonymous { + recipient_tag, + extra_surb_request, + }, + } + } + + pub(crate) fn inner_fragment_identifier(&self) -> FragmentIdentifier { + self.message_chunk.fragment_identifier() + } + + pub(crate) fn fragment_data(&self) -> Fragment { + self.message_chunk.clone() + } + fn update_delay(&mut self, new_delay: SphinxDelay) { self.delay = new_delay; } @@ -76,10 +117,6 @@ impl PendingAcknowledgement { /// AcknowledgementControllerConnectors represents set of channels for communication with /// other parts of the system in order to support acknowledgements and retransmission. pub(super) struct AcknowledgementControllerConnectors { - /// Channel used for forwarding prepared sphinx messages into the poisson sender - /// to be sent to the mix network. - real_message_sender: BatchRealMessageSender, - /// Channel used for receiving raw messages from a client. The messages need to be put /// into sphinx packets first. input_receiver: InputMessageReceiver, @@ -91,20 +128,28 @@ pub(super) struct AcknowledgementControllerConnectors { /// Channel used for receiving acknowledgements from the mix network. ack_receiver: AcknowledgementReceiver, + + /// Channel used for sending request to `ActionController` to deal with anything ack-related, + ack_action_sender: AckActionSender, + + /// Channel used for receiving request by `ActionController` to deal with anything ack-related, + ack_action_receiver: AckActionReceiver, } impl AcknowledgementControllerConnectors { pub(super) fn new( - real_message_sender: BatchRealMessageSender, input_receiver: InputMessageReceiver, sent_notifier: SentPacketNotificationReceiver, ack_receiver: AcknowledgementReceiver, + ack_action_sender: AckActionSender, + ack_action_receiver: AckActionReceiver, ) -> Self { AcknowledgementControllerConnectors { - real_message_sender, input_receiver, sent_notifier, ack_receiver, + ack_action_sender, + ack_action_receiver, } } } @@ -117,28 +162,15 @@ pub(super) struct Config { /// Given ack timeout in the form a * BASE_DELAY + b, it specifies the multiplier `a` ack_wait_multiplier: f64, - /// Average delay an acknowledgement packet is going to get delayed at a single mixnode. - average_ack_delay: Duration, - - /// Average delay a data packet is going to get delayed at a single mixnode. - average_packet_delay: Duration, - /// Predefined packet size used for the encapsulated messages. packet_size: PacketSize, } impl Config { - pub(super) fn new( - ack_wait_addition: Duration, - ack_wait_multiplier: f64, - average_ack_delay: Duration, - average_packet_delay: Duration, - ) -> Self { + pub(super) fn new(ack_wait_addition: Duration, ack_wait_multiplier: f64) -> Self { Config { ack_wait_addition, ack_wait_multiplier, - average_ack_delay, - average_packet_delay, packet_size: Default::default(), } } @@ -162,68 +194,51 @@ where impl AcknowledgementController where - R: 'static + CryptoRng + Rng + Clone + Send, + R: 'static + CryptoRng + Rng + Clone + Send + Sync, { - #[allow(clippy::too_many_arguments)] pub(super) fn new( config: Config, - rng: R, - topology_access: TopologyAccessor, ack_key: Arc, - ack_recipient: Recipient, connectors: AcknowledgementControllerConnectors, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, + message_handler: MessageHandler, + reply_controller_sender: ReplyControllerSender, ) -> Self { let (retransmission_tx, retransmission_rx) = mpsc::unbounded(); let action_config = action_controller::Config::new(config.ack_wait_addition, config.ack_wait_multiplier); - let (action_controller, action_sender) = - ActionController::new(action_config, retransmission_tx); - - let message_preparer = MessagePreparer::new( - rng, - ack_recipient, - config.average_packet_delay, - config.average_ack_delay, - ) - .with_custom_real_message_packet_size(config.packet_size); + let action_controller = ActionController::new( + action_config, + retransmission_tx, + connectors.ack_action_receiver, + ); // will listen for any acks coming from the network let acknowledgement_listener = AcknowledgementListener::new( Arc::clone(&ack_key), connectors.ack_receiver, - action_sender.clone(), + connectors.ack_action_sender.clone(), ); // will listen for any new messages from the client let input_message_listener = InputMessageListener::new( - Arc::clone(&ack_key), - ack_recipient, connectors.input_receiver, - message_preparer.clone(), - action_sender.clone(), - connectors.real_message_sender.clone(), - topology_access.clone(), - #[cfg(feature = "reply-surb")] - reply_key_storage, + message_handler.clone(), + reply_controller_sender.clone(), ); // will listen for any ack timeouts and trigger retransmission let retransmission_request_listener = RetransmissionRequestListener::new( - Arc::clone(&ack_key), - ack_recipient, - message_preparer, - action_sender.clone(), - connectors.real_message_sender, + connectors.ack_action_sender.clone(), + message_handler, retransmission_rx, - topology_access, + reply_controller_sender, ); // will listen for events indicating the packet was sent through the network so that // the retransmission timer should be started. let sent_notification_listener = - SentNotificationListener::new(connectors.sent_notifier, action_sender); + SentNotificationListener::new(connectors.sent_notifier, connectors.ack_action_sender); AcknowledgementController { acknowledgement_listener, @@ -234,7 +249,7 @@ where } } - pub(super) fn start_with_shutdown(self, shutdown: task::ShutdownListener) { + pub(super) fn start_with_shutdown(self, shutdown: task::TaskClient) { let mut acknowledgement_listener = self.acknowledgement_listener; let mut input_message_listener = self.input_message_listener; let mut retransmission_request_listener = self.retransmission_request_listener; @@ -278,35 +293,4 @@ where debug!("The controller has finished execution!"); }); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) fn start(self) { - let mut acknowledgement_listener = self.acknowledgement_listener; - let mut input_message_listener = self.input_message_listener; - let mut retransmission_request_listener = self.retransmission_request_listener; - let mut sent_notification_listener = self.sent_notification_listener; - let mut action_controller = self.action_controller; - - spawn_future(async move { - acknowledgement_listener.run().await; - error!("The acknowledgement listener has finished execution!"); - }); - spawn_future(async move { - input_message_listener.run().await; - error!("The input listener has finished execution!"); - }); - spawn_future(async move { - retransmission_request_listener.run().await; - error!("The retransmission request listener has finished execution!"); - }); - spawn_future(async move { - sent_notification_listener.run().await; - error!("The sent notification listener has finished execution!"); - }); - spawn_future(async move { - action_controller.run().await; - error!("The controller has finished execution!"); - }); - } } diff --git a/clients/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs b/clients/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs index 190f23e50c..d8fb2b0a8d 100644 --- a/clients/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs +++ b/clients/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs @@ -1,82 +1,101 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use super::{ - action_controller::{Action, ActionSender}, + action_controller::{AckActionSender, Action}, PendingAcknowledgement, RetransmissionRequestReceiver, }; -use crate::client::{ - real_messages_control::real_traffic_stream::{BatchRealMessageSender, RealMessage}, - topology_control::TopologyAccessor, -}; - +use crate::client::real_messages_control::acknowledgement_control::PacketDestination; +use crate::client::real_messages_control::message_handler::{MessageHandler, PreparationError}; +use crate::client::real_messages_control::real_traffic_stream::RealMessage; +use crate::client::replies::reply_controller::ReplyControllerSender; use client_connections::TransmissionLane; use futures::StreamExt; use log::*; -use nymsphinx::{ - acknowledgements::AckKey, addressing::clients::Recipient, preparer::MessagePreparer, -}; +use nymsphinx::addressing::clients::Recipient; +use nymsphinx::chunking::fragment::Fragment; +use nymsphinx::preparer::PreparedFragment; use rand::{CryptoRng, Rng}; use std::sync::{Arc, Weak}; // responsible for packet retransmission upon fired timer -pub(super) struct RetransmissionRequestListener -where - R: CryptoRng + Rng, -{ - ack_key: Arc, - ack_recipient: Recipient, - message_preparer: MessagePreparer, - action_sender: ActionSender, - real_message_sender: BatchRealMessageSender, +pub(super) struct RetransmissionRequestListener { + action_sender: AckActionSender, + message_handler: MessageHandler, request_receiver: RetransmissionRequestReceiver, - topology_access: TopologyAccessor, + reply_controller_sender: ReplyControllerSender, } impl RetransmissionRequestListener where R: CryptoRng + Rng, { - #[allow(clippy::too_many_arguments)] pub(super) fn new( - ack_key: Arc, - ack_recipient: Recipient, - message_preparer: MessagePreparer, - action_sender: ActionSender, - real_message_sender: BatchRealMessageSender, + action_sender: AckActionSender, + message_handler: MessageHandler, request_receiver: RetransmissionRequestReceiver, - topology_access: TopologyAccessor, + reply_controller_sender: ReplyControllerSender, ) -> Self { RetransmissionRequestListener { - ack_key, - ack_recipient, - message_preparer, action_sender, - real_message_sender, + message_handler, request_receiver, - topology_access, + reply_controller_sender, } } - async fn on_retransmission_request(&mut self, timed_out_ack: Weak) { - let timed_out_ack = match timed_out_ack.upgrade() { + async fn prepare_normal_retransmission_chunk( + &mut self, + packet_recipient: Recipient, + chunk_data: Fragment, + ) -> Result { + debug!("retransmitting normal packet..."); + + self.message_handler + .try_prepare_single_chunk_for_sending(packet_recipient, chunk_data) + .await + } + + async fn on_retransmission_request( + &mut self, + weak_timed_out_ack: Weak, + ) { + let timed_out_ack = match weak_timed_out_ack.upgrade() { Some(timed_out_ack) => timed_out_ack, None => { debug!("We received an ack JUST as we were about to retransmit [1]"); return; } }; - let packet_recipient = &timed_out_ack.recipient; - let chunk_clone = timed_out_ack.message_chunk.clone(); - let frag_id = chunk_clone.fragment_identifier(); - let topology_permit = self.topology_access.get_read_permit().await; - let topology_ref = match topology_permit - .try_get_valid_topology_ref(&self.ack_recipient, Some(packet_recipient)) - { - Some(topology_ref) => topology_ref, - None => { - warn!("Could not retransmit the packet - the network topology is invalid"); + let maybe_prepared_fragment = match &timed_out_ack.destination { + PacketDestination::Anonymous { + recipient_tag, + extra_surb_request, + } => { + // if this is retransmission for reply, offload it to the dedicated task + // that deals with all the surbs + return self.reply_controller_sender.send_retransmission_data( + *recipient_tag, + weak_timed_out_ack, + *extra_surb_request, + ); + } + PacketDestination::KnownRecipient(recipient) => { + self.prepare_normal_retransmission_chunk( + **recipient, + timed_out_ack.message_chunk.clone(), + ) + .await + } + }; + + let frag_id = timed_out_ack.message_chunk.fragment_identifier(); + + let prepared_fragment = match maybe_prepared_fragment { + Ok(prepared_fragment) => prepared_fragment, + Err(err) => { + warn!("Could not retransmit the packet - {err}"); // we NEED to start timer here otherwise we will have this guy permanently stuck in memory self.action_sender .unbounded_send(Action::new_start_timer(frag_id)) @@ -85,11 +104,6 @@ where } }; - let prepared_fragment = self - .message_preparer - .prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, packet_recipient) - .unwrap(); - // if we have the ONLY strong reference to the ack data, it means it was removed from the // pending acks if Arc::strong_count(&timed_out_ack) == 1 { @@ -101,7 +115,6 @@ where // we no longer need the reference - let's drop it so that if somehow `UpdateTimer` action // reached the controller before this function terminated, the controller would not panic. drop(timed_out_ack); - let new_delay = prepared_fragment.total_delay; // We know this update will be reflected by the `StartTimer` Action performed when this @@ -116,16 +129,15 @@ where .unwrap(); // send to `OutQueueControl` to eventually send to the mix network - self.real_message_sender - .send(( + self.message_handler + .forward_messages( vec![RealMessage::new(prepared_fragment.mix_packet, frag_id)], TransmissionLane::Retransmission, - )) + ) .await - .expect("BatchRealMessageReceiver has stopped receiving!"); } - pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started RetransmissionRequestListener with graceful shutdown support"); while !shutdown.is_shutdown() { @@ -145,14 +157,4 @@ where shutdown.recv_timeout().await; log::debug!("RetransmissionRequestListener: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) async fn run(&mut self) { - debug!("Started RetransmissionRequestListener without graceful shutdown support"); - - while let Some(timed_out_ack) = self.request_receiver.next().await { - self.on_retransmission_request(timed_out_ack).await; - } - } } diff --git a/clients/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs b/clients/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs index 958c696aa7..a931ce4962 100644 --- a/clients/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs +++ b/clients/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::action_controller::{Action, ActionSender}; +use super::action_controller::{AckActionSender, Action}; use super::SentPacketNotificationReceiver; use futures::StreamExt; use log::*; @@ -13,13 +13,13 @@ use nymsphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID}; /// accidentally fire retransmission way quicker than we should have. pub(super) struct SentNotificationListener { sent_notifier: SentPacketNotificationReceiver, - action_sender: ActionSender, + action_sender: AckActionSender, } impl SentNotificationListener { pub(super) fn new( sent_notifier: SentPacketNotificationReceiver, - action_sender: ActionSender, + action_sender: AckActionSender, ) -> Self { SentNotificationListener { sent_notifier, @@ -31,18 +31,13 @@ impl SentNotificationListener { if frag_id == COVER_FRAG_ID { trace!("sent off a cover message - no need to start retransmission timer!"); return; - } else if frag_id.is_reply() { - debug!("sent off a reply message - no need to start retransmission timer!"); - // TODO: probably there will need to be some extra procedure here, like it would - // be nice to know that our reply actually reached the recipient (i.e. we got the ack) - return; } self.action_sender .unbounded_send(Action::new_start_timer(frag_id)) .unwrap(); } - pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started SentNotificationListener with graceful shutdown support"); while !shutdown.is_shutdown() { @@ -56,7 +51,7 @@ impl SentNotificationListener { break; } }, - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("SentNotificationListener: Received shutdown"); } } @@ -64,14 +59,4 @@ impl SentNotificationListener { assert!(shutdown.is_shutdown_poll()); log::debug!("SentNotificationListener: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) async fn run(&mut self) { - debug!("Started SentNotificationListener without graceful shutdown support"); - - while let Some(frag_id) = self.sent_notifier.next().await { - self.on_sent_message(frag_id).await; - } - } } diff --git a/clients/client-core/src/client/real_messages_control/message_handler.rs b/clients/client-core/src/client/real_messages_control/message_handler.rs new file mode 100644 index 0000000000..d6fcaab0c0 --- /dev/null +++ b/clients/client-core/src/client/real_messages_control/message_handler.rs @@ -0,0 +1,566 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement; +use crate::client::real_messages_control::real_traffic_stream::{ + BatchRealMessageSender, RealMessage, +}; +use crate::client::real_messages_control::{AckActionSender, Action}; +use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, SentReplyKeys, UsedSenderTags}; +use crate::client::topology_control::{TopologyAccessor, TopologyReadPermit}; +use client_connections::TransmissionLane; +use log::{debug, error, info, trace, warn}; +use nymsphinx::acknowledgements::AckKey; +use nymsphinx::addressing::clients::Recipient; +use nymsphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessage, ReplyMessage}; +use nymsphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey}; +use nymsphinx::chunking::fragment::{Fragment, FragmentIdentifier}; +use nymsphinx::message::NymMessage; +use nymsphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS}; +use nymsphinx::preparer::{MessagePreparer, PreparedFragment}; +use nymsphinx::Delay; +use rand::{CryptoRng, Rng}; +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Duration; +use thiserror::Error; +use topology::{NymTopology, NymTopologyError}; + +// TODO: move that error elsewhere since it seems to be contaminating different files +#[derive(Debug, Clone, Error)] +pub enum PreparationError { + #[error(transparent)] + NymTopologyError(#[from] NymTopologyError), + + #[error("The received message cannot be sent using a single reply surb. It ended up getting split into {fragments} fragments.")] + MessageTooLongForSingleSurb { fragments: usize }, + + #[error("Not enough reply SURBs to send the message. We have {available} available and require at least {required}.")] + NotEnoughSurbs { available: usize, required: usize }, +} + +impl PreparationError { + fn return_surbs(self, returned_surbs: Vec) -> SurbWrappedPreparationError { + SurbWrappedPreparationError { + source: self, + returned_surbs: Some(returned_surbs), + } + } +} + +#[derive(Debug, Error)] +#[error("Failed to prepare packets - {source}. {} reply surbs will be returned", .returned_surbs.as_ref().map(|s| s.len()).unwrap_or_default())] +pub struct SurbWrappedPreparationError { + #[source] + source: PreparationError, + + returned_surbs: Option>, +} + +impl From for SurbWrappedPreparationError +where + T: Into, +{ + fn from(err: T) -> Self { + SurbWrappedPreparationError { + source: err.into(), + returned_surbs: None, + } + } +} + +impl SurbWrappedPreparationError { + pub(crate) fn return_unused_surbs( + self, + surb_storage: &ReceivedReplySurbsMap, + target: &AnonymousSenderTag, + ) -> PreparationError { + if let Some(reply_surbs) = self.returned_surbs { + surb_storage.insert_surbs(target, reply_surbs) + } + self.source + } +} + +#[derive(Clone)] +pub(crate) struct Config { + /// Key used to decrypt contents of received SURBAcks + ack_key: Arc, + + /// Address of this client which also represent an address to which all acknowledgements + /// and surb-based are going to be sent. + sender_address: Recipient, + + /// Average delay a data packet is going to get delay at a single mixnode. + average_packet_delay: Duration, + + /// Average delay an acknowledgement packet is going to get delay at a single mixnode. + average_ack_delay: Duration, + + /// Number of mix hops each packet ('real' message, ack, reply) is expected to take. + /// Note that it does not include gateway hops. + num_mix_hops: u8, + + /// Predefined packet size used for the encapsulated messages. + packet_size: PacketSize, +} + +impl Config { + pub fn new( + ack_key: Arc, + sender_address: Recipient, + average_packet_delay: Duration, + average_ack_delay: Duration, + ) -> Self { + Config { + ack_key, + sender_address, + average_packet_delay, + average_ack_delay, + num_mix_hops: DEFAULT_NUM_MIX_HOPS, + packet_size: PacketSize::default(), + } + } + + /// Allows setting non-default number of expected mix hops in the network. + #[allow(dead_code)] + pub fn with_mix_hops(mut self, hops: u8) -> Self { + self.num_mix_hops = hops; + self + } + + /// Allows setting non-default size of the sphinx packets sent out. + pub fn with_custom_packet_size(mut self, packet_size: PacketSize) -> Self { + self.packet_size = packet_size; + self + } +} + +#[derive(Clone)] +pub(crate) struct MessageHandler { + config: Config, + rng: R, + message_preparer: MessagePreparer, + action_sender: AckActionSender, + real_message_sender: BatchRealMessageSender, + topology_access: TopologyAccessor, + reply_key_storage: SentReplyKeys, + tag_storage: UsedSenderTags, +} + +impl MessageHandler +where + R: CryptoRng + Rng, +{ + pub(crate) fn new( + config: Config, + rng: R, + action_sender: AckActionSender, + real_message_sender: BatchRealMessageSender, + topology_access: TopologyAccessor, + reply_key_storage: SentReplyKeys, + tag_storage: UsedSenderTags, + ) -> Self + where + R: Copy, + { + let message_preparer = MessagePreparer::new( + rng, + config.sender_address, + config.average_packet_delay, + config.average_ack_delay, + ) + .with_custom_real_message_packet_size(config.packet_size) + .with_mix_hops(config.num_mix_hops); + + MessageHandler { + config, + rng, + message_preparer, + action_sender, + real_message_sender, + topology_access, + reply_key_storage, + tag_storage, + } + } + + fn get_or_create_sender_tag(&mut self, recipient: &Recipient) -> AnonymousSenderTag { + if let Some(existing) = self.tag_storage.try_get_existing(recipient) { + trace!("we already had sender tag for {recipient}"); + existing + } else { + info!("creating new sender tag for {recipient}"); + let new_tag = AnonymousSenderTag::new_random(&mut self.rng); + self.tag_storage.insert_new(recipient, new_tag); + info!("we'll be using {new_tag} for all anonymous messages sent to {recipient}"); + new_tag + } + } + + fn get_topology<'a>( + &self, + permit: &'a TopologyReadPermit<'a>, + ) -> Result<&'a NymTopology, PreparationError> { + match permit.try_get_valid_topology_ref(&self.config.sender_address, None) { + Ok(topology_ref) => Ok(topology_ref), + Err(err) => { + warn!("Could not process the packet - the network topology is invalid - {err}"); + Err(err.into()) + } + } + } + + async fn generate_reply_surbs_with_keys( + &mut self, + amount: usize, + ) -> Result<(Vec, Vec), PreparationError> { + let topology_permit = self.topology_access.get_read_permit().await; + let topology = self.get_topology(&topology_permit)?; + + let reply_surbs = self + .message_preparer + .generate_reply_surbs(amount, topology)?; + + let reply_keys = reply_surbs + .iter() + .map(|s| *s.encryption_key()) + .collect::>(); + + Ok((reply_surbs, reply_keys)) + } + + pub(crate) async fn try_send_single_surb_message( + &mut self, + target: AnonymousSenderTag, + message: ReplyMessage, + reply_surb: ReplySurb, + is_extra_surb_request: bool, + ) -> Result<(), SurbWrappedPreparationError> { + let mut fragment = self + .message_preparer + .pad_and_split_message(NymMessage::new_reply(message)); + if fragment.len() > 1 { + // well, it's not a single surb message + return Err(SurbWrappedPreparationError { + source: PreparationError::MessageTooLongForSingleSurb { + fragments: fragment.len(), + }, + returned_surbs: Some(vec![reply_surb]), + }); + } + + let chunk = fragment.pop().unwrap(); + let chunk_clone = chunk.clone(); + let prepared_fragment = self + .try_prepare_single_reply_chunk_for_sending(reply_surb, chunk_clone) + .await?; + + let real_messages = + RealMessage::new(prepared_fragment.mix_packet, chunk.fragment_identifier()); + let delay = prepared_fragment.total_delay; + let pending_ack = + PendingAcknowledgement::new_anonymous(chunk, delay, target, is_extra_surb_request); + + let lane = if is_extra_surb_request { + TransmissionLane::ReplySurbRequest + } else { + TransmissionLane::General + }; + + self.forward_messages(vec![real_messages], lane).await; + self.insert_pending_acks(vec![pending_ack]); + Ok(()) + } + + pub(crate) async fn try_request_additional_reply_surbs( + &mut self, + from: AnonymousSenderTag, + reply_surb: ReplySurb, + amount: u32, + ) -> Result<(), SurbWrappedPreparationError> { + debug!("requesting {amount} reply SURBs from {from}"); + + let surbs_request = + ReplyMessage::new_surb_request_message(self.config.sender_address, amount); + self.try_send_single_surb_message(from, surbs_request, reply_surb, true) + .await + } + + // // TODO: this will require additional argument to make it use different variant of `ReplyMessage` + pub(crate) fn split_reply_message(&mut self, message: Vec) -> Vec { + self.message_preparer + .pad_and_split_message(NymMessage::new_reply(ReplyMessage::new_data_message( + message, + ))) + } + + pub(crate) async fn send_retransmission_reply_chunks( + &mut self, + prepared_fragments: Vec, + lane: TransmissionLane, + ) { + let mut real_messages = Vec::with_capacity(prepared_fragments.len()); + + for prepared in prepared_fragments { + self.update_ack_delay(prepared.fragment_identifier, prepared.total_delay); + real_messages.push(prepared.into()) + } + + self.forward_messages(real_messages, lane).await; + } + + pub(crate) async fn try_send_reply_chunks_on_lane( + &mut self, + target: AnonymousSenderTag, + fragments: Vec, + reply_surbs: Vec, + lane: TransmissionLane, + ) -> Result<(), SurbWrappedPreparationError> { + // TODO: technically this is performing an unnecessary cloning, but in the grand scheme of things + // is it really that bad? + self.try_send_reply_chunks( + target, + fragments.into_iter().map(|f| (lane, f)).collect(), + reply_surbs, + ) + .await + } + + pub(crate) async fn try_send_reply_chunks( + &mut self, + target: AnonymousSenderTag, + fragments: Vec<(TransmissionLane, Fragment)>, + reply_surbs: Vec, + ) -> Result<(), SurbWrappedPreparationError> { + let prepared_fragments = self + .prepare_reply_chunks_for_sending( + fragments.iter().map(|(_, f)| f.clone()).collect(), + reply_surbs, + ) + .await?; + + let mut pending_acks = Vec::with_capacity(fragments.len()); + let mut to_forward: HashMap<_, Vec<_>> = HashMap::new(); + + for (raw, prepared) in fragments.into_iter().zip(prepared_fragments.into_iter()) { + let lane = raw.0; + let fragment = raw.1; + + let real_message = RealMessage::new(prepared.mix_packet, prepared.fragment_identifier); + let delay = prepared.total_delay; + let pending_ack = PendingAcknowledgement::new_anonymous(fragment, delay, target, false); + + let entry = to_forward.entry(lane).or_default(); + entry.push(real_message); + pending_acks.push(pending_ack); + } + + for (lane, real_messages) in to_forward { + self.forward_messages(real_messages, lane).await; + } + + self.insert_pending_acks(pending_acks); + Ok(()) + } + + pub(crate) async fn try_send_plain_message( + &mut self, + recipient: Recipient, + message: Vec, + lane: TransmissionLane, + ) -> Result<(), PreparationError> { + let message = NymMessage::new_plain(message); + self.try_split_and_send_non_reply_message(message, recipient, lane) + .await + } + + pub(crate) async fn try_split_and_send_non_reply_message( + &mut self, + message: NymMessage, + recipient: Recipient, + lane: TransmissionLane, + ) -> Result<(), PreparationError> { + // TODO: I really dislike existence of this assertion, it implies code has to be re-organised + debug_assert!(!matches!(message, NymMessage::Reply(_))); + + // TODO2: it's really annoying we have to get topology permit again here due to borrow-checker + let topology_permit = self.topology_access.get_read_permit().await; + let topology = self.get_topology(&topology_permit)?; + + let fragments = self.message_preparer.pad_and_split_message(message); + + let mut pending_acks = Vec::with_capacity(fragments.len()); + let mut real_messages = Vec::with_capacity(fragments.len()); + for fragment in fragments { + // we need to clone it because we need to keep it in memory in case we had to retransmit + // it. And then we'd need to recreate entire ACK again. + let chunk_clone = fragment.clone(); + let prepared_fragment = self.message_preparer.prepare_chunk_for_sending( + chunk_clone, + topology, + &self.config.ack_key, + &recipient, + )?; + + let real_message = + RealMessage::new(prepared_fragment.mix_packet, fragment.fragment_identifier()); + let delay = prepared_fragment.total_delay; + let pending_ack = PendingAcknowledgement::new_known(fragment, delay, recipient); + + real_messages.push(real_message); + pending_acks.push(pending_ack); + } + + self.insert_pending_acks(pending_acks); + self.forward_messages(real_messages, lane).await; + + Ok(()) + } + + pub(crate) async fn try_send_additional_reply_surbs( + &mut self, + recipient: Recipient, + amount: u32, + ) -> Result<(), PreparationError> { + let sender_tag = self.get_or_create_sender_tag(&recipient); + let (reply_surbs, reply_keys) = + self.generate_reply_surbs_with_keys(amount as usize).await?; + + let message = NymMessage::new_repliable(RepliableMessage::new_additional_surbs( + sender_tag, + reply_surbs, + )); + + self.try_split_and_send_non_reply_message( + message, + recipient, + TransmissionLane::AdditionalReplySurbs, + ) + .await?; + + log::trace!("storing {} reply keys", reply_keys.len()); + self.reply_key_storage.insert_multiple(reply_keys); + + Ok(()) + } + + pub(crate) async fn try_send_message_with_reply_surbs( + &mut self, + recipient: Recipient, + message: Vec, + num_reply_surbs: u32, + lane: TransmissionLane, + ) -> Result<(), SurbWrappedPreparationError> { + let sender_tag = self.get_or_create_sender_tag(&recipient); + let (reply_surbs, reply_keys) = self + .generate_reply_surbs_with_keys(num_reply_surbs as usize) + .await?; + + let message = + NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs)); + + self.try_split_and_send_non_reply_message(message, recipient, lane) + .await?; + + log::trace!("storing {} reply keys", reply_keys.len()); + self.reply_key_storage.insert_multiple(reply_keys); + + Ok(()) + } + + pub(crate) async fn try_prepare_single_chunk_for_sending( + &mut self, + recipient: Recipient, + chunk: Fragment, + ) -> Result { + let topology_permit = self.topology_access.get_read_permit().await; + let topology = self.get_topology(&topology_permit)?; + + let prepared_fragment = self + .message_preparer + .prepare_chunk_for_sending(chunk, topology, &self.config.ack_key, &recipient) + .unwrap(); + + Ok(prepared_fragment) + } + + pub(crate) async fn prepare_reply_chunks_for_sending( + &mut self, + fragments: Vec, + reply_surbs: Vec, + ) -> Result, SurbWrappedPreparationError> { + debug_assert_ne!( + fragments.len(), + reply_surbs.len(), + "attempted to send {} fragments with {} reply surbs", + fragments.len(), + reply_surbs.len() + ); + + let topology_permit = self.topology_access.get_read_permit().await; + let topology = match self.get_topology(&topology_permit) { + Ok(topology) => topology, + Err(err) => return Err(err.return_surbs(reply_surbs)), + }; + + Ok(fragments + .into_iter() + .zip(reply_surbs.into_iter()) + .map(|(fragment, reply_surb)| { + // unwrap here is fine as we know we have a valid topology + self.message_preparer + .prepare_reply_chunk_for_sending( + fragment, + topology, + &self.config.ack_key, + reply_surb, + ) + .unwrap() + }) + .collect()) + } + + pub(crate) async fn try_prepare_single_reply_chunk_for_sending( + &mut self, + reply_surb: ReplySurb, + chunk: Fragment, + ) -> Result { + let topology_permit = self.topology_access.get_read_permit().await; + let topology = match self.get_topology(&topology_permit) { + Ok(topology) => topology, + Err(err) => return Err(err.return_surbs(vec![reply_surb])), + }; + + let prepared_fragment = self + .message_preparer + .prepare_reply_chunk_for_sending(chunk, topology, &self.config.ack_key, reply_surb) + .unwrap(); + + Ok(prepared_fragment) + } + + pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) { + self.action_sender + .unbounded_send(Action::UpdateDelay(id, new_delay)) + .expect("action control task has died") + } + + pub(crate) fn insert_pending_acks(&self, pending_acks: Vec) { + self.action_sender + .unbounded_send(Action::new_insert(pending_acks)) + .expect("action control task has died") + } + + // tells real message sender (with the poisson timer) to send this to the mix network + pub(crate) async fn forward_messages( + &self, + messages: Vec, + transmission_lane: TransmissionLane, + ) { + self.real_message_sender + .send((messages, transmission_lane)) + .await + .expect("real message receiver task (OutQueueControl) has died"); + } +} diff --git a/clients/client-core/src/client/real_messages_control/mod.rs b/clients/client-core/src/client/real_messages_control/mod.rs index e46c02e31e..df04365c9b 100644 --- a/clients/client-core/src/client/real_messages_control/mod.rs +++ b/clients/client-core/src/client/real_messages_control/mod.rs @@ -8,6 +8,11 @@ use self::{ acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl, }; +use crate::client::real_messages_control::message_handler::MessageHandler; +use crate::client::replies::reply_controller::{ + ReplyController, ReplyControllerReceiver, ReplyControllerSender, +}; +use crate::client::replies::reply_storage::CombinedReplyStorage; use crate::{ client::{ inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender, @@ -27,11 +32,13 @@ use rand::{rngs::OsRng, CryptoRng, Rng}; use std::sync::Arc; use std::time::Duration; -#[cfg(feature = "reply-surb")] -use crate::client::reply_key_storage::ReplyKeyStorage; +use crate::client::replies::reply_controller; +use crate::config; +pub(crate) use acknowledgement_control::{AckActionSender, Action}; -mod acknowledgement_control; -mod real_traffic_stream; +pub(crate) mod acknowledgement_control; +pub(crate) mod message_handler; +pub(crate) mod real_traffic_stream; // TODO: ack_key and self_recipient shouldn't really be part of this config pub struct Config { @@ -62,31 +69,102 @@ pub struct Config { /// Predefined packet size used for the encapsulated messages. packet_size: PacketSize, + + /// Defines the minimum number of reply surbs the client would request. + minimum_reply_surb_request_size: u32, + + /// Defines the maximum number of reply surbs the client would request. + maximum_reply_surb_request_size: u32, + + /// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once. + maximum_allowed_reply_surb_request_size: u32, + + /// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking + /// for more even though in theory they wouldn't need to. + maximum_reply_surb_waiting_period: Duration, + + /// Defines maximum amount of time given reply surb is going to be valid for. + /// This is going to be superseded by key rotation once implemented. + maximum_reply_surb_age: Duration, + + /// Defines maximum amount of time given reply key is going to be valid for. + /// This is going to be superseded by key rotation once implemented. + maximum_reply_key_age: Duration, +} + +impl<'a> From<&'a Config> for acknowledgement_control::Config { + fn from(cfg: &'a Config) -> Self { + acknowledgement_control::Config::new(cfg.ack_wait_addition, cfg.ack_wait_multiplier) + .with_custom_packet_size(cfg.packet_size) + } +} + +impl<'a> From<&'a Config> for real_traffic_stream::Config { + fn from(cfg: &'a Config) -> Self { + real_traffic_stream::Config::new( + Arc::clone(&cfg.ack_key), + cfg.self_recipient, + cfg.average_ack_delay_duration, + cfg.average_packet_delay_duration, + cfg.average_message_sending_delay, + cfg.disable_main_poisson_packet_distribution, + ) + .with_custom_cover_packet_size(cfg.packet_size) + } +} + +impl<'a> From<&'a Config> for reply_controller::Config { + fn from(cfg: &'a Config) -> Self { + reply_controller::Config::new( + cfg.minimum_reply_surb_request_size, + cfg.maximum_reply_surb_request_size, + cfg.maximum_allowed_reply_surb_request_size, + cfg.maximum_reply_surb_waiting_period, + cfg.maximum_reply_surb_age, + cfg.maximum_reply_key_age, + ) + } +} + +impl<'a> From<&'a Config> for message_handler::Config { + fn from(cfg: &'a Config) -> Self { + message_handler::Config::new( + Arc::clone(&cfg.ack_key), + cfg.self_recipient, + cfg.average_packet_delay_duration, + cfg.average_ack_delay_duration, + ) + .with_custom_packet_size(cfg.packet_size) + } } impl Config { - // TODO: change the config into a builder - #[allow(clippy::too_many_arguments)] pub fn new( + base_client_debug_config: &config::DebugConfig, ack_key: Arc, - ack_wait_multiplier: f64, - ack_wait_addition: Duration, - average_ack_delay_duration: Duration, - average_message_sending_delay: Duration, - average_packet_delay_duration: Duration, - disable_main_poisson_packet_distribution: bool, self_recipient: Recipient, ) -> Self { Config { ack_key, - ack_wait_addition, - ack_wait_multiplier, self_recipient, - average_message_sending_delay, - average_packet_delay_duration, - average_ack_delay_duration, - disable_main_poisson_packet_distribution, packet_size: Default::default(), + ack_wait_addition: base_client_debug_config.ack_wait_addition, + ack_wait_multiplier: base_client_debug_config.ack_wait_multiplier, + average_message_sending_delay: base_client_debug_config.message_sending_average_delay, + average_packet_delay_duration: base_client_debug_config.average_packet_delay, + average_ack_delay_duration: base_client_debug_config.average_ack_delay, + disable_main_poisson_packet_distribution: base_client_debug_config + .disable_main_poisson_packet_distribution, + minimum_reply_surb_request_size: base_client_debug_config + .minimum_reply_surb_request_size, + maximum_reply_surb_request_size: base_client_debug_config + .maximum_reply_surb_request_size, + maximum_allowed_reply_surb_request_size: base_client_debug_config + .maximum_allowed_reply_surb_request_size, + maximum_reply_surb_waiting_period: base_client_debug_config + .maximum_reply_surb_waiting_period, + maximum_reply_surb_age: base_client_debug_config.maximum_reply_surb_age, + maximum_reply_key_age: base_client_debug_config.maximum_reply_key_age, } } @@ -95,75 +173,84 @@ impl Config { } } -pub struct RealMessagesController +pub(crate) struct RealMessagesController where R: CryptoRng + Rng, { out_queue_control: OutQueueControl, ack_control: AcknowledgementController, + reply_control: ReplyController, } // obviously when we finally make shared rng that is on 'higher' level, this should become // generic `R` impl RealMessagesController { #[allow(clippy::too_many_arguments)] - pub fn new( + pub(crate) fn new( config: Config, ack_receiver: AcknowledgementReceiver, input_receiver: InputMessageReceiver, mix_sender: BatchMixMessageSender, topology_access: TopologyAccessor, + reply_storage: CombinedReplyStorage, + // so much refactoring needed, but this is temporary just to test things out + reply_controller_sender: ReplyControllerSender, + reply_controller_receiver: ReplyControllerReceiver, lane_queue_lengths: LaneQueueLengths, client_connection_rx: ConnectionCommandReceiver, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, ) -> Self { let rng = OsRng; + // create channels for inter-task communication let (real_message_sender, real_message_receiver) = tokio::sync::mpsc::channel(1); let (sent_notifier_tx, sent_notifier_rx) = mpsc::unbounded(); - + let (ack_action_tx, ack_action_rx) = mpsc::unbounded(); let ack_controller_connectors = AcknowledgementControllerConnectors::new( - real_message_sender, input_receiver, sent_notifier_rx, ack_receiver, + ack_action_tx.clone(), + ack_action_rx, ); - let ack_control_config = acknowledgement_control::Config::new( - config.ack_wait_addition, - config.ack_wait_multiplier, - config.average_ack_delay_duration, - config.average_packet_delay_duration, - ) - .with_custom_packet_size(config.packet_size); + // create all configs for the components + let ack_control_config = (&config).into(); + let out_queue_config = (&config).into(); + let reply_controller_config = (&config).into(); + let message_handler_config = (&config).into(); + + // create the actual components + let message_handler = MessageHandler::new( + message_handler_config, + rng, + ack_action_tx, + real_message_sender, + topology_access.clone(), + reply_storage.key_storage(), + reply_storage.tags_storage(), + ); let ack_control = AcknowledgementController::new( ack_control_config, - rng, - topology_access.clone(), Arc::clone(&config.ack_key), - config.self_recipient, ack_controller_connectors, - #[cfg(feature = "reply-surb")] - reply_key_storage, + message_handler.clone(), + reply_controller_sender, ); - let out_queue_config = real_traffic_stream::Config::new( - config.average_ack_delay_duration, - config.average_packet_delay_duration, - config.average_message_sending_delay, - config.disable_main_poisson_packet_distribution, - ) - .with_custom_cover_packet_size(config.packet_size); + let reply_control = ReplyController::new( + reply_controller_config, + message_handler, + reply_storage, + reply_controller_receiver, + ); let out_queue_control = OutQueueControl::new( out_queue_config, - Arc::clone(&config.ack_key), + rng, sent_notifier_tx, mix_sender, real_message_receiver, - rng, - config.self_recipient, topology_access, lane_queue_lengths, client_connection_rx, @@ -172,30 +259,26 @@ impl RealMessagesController { RealMessagesController { out_queue_control, ack_control, + reply_control, } } - pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) { + pub fn start_with_shutdown(self, shutdown: task::TaskClient) { let mut out_queue_control = self.out_queue_control; let ack_control = self.ack_control; + let mut reply_control = self.reply_control; let shutdown_handle = shutdown.clone(); spawn_future(async move { out_queue_control.run_with_shutdown(shutdown_handle).await; debug!("The out queue controller has finished execution!"); }); + let shutdown_handle = shutdown.clone(); + spawn_future(async move { + reply_control.run_with_shutdown(shutdown_handle).await; + debug!("The reply controller has finished execution!"); + }); + ack_control.start_with_shutdown(shutdown); } - - #[cfg(target_arch = "wasm32")] - pub fn start(self) { - let mut out_queue_control = self.out_queue_control; - let ack_control = self.ack_control; - - spawn_future(async move { - out_queue_control.run().await; - debug!("The out queue controller has finished execution!"); - }); - ack_control.start(); - } } diff --git a/clients/client-core/src/client/real_messages_control/real_traffic_stream.rs b/clients/client-core/src/client/real_messages_control/real_traffic_stream.rs index c6ec08c15e..2abbd4661e 100644 --- a/clients/client-core/src/client/real_messages_control/real_traffic_stream.rs +++ b/clients/client-core/src/client/real_messages_control/real_traffic_stream.rs @@ -1,9 +1,11 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use self::sending_delay_controller::SendingDelayController; use crate::client::mix_traffic::BatchMixMessageSender; use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender; use crate::client::topology_control::TopologyAccessor; +use crate::client::transmission_buffer::TransmissionBuffer; use client_connections::{ ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane, }; @@ -16,6 +18,7 @@ use nymsphinx::chunking::fragment::FragmentIdentifier; use nymsphinx::cover::generate_loop_cover_packet; use nymsphinx::forwarding::packet::MixPacket; use nymsphinx::params::PacketSize; +use nymsphinx::preparer::PreparedFragment; use nymsphinx::utils::sample_poisson_duration; use rand::{CryptoRng, Rng}; use std::pin::Pin; @@ -28,25 +31,16 @@ use tokio::time; #[cfg(target_arch = "wasm32")] use wasm_timer; -use self::{ - sending_delay_controller::SendingDelayController, transmission_buffer::TransmissionBuffer, -}; - mod sending_delay_controller; -mod transmission_buffer; - -#[cfg(not(target_arch = "wasm32"))] -fn get_time_now() -> time::Instant { - time::Instant::now() -} - -#[cfg(target_arch = "wasm32")] -fn get_time_now() -> wasm_timer::Instant { - wasm_timer::Instant::now() -} /// Configurable parameters of the `OutQueueControl` pub(crate) struct Config { + /// Key used to encrypt and decrypt content of an ACK packet. + ack_key: Arc, + + /// Represents full address of this client. + our_full_destination: Recipient, + /// Average delay an acknowledgement packet is going to get delay at a single mixnode. average_ack_delay: Duration, @@ -66,12 +60,16 @@ pub(crate) struct Config { impl Config { pub(crate) fn new( + ack_key: Arc, + our_full_destination: Recipient, average_ack_delay: Duration, average_packet_delay: Duration, average_message_sending_delay: Duration, disable_poisson_packet_distribution: bool, ) -> Self { Config { + ack_key, + our_full_destination, average_ack_delay, average_packet_delay, average_message_sending_delay, @@ -93,9 +91,6 @@ where /// Configurable parameters of the `ActionController` config: Config, - /// Key used to encrypt and decrypt content of an ACK packet. - ack_key: Arc, - /// Channel used for notifying of a real packet being sent out. Used to start up retransmission timer. sent_notifier: SentPacketNotificationSender, @@ -119,9 +114,6 @@ where /// before being sent out into the network. real_receiver: BatchRealMessageReceiver, - /// Represents full address of this client. - our_full_destination: Recipient, - /// Instance of a cryptographically secure random number generator. rng: R, @@ -130,7 +122,7 @@ where /// Buffer containing all incoming real messages keyed by transmission lane, that we will send /// out to the mixnet. - transmission_buffer: TransmissionBuffer, + transmission_buffer: TransmissionBuffer, /// Incoming channel for being notified of closed connections, so that we can close lanes /// corresponding to connections. To avoid sending traffic unnecessary @@ -144,9 +136,23 @@ where pub(crate) struct RealMessage { mix_packet: MixPacket, fragment_id: FragmentIdentifier, + // TODO: add info about it being constructed with reply-surb +} + +impl From for RealMessage { + fn from(fragment: PreparedFragment) -> Self { + RealMessage { + mix_packet: fragment.mix_packet, + fragment_id: fragment.fragment_identifier, + } + } } impl RealMessage { + pub(crate) fn packet_size(&self) -> usize { + self.mix_packet.sphinx_packet().len() + } + pub(crate) fn new(mix_packet: MixPacket, fragment_id: FragmentIdentifier) -> Self { RealMessage { mix_packet, @@ -175,28 +181,24 @@ where #[allow(clippy::too_many_arguments)] pub(crate) fn new( config: Config, - ack_key: Arc, + rng: R, sent_notifier: SentPacketNotificationSender, mix_tx: BatchMixMessageSender, real_receiver: BatchRealMessageReceiver, - rng: R, - our_full_destination: Recipient, topology_access: TopologyAccessor, lane_queue_lengths: LaneQueueLengths, client_connection_rx: ConnectionCommandReceiver, ) -> Self { OutQueueControl { config, - ack_key, sent_notifier, next_delay: None, sending_delay_controller: Default::default(), mix_tx, real_receiver, - our_full_destination, rng, topology_access, - transmission_buffer: Default::default(), + transmission_buffer: TransmissionBuffer::new(), client_connection_rx, lane_queue_lengths, } @@ -220,24 +222,23 @@ where // poisson delay, but is it really a problem? let topology_permit = self.topology_access.get_read_permit().await; // the ack is sent back to ourselves (and then ignored) - let topology_ref_option = topology_permit.try_get_valid_topology_ref( - &self.our_full_destination, - Some(&self.our_full_destination), - ); - if topology_ref_option.is_none() { - warn!( - "No valid topology detected - won't send any loop cover message this time" - ); - return; - } - let topology_ref = topology_ref_option.unwrap(); + let topology_ref = match topology_permit.try_get_valid_topology_ref( + &self.config.our_full_destination, + Some(&self.config.our_full_destination), + ) { + Ok(topology) => topology, + Err(err) => { + warn!("We're not going to send any loop cover message this time, as the current topology seem to be invalid - {err}"); + return; + } + }; ( generate_loop_cover_packet( &mut self.rng, topology_ref, - &self.ack_key, - &self.our_full_destination, + &self.config.ack_key, + &self.config.our_full_destination, self.config.average_ack_delay, self.config.average_packet_delay, self.config.cover_packet_size, @@ -254,7 +255,7 @@ where }; if let Err(err) = self.mix_tx.send(vec![next_message]).await { - log::error!("Failed to send: {}", err); + log::error!("Failed to send: {err}"); } // notify ack controller about sending our message only after we actually managed to push it @@ -318,7 +319,9 @@ where fn pop_next_message(&mut self) -> Option { // Pop the next message from the transmission buffer - let (lane, real_next) = self.transmission_buffer.pop_next_message_at_random()?; + let (lane, real_next) = self + .transmission_buffer + .pop_next_message_at_random(&mut self.rng)?; // Update the published queue length let lane_length = self.transmission_buffer.lane_length(&lane); @@ -459,7 +462,9 @@ where } #[cfg(not(target_arch = "wasm32"))] - fn log_status(&self) { + fn log_status(&self, shutdown: &mut task::TaskClient) { + use crate::error::ClientCoreStatusMessage; + let packets = self.transmission_buffer.total_size(); let backlog = self.transmission_buffer.total_size_in_bytes() as f64 / 1024.0; let lanes = self.transmission_buffer.num_lanes(); @@ -483,19 +488,26 @@ where } else { log::debug!("{status_str}"); } + + // Send status message to whoever is listening (possibly UI) + if mult == self.sending_delay_controller.max_multiplier() { + shutdown.send_status_msg(Box::new(ClientCoreStatusMessage::GatewayIsVerySlow)); + } else if mult > self.sending_delay_controller.min_multiplier() { + shutdown.send_status_msg(Box::new(ClientCoreStatusMessage::GatewayIsSlow)); + } } #[cfg(not(target_arch = "wasm32"))] fn log_status_infrequent(&self) { if self.sending_delay_controller.current_multiplier() > 1 { log::warn!( - "Unable to send packets fast enough - sending delay multiplier set to: {}", + "Unable to send packets at the default rate - rate reduced by setting the delay multiplier set to: {}", self.sending_delay_controller.current_multiplier() ); } } - pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started OutQueueControl with graceful shutdown support"); #[cfg(not(target_arch = "wasm32"))] @@ -506,11 +518,11 @@ where while !shutdown.is_shutdown() { tokio::select! { biased; - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("OutQueueControl: Received shutdown"); } _ = status_timer.tick() => { - self.log_status(); + self.log_status(&mut shutdown); } _ = infrequent_status_timer.tick() => { self.log_status_infrequent(); @@ -547,16 +559,6 @@ where } log::debug!("OutQueueControl: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - pub(super) async fn run(&mut self) { - debug!("Started OutQueueControl without graceful shutdown support"); - - while let Some(next_message) = self.next().await { - self.on_message(next_message).await; - } - } } impl Stream for OutQueueControl diff --git a/clients/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs b/clients/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs index b22ffae7ff..b2d656f763 100644 --- a/clients/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs +++ b/clients/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs @@ -1,14 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::get_time_now; +use crate::client::helpers::{get_time_now, Instant}; use std::time::Duration; -#[cfg(not(target_arch = "wasm32"))] -use tokio::time; -#[cfg(target_arch = "wasm32")] -use wasm_timer; - // The minimum time between increasing the average delay between packets. If we hit the ceiling in // the available buffer space we want to take somewhat swift action, but we still need to give a // short time to give the channel a chance reduce pressure. @@ -39,19 +34,11 @@ pub(crate) struct SendingDelayController { lower_bound: u32, /// To make sure we don't change the multiplier to fast, we limit a change to some duration - #[cfg(not(target_arch = "wasm32"))] - time_when_changed: time::Instant, - - #[cfg(target_arch = "wasm32")] - time_when_changed: wasm_timer::Instant, + time_when_changed: Instant, /// If we have a long enough time without any backpressure detected we try reducing the sending /// delay multiplier - #[cfg(not(target_arch = "wasm32"))] - time_when_backpressure_detected: time::Instant, - - #[cfg(target_arch = "wasm32")] - time_when_backpressure_detected: wasm_timer::Instant, + time_when_backpressure_detected: Instant, } impl Default for SendingDelayController { @@ -77,6 +64,16 @@ impl SendingDelayController { self.current_multiplier } + #[cfg(not(target_arch = "wasm32"))] + pub(crate) fn min_multiplier(&self) -> u32 { + self.lower_bound + } + + #[cfg(not(target_arch = "wasm32"))] + pub(crate) fn max_multiplier(&self) -> u32 { + self.upper_bound + } + pub(crate) fn increase_delay_multiplier(&mut self) { if self.current_multiplier < self.upper_bound { self.current_multiplier = diff --git a/clients/client-core/src/client/received_buffer.rs b/clients/client-core/src/client/received_buffer.rs index ed693993a2..afafec44ca 100644 --- a/clients/client-core/src/client/received_buffer.rs +++ b/clients/client-core/src/client/received_buffer.rs @@ -1,26 +1,26 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::client::replies::reply_controller::ReplyControllerSender; +use crate::client::replies::reply_storage::SentReplyKeys; use crate::spawn_future; use crypto::asymmetric::encryption; +use crypto::Digest; use futures::channel::mpsc; use futures::lock::Mutex; use futures::StreamExt; use gateway_client::MixnetMessageReceiver; use log::*; +use nymsphinx::anonymous_replies::requests::{ + RepliableMessage, RepliableMessageContent, ReplyMessage, ReplyMessageContent, +}; +use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEncryptionKey}; +use nymsphinx::message::{NymMessage, PlainMessage}; +use nymsphinx::params::ReplySurbKeyDigestAlgorithm; use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage}; use std::collections::HashSet; use std::sync::Arc; -#[cfg(feature = "reply-surb")] -use crate::client::reply_key_storage::ReplyKeyStorage; -#[cfg(feature = "reply-surb")] -use crypto::{symmetric::stream_cipher, Digest}; -#[cfg(feature = "reply-surb")] -use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEncryptionKey}; -#[cfg(feature = "reply-surb")] -use nymsphinx::params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorithm}; - // Buffer Requests to say "hey, send any reconstructed messages to this channel" // or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead" pub type ReceivedBufferRequestSender = mpsc::UnboundedSender; @@ -46,26 +46,15 @@ struct ReceivedMessagesBufferInner { } impl ReceivedMessagesBufferInner { - fn process_received_fragment(&mut self, raw_fragment: Vec) -> Option { - let fragment_data = match self - .message_receiver - .recover_plaintext(self.local_encryption_keypair.private_key(), raw_fragment) - { - Err(e) => { - warn!("failed to recover fragment data: {:?}. The whole underlying message might be corrupted and unrecoverable!", e); - return None; - } - Ok(frag_data) => frag_data, - }; - - if nymsphinx::cover::is_cover(&fragment_data) { + fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option { + if nymsphinx::cover::is_cover(fragment_data) { trace!("The message was a loop cover message! Skipping it"); return None; } - let fragment = match self.message_receiver.recover_fragment(&fragment_data) { - Err(e) => { - warn!("failed to recover fragment from raw data: {:?}. The whole underlying message might be corrupted and unrecoverable!", e); + let fragment = match self.message_receiver.recover_fragment(fragment_data) { + Err(err) => { + warn!("failed to recover fragment from raw data: {err}. The whole underlying message might be corrupted and unrecoverable!"); return None; } Ok(frag) => frag, @@ -79,9 +68,10 @@ impl ReceivedMessagesBufferInner { // if we returned an error the underlying message is malformed in some way match self.message_receiver.insert_new_fragment(fragment) { Err(err) => match err { - MessageRecoveryError::MalformedReconstructedMessage(message_sets) => { + MessageRecoveryError::MalformedReconstructedMessage { source, used_sets } => { + error!("message reconstruction failed - {source}. Attempting to re-use the message sets..."); // TODO: should we really insert reconstructed sets? could this be abused for some attack? - for set_id in message_sets { + for set_id in used_sets { if !self.recently_reconstructed.insert(set_id) { // or perhaps we should even panic at this point? error!("Reconstructed another message containing already used set id!") @@ -107,6 +97,34 @@ impl ReceivedMessagesBufferInner { }, } } + + fn process_received_reply( + &mut self, + reply_ciphertext: &mut [u8], + reply_key: SurbEncryptionKey, + ) -> Option { + // note: this performs decryption IN PLACE without extra allocation + self.message_receiver + .recover_plaintext_from_reply(reply_ciphertext, reply_key); + let fragment_data = reply_ciphertext; + + self.recover_from_fragment(fragment_data) + } + + fn process_received_regular_packet(&mut self, mut raw_fragment: Vec) -> Option { + let fragment_data = match self.message_receiver.recover_plaintext_from_regular_packet( + self.local_encryption_keypair.private_key(), + &mut raw_fragment, + ) { + Err(err) => { + warn!("failed to recover fragment data: {err}. The whole underlying message might be corrupted and unrecoverable!"); + return None; + } + Ok(frag_data) => frag_data, + }; + + self.recover_from_fragment(fragment_data) + } } #[derive(Debug, Clone)] @@ -114,17 +132,15 @@ impl ReceivedMessagesBufferInner { // You should always use .clone() to create additional instances struct ReceivedMessagesBuffer { inner: Arc>, - - /// Storage containing keys to all [`ReplySURB`]s ever sent out that we did not receive back. - // There's no need to put it behind a Mutex since it's already properly concurrent - #[cfg(feature = "reply-surb")] - reply_key_storage: ReplyKeyStorage, + reply_key_storage: SentReplyKeys, + reply_controller_sender: ReplyControllerSender, } impl ReceivedMessagesBuffer { fn new( local_encryption_keypair: Arc, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, + reply_key_storage: SentReplyKeys, + reply_controller_sender: ReplyControllerSender, ) -> Self { ReceivedMessagesBuffer { inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner { @@ -134,8 +150,8 @@ impl ReceivedMessagesBuffer { message_sender: None, recently_reconstructed: HashSet::new(), })), - #[cfg(feature = "reply-surb")] reply_key_storage, + reply_controller_sender, } } @@ -177,34 +193,139 @@ impl ReceivedMessagesBuffer { guard.message_sender = Some(sender); } - async fn add_reconstructed_messages(&mut self, msgs: Vec) { - debug!("Adding {:?} new messages to the buffer!", msgs.len()); - trace!("Adding new messages to the buffer! {:?}", msgs); - self.inner.lock().await.messages.extend(msgs) + fn handle_reconstructed_plain_messages( + &mut self, + msgs: Vec, + ) -> Vec { + msgs.into_iter().map(Into::into).collect() } - #[cfg(feature = "reply-surb")] - fn process_received_reply( - reply_ciphertext: &[u8], - reply_key: SurbEncryptionKey, - ) -> Option { - let zero_iv = stream_cipher::zero_iv::(); + fn handle_reconstructed_repliable_messages( + &mut self, + msgs: Vec, + ) -> Vec { + let mut reconstructed = Vec::new(); + for msg in msgs { + let (reply_surbs, from_surb_request) = match msg.content { + RepliableMessageContent::Data { + message, + reply_surbs, + } => { + trace!( + "received message that also contained additional {} reply surbs from {:?}!", + reply_surbs.len(), + msg.sender_tag + ); - let mut reply_msg = stream_cipher::decrypt::( - reply_key.inner(), - &zero_iv, - reply_ciphertext, - ); - if let Err(err) = MessageReceiver::remove_padding(&mut reply_msg) { - warn!("Received reply had malformed padding! - {:?}", err); - None - } else { - // TODO: perhaps having to say it doesn't have a surb an indication the type should be changed? - Some(ReconstructedMessage { - message: reply_msg, - reply_surb: None, - }) + reconstructed.push(ReconstructedMessage::new(message, msg.sender_tag)); + + (reply_surbs, false) + } + RepliableMessageContent::AdditionalSurbs { reply_surbs } => { + trace!( + "received additional {} reply surbs from {:?}!", + reply_surbs.len(), + msg.sender_tag + ); + (reply_surbs, true) + } + RepliableMessageContent::Heartbeat { + additional_reply_surbs, + } => { + error!("received a repliable heartbeat message - we don't know how to handle it yet (and we won't know until future PRs)"); + (additional_reply_surbs, false) + } + }; + + self.reply_controller_sender.send_additional_surbs( + msg.sender_tag, + reply_surbs, + from_surb_request, + ) } + reconstructed + } + + fn handle_reconstructed_reply_messages( + &mut self, + msgs: Vec, + ) -> Vec { + let mut reconstructed = Vec::new(); + for msg in msgs { + match msg.content { + ReplyMessageContent::Data { message } => reconstructed.push(message.into()), + ReplyMessageContent::SurbRequest { recipient, amount } => { + debug!("received request for {amount} additional reply SURBs from {recipient}"); + self.reply_controller_sender + .send_additional_surbs_request(*recipient, amount); + } + } + } + reconstructed + } + + async fn handle_reconstructed_messages(&mut self, msgs: Vec) { + if msgs.is_empty() { + return; + } + + let mut plain_messages = Vec::new(); + let mut repliable_messages = Vec::new(); + let mut reply_messages = Vec::new(); + + for msg in msgs { + match msg { + NymMessage::Plain(plain) => plain_messages.push(plain), + NymMessage::Repliable(repliable) => repliable_messages.push(repliable), + NymMessage::Reply(reply) => reply_messages.push(reply), + } + } + + let mut reconstructed_messages = self.handle_reconstructed_plain_messages(plain_messages); + reconstructed_messages + .append(&mut self.handle_reconstructed_repliable_messages(repliable_messages)); + reconstructed_messages + .append(&mut self.handle_reconstructed_reply_messages(reply_messages)); + + let mut inner_guard = self.inner.lock().await; + debug!( + "Adding {:?} new messages to the buffer!", + reconstructed_messages.len() + ); + + if let Some(sender) = &inner_guard.message_sender { + trace!("Sending reconstructed messages to announced sender"); + if let Err(err) = sender.unbounded_send(reconstructed_messages) { + warn!("The reconstructed message receiver went offline without explicit notification (relevant error: - {err})"); + inner_guard.message_sender = None; + inner_guard.messages.extend(err.into_inner()); + } + } else { + trace!("No sender available - buffering reconstructed messages"); + inner_guard.messages.extend(reconstructed_messages) + } + } + + // this function doesn't really belong here... + fn get_reply_key<'a>( + &self, + raw_message: &'a mut [u8], + ) -> Option<(SurbEncryptionKey, &'a mut [u8])> { + let reply_surb_digest_size = ReplySurbKeyDigestAlgorithm::output_size(); + if raw_message.len() < reply_surb_digest_size { + return None; + } + + let possible_key_digest = + EncryptionKeyDigest::clone_from_slice(&raw_message[..reply_surb_digest_size]); + self.reply_key_storage + .try_pop(possible_key_digest) + .map(|reply_encryption_key| { + ( + *reply_encryption_key, + &mut raw_message[reply_surb_digest_size..], + ) + }) } async fn handle_new_received(&mut self, msgs: Vec>) { @@ -217,69 +338,27 @@ impl ReceivedMessagesBuffer { let mut inner_guard = self.inner.lock().await; // first check if this is a reply or a chunked message - // TODO: verify with @AP if this way of doing it is safe or whether it could - // cause some attacks due to, I don't know, stupid edge case collisions? - // Update: this DOES introduce a possible leakage: https://github.com/nymtech/nym/issues/296 - for msg in msgs { - // TODO: - // 1. make it nicer - // 2. make it not feature-locked - - #[cfg(feature = "reply-surb")] - { - let reply_surb_digest_size = ReplySurbKeyDigestAlgorithm::output_size(); - - let possible_key_digest = - EncryptionKeyDigest::clone_from_slice(&msg[..reply_surb_digest_size]); - - // check first `HasherOutputSize` bytes if they correspond to known encryption key - // if yes - this is a reply message - - // TODO: this might be a bottleneck - since the keys are stored on disk we, presumably, - // are doing a disk operation every single received fragment - if let Some(reply_encryption_key) = self - .reply_key_storage - .get_and_remove_encryption_key(possible_key_digest) - .expect("storage operation failed!") - { - if let Some(completed_message) = Self::process_received_reply( - &msg[reply_surb_digest_size..], - reply_encryption_key, - ) { - completed_messages.push(completed_message) - } + // note: there's a possible information leakage associated with this check https://github.com/nymtech/nym/issues/296 + for mut msg in msgs { + // check first `HasherOutputSize` bytes if they correspond to known encryption key + // if yes - this is a reply message + let completed_message = + if let Some((reply_key, reply_message)) = self.get_reply_key(&mut msg) { + inner_guard.process_received_reply(reply_message, reply_key) } else { - // otherwise - it's a 'normal' message - if let Some(completed_message) = inner_guard.process_received_fragment(msg) { - completed_messages.push(completed_message) - } - } - } + inner_guard.process_received_regular_packet(msg) + }; - #[cfg(not(feature = "reply-surb"))] - if let Some(completed_message) = inner_guard.process_received_fragment(msg) { - completed_messages.push(completed_message) + if let Some(completed) = completed_message { + info!("received {completed}"); + completed_messages.push(completed) } } + drop(inner_guard); + if !completed_messages.is_empty() { - if let Some(sender) = &inner_guard.message_sender { - trace!("Sending reconstructed messages to announced sender"); - if let Err(err) = sender.unbounded_send(completed_messages) { - warn!("The reconstructed message receiver went offline without explicit notification (relevant error: - {:?})", err); - // make sure to drop the lock to not deadlock - // (it is required by `add_reconstructed_messages`) - inner_guard.message_sender = None; - drop(inner_guard); - self.add_reconstructed_messages(err.into_inner()).await; - } - } else { - // make sure to drop the lock to not deadlock - // (it is required by `add_reconstructed_messages`) - drop(inner_guard); - trace!("No sender available - buffering reconstructed messages"); - self.add_reconstructed_messages(completed_messages).await; - } + self.handle_reconstructed_messages(completed_messages).await } } } @@ -320,21 +399,20 @@ impl RequestReceiver { } } - async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started RequestReceiver with graceful shutdown support"); while !shutdown.is_shutdown() { tokio::select! { biased; - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("RequestReceiver: Received shutdown"); } request = self.query_receiver.next() => { - match request { - Some(message) => self.handle_message(message).await, - None => { - log::trace!("RequestReceiver: Stopping since channel closed"); - break; - }, + if let Some(message) = request { + self.handle_message(message).await + } else { + log::trace!("RequestReceiver: Stopping since channel closed"); + break; } }, } @@ -342,16 +420,6 @@ impl RequestReceiver { shutdown.recv_timeout().await; log::debug!("RequestReceiver: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - async fn run(&mut self) { - debug!("Started RequestReceiver without graceful shutdown support"); - - while let Some(message) = self.query_receiver.next().await { - self.handle_message(message).await - } - } } struct FragmentedMessageReceiver { @@ -370,20 +438,19 @@ impl FragmentedMessageReceiver { } } - async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { + async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { debug!("Started FragmentedMessageReceiver with graceful shutdown support"); while !shutdown.is_shutdown() { tokio::select! { - new_messages = self.mixnet_packet_receiver.next() => match new_messages { - Some(new_messages) => { + new_messages = self.mixnet_packet_receiver.next() => { + if let Some(new_messages) = new_messages { self.received_buffer.handle_new_received(new_messages).await; - } - None => { + } else { log::trace!("FragmentedMessageReceiver: Stopping since channel closed"); break; } }, - _ = shutdown.recv() => { + _ = shutdown.recv_with_delay() => { log::trace!("FragmentedMessageReceiver: Received shutdown"); } } @@ -391,34 +458,25 @@ impl FragmentedMessageReceiver { shutdown.recv_timeout().await; log::debug!("FragmentedMessageReceiver: Exiting"); } - - // todo: think whether this is still required - #[allow(dead_code)] - async fn run(&mut self) { - debug!("Started FragmentedMessageReceiver without graceful shutdown support"); - - while let Some(new_messages) = self.mixnet_packet_receiver.next().await { - self.received_buffer.handle_new_received(new_messages).await; - } - } } -pub struct ReceivedMessagesBufferController { +pub(crate) struct ReceivedMessagesBufferController { fragmented_message_receiver: FragmentedMessageReceiver, request_receiver: RequestReceiver, } impl ReceivedMessagesBufferController { - pub fn new( + pub(crate) fn new( local_encryption_keypair: Arc, query_receiver: ReceivedBufferRequestReceiver, mixnet_packet_receiver: MixnetMessageReceiver, - #[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, + reply_key_storage: SentReplyKeys, + reply_controller_sender: ReplyControllerSender, ) -> Self { let received_buffer = ReceivedMessagesBuffer::new( local_encryption_keypair, - #[cfg(feature = "reply-surb")] reply_key_storage, + reply_controller_sender, ); ReceivedMessagesBufferController { @@ -430,7 +488,7 @@ impl ReceivedMessagesBufferController { } } - pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) { + pub fn start_with_shutdown(self, shutdown: task::TaskClient) { let mut fragmented_message_receiver = self.fragmented_message_receiver; let mut request_receiver = self.request_receiver; @@ -444,16 +502,4 @@ impl ReceivedMessagesBufferController { request_receiver.run_with_shutdown(shutdown).await; }); } - - #[cfg(target_arch = "wasm32")] - pub fn start(self) { - let mut fragmented_message_receiver = self.fragmented_message_receiver; - let mut request_receiver = self.request_receiver; - spawn_future(async move { - fragmented_message_receiver.run().await; - }); - spawn_future(async move { - request_receiver.run().await; - }); - } } diff --git a/clients/client-core/src/client/replies/mod.rs b/clients/client-core/src/client/replies/mod.rs new file mode 100644 index 0000000000..61b5f19abf --- /dev/null +++ b/clients/client-core/src/client/replies/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod reply_controller; +pub mod reply_storage; diff --git a/clients/client-core/src/client/replies/reply_controller/mod.rs b/clients/client-core/src/client/replies/reply_controller/mod.rs new file mode 100644 index 0000000000..e8b8d980a9 --- /dev/null +++ b/clients/client-core/src/client/replies/reply_controller/mod.rs @@ -0,0 +1,865 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement; +use crate::client::real_messages_control::message_handler::{MessageHandler, PreparationError}; +use crate::client::replies::reply_storage::CombinedReplyStorage; +use client_connections::{ConnectionId, TransmissionLane}; +use futures::channel::oneshot; +use futures::StreamExt; +use log::{debug, error, info, trace, warn}; +use nymsphinx::addressing::clients::Recipient; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; +use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::chunking::fragment::{Fragment, FragmentIdentifier}; +use rand::{CryptoRng, Rng}; +use std::cmp::{max, min}; +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, HashMap}; +use std::sync::{Arc, Weak}; +use std::time::Duration; +use time::OffsetDateTime; + +use crate::client::helpers::new_interval_stream; +use crate::client::transmission_buffer::TransmissionBuffer; +pub(crate) use requests::{ReplyControllerMessage, ReplyControllerReceiver, ReplyControllerSender}; + +pub mod requests; + +pub struct Config { + min_surb_request_size: u32, + max_surb_request_size: u32, + maximum_allowed_reply_surb_request_size: u32, + max_surb_waiting_period: Duration, + max_reply_surb_age: Duration, + max_reply_key_age: Duration, +} + +impl Config { + pub(crate) fn new( + min_surb_request_size: u32, + max_surb_request_size: u32, + maximum_allowed_reply_surb_request_size: u32, + max_surb_waiting_period: Duration, + max_reply_surb_age: Duration, + max_reply_key_age: Duration, + ) -> Self { + Self { + min_surb_request_size, + max_surb_request_size, + maximum_allowed_reply_surb_request_size, + max_surb_waiting_period, + max_reply_surb_age, + max_reply_key_age, + } + } +} + +// the purpose of this task: +// - buffers split messages from input message listener if there were insufficient surbs to send them +// - upon getting extra surbs, resends them +// - so I guess it will handle all 'RepliableMessage' and requests from 'ReplyMessage' +// - replies to "give additional surbs" requests +// - will reply to future heartbeats + +// TODO: this should be split into ingress and egress controllers +// because currently its trying to perform two distinct jobs +pub struct ReplyController { + config: Config, + + // TODO: incorporate that field at some point + // and use binomial distribution to determine the expected required number + // of surbs required to send the message through + // expected_reliability: f32, + request_receiver: ReplyControllerReceiver, + pending_replies: HashMap>, + + /// Retransmission packets that have already timed out and are waiting for additional reply SURBs + /// so that they could be sent back to the network. Once we receive more SURBs, we should send them ASAP. + // TODO: when purging stale entries, we must take extra care to also purge all pending ACK data!! + pending_retransmissions: + HashMap>>, + + message_handler: MessageHandler, + full_reply_storage: CombinedReplyStorage, +} + +impl ReplyController +where + R: CryptoRng + Rng, +{ + pub(crate) fn new( + config: Config, + message_handler: MessageHandler, + full_reply_storage: CombinedReplyStorage, + request_receiver: ReplyControllerReceiver, + ) -> Self { + ReplyController { + config, + request_receiver, + pending_replies: HashMap::new(), + pending_retransmissions: HashMap::new(), + message_handler, + full_reply_storage, + } + } + + fn insert_pending_replies>( + &mut self, + recipient: &AnonymousSenderTag, + fragments: I, + lane: TransmissionLane, + ) { + self.pending_replies + .entry(*recipient) + .or_insert_with(TransmissionBuffer::new) + .store(&lane, fragments) + } + + fn re_insert_pending_replies( + &mut self, + recipient: &AnonymousSenderTag, + fragments: Vec<(TransmissionLane, Fragment)>, + ) { + // the buffer should ALWAYS exist at this point, if it doesn't, it's a bug... + self.pending_replies + .entry(*recipient) + .or_insert_with(TransmissionBuffer::new) + .store_multiple(fragments) + } + + fn re_insert_pending_retransmission( + &mut self, + recipient: &AnonymousSenderTag, + data: Vec>, + ) { + // the underlying entry MUST exist as we've just got data from there + let map_entry = self + .pending_retransmissions + .get_mut(recipient) + .expect("our pending retransmission entry is somehow gone!"); + + for pending in data { + // if it's 0, we don't need to do anything - we just got that ack! + if Arc::strong_count(&pending) > 1 { + let id = pending.inner_fragment_identifier(); + let downgraded = Arc::downgrade(&pending); + map_entry.insert(id, downgraded); + } + } + } + + fn should_request_more_surbs(&self, target: &AnonymousSenderTag) -> bool { + trace!("checking if we should request more surbs from {:?}", target); + + let pending_queue_size = self + .pending_replies + .get(target) + .map(|pending_queue| pending_queue.total_size()) + .unwrap_or_default(); + + let retransmission_queue = self + .pending_retransmissions + .get(target) + .map(|pending_queue| pending_queue.len()) + .unwrap_or_default(); + + let total_queue = pending_queue_size + retransmission_queue; + + // simple as that - there's absolutely nothing to retransmit + if total_queue == 0 { + return false; + } + + let available_surbs = self + .full_reply_storage + .surbs_storage_ref() + .available_surbs(target); + let pending_surbs = self + .full_reply_storage + .surbs_storage_ref() + .pending_reception(target) as usize; + let min_surbs_threshold = self + .full_reply_storage + .surbs_storage_ref() + .min_surb_threshold(); + let max_surbs_threshold = self + .full_reply_storage + .surbs_storage_ref() + .max_surb_threshold(); + + debug!("total queue size: {total_queue} = pending data {pending_queue_size} + pending retransmission {retransmission_queue}, available surbs: {available_surbs} pending surbs: {pending_surbs} threshold range: {min_surbs_threshold}..{max_surbs_threshold}"); + + (pending_surbs + available_surbs) < max_surbs_threshold + && (pending_surbs + available_surbs) < (total_queue + min_surbs_threshold) + } + + async fn handle_send_reply( + &mut self, + recipient_tag: AnonymousSenderTag, + data: Vec, + lane: TransmissionLane, + ) { + if !self + .full_reply_storage + .surbs_storage_ref() + .contains_surbs_for(&recipient_tag) + { + warn!("received reply request for {:?} but we don't have any surbs stored for that recipient!", recipient_tag); + return; + } + + trace!("handling reply to {:?}", recipient_tag); + let mut fragments = self.message_handler.split_reply_message(data); + let total_size = fragments.len(); + trace!("This reply requires {:?} SURBs", total_size); + + let available_surbs = self + .full_reply_storage + .surbs_storage_ref() + .available_surbs(&recipient_tag); + let min_surbs_threshold = self + .full_reply_storage + .surbs_storage_ref() + .min_surb_threshold(); + + let max_to_send = if available_surbs > min_surbs_threshold { + min(fragments.len(), available_surbs - min_surbs_threshold) + } else { + 0 + }; + + if max_to_send > 0 { + let (surbs, _surbs_left) = self + .full_reply_storage + .surbs_storage_ref() + .get_reply_surbs(&recipient_tag, max_to_send); + + if let Some(reply_surbs) = surbs { + let to_send = fragments.drain(..max_to_send).collect::>(); + if let Err(err) = self + .message_handler + .try_send_reply_chunks_on_lane( + recipient_tag, + to_send.clone(), + reply_surbs, + lane, + ) + .await + { + let err = err.return_unused_surbs( + self.full_reply_storage.surbs_storage_ref(), + &recipient_tag, + ); + warn!("failed to send reply to {recipient_tag}: {err}"); + self.insert_pending_replies(&recipient_tag, to_send, lane); + } + } + } + + // if there's leftover data we didn't send because we didn't have enough (or any) surbs - buffer it + if !fragments.is_empty() { + self.insert_pending_replies(&recipient_tag, fragments, lane); + } + + if self.should_request_more_surbs(&recipient_tag) { + self.request_reply_surbs_for_queue_clearing(recipient_tag) + .await; + } + } + + async fn request_additional_reply_surbs( + &mut self, + target: AnonymousSenderTag, + amount: u32, + ) -> Result<(), PreparationError> { + let reply_surb = self + .full_reply_storage + .surbs_storage_ref() + .get_reply_surb_ignoring_threshold(&target) + .and_then(|(reply_surb, _)| reply_surb) + .ok_or(PreparationError::NotEnoughSurbs { + available: 0, + required: 1, + })?; + + if let Err(err) = self + .message_handler + .try_request_additional_reply_surbs(target, reply_surb, amount) + .await + { + let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target); + warn!( + "failed to request additional surbs from {:?} - {err}", + target + ); + return Err(err); + } else { + self.full_reply_storage + .surbs_storage_ref() + .increment_pending_reception(&target, amount); + } + + Ok(()) + } + + async fn try_clear_pending_retransmission(&mut self, target: AnonymousSenderTag) { + trace!("trying to clear pending retransmission queue"); + let available_surbs = self + .full_reply_storage + .surbs_storage_ref() + .available_surbs(&target); + let min_surbs_threshold = self + .full_reply_storage + .surbs_storage_ref() + .min_surb_threshold(); + + let max_to_clear = if available_surbs > min_surbs_threshold { + available_surbs - min_surbs_threshold + } else { + trace!("we don't have enough surbs for retransmission queue clearing..."); + return; + }; + trace!("we can clear up to {max_to_clear} entries"); + + let Some(pending) = self.pending_retransmissions.get_mut(&target) else { + trace!("there are no pending retransmissions for {target}!"); + return; + }; + + let mut to_take = Vec::new(); + + while to_take.len() < max_to_clear { + if let Some((_, data)) = pending.pop_first() { + // no need to do anything if we failed to upgrade the reference, + // it means we got the ack while the data was waiting in the queue + if let Some(upgraded) = data.upgrade() { + to_take.push(upgraded) + } + } else { + // our map is empty! + break; + } + } + + if to_take.is_empty() { + // no need to do anything + return; + } + + let (surbs_for_reply, _) = self + .full_reply_storage + .surbs_storage_ref() + .get_reply_surbs(&target, to_take.len()); + + let Some(surbs_for_reply) = surbs_for_reply else { + error!("somehow different task has stolen our reply surbs! - this should have been impossible"); + self.re_insert_pending_retransmission(&target, to_take); + return; + }; + + let to_send_vec = to_take.iter().map(|ack| ack.fragment_data()).collect(); + + let prepared_fragments = match self + .message_handler + .prepare_reply_chunks_for_sending(to_send_vec, surbs_for_reply) + .await + { + Ok(prepared) => prepared, + Err(err) => { + let err = + err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target); + self.re_insert_pending_retransmission(&target, to_take); + + warn!( + "failed to clear pending retransmission queue for {:?} - {err}", + target + ); + return; + } + }; + + // we can't fail at this point, so drop all references to acks so that timer updates wouldn't blow up + drop(to_take); + + self.message_handler + .send_retransmission_reply_chunks(prepared_fragments, TransmissionLane::Retransmission) + .await; + } + + fn pop_at_most_pending_replies( + &mut self, + from: &AnonymousSenderTag, + amount: usize, + ) -> Option> { + // if possible, pop all pending replies, if not, pop only entries for which we'd have a reply surb + let total = self.pending_replies.get(from)?.total_size(); + trace!("pending queue has {total} elements"); + if total == 0 { + return None; + } + self.pending_replies + .get_mut(from)? + .pop_at_most_n_next_messages_at_random(amount) + } + + async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) { + trace!("trying to clear pending queue"); + let available_surbs = self + .full_reply_storage + .surbs_storage_ref() + .available_surbs(&target); + let min_surbs_threshold = self + .full_reply_storage + .surbs_storage_ref() + .min_surb_threshold(); + + let max_to_clear = if available_surbs > min_surbs_threshold { + available_surbs - min_surbs_threshold + } else { + trace!("we don't have enough surbs for queue clearing..."); + return; + }; + trace!("we can clear up to {max_to_clear} entries"); + + // we're guaranteed to not get more entries than we have reply surbs for + if let Some(to_send) = self.pop_at_most_pending_replies(&target, max_to_clear) { + let to_send_clone = to_send.clone(); + + if to_send_clone.is_empty() { + panic!( + "please let the devs know if you ever see this message (reply_controller.rs)" + ); + } + + let (surbs_for_reply, _) = self + .full_reply_storage + .surbs_storage_ref() + .get_reply_surbs(&target, to_send_clone.len()); + + let Some(surbs_for_reply) = surbs_for_reply else { + error!("somehow different task has stolen our reply surbs! - this should have been impossible"); + self.re_insert_pending_replies(&target, to_send); + return; + }; + + if let Err(err) = self + .message_handler + .try_send_reply_chunks(target, to_send_clone, surbs_for_reply) + .await + { + let err = + err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target); + self.re_insert_pending_replies(&target, to_send); + warn!("failed to clear pending queue for {:?} - {err}", target); + } + } else { + trace!("the pending queue is empty"); + } + } + + async fn handle_received_surbs( + &mut self, + from: AnonymousSenderTag, + reply_surbs: Vec, + from_surb_request: bool, + ) { + trace!("handling received surbs"); + + // clear the requesting flag since we should have been asking for surbs + self.full_reply_storage + .surbs_storage_ref() + .reset_surbs_last_received_at(&from); + if from_surb_request { + self.full_reply_storage + .surbs_storage_ref() + .decrement_pending_reception(&from, reply_surbs.len() as u32); + } + + // store received surbs + self.full_reply_storage + .surbs_storage_ref() + .insert_surbs(&from, reply_surbs); + + // use as many as we can for clearing pending retransmission queue + self.try_clear_pending_retransmission(from).await; + + // use as many as we can for clearing pending 'normal' queue + self.try_clear_pending_queue(from).await; + + // if we have to, request more + if self.should_request_more_surbs(&from) { + self.request_reply_surbs_for_queue_clearing(from).await; + } + } + + async fn handle_surb_request(&mut self, recipient: Recipient, mut amount: u32) { + // 1. check whether we sent any surbs in the past to this recipient, otherwise + // they have no business in asking for more + if !self + .full_reply_storage + .tags_storage_ref() + .exists(&recipient) + { + warn!("{recipient} asked us for reply SURBs even though we never sent them any anonymous messages before!"); + return; + } + + // 2. check whether the requested amount is within sane range + if amount > self.config.maximum_allowed_reply_surb_request_size { + warn!("The requested reply surb amount is larger than our maximum allowed ({amount} > {}). Lowering it to a more sane value...", self.config.maximum_allowed_reply_surb_request_size); + amount = self.config.maximum_allowed_reply_surb_request_size; + } + + // 3. construct and send the surbs away + // (send them in smaller batches to make the experience a bit smoother + let mut remaining = amount; + while remaining > 0 { + let to_send = min(remaining, 100); + if let Err(err) = self + .message_handler + .try_send_additional_reply_surbs(recipient, to_send) + .await + { + warn!("failed to send additional surbs to {recipient} - {err}"); + } else { + trace!("sent {to_send} reply SURBs to {recipient}"); + } + + remaining -= to_send; + } + } + + fn buffer_pending_ack( + &mut self, + recipient: AnonymousSenderTag, + ack_ref: Arc, + weak_ack_ref: Weak, + ) { + let frag_id = ack_ref.inner_fragment_identifier(); + if let Some(existing) = self.pending_retransmissions.get_mut(&recipient) { + if let Entry::Vacant(e) = existing.entry(frag_id) { + e.insert(weak_ack_ref); + } else { + warn!("we're already trying to retransmit {frag_id}. We must be really behind in surbs!"); + } + } else { + let mut inner = BTreeMap::new(); + inner.insert(frag_id, weak_ack_ref); + self.pending_retransmissions.insert(recipient, inner); + } + } + + async fn handle_reply_retransmission( + &mut self, + recipient_tag: AnonymousSenderTag, + timed_out_ack: Weak, + extra_surbs_request: bool, + ) { + // seems we got the ack in the end + let ack_ref = match timed_out_ack.upgrade() { + Some(ack) => ack, + None => { + debug!("we received the ack for one of the reply packets as we were putting it in the retransmission queue"); + return; + } + }; + + // if this is retransmission for obtaining additional reply surbs, + // we can dip below the storage threshold + let (maybe_reply_surb, _) = if extra_surbs_request { + self.full_reply_storage + .surbs_storage_ref() + .get_reply_surb_ignoring_threshold(&recipient_tag) + } else { + self.full_reply_storage + .surbs_storage_ref() + .get_reply_surb(&recipient_tag) + } + .expect("attempted to retransmit a packet to an unknown recipient - we shouldn't have sent the original packet in the first place!"); + + if let Some(reply_surb) = maybe_reply_surb { + match self + .message_handler + .try_prepare_single_reply_chunk_for_sending(reply_surb, ack_ref.fragment_data()) + .await + { + Ok(prepared) => { + // drop the ack ref so that controller would not panic on `UpdateTimer` if that task + // got to handle the action before this function terminated (which is very much + // possible if `forward_messages` takes a while) + drop(ack_ref); + + self.message_handler + .update_ack_delay(prepared.fragment_identifier, prepared.total_delay); + self.message_handler + .forward_messages(vec![prepared.into()], TransmissionLane::Retransmission) + .await; + } + Err(err) => { + let err = err.return_unused_surbs( + self.full_reply_storage.surbs_storage_ref(), + &recipient_tag, + ); + warn!("failed to prepare message for retransmission - {err}"); + // we buffer that packet and to try another day + self.buffer_pending_ack(recipient_tag, ack_ref, timed_out_ack); + + if self.should_request_more_surbs(&recipient_tag) { + self.request_reply_surbs_for_queue_clearing(recipient_tag) + .await; + } + } + }; + } else { + self.buffer_pending_ack(recipient_tag, ack_ref, timed_out_ack); + + if self.should_request_more_surbs(&recipient_tag) { + self.request_reply_surbs_for_queue_clearing(recipient_tag) + .await; + } + } + } + + // to be honest this doesn't make a lot of sense in the context of `connection_id`, + // it should really be asked per tag + fn handle_lane_queue_length( + &self, + connection_id: ConnectionId, + response_channel: oneshot::Sender, + ) { + // TODO: if we ever have duplicate ids for different senders, it means our rng is super weak + // thus I don't think we have to worry about it? + let lane = TransmissionLane::ConnectionId(connection_id); + for buf in self.pending_replies.values() { + if let Some(length) = buf.lane_length(&lane) { + if response_channel.send(length).is_err() { + error!("the requester for lane queue length has dropped the response channel!") + } + return; + } + } + // make sure that if we didn't find that lane, we reply with 0 + if response_channel.send(0).is_err() { + error!("the requester for lane queue length has dropped the response channel!") + } + } + + async fn handle_request(&mut self, request: ReplyControllerMessage) { + match request { + ReplyControllerMessage::RetransmitReply { + recipient, + timed_out_ack, + extra_surb_request, + } => { + self.handle_reply_retransmission(recipient, timed_out_ack, extra_surb_request) + .await + } + ReplyControllerMessage::SendReply { + recipient, + message, + lane, + } => self.handle_send_reply(recipient, message, lane).await, + ReplyControllerMessage::AdditionalSurbs { + sender_tag, + reply_surbs, + from_surb_request, + } => { + self.handle_received_surbs(sender_tag, reply_surbs, from_surb_request) + .await + } + ReplyControllerMessage::LaneQueueLength { + connection_id, + response_channel, + } => self.handle_lane_queue_length(connection_id, response_channel), + ReplyControllerMessage::AdditionalSurbsRequest { recipient, amount } => { + self.handle_surb_request(*recipient, amount).await + } + } + } + + // TODO: modify this method to more accurately determine the amount of surbs it needs to request + // it should take into consideration the average latency, sending rate and queue size. + // it should request as many surbs as it takes to saturate its sending rate before next batch arrives + async fn request_reply_surbs_for_queue_clearing(&mut self, target: AnonymousSenderTag) { + trace!("requesting surbs for queues clearing"); + + let pending_queue_size = self + .pending_replies + .get(&target) + .map(|pending_queue| pending_queue.total_size()) + .unwrap_or_default(); + + let retransmission_queue = self + .pending_retransmissions + .get(&target) + .map(|pending_queue| pending_queue.len()) + .unwrap_or_default(); + + let total_queue = (pending_queue_size + retransmission_queue) as u32; + + if total_queue == 0 { + trace!("the pending queues for {:?} are already empty", target); + return; + } + + let request_size = min( + self.config.max_surb_request_size, + max(total_queue, self.config.min_surb_request_size), + ); + + if let Err(err) = self + .request_additional_reply_surbs(target, request_size) + .await + { + warn!("failed to request additional surbs... - {err}") + } + } + + async fn inspect_stale_entries(&mut self) { + let mut to_request = Vec::new(); + let mut to_remove = Vec::new(); + + let now = OffsetDateTime::now_utc(); + for (pending_reply_target, vals) in &self.pending_replies { + if vals.is_empty() { + continue; + } + + let Some(last_received) = self.full_reply_storage.surbs_storage_ref().surbs_last_received_at(pending_reply_target) else { + error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.total_size()); + to_remove.push(*pending_reply_target); + continue; + }; + + // this should never ever happen (famous last words, eh?), but in case it DOES happen eventually + // purge that malformed data + let Ok(last_received_time) = OffsetDateTime::from_unix_timestamp(last_received) else { + error!("somehow our stored timestamp ({last_received}) for surbs from {pending_reply_target} is corrupted!. Going to remove all the associated entries"); + to_remove.push(*pending_reply_target); + continue; + }; + + let diff = now - last_received_time; + + if diff > self.config.max_surb_waiting_period { + warn!("We haven't received any surbs in {:?} from {pending_reply_target}. Going to explicitly ask for more", diff); + to_request.push(*pending_reply_target); + } + } + + for pending_reply_target in to_request { + self.request_reply_surbs_for_queue_clearing(pending_reply_target) + .await; + self.full_reply_storage + .surbs_storage_ref() + .reset_pending_reception(&pending_reply_target) + } + for to_remove in to_remove { + self.pending_replies.remove(&to_remove); + } + } + + async fn invalidate_old_data(&self) { + let now = OffsetDateTime::now_utc(); + + let mut to_remove_surbs = Vec::new(); + let mut to_remove_keys = Vec::new(); + for map_ref in self.full_reply_storage.surbs_storage_ref().as_raw_iter() { + let (sender, received) = map_ref.pair(); + // TODO: handle the following edge case: + // there's a malicious client sending us exactly one reply surb just before we should have invalidated + // the data thus making us keep everything in memory + // possible solution: keep timestamp PER reply surb (but that seems like an overkill) + // but I doubt this is ever going to be a problem... + // ... + // However, if you're reading this message, it probably became a legit problem, + // so I guess add timestamp per surb then? chop-chop. + + let last_received = received.surbs_last_received_at(); + // this should never ever happen (famous last words, eh?), but in case it DOES happen eventually + // purge that malformed data + let Ok(last_received_time) = OffsetDateTime::from_unix_timestamp(last_received) else { + error!("somehow our stored timestamp ({last_received}) for surbs from {sender} is corrupted!. Going to remove all the associated entries"); + to_remove_surbs.push(*sender); + continue; + }; + let diff = now - last_received_time; + + if diff > self.config.max_reply_surb_age { + info!("it's been {diff:?} since we last received any reply surb from {sender}. Going to remove all stored entries..."); + + to_remove_surbs.push(*sender); + } + } + + for map_ref in self.full_reply_storage.key_storage_ref().as_raw_iter() { + let (digest, reply_key) = map_ref.pair(); + + // this should never ever happen (famous last words, eh?), but in case it DOES happen eventually + // purge that malformed data + let Ok(sent_at) = OffsetDateTime::from_unix_timestamp(reply_key.sent_at_timestamp) else { + error!("somehow our stored timestamp ({}) for one of our reply key is corrupted!. Going to remove all the entry", reply_key.sent_at_timestamp); + to_remove_keys.push(*digest); + continue; + }; + + let diff = now - sent_at; + + if diff > self.config.max_reply_key_age { + debug!("it's been {diff:?} since we created this reply key. it's probably never going to get used, so we're going to purge it..."); + to_remove_keys.push(*digest); + } + } + + for to_remove in to_remove_surbs { + self.full_reply_storage + .surbs_storage_ref() + .remove(&to_remove); + } + + for to_remove in to_remove_keys { + self.full_reply_storage.key_storage().remove(to_remove) + } + } + + // #[cfg(not(target_arch = "wasm32"))] + // async fn log_status(&self) { + // todo!() + // } + + pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) { + debug!("Started ReplyController with graceful shutdown support"); + + let polling_rate = Duration::from_secs(5); + let mut stale_inspection = new_interval_stream(polling_rate); + + // this is in the order of hours/days so we don't have to poll it that often + let polling_rate = Duration::from_secs(self.config.max_reply_surb_age.as_secs() / 10); + let mut invalidation_inspection = new_interval_stream(polling_rate); + + while !shutdown.is_shutdown() { + tokio::select! { + biased; + _ = shutdown.recv_with_delay() => { + log::trace!("ReplyController: Received shutdown"); + }, + req = self.request_receiver.next() => match req { + Some(req) => self.handle_request(req).await, + None => { + log::trace!("ReplyController: Stopping since channel closed"); + break; + } + }, + _ = stale_inspection.next() => { + self.inspect_stale_entries().await + }, + _ = invalidation_inspection.next() => { + self.invalidate_old_data().await + } + } + } + assert!(shutdown.is_shutdown_poll()); + log::debug!("ReplyController: Exiting"); + } +} diff --git a/clients/client-core/src/client/replies/reply_controller/requests.rs b/clients/client-core/src/client/replies/reply_controller/requests.rs new file mode 100644 index 0000000000..1f47f2ca07 --- /dev/null +++ b/clients/client-core/src/client/replies/reply_controller/requests.rs @@ -0,0 +1,136 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement; +use client_connections::{ConnectionId, TransmissionLane}; +use futures::channel::{mpsc, oneshot}; +use log::error; +use nymsphinx::addressing::clients::Recipient; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; +use nymsphinx::anonymous_replies::ReplySurb; +use std::sync::Weak; + +pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) { + let (tx, rx) = mpsc::unbounded(); + (tx.into(), rx) +} + +#[derive(Debug, Clone)] +pub struct ReplyControllerSender(mpsc::UnboundedSender); + +impl From> for ReplyControllerSender { + fn from(inner: mpsc::UnboundedSender) -> Self { + ReplyControllerSender(inner) + } +} + +impl ReplyControllerSender { + pub(crate) fn send_retransmission_data( + &self, + recipient: AnonymousSenderTag, + timed_out_ack: Weak, + extra_surb_request: bool, + ) { + self.0 + .unbounded_send(ReplyControllerMessage::RetransmitReply { + recipient, + timed_out_ack, + extra_surb_request, + }) + .expect("ReplyControllerReceiver has died!") + } + + pub(crate) fn send_reply( + &self, + recipient: AnonymousSenderTag, + message: Vec, + lane: TransmissionLane, + ) { + self.0 + .unbounded_send(ReplyControllerMessage::SendReply { + recipient, + message, + lane, + }) + .expect("ReplyControllerReceiver has died!") + } + + pub(crate) fn send_additional_surbs( + &self, + sender_tag: AnonymousSenderTag, + reply_surbs: Vec, + from_surb_request: bool, + ) { + self.0 + .unbounded_send(ReplyControllerMessage::AdditionalSurbs { + sender_tag, + reply_surbs, + from_surb_request, + }) + .expect("ReplyControllerReceiver has died!") + } + + pub(crate) fn send_additional_surbs_request(&self, recipient: Recipient, amount: u32) { + self.0 + .unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest { + recipient: Box::new(recipient), + amount, + }) + .expect("ReplyControllerReceiver has died!") + } + + pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize { + let (response_tx, response_rx) = oneshot::channel(); + self.0 + .unbounded_send(ReplyControllerMessage::LaneQueueLength { + connection_id, + response_channel: response_tx, + }) + .expect("ReplyControllerReceiver has died!"); + + match response_rx.await { + Ok(length) => length, + Err(_) => { + error!("The reply controller has dropped our response channel!"); + // TODO: should we panic here instead? this message implies something weird and unrecoverable has happened + 0 + } + } + } +} + +pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver; + +#[derive(Debug)] +pub(crate) enum ReplyControllerMessage { + RetransmitReply { + recipient: AnonymousSenderTag, + timed_out_ack: Weak, + extra_surb_request: bool, + }, + + SendReply { + recipient: AnonymousSenderTag, + message: Vec, + lane: TransmissionLane, + }, + + AdditionalSurbs { + sender_tag: AnonymousSenderTag, + reply_surbs: Vec, + from_surb_request: bool, + }, + + // this one doesn't belong here either... + LaneQueueLength { + connection_id: ConnectionId, + response_channel: oneshot::Sender, + }, + + // Should this also be handled in here? it's technically a completely different side of the pipe + // let's see how it works when combined, might split it before creating PR + AdditionalSurbsRequest { + recipient: Box, + amount: u32, + }, +} diff --git a/clients/client-core/src/client/replies/reply_storage/backend/browser_backend.rs b/clients/client-core/src/client/replies/reply_storage/backend/browser_backend.rs new file mode 100644 index 0000000000..9711ea759f --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/backend/browser_backend.rs @@ -0,0 +1,43 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::backend::Empty; +use crate::client::replies::reply_storage::{CombinedReplyStorage, ReplyStorageBackend}; +use async_trait::async_trait; + +// well, right now we don't have the browser storage : ( +// so we keep everything in memory +pub struct Backend { + empty: Empty, +} + +impl Backend { + pub fn new(min_surb_threshold: usize, max_surb_threshold: usize) -> Self { + Backend { + empty: Empty { + min_surb_threshold, + max_surb_threshold, + }, + } + } +} + +#[async_trait] +impl ReplyStorageBackend for Backend { + type StorageError = ::StorageError; + + async fn flush_surb_storage( + &mut self, + storage: &CombinedReplyStorage, + ) -> Result<(), Self::StorageError> { + self.empty.flush_surb_storage(storage).await + } + + async fn init_fresh(&mut self, fresh: &CombinedReplyStorage) -> Result<(), Self::StorageError> { + self.empty.init_fresh(fresh).await + } + + async fn load_surb_storage(&self) -> Result { + self.empty.load_surb_storage().await + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/error.rs b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/error.rs new file mode 100644 index 0000000000..8164762123 --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/error.rs @@ -0,0 +1,53 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::io; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum StorageError { + #[error("the provided database path doesn't have a filename defined")] + DatabasePathWithoutFilename { provided_path: PathBuf }, + + #[error("failed to rename our databse file - {source}")] + DatabaseRenameError { + #[source] + source: io::Error, + }, + + #[error("failed to rename our old databse file - {source}")] + DatabaseOldFileRemoveError { + #[source] + source: io::Error, + }, + + #[error("failed to perform sqlx migration: {source}")] + MigrationError { + #[source] + #[from] + source: sqlx::migrate::MigrateError, + }, + + #[error("failed to connect to the underlying connection pool: {source}")] + DatabaseConnectionError { + #[source] + source: sqlx::error::Error, + }, + + #[error("failed to run the SQL query: {source}")] + QueryError { + #[source] + #[from] + source: sqlx::error::Error, + }, + + #[error("The loaded data is inconsistent - it seems that on the last shutdown the client hasn't finished the data flush. You may have to remove the entire storage manually")] + IncompleteDataFlush, + + #[error("data retrieved from the underlying storage is corrupted: {details}")] + CorruptedData { + details: String, + // err: Option> + }, +} diff --git a/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/manager.rs b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/manager.rs new file mode 100644 index 0000000000..578c0021a3 --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/manager.rs @@ -0,0 +1,257 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::backend::fs_backend::error::StorageError; +use crate::client::replies::reply_storage::backend::fs_backend::models::{ + ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender, +}; +use log::{error, info}; +use sqlx::ConnectOptions; +use std::path::Path; + +#[derive(Debug, Clone)] +pub(crate) struct StorageManager { + pub(crate) connection_pool: sqlx::SqlitePool, +} + +// all SQL goes here +impl StorageManager { + pub(crate) async fn init>( + database_path: P, + fresh: bool, + ) -> Result { + let mut opts = sqlx::sqlite::SqliteConnectOptions::new() + .filename(database_path) + .create_if_missing(fresh); + + opts.disable_statement_logging(); + + let connection_pool = match sqlx::SqlitePool::connect_with(opts).await { + Ok(pool) => pool, + Err(err) => { + error!("Failed to connect to SQLx database: {err}"); + return Err(StorageError::DatabaseConnectionError { source: err }); + } + }; + + if let Err(err) = sqlx::migrate!("./fs_surbs_migrations") + .run(&connection_pool) + .await + { + error!("Failed to initialize SQLx database: {err}"); + return Err(err.into()); + } + + info!("Database migration finished!"); + Ok(StorageManager { connection_pool }) + } + + #[allow(dead_code)] + pub(crate) async fn status_table_exists(&self) -> Result { + sqlx::query!("SELECT name FROM sqlite_master WHERE type='table' AND name='status'") + .fetch_optional(&self.connection_pool) + .await + .map(|r| r.is_some()) + } + + pub(crate) async fn create_status_table(&self) -> Result<(), sqlx::Error> { + sqlx::query!("INSERT INTO status(flush_in_progress, previous_flush_timestamp, client_in_use) VALUES (0, 0, 1)") + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_flush_status(&self) -> Result { + sqlx::query!("SELECT flush_in_progress FROM status;") + .fetch_one(&self.connection_pool) + .await + .map(|r| r.flush_in_progress > 0) + } + + pub(crate) async fn set_previous_flush_timestamp( + &self, + timestamp: i64, + ) -> Result<(), sqlx::Error> { + sqlx::query!("UPDATE status SET previous_flush_timestamp = ?", timestamp) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_previous_flush_timestamp(&self) -> Result { + sqlx::query!("SELECT previous_flush_timestamp FROM status;") + .fetch_one(&self.connection_pool) + .await + .map(|r| r.previous_flush_timestamp) + } + + pub(crate) async fn set_flush_status(&self, in_progress: bool) -> Result<(), sqlx::Error> { + let in_progress_int = i64::from(in_progress); + sqlx::query!("UPDATE status SET flush_in_progress = ?", in_progress_int) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_client_in_use_status(&self) -> Result { + sqlx::query!("SELECT client_in_use FROM status;") + .fetch_one(&self.connection_pool) + .await + .map(|r| r.client_in_use > 0) + } + + pub(crate) async fn set_client_in_use_status(&self, in_use: bool) -> Result<(), sqlx::Error> { + let in_use_int = i64::from(in_use); + sqlx::query!("UPDATE status SET client_in_use = ?", in_use_int) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn delete_all_tags(&self) -> Result<(), sqlx::Error> { + sqlx::query!("DELETE FROM sender_tag;") + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_tags(&self) -> Result, sqlx::Error> { + sqlx::query_as!(StoredSenderTag, "SELECT * FROM sender_tag;",) + .fetch_all(&self.connection_pool) + .await + } + + pub(crate) async fn insert_tag(&self, stored_tag: StoredSenderTag) -> Result<(), sqlx::Error> { + sqlx::query!( + r#" + INSERT INTO sender_tag(recipient, tag) VALUES (?, ?); + "#, + stored_tag.recipient, + stored_tag.tag + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn delete_all_reply_keys(&self) -> Result<(), sqlx::Error> { + sqlx::query!("DELETE FROM reply_key;") + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_reply_keys(&self) -> Result, sqlx::Error> { + sqlx::query_as!(StoredReplyKey, "SELECT * FROM reply_key;",) + .fetch_all(&self.connection_pool) + .await + } + + pub(crate) async fn insert_reply_key( + &self, + stored_reply_key: StoredReplyKey, + ) -> Result<(), sqlx::Error> { + sqlx::query!( + r#" + INSERT INTO reply_key(key_digest, reply_key, sent_at_timestamp) VALUES (?, ?, ?); + "#, + stored_reply_key.key_digest, + stored_reply_key.reply_key, + stored_reply_key.sent_at_timestamp + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_surb_senders(&self) -> Result, sqlx::Error> { + sqlx::query_as!(StoredSurbSender, "SELECT * FROM reply_surb_sender;",) + .fetch_all(&self.connection_pool) + .await + } + + pub(crate) async fn insert_surb_sender( + &self, + stored_surb_sender: StoredSurbSender, + ) -> Result { + let id = sqlx::query!( + r#" + INSERT INTO reply_surb_sender(tag, last_sent_timestamp) VALUES (?, ?); + "#, + stored_surb_sender.tag, + stored_surb_sender.last_sent_timestamp + ) + .execute(&self.connection_pool) + .await? + .last_insert_rowid(); + Ok(id) + } + + pub(crate) async fn get_reply_surbs( + &self, + sender_id: i64, + ) -> Result, sqlx::Error> { + sqlx::query_as!( + StoredReplySurb, + "SELECT * FROM reply_surb WHERE reply_surb_sender_id = ?", + sender_id + ) + .fetch_all(&self.connection_pool) + .await + } + + pub(crate) async fn delete_all_reply_surb_data(&self) -> Result<(), sqlx::Error> { + sqlx::query!("DELETE FROM reply_surb;") + .execute(&self.connection_pool) + .await?; + + sqlx::query!("DELETE FROM reply_surb_sender;") + .execute(&self.connection_pool) + .await?; + + Ok(()) + } + + pub(crate) async fn insert_reply_surb( + &self, + stored_reply_surb: StoredReplySurb, + ) -> Result<(), sqlx::Error> { + sqlx::query!( + r#" + INSERT INTO reply_surb(reply_surb_sender_id, reply_surb) VALUES (?, ?); + "#, + stored_reply_surb.reply_surb_sender_id, + stored_reply_surb.reply_surb + ) + .execute(&self.connection_pool) + .await?; + Ok(()) + } + + pub(crate) async fn get_reply_surb_storage_metadata( + &self, + ) -> Result { + sqlx::query_as!( + ReplySurbStorageMetadata, + r#" + SELECT min_reply_surb_threshold as "min_reply_surb_threshold: u32", max_reply_surb_threshold as "max_reply_surb_threshold: u32" FROM reply_surb_storage_metadata; + "#, + ) + .fetch_one(&self.connection_pool) + .await + } + + pub(crate) async fn insert_reply_surb_storage_metadata( + &self, + metadata: ReplySurbStorageMetadata, + ) -> Result<(), sqlx::Error> { + sqlx::query!(r#" + INSERT INTO reply_surb_storage_metadata(min_reply_surb_threshold, max_reply_surb_threshold) + VALUES (?, ?); + "#, + metadata.min_reply_surb_threshold, + metadata.max_reply_surb_threshold, + ).execute(&self.connection_pool).await?; + Ok(()) + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/mod.rs b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/mod.rs new file mode 100644 index 0000000000..9fd48bcd2c --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/mod.rs @@ -0,0 +1,348 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager; +use crate::client::replies::reply_storage::backend::fs_backend::models::{ + ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender, +}; +use crate::client::replies::reply_storage::surb_storage::ReceivedReplySurbs; +use crate::client::replies::reply_storage::{ + CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags, +}; +use async_trait::async_trait; +use log::{error, info, warn}; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; +use std::fs; +use std::path::{Path, PathBuf}; +use time::OffsetDateTime; + +pub use self::error::StorageError; + +mod error; +mod manager; +mod models; + +#[derive(Debug)] +pub struct Backend { + temporary_old_path: Option, + database_path: PathBuf, + manager: StorageManager, +} + +impl Backend { + const OLD_EXTENSION: &'static str = "old"; + + pub async fn init>(database_path: P) -> Result { + let owned_path: PathBuf = database_path.as_ref().into(); + if owned_path.file_name().is_none() { + return Err(StorageError::DatabasePathWithoutFilename { + provided_path: owned_path, + }); + } + + let backend = Backend { + temporary_old_path: None, + database_path: owned_path, + manager: StorageManager::init(database_path, true).await?, + }; + + backend.manager.create_status_table().await?; + + Ok(backend) + } + + pub async fn try_load>(database_path: P) -> Result { + let owned_path: PathBuf = database_path.as_ref().into(); + if owned_path.file_name().is_none() { + return Err(StorageError::DatabasePathWithoutFilename { + provided_path: owned_path, + }); + } + + let manager = StorageManager::init(database_path, false).await?; + + // the database flush wasn't fully finished and thus the data is in inconsistent state + // (we don't really know what's properly saved or what's not) + if manager.get_flush_status().await? { + return Err(StorageError::IncompleteDataFlush); + } + + let last_flush_timestamp = manager.get_previous_flush_timestamp().await?; + if last_flush_timestamp == 0 { + // either this client has been running since 1970 or the flush failed + return Err(StorageError::IncompleteDataFlush); + } + + // the process has gone down without full graceful shutdown, + // meaning the database doesn't contain valid data anymore + // so we have to purge it + if manager.get_client_in_use_status().await? { + error!("the client hasn't undergone through graceful shutdown the last time it's gone down - we can't trust its reply surbs or stored encryption keys. They shall get purged"); + manager.delete_all_reply_surb_data().await?; + manager.delete_all_reply_keys().await?; + } + + if let Err(err) = manager.get_reply_surb_storage_metadata().await { + // we can't recover here, we HAVE TO initialise fresh (because we don't know correct starting metadata) + error!("it seems the client has been shutdown gracefully - we're missing valid surb data dump. the existing database cannot be used"); + return Err(err.into()); + } + + let last_flush = match OffsetDateTime::from_unix_timestamp(last_flush_timestamp) { + Ok(last_flush) => last_flush, + Err(err) => { + return Err(StorageError::CorruptedData { + details: format!("failed to parse stored timestamp - {err}"), + }); + } + }; + + // in theory clients can use our reply surbs whenever they want, even a year in the future + // (assuming no key rotation has happened) + // but the way it's currently coded, everyone will purge old data + let since_last_flush = OffsetDateTime::now_utc() - last_flush; + if since_last_flush.whole_days() > 0 { + info!("it's been over {} days and {} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours()); + manager.delete_all_reply_surb_data().await?; + } + + if since_last_flush.whole_days() > 1 { + info!("it's been over {} days and {} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours()); + manager.delete_all_reply_keys().await?; + } + + if since_last_flush.whole_days() > 2 { + info!("it's been over {} days and {} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours()); + manager.delete_all_tags().await?; + } + + Ok(Backend { + temporary_old_path: None, + database_path: owned_path, + manager, + }) + } + + async fn close_pool(&mut self) { + self.manager.connection_pool.close().await; + } + + async fn rotate(&mut self) -> Result<(), StorageError> { + self.close_pool().await; + + let new_extension = if let Some(existing_extension) = + self.database_path.extension().and_then(|ext| ext.to_str()) + { + format!("{existing_extension}.{}", Self::OLD_EXTENSION) + } else { + Self::OLD_EXTENSION.to_string() + }; + + let mut temp_old = self.database_path.clone(); + temp_old.set_extension(new_extension); + + fs::rename(&self.database_path, &temp_old) + .map_err(|err| StorageError::DatabaseRenameError { source: err })?; + self.manager = StorageManager::init(&self.database_path, true).await?; + self.manager.create_status_table().await?; + + self.temporary_old_path = Some(temp_old); + Ok(()) + } + + fn remove_old(&mut self) -> Result<(), StorageError> { + if let Some(old_path) = self.temporary_old_path.take() { + fs::remove_file(old_path) + .map_err(|err| StorageError::DatabaseOldFileRemoveError { source: err }) + } else { + warn!("the old database file doesn't seem to exist!"); + Ok(()) + } + } + + async fn start_storage_flush(&self) -> Result<(), StorageError> { + Ok(self.manager.set_flush_status(true).await?) + } + + async fn end_storage_flush(&self) -> Result<(), StorageError> { + self.manager + .set_previous_flush_timestamp(OffsetDateTime::now_utc().unix_timestamp()) + .await?; + Ok(self.manager.set_flush_status(false).await?) + } + + async fn start_client_use(&self) -> Result<(), StorageError> { + Ok(self.manager.set_client_in_use_status(true).await?) + } + + async fn stop_client_use(&self) -> Result<(), StorageError> { + Ok(self.manager.set_client_in_use_status(false).await?) + } + + async fn get_stored_tags(&self) -> Result { + let stored = self.manager.get_tags().await?; + + // stop at the first instance of corruption. if even a single entry is malformed, + // something weird has happened and we can't trust the rest of the data + let raw = stored + .into_iter() + .map(TryInto::try_into) + .collect::>()?; + + Ok(UsedSenderTags::from_raw(raw)) + } + + async fn dump_sender_tags(&self, tags: &UsedSenderTags) -> Result<(), StorageError> { + for map_ref in tags.as_raw_iter() { + let (recipient, tag) = map_ref.pair(); + self.manager + .insert_tag(StoredSenderTag::new(*recipient, *tag)) + .await?; + } + Ok(()) + } + + async fn get_stored_reply_keys(&self) -> Result { + let stored = self.manager.get_reply_keys().await?; + + // stop at the first instance of corruption. if even a single entry is malformed, + // something weird has happened and we can't trust the rest of the data + let raw = stored + .into_iter() + .map(TryInto::try_into) + .collect::>()?; + + Ok(SentReplyKeys::from_raw(raw)) + } + + async fn dump_sender_reply_keys(&self, reply_keys: &SentReplyKeys) -> Result<(), StorageError> { + for map_ref in reply_keys.as_raw_iter() { + let (digest, key) = map_ref.pair(); + self.manager + .insert_reply_key(StoredReplyKey::new(*digest, *key)) + .await?; + } + Ok(()) + } + + async fn get_stored_reply_surbs(&self) -> Result { + let surb_senders = self.manager.get_surb_senders().await?; + + let metadata = self.get_reply_surb_storage_metadata().await?; + let mut received_surbs = Vec::with_capacity(surb_senders.len()); + for sender in surb_senders { + let sender_id = sender.id; + let (sender_tag, surbs_last_received_at_timestamp): (AnonymousSenderTag, i64) = + sender.try_into()?; + let stored_surbs = self + .manager + .get_reply_surbs(sender_id) + .await? + .into_iter() + .map(|raw| raw.try_into()) + .collect::>()?; + + received_surbs.push(( + sender_tag, + ReceivedReplySurbs::new_retrieved(stored_surbs, surbs_last_received_at_timestamp), + )) + } + + Ok(ReceivedReplySurbsMap::from_raw( + metadata.min_reply_surb_threshold as usize, + metadata.max_reply_surb_threshold as usize, + received_surbs, + )) + } + + async fn dump_reply_surbs( + &self, + reply_surbs: &ReceivedReplySurbsMap, + ) -> Result<(), StorageError> { + for map_ref in reply_surbs.as_raw_iter() { + let (tag, received_surbs) = map_ref.pair(); + let sender_id = self + .manager + .insert_surb_sender(StoredSurbSender::new( + *tag, + received_surbs.surbs_last_received_at(), + )) + .await?; + + for reply_surb in received_surbs.surbs_ref() { + self.manager + .insert_reply_surb(StoredReplySurb::new(sender_id, reply_surb)) + .await? + } + } + Ok(()) + } + + async fn get_reply_surb_storage_metadata( + &self, + ) -> Result { + self.manager + .get_reply_surb_storage_metadata() + .await + .map_err(Into::into) + } + + async fn dump_reply_surb_storage_metadata( + &self, + reply_surbs: &ReceivedReplySurbsMap, + ) -> Result<(), StorageError> { + self.manager + .insert_reply_surb_storage_metadata(ReplySurbStorageMetadata::new( + reply_surbs.min_surb_threshold(), + reply_surbs.max_surb_threshold(), + )) + .await + .map_err(Into::into) + } +} + +#[async_trait] +impl ReplyStorageBackend for Backend { + type StorageError = error::StorageError; + + async fn start_storage_session(&self) -> Result<(), Self::StorageError> { + self.start_client_use().await + } + + async fn flush_surb_storage( + &mut self, + storage: &CombinedReplyStorage, + ) -> Result<(), Self::StorageError> { + // close all connections (there should be none! and rename the file to contain .old extension) + self.rotate().await?; + self.start_storage_flush().await?; + + self.dump_sender_tags(storage.tags_storage_ref()).await?; + self.dump_sender_reply_keys(storage.key_storage_ref()) + .await?; + let surbs_ref = storage.surbs_storage_ref(); + self.dump_reply_surb_storage_metadata(surbs_ref).await?; + self.dump_reply_surbs(surbs_ref).await?; + + self.remove_old()?; + self.end_storage_flush().await + } + + async fn init_fresh(&mut self, fresh: &CombinedReplyStorage) -> Result<(), Self::StorageError> { + // for now nothing more to do apart from dumping the metadata + self.dump_reply_surb_storage_metadata(fresh.surbs_storage_ref()) + .await + } + + async fn load_surb_storage(&self) -> Result { + let reply_keys = self.get_stored_reply_keys().await?; + let tags = self.get_stored_tags().await?; + let reply_surbs = self.get_stored_reply_surbs().await?; + + Ok(CombinedReplyStorage::load(reply_keys, reply_surbs, tags)) + } + + async fn stop_storage_session(self) -> Result<(), Self::StorageError> { + self.stop_client_use().await + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/models.rs b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/models.rs new file mode 100644 index 0000000000..65088ad05e --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/backend/fs_backend/models.rs @@ -0,0 +1,185 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::backend::fs_backend::error::StorageError; +use crate::client::replies::reply_storage::key_storage::UsedReplyKey; +use crypto::generic_array::typenum::Unsigned; +use crypto::Digest; +use nymsphinx::addressing::clients::{Recipient, RecipientBytes}; +use nymsphinx::anonymous_replies::encryption_key::EncryptionKeyDigest; +use nymsphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE}; +use nymsphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey, SurbEncryptionKeySize}; +use nymsphinx::params::ReplySurbKeyDigestAlgorithm; + +#[derive(Debug, Clone)] +pub(crate) struct StoredSenderTag { + pub(crate) recipient: Vec, + pub(crate) tag: Vec, +} + +impl StoredSenderTag { + pub(crate) fn new(recipient: RecipientBytes, tag: AnonymousSenderTag) -> StoredSenderTag { + StoredSenderTag { + recipient: recipient.to_vec(), + tag: tag.to_bytes().to_vec(), + } + } +} + +impl TryFrom for (RecipientBytes, AnonymousSenderTag) { + type Error = StorageError; + + fn try_from(value: StoredSenderTag) -> Result { + let recipient_len = value.recipient.len(); + let Ok(recipient_bytes) = value.recipient.try_into() else { + return Err(StorageError::CorruptedData { + details: format!( + "the retrieved recipient has length of {recipient_len} while {} was expected", + Recipient::LEN + ), + }); + }; + + let tag_len = value.tag.len(); + let Ok(sender_tag_bytes) = value.tag.try_into() else { + return Err(StorageError::CorruptedData { + details: format!( + "the retrieved sender tag has length of {tag_len} while {} was expected", + SENDER_TAG_SIZE + ), + }); + }; + + Ok(( + recipient_bytes, + AnonymousSenderTag::from_bytes(sender_tag_bytes), + )) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct StoredReplyKey { + pub(crate) key_digest: Vec, + pub(crate) reply_key: Vec, + pub(crate) sent_at_timestamp: i64, +} + +impl StoredReplyKey { + pub(crate) fn new(key_digest: EncryptionKeyDigest, reply_key: UsedReplyKey) -> StoredReplyKey { + StoredReplyKey { + key_digest: key_digest.to_vec(), + reply_key: (*reply_key).to_bytes(), + sent_at_timestamp: reply_key.sent_at_timestamp, + } + } +} + +impl TryFrom for (EncryptionKeyDigest, UsedReplyKey) { + type Error = StorageError; + + fn try_from(value: StoredReplyKey) -> Result { + let expected_reply_key_digest_size = ReplySurbKeyDigestAlgorithm::output_size(); + let reply_key_digest_size = value.key_digest.len(); + + let Some(digest) = EncryptionKeyDigest::from_exact_iter(value.key_digest) else { + return Err(StorageError::CorruptedData { + details: format!( + "the reply surb digest has length of {reply_key_digest_size} while {expected_reply_key_digest_size} was expected", + ), + }); + }; + + let reply_key_len = value.reply_key.len(); + let Ok(reply_key) = SurbEncryptionKey::try_from_bytes(&value.reply_key) else { + return Err(StorageError::CorruptedData { + details: format!( + "the reply key has length of {reply_key_len} while {} was expected", + SurbEncryptionKeySize::USIZE + ), + }); + }; + + Ok(( + digest, + UsedReplyKey::new(reply_key, value.sent_at_timestamp), + )) + } +} + +pub(crate) struct StoredSurbSender { + pub(crate) id: i64, + pub(crate) tag: Vec, + pub(crate) last_sent_timestamp: i64, +} + +impl StoredSurbSender { + pub(crate) fn new(tag: AnonymousSenderTag, last_sent_timestamp: i64) -> Self { + StoredSurbSender { + // for the purposes of STORING data, + // we ignore that field anyway + id: 0, + tag: tag.to_bytes().to_vec(), + last_sent_timestamp, + } + } +} + +impl TryFrom for (AnonymousSenderTag, i64) { + type Error = StorageError; + + fn try_from(value: StoredSurbSender) -> Result { + let tag_len = value.tag.len(); + let Ok(sender_tag_bytes) = value.tag.try_into() else { + return Err(StorageError::CorruptedData { + details: format!( + "the retrieved sender tag has length of {tag_len} while {} was expected", + SENDER_TAG_SIZE + ), + }); + }; + + Ok(( + AnonymousSenderTag::from_bytes(sender_tag_bytes), + value.last_sent_timestamp, + )) + } +} + +pub(crate) struct StoredReplySurb { + pub(crate) reply_surb_sender_id: i64, + pub(crate) reply_surb: Vec, +} + +impl StoredReplySurb { + pub(crate) fn new(reply_surb_sender_id: i64, reply_surb: &ReplySurb) -> Self { + StoredReplySurb { + reply_surb_sender_id, + reply_surb: reply_surb.to_bytes(), + } + } +} + +impl TryFrom for ReplySurb { + type Error = StorageError; + + fn try_from(value: StoredReplySurb) -> Result { + ReplySurb::from_bytes(&value.reply_surb).map_err(|err| StorageError::CorruptedData { + details: format!("failed to recover the reply surb: {err}"), + }) + } +} + +#[derive(Copy, Clone)] +pub(crate) struct ReplySurbStorageMetadata { + pub(crate) min_reply_surb_threshold: u32, + pub(crate) max_reply_surb_threshold: u32, +} + +impl ReplySurbStorageMetadata { + pub(crate) fn new(min_reply_surb_threshold: usize, max_reply_surb_threshold: usize) -> Self { + Self { + min_reply_surb_threshold: min_reply_surb_threshold as u32, + max_reply_surb_threshold: max_reply_surb_threshold as u32, + } + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/backend/mod.rs b/clients/client-core/src/client/replies/reply_storage/backend/mod.rs new file mode 100644 index 0000000000..d80231ae1e --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/backend/mod.rs @@ -0,0 +1,78 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::CombinedReplyStorage; +use async_trait::async_trait; +use std::error::Error; +use thiserror::Error; + +#[cfg(target_arch = "wasm32")] +pub mod browser_backend; + +#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] +pub mod fs_backend; + +// #[cfg(all(test, feature = "std"))] +// third case: node with actual filesystem + +#[derive(Debug, Error)] +#[error("no information provided")] +pub struct UndefinedError; + +pub struct Empty { + // we need to keep 'basic' metadata here to "load" the CombinedReplyStorage + min_surb_threshold: usize, + max_surb_threshold: usize, +} + +#[async_trait] +impl ReplyStorageBackend for Empty { + type StorageError = UndefinedError; + + async fn flush_surb_storage( + &mut self, + _storage: &CombinedReplyStorage, + ) -> Result<(), Self::StorageError> { + Ok(()) + } + + async fn init_fresh( + &mut self, + _fresh: &CombinedReplyStorage, + ) -> Result<(), Self::StorageError> { + Ok(()) + } + + async fn load_surb_storage(&self) -> Result { + Ok(CombinedReplyStorage::new( + self.min_surb_threshold, + self.max_surb_threshold, + )) + } +} + +#[async_trait] +pub trait ReplyStorageBackend: Sized { + type StorageError: Error + 'static; + + async fn start_storage_session(&self) -> Result<(), Self::StorageError> { + Ok(()) + } + + // reply keys and surbs would need additional field set when data is loaded + // so if there's some failure, we'd trash it all + async fn flush_surb_storage( + &mut self, + storage: &CombinedReplyStorage, + ) -> Result<(), Self::StorageError>; + + /// The purpose of this call is to save any metadata that might be present. + /// (such as surb thresholds) + async fn init_fresh(&mut self, fresh: &CombinedReplyStorage) -> Result<(), Self::StorageError>; + + async fn load_surb_storage(&self) -> Result; + + async fn stop_storage_session(self) -> Result<(), Self::StorageError> { + Ok(()) + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/combined.rs b/clients/client-core/src/client/replies/reply_storage/combined.rs new file mode 100644 index 0000000000..c9fb9e4a5c --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/combined.rs @@ -0,0 +1,60 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, SentReplyKeys, UsedSenderTags}; + +#[derive(Debug, Clone)] +pub struct CombinedReplyStorage { + sent_reply_keys: SentReplyKeys, + received_reply_surbs: ReceivedReplySurbsMap, + used_tags: UsedSenderTags, +} + +impl CombinedReplyStorage { + pub fn new(min_surb_threshold: usize, max_surb_threshold: usize) -> CombinedReplyStorage { + CombinedReplyStorage { + sent_reply_keys: SentReplyKeys::new(), + received_reply_surbs: ReceivedReplySurbsMap::new( + min_surb_threshold, + max_surb_threshold, + ), + used_tags: UsedSenderTags::new(), + } + } + + pub fn load( + sent_reply_keys: SentReplyKeys, + received_reply_surbs: ReceivedReplySurbsMap, + used_tags: UsedSenderTags, + ) -> Self { + CombinedReplyStorage { + sent_reply_keys, + received_reply_surbs, + used_tags, + } + } + + pub fn key_storage(&self) -> SentReplyKeys { + self.sent_reply_keys.clone() + } + + pub fn surbs_storage(&self) -> ReceivedReplySurbsMap { + self.received_reply_surbs.clone() + } + + pub fn tags_storage(&self) -> UsedSenderTags { + self.used_tags.clone() + } + + pub fn key_storage_ref(&self) -> &SentReplyKeys { + &self.sent_reply_keys + } + + pub fn surbs_storage_ref(&self) -> &ReceivedReplySurbsMap { + &self.received_reply_surbs + } + + pub fn tags_storage_ref(&self) -> &UsedSenderTags { + &self.used_tags + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/key_storage.rs b/clients/client-core/src/client/replies/reply_storage/key_storage.rs new file mode 100644 index 0000000000..9c68f40e06 --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/key_storage.rs @@ -0,0 +1,86 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use dashmap::iter::Iter; +use dashmap::DashMap; +use nymsphinx::anonymous_replies::encryption_key::EncryptionKeyDigest; +use nymsphinx::anonymous_replies::SurbEncryptionKey; +use std::ops::Deref; +use std::sync::Arc; +use time::OffsetDateTime; + +#[derive(Debug, Clone)] +pub struct SentReplyKeys { + inner: Arc, +} + +#[derive(Debug)] +struct SentReplyKeysInner { + data: DashMap, +} + +impl SentReplyKeys { + pub(crate) fn new() -> SentReplyKeys { + SentReplyKeys { + inner: Arc::new(SentReplyKeysInner { + data: DashMap::new(), + }), + } + } + + #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] + pub(crate) fn from_raw(raw: Vec<(EncryptionKeyDigest, UsedReplyKey)>) -> SentReplyKeys { + SentReplyKeys { + inner: Arc::new(SentReplyKeysInner { + data: raw.into_iter().collect(), + }), + } + } + + pub(crate) fn as_raw_iter(&self) -> Iter<'_, EncryptionKeyDigest, UsedReplyKey> { + self.inner.data.iter() + } + + pub(crate) fn insert_multiple(&self, keys: Vec) { + let now = OffsetDateTime::now_utc().unix_timestamp(); + for key in keys { + self.insert(UsedReplyKey::new(key, now)) + } + } + + pub(crate) fn insert(&self, key: UsedReplyKey) { + self.inner.data.insert(key.compute_digest(), key); + } + + pub(crate) fn try_pop(&self, digest: EncryptionKeyDigest) -> Option { + self.inner.data.remove(&digest).map(|(_k, v)| v) + } + + pub(crate) fn remove(&self, digest: EncryptionKeyDigest) { + self.inner.data.remove(&digest); + } +} + +#[derive(Debug, Copy, Clone)] +pub(crate) struct UsedReplyKey { + key: SurbEncryptionKey, + // the purpose of this field is to perform invalidation at relatively very long intervals + pub(crate) sent_at_timestamp: i64, +} + +impl UsedReplyKey { + pub(crate) fn new(key: SurbEncryptionKey, sent_at_timestamp: i64) -> Self { + UsedReplyKey { + key, + sent_at_timestamp, + } + } +} + +impl Deref for UsedReplyKey { + type Target = SurbEncryptionKey; + + fn deref(&self) -> &Self::Target { + &self.key + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/mod.rs b/clients/client-core/src/client/replies/reply_storage/mod.rs new file mode 100644 index 0000000000..c74ad2bb6b --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/mod.rs @@ -0,0 +1,64 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub use crate::client::replies::reply_storage::combined::CombinedReplyStorage; +pub use crate::client::replies::reply_storage::key_storage::SentReplyKeys; +pub use crate::client::replies::reply_storage::surb_storage::ReceivedReplySurbsMap; +pub use crate::client::replies::reply_storage::tag_storage::UsedSenderTags; +pub use backend::*; + +mod backend; +mod combined; +mod key_storage; +mod surb_storage; +mod tag_storage; + +// only really exists to get information about shutdown and save data to the backing storage +pub struct PersistentReplyStorage +where + T: ReplyStorageBackend, +{ + backend: T, +} + +impl PersistentReplyStorage +where + T: ReplyStorageBackend + Send + Sync, +{ + pub fn new(backend: T) -> Self { + PersistentReplyStorage { backend } + } + + pub async fn load_state_from_backend(&self) -> Result { + self.backend.load_surb_storage().await + } + + // this will have to get enabled after merging develop + pub async fn flush_on_shutdown( + mut self, + mem_state: CombinedReplyStorage, + mut shutdown: task::TaskClient, + ) { + use log::{debug, error, info, warn}; + + debug!("Started PersistentReplyStorage"); + if let Err(err) = self.backend.start_storage_session().await { + error!("failed to start the storage session - {err}"); + return; + } + + shutdown.recv().await; + + info!("PersistentReplyStorage is flushing all reply-related data to underlying storage"); + warn!("you MUST NOT forcefully shutdown now or you risk data corruption!"); + if let Err(err) = self.backend.flush_surb_storage(&mem_state).await { + error!("failed to flush our reply-related data to the persistent storage: {err}") + } else { + info!("Data flush is complete") + } + + if let Err(err) = self.backend.stop_storage_session().await { + error!("failed to properly stop the storage session - {err}. We might not be able to smoothly restore it") + } + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/surb_storage.rs b/clients/client-core/src/client/replies/reply_storage/surb_storage.rs new file mode 100644 index 0000000000..ed2fd8d846 --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/surb_storage.rs @@ -0,0 +1,278 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use dashmap::iter::Iter; +use dashmap::DashMap; +use log::trace; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; +use nymsphinx::anonymous_replies::ReplySurb; +use std::collections::VecDeque; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use time::OffsetDateTime; + +#[derive(Debug, Clone)] +pub struct ReceivedReplySurbsMap { + inner: Arc, +} + +#[derive(Debug)] +struct ReceivedReplySurbsMapInner { + data: DashMap, + + // the minimum amount of surbs that have to be kept in storage for requests for more surbs + min_surb_threshold: AtomicUsize, + + // the maximum amount of surbs that we want to keep in storage so that we don't over-request them + max_surb_threshold: AtomicUsize, +} + +impl ReceivedReplySurbsMap { + pub(crate) fn new( + min_surb_threshold: usize, + max_surb_threshold: usize, + ) -> ReceivedReplySurbsMap { + ReceivedReplySurbsMap { + inner: Arc::new(ReceivedReplySurbsMapInner { + data: DashMap::new(), + min_surb_threshold: AtomicUsize::new(min_surb_threshold), + max_surb_threshold: AtomicUsize::new(max_surb_threshold), + }), + } + } + + #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] + pub(crate) fn from_raw( + min_surb_threshold: usize, + max_surb_threshold: usize, + raw: Vec<(AnonymousSenderTag, ReceivedReplySurbs)>, + ) -> ReceivedReplySurbsMap { + ReceivedReplySurbsMap { + inner: Arc::new(ReceivedReplySurbsMapInner { + data: raw.into_iter().collect(), + min_surb_threshold: AtomicUsize::new(min_surb_threshold), + max_surb_threshold: AtomicUsize::new(max_surb_threshold), + }), + } + } + + pub(crate) fn as_raw_iter(&self) -> Iter<'_, AnonymousSenderTag, ReceivedReplySurbs> { + self.inner.data.iter() + } + + pub(crate) fn remove(&self, target: &AnonymousSenderTag) { + self.inner.data.remove(target); + } + + pub(crate) fn reset_surbs_last_received_at(&self, target: &AnonymousSenderTag) { + if let Some(mut entry) = self.inner.data.get_mut(target) { + entry.surbs_last_received_at_timestamp = OffsetDateTime::now_utc().unix_timestamp(); + } + } + + pub(crate) fn surbs_last_received_at(&self, target: &AnonymousSenderTag) -> Option { + self.inner + .data + .get(target) + .map(|e| e.surbs_last_received_at()) + } + + pub(crate) fn pending_reception(&self, target: &AnonymousSenderTag) -> u32 { + self.inner + .data + .get(target) + .map(|e| e.pending_reception()) + .unwrap_or_default() + } + + pub(crate) fn increment_pending_reception( + &self, + target: &AnonymousSenderTag, + amount: u32, + ) -> Option { + self.inner + .data + .get_mut(target) + .map(|mut e| e.increment_pending_reception(amount)) + } + + pub(crate) fn decrement_pending_reception( + &self, + target: &AnonymousSenderTag, + amount: u32, + ) -> Option { + self.inner + .data + .get_mut(target) + .map(|mut e| e.decrement_pending_reception(amount)) + } + + pub(crate) fn reset_pending_reception(&self, target: &AnonymousSenderTag) { + if let Some(mut e) = self.inner.data.get_mut(target) { + e.reset_pending_reception() + } + } + + pub(crate) fn min_surb_threshold(&self) -> usize { + self.inner.min_surb_threshold.load(Ordering::Relaxed) + } + + pub(crate) fn max_surb_threshold(&self) -> usize { + self.inner.max_surb_threshold.load(Ordering::Relaxed) + } + + pub(crate) fn available_surbs(&self, target: &AnonymousSenderTag) -> usize { + self.inner + .data + .get(target) + .map(|entry| entry.items_left()) + .unwrap_or_default() + } + + pub(crate) fn contains_surbs_for(&self, target: &AnonymousSenderTag) -> bool { + self.inner.data.contains_key(target) + } + + pub(crate) fn get_reply_surbs( + &self, + target: &AnonymousSenderTag, + amount: usize, + ) -> (Option>, usize) { + if let Some(mut entry) = self.inner.data.get_mut(target) { + let surbs_left = entry.items_left(); + if surbs_left < self.min_surb_threshold() + amount { + (None, surbs_left) + } else { + entry.get_reply_surbs(amount) + } + } else { + (None, 0) + } + } + + pub(crate) fn get_reply_surb_ignoring_threshold( + &self, + target: &AnonymousSenderTag, + ) -> Option<(Option, usize)> { + self.inner + .data + .get_mut(target) + .map(|mut s| s.get_reply_surb()) + } + + pub(crate) fn get_reply_surb( + &self, + target: &AnonymousSenderTag, + ) -> Option<(Option, usize)> { + self.inner.data.get_mut(target).map(|mut entry| { + let surbs_left = entry.items_left(); + if surbs_left < self.min_surb_threshold() { + (None, surbs_left) + } else { + entry.get_reply_surb() + } + }) + } + + pub(crate) fn insert_surbs>( + &self, + target: &AnonymousSenderTag, + surbs: I, + ) { + if let Some(mut existing_data) = self.inner.data.get_mut(target) { + existing_data.insert_reply_surbs(surbs) + } else { + let new_entry = ReceivedReplySurbs::new(surbs.into_iter().collect()); + self.inner.data.insert(*target, new_entry); + } + } +} + +#[derive(Debug)] +pub(crate) struct ReceivedReplySurbs { + // in the future we'd probably want to put extra data here to indicate when the SURBs got received + // so we could invalidate entries from the previous key rotations + data: VecDeque, + + pending_reception: u32, + surbs_last_received_at_timestamp: i64, +} + +impl ReceivedReplySurbs { + fn new(initial_surbs: VecDeque) -> Self { + ReceivedReplySurbs { + data: initial_surbs, + pending_reception: 0, + surbs_last_received_at_timestamp: OffsetDateTime::now_utc().unix_timestamp(), + } + } + + #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] + pub(crate) fn new_retrieved( + surbs: Vec, + surbs_last_received_at_timestamp: i64, + ) -> ReceivedReplySurbs { + ReceivedReplySurbs { + data: surbs.into(), + pending_reception: 0, + surbs_last_received_at_timestamp, + } + } + + #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] + pub(crate) fn surbs_ref(&self) -> &VecDeque { + &self.data + } + + pub(crate) fn surbs_last_received_at(&self) -> i64 { + self.surbs_last_received_at_timestamp + } + + pub(crate) fn pending_reception(&self) -> u32 { + self.pending_reception + } + + pub(crate) fn increment_pending_reception(&mut self, amount: u32) -> u32 { + self.pending_reception += amount; + self.pending_reception + } + + pub(crate) fn decrement_pending_reception(&mut self, amount: u32) -> u32 { + self.pending_reception = self.pending_reception.saturating_sub(amount); + self.pending_reception + } + + pub(crate) fn reset_pending_reception(&mut self) { + self.pending_reception = 0; + } + + pub(crate) fn get_reply_surbs(&mut self, amount: usize) -> (Option>, usize) { + if self.items_left() < amount { + (None, self.items_left()) + } else { + let surbs = self.data.drain(..amount).collect(); + (Some(surbs), self.items_left()) + } + } + + pub(crate) fn get_reply_surb(&mut self) -> (Option, usize) { + (self.pop_surb(), self.items_left()) + } + + fn pop_surb(&mut self) -> Option { + self.data.pop_front() + } + + fn items_left(&self) -> usize { + self.data.len() + } + + // realistically we're always going to be getting multiple surbs at once + pub(crate) fn insert_reply_surbs>(&mut self, surbs: I) { + let mut v = surbs.into_iter().collect::>(); + trace!("storing {} surbs in the storage", v.len()); + self.data.append(&mut v); + self.surbs_last_received_at_timestamp = OffsetDateTime::now_utc().unix_timestamp(); + trace!("we now have {} surbs!", self.data.len()); + } +} diff --git a/clients/client-core/src/client/replies/reply_storage/tag_storage.rs b/clients/client-core/src/client/replies/reply_storage/tag_storage.rs new file mode 100644 index 0000000000..c91d2b003e --- /dev/null +++ b/clients/client-core/src/client/replies/reply_storage/tag_storage.rs @@ -0,0 +1,59 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use dashmap::DashMap; +use nymsphinx::addressing::clients::{Recipient, RecipientBytes}; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; +use std::sync::Arc; + +#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] +use dashmap::iter::Iter; + +#[derive(Debug, Clone)] +pub struct UsedSenderTags { + inner: Arc, +} + +#[derive(Debug)] +struct UsedSenderTagsInner { + data: DashMap, +} + +impl UsedSenderTags { + pub(crate) fn new() -> UsedSenderTags { + UsedSenderTags { + inner: Arc::new(UsedSenderTagsInner { + data: DashMap::new(), + }), + } + } + + #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] + pub(crate) fn from_raw(raw: Vec<(RecipientBytes, AnonymousSenderTag)>) -> UsedSenderTags { + UsedSenderTags { + inner: Arc::new(UsedSenderTagsInner { + data: raw.into_iter().collect(), + }), + } + } + + #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] + pub(crate) fn as_raw_iter(&self) -> Iter<'_, RecipientBytes, AnonymousSenderTag> { + self.inner.data.iter() + } + + pub(crate) fn insert_new(&self, recipient: &Recipient, tag: AnonymousSenderTag) { + self.inner.data.insert(recipient.to_bytes(), tag); + } + + pub(crate) fn try_get_existing(&self, recipient: &Recipient) -> Option { + self.inner + .data + .get(&recipient.to_bytes()) + .map(|r| *r.value()) + } + + pub(crate) fn exists(&self, recipient: &Recipient) -> bool { + self.inner.data.contains_key(&recipient.to_bytes()) + } +} diff --git a/clients/client-core/src/client/reply_key_storage.rs b/clients/client-core/src/client/reply_key_storage.rs deleted file mode 100644 index bfe1c43b9b..0000000000 --- a/clients/client-core/src/client/reply_key_storage.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crypto::generic_array::typenum::Unsigned; -use log::*; -use nymsphinx::anonymous_replies::{ - encryption_key::EncryptionKeyDigest, SurbEncryptionKey, SurbEncryptionKeySize, -}; -use std::path::Path; - -#[derive(Debug, thiserror::Error)] -pub enum ReplyKeyStorageError { - #[error("DB Read Error: {0}")] - DbReadError(sled::Error), - #[error("DB Write Error: {0}")] - DbWriteError(sled::Error), - #[error("DB Open Error: {0}")] - DbOpenError(sled::Error), -} - -/// Permanent storage for keys in all sent [`ReplySURB`] -/// -/// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for -/// payload encryption. In order to -decrypt whatever reply we receive, we need to know which -/// key to use for that purpose. We do it based on received `H(t)` which has to be included -/// with each reply. -/// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to -/// have a permanent storage for all the keys that we might ever see in the future. -#[derive(Debug, Clone)] -pub struct ReplyKeyStorage { - db: sled::Db, -} - -impl ReplyKeyStorage { - pub fn load>(path: P) -> Result { - let db = match sled::open(path) { - Err(e) => return Err(ReplyKeyStorageError::DbOpenError(e)), - Ok(db) => db, - }; - - Ok(ReplyKeyStorage { db }) - } - - fn read_encryption_key(&self, raw_key: sled::IVec) -> SurbEncryptionKey { - let key_bytes_ref = raw_key.as_ref(); - // if this fails it means we have some database corruption and we - // absolutely can't continue - - if key_bytes_ref.len() != SurbEncryptionKeySize::USIZE { - error!("REPLY KEY STORAGE DATA CORRUPTION - ENCRYPTION KEY HAS INVALID LENGTH"); - panic!("REPLY KEY STORAGE DATA CORRUPTION - ENCRYPTION KEY HAS INVALID LENGTH"); - } - - // this can only fail if the bytes have invalid length but we already asserted it - SurbEncryptionKey::try_from_bytes(key_bytes_ref).unwrap() - } - - // TOOD: perhaps we could also store some part of original message here too? - pub fn insert_encryption_key( - &mut self, - encryption_key: SurbEncryptionKey, - ) -> Result<(), ReplyKeyStorageError> { - let digest = encryption_key.compute_digest(); - - let insertion_result = match self.db.insert(digest, encryption_key.to_bytes()) { - Err(e) => Err(ReplyKeyStorageError::DbWriteError(e)), - Ok(existing_key) => { - if existing_key.is_some() { - panic!("HASH COLLISION DETECTED") - }; - Ok(()) - } - }; - - // TODO: perhaps we could implement some batching mechanism to avoid frequent flushes? - self.db.flush().unwrap(); - insertion_result - } - - // Once we use key once, we do not expect to use it again - pub fn get_and_remove_encryption_key( - &self, - key_digest: EncryptionKeyDigest, - ) -> Result, ReplyKeyStorageError> { - let removal_result = match self.db.remove(key_digest) { - Err(e) => Err(ReplyKeyStorageError::DbReadError(e)), - Ok(existing_key) => { - Ok(existing_key.map(|existing_key| self.read_encryption_key(existing_key))) - } - }; - - // TODO: not sure how to feel about flushing it every single time here... - // same with insertion - self.db.flush().unwrap(); - removal_result - } -} diff --git a/clients/client-core/src/client/topology_control.rs b/clients/client-core/src/client/topology_control.rs index 6d1b82e066..fd5b27860d 100644 --- a/clients/client-core/src/client/topology_control.rs +++ b/clients/client-core/src/client/topology_control.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::spawn_future; @@ -10,10 +10,9 @@ use rand::seq::SliceRandom; use rand::thread_rng; use std::ops::Deref; use std::sync::Arc; -use std::time; use std::time::Duration; use tokio::sync::{RwLock, RwLockReadGuard}; -use topology::{nym_topology_from_detailed, NymTopology}; +use topology::{nym_topology_from_detailed, NymTopology, NymTopologyError}; use url::Url; // I'm extremely curious why compiler NEVER complained about lack of Debug here before @@ -55,18 +54,36 @@ impl<'a> TopologyReadPermit<'a> { &'a self, ack_recipient: &Recipient, packet_recipient: Option<&Recipient>, - ) -> Option<&'a NymTopology> { - // Note: implicit deref with Deref for TopologyReadPermit is happening here - let topology_ref_option = self.permit.as_ref(); - topology_ref_option.as_ref().filter(|topology_ref| { - !(!topology_ref.can_construct_path_through(DEFAULT_NUM_MIX_HOPS) - || !topology_ref.gateway_exists(ack_recipient.gateway()) - || if let Some(packet_recipient) = packet_recipient { - !topology_ref.gateway_exists(packet_recipient.gateway()) - } else { - false - }) - }) + ) -> Result<&'a NymTopology, NymTopologyError> { + // 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through? + let topology = self + .permit + .as_ref() + .as_ref() + .ok_or(NymTopologyError::EmptyNetworkTopology)?; + + // 2. does it have any mixnode at all? + // 3. does it have any gateways at all? + // 4. does it have a mixnode on each layer? + topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?; + + // 5. does it contain OUR gateway (so that we could create an ack packet)? + if !topology.gateway_exists(ack_recipient.gateway()) { + return Err(NymTopologyError::NonExistentGatewayError { + identity_key: ack_recipient.gateway().to_base58_string(), + }); + } + + // 6. for our target recipient, does it contain THEIR gateway (so that we could create + if let Some(recipient) = packet_recipient { + if !topology.gateway_exists(recipient.gateway()) { + return Err(NymTopologyError::NonExistentGatewayError { + identity_key: recipient.gateway().to_base58_string(), + }); + } + } + + Ok(topology) } } @@ -104,10 +121,10 @@ impl TopologyAccessor { // only used by the client at startup to get a slightly more reasonable error message // (currently displays as unused because health checker is disabled due to required changes) - pub async fn is_routable(&self) -> bool { + pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> { match &self.inner.read().await.0 { - None => false, - Some(ref topology) => topology.can_construct_path_through(DEFAULT_NUM_MIX_HOPS), + None => Err(NymTopologyError::EmptyNetworkTopology), + Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS), } } } @@ -119,19 +136,15 @@ impl Default for TopologyAccessor { } pub struct TopologyRefresherConfig { - validator_api_urls: Vec, - refresh_rate: time::Duration, + nym_api_urls: Vec, + refresh_rate: Duration, client_version: String, } impl TopologyRefresherConfig { - pub fn new( - validator_api_urls: Vec, - refresh_rate: time::Duration, - client_version: String, - ) -> Self { + pub fn new(nym_api_urls: Vec, refresh_rate: Duration, client_version: String) -> Self { TopologyRefresherConfig { - validator_api_urls, + nym_api_urls, refresh_rate, client_version, } @@ -139,10 +152,10 @@ impl TopologyRefresherConfig { } pub struct TopologyRefresher { - validator_client: validator_client::client::ApiClient, + validator_client: validator_client::client::NymApiClient, client_version: String, - validator_api_urls: Vec, + nym_api_urls: Vec, topology_accessor: TopologyAccessor, refresh_rate: Duration, @@ -152,14 +165,14 @@ pub struct TopologyRefresher { impl TopologyRefresher { pub fn new(mut cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self { - cfg.validator_api_urls.shuffle(&mut thread_rng()); + cfg.nym_api_urls.shuffle(&mut thread_rng()); TopologyRefresher { - validator_client: validator_client::client::ApiClient::new( - cfg.validator_api_urls[0].clone(), + validator_client: validator_client::client::NymApiClient::new( + cfg.nym_api_urls[0].clone(), ), client_version: cfg.client_version, - validator_api_urls: cfg.validator_api_urls, + nym_api_urls: cfg.nym_api_urls, topology_accessor, refresh_rate: cfg.refresh_rate, currently_used_api: 0, @@ -167,15 +180,15 @@ impl TopologyRefresher { } } - fn use_next_validator_api(&mut self) { - if self.validator_api_urls.len() == 1 { - warn!("There's only a single validator API available - it won't be possible to use a different one"); + fn use_next_nym_api(&mut self) { + if self.nym_api_urls.len() == 1 { + warn!("There's only a single nym API available - it won't be possible to use a different one"); return; } - self.currently_used_api = (self.currently_used_api + 1) % self.validator_api_urls.len(); + self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len(); self.validator_client - .change_validator_api(self.validator_api_urls[self.currently_used_api].clone()) + .change_nym_api(self.nym_api_urls[self.currently_used_api].clone()) } /// Verifies whether nodes a reasonably distributed among all mix layers. @@ -187,13 +200,10 @@ impl TopologyRefresher { /// # Arguments /// /// * `topology`: active topology constructed from validator api data - /// * `mixnodes_count`: total number of active mixnodes - fn check_layer_distribution( - &self, - active_topology: &NymTopology, - mixnodes_count: usize, - ) -> bool { + fn check_layer_distribution(&self, active_topology: &NymTopology) -> bool { let mixes = active_topology.mixes(); + let mixnodes_count = active_topology.num_mixnodes(); + if active_topology.gateways().is_empty() { return false; } @@ -244,7 +254,7 @@ impl TopologyRefresher { let mixnodes = match self.validator_client.get_cached_active_mixnodes().await { Err(err) => { - error!("failed to get network mixnodes - {}", err); + error!("failed to get network mixnodes - {err}"); return None; } Ok(mixes) => mixes, @@ -252,17 +262,16 @@ impl TopologyRefresher { let gateways = match self.validator_client.get_cached_gateways().await { Err(err) => { - error!("failed to get network gateways - {}", err); + error!("failed to get network gateways - {err}"); return None; } Ok(gateways) => gateways, }; - let mixnodes_count = mixnodes.len(); let topology = nym_topology_from_detailed(mixnodes, gateways) .filter_system_version(&self.client_version); - if !self.check_layer_distribution(&topology, mixnodes_count) { + if !self.check_layer_distribution(&topology) { warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used."); None } else { @@ -275,7 +284,7 @@ impl TopologyRefresher { let new_topology = self.get_current_compatible_topology().await; if new_topology.is_none() { - self.use_next_validator_api(); + self.use_next_nym_api(); } if new_topology.is_none() && self.was_latest_valid { @@ -293,11 +302,11 @@ impl TopologyRefresher { .await; } - pub async fn is_topology_routable(&self) -> bool { - self.topology_accessor.is_routable().await + pub async fn ensure_topology_is_routable(&self) -> Result<(), NymTopologyError> { + self.topology_accessor.ensure_is_routable().await } - pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) { + pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) { spawn_future(async move { debug!("Started TopologyRefresher with graceful shutdown support"); @@ -324,21 +333,4 @@ impl TopologyRefresher { log::debug!("TopologyRefresher: Exiting"); }) } - - pub fn start(mut self) { - spawn_future(async move { - #[cfg(not(target_arch = "wasm32"))] - let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval( - self.refresh_rate, - )); - - #[cfg(target_arch = "wasm32")] - let mut interval = - gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32); - - while (interval.next().await).is_some() { - self.refresh().await; - } - }) - } } diff --git a/clients/client-core/src/client/real_messages_control/real_traffic_stream/transmission_buffer.rs b/clients/client-core/src/client/transmission_buffer.rs similarity index 51% rename from clients/client-core/src/client/real_messages_control/real_traffic_stream/transmission_buffer.rs rename to clients/client-core/src/client/transmission_buffer.rs index b47aaf470e..1420ce0a7b 100644 --- a/clients/client-core/src/client/real_messages_control/real_traffic_stream/transmission_buffer.rs +++ b/clients/client-core/src/client/transmission_buffer.rs @@ -1,38 +1,57 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::client::helpers::{get_time_now, Instant}; +use crate::client::real_messages_control::real_traffic_stream::RealMessage; use client_connections::TransmissionLane; -use rand::seq::SliceRandom; +use nymsphinx::chunking::fragment::Fragment; +use rand::{seq::SliceRandom, Rng}; use std::{ collections::{HashMap, HashSet, VecDeque}, time::Duration, }; -#[cfg(not(target_arch = "wasm32"))] -use tokio::time; - -#[cfg(target_arch = "wasm32")] -use wasm_timer; - -use super::{get_time_now, RealMessage}; - // The number of lanes included in the oldest set. Used when we need to prioritize traffic. -const OLDEST_LANE_SET_SIZE: usize = 5; +const OLDEST_LANE_SET_SIZE: usize = 4; // As a way of prune connections we also check for timeouts. const MSG_CONSIDERED_STALE_AFTER_SECS: u64 = 10 * 60; -#[derive(Default)] -pub(crate) struct TransmissionBuffer { - buffer: HashMap, +pub(crate) trait SizedData { + fn data_size(&self) -> usize; } -impl TransmissionBuffer { +impl SizedData for RealMessage { + fn data_size(&self) -> usize { + self.packet_size() + } +} + +impl SizedData for Fragment { + fn data_size(&self) -> usize { + // note that raw `Fragment` is smaller than sphinx packet payload + // as it doesn't include surb-ack or the [shared] key materials + self.payload_size() + } +} + +#[derive(Default)] +pub(crate) struct TransmissionBuffer { + buffer: HashMap>, +} + +impl TransmissionBuffer { + pub(crate) fn new() -> Self { + TransmissionBuffer { + buffer: HashMap::new(), + } + } + #[allow(unused)] pub(crate) fn is_empty(&self) -> bool { self.buffer.is_empty() } - pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option { + pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option> { self.buffer.remove(lane) } @@ -57,20 +76,22 @@ impl TransmissionBuffer { .collect() } - #[cfg(not(target_arch = "wasm32"))] pub(crate) fn total_size(&self) -> usize { self.buffer.values().map(LaneBufferEntry::len).sum() } #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn total_size_in_bytes(&self) -> usize { + pub(crate) fn total_size_in_bytes(&self) -> usize + where + T: SizedData, + { self.buffer .values() .map(|lane_buffer_entry| { lane_buffer_entry - .real_messages + .items .iter() - .map(|real_message| real_message.mix_packet.sphinx_packet().len()) + .map(|item| item.data_size()) .sum::() }) .sum() @@ -92,36 +113,51 @@ impl TransmissionBuffer { .collect() } - pub(crate) fn store(&mut self, lane: &TransmissionLane, real_messages: Vec) { + pub(crate) fn store>(&mut self, lane: &TransmissionLane, items: I) { if let Some(lane_buffer_entry) = self.buffer.get_mut(lane) { - lane_buffer_entry.append(real_messages); + lane_buffer_entry.extend(items); } else { self.buffer - .insert(*lane, LaneBufferEntry::new(real_messages)); + .insert(*lane, LaneBufferEntry::new(items.into_iter().collect())); } } - fn pick_random_lane(&self) -> Option<&TransmissionLane> { - let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect(); - lanes.choose(&mut rand::thread_rng()).copied() + pub(crate) fn store_multiple(&mut self, items: Vec<(TransmissionLane, T)>) { + for (lane, item) in items { + self.buffer + .entry(lane) + .or_insert_with(LaneBufferEntry::new_empty) + .push_item(item) + } } - fn pick_random_small_lane(&self) -> Option<&TransmissionLane> { + fn pick_random_lane(&self, rng: &mut R) -> Option<&TransmissionLane> { + let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect(); + lanes.choose(rng).copied() + } + + fn pick_random_small_lane(&self, rng: &mut R) -> Option<&TransmissionLane> { let lanes: Vec<&TransmissionLane> = self .buffer .iter() .filter(|(_, v)| v.is_small()) .map(|(k, _)| k) .collect(); - lanes.choose(&mut rand::thread_rng()).copied() + lanes.choose(rng).copied() } - fn pick_random_old_lane(&self) -> Option { - let lanes = self.get_oldest_set(); - lanes.choose(&mut rand::thread_rng()).copied() + // 2/3 chance to pick from the old lanes + fn pick_random_old_lane(&self, rng: &mut R) -> Option { + let rand = &mut rand::thread_rng(); + if rand.gen_ratio(2, 3) { + let lanes = self.get_oldest_set(); + lanes.choose(rand).copied() + } else { + self.pick_random_lane(rng).copied() + } } - fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option { + fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option { let real_msgs_queued = self.buffer.get_mut(lane)?; let real_next = real_msgs_queued.pop_front()?; real_msgs_queued.messages_transmitted += 1; @@ -131,19 +167,48 @@ impl TransmissionBuffer { Some(real_next) } - pub(crate) fn pop_next_message_at_random(&mut self) -> Option<(TransmissionLane, RealMessage)> { + pub(crate) fn pop_at_most_n_next_messages_at_random( + &mut self, + n: usize, + ) -> Option> { + // let start = Instant::now(); + + if self.buffer.is_empty() { + return None; + } + + let rng = &mut rand::thread_rng(); + let mut items = Vec::with_capacity(n); + + while items.len() < n { + let Some(next) = self.pop_next_message_at_random(rng) else { + break + }; + items.push(next) + } + + // todo!("time time taken"); + + Some(items) + } + + pub(crate) fn pop_next_message_at_random( + &mut self, + // turns out the caller always have access to some rng, so no point in instantiating new one + rng: &mut R, + ) -> Option<(TransmissionLane, T)> { if self.buffer.is_empty() { return None; } // Very basic heuristic where we prioritize according to small lanes first, the older lanes // to try to finish lanes when possible, then the rest. - let lane = if let Some(small_lane) = self.pick_random_small_lane() { + let lane = if let Some(small_lane) = self.pick_random_small_lane(rng) { *small_lane - } else if let Some(old_lane) = self.pick_random_old_lane() { + } else if let Some(old_lane) = self.pick_random_old_lane(rng) { old_lane } else { - *self.pick_random_lane()? + *self.pick_random_lane(rng)? }; let msg = self.pop_front_from_lane(&lane)?; @@ -165,35 +230,46 @@ impl TransmissionBuffer { } } -pub(crate) struct LaneBufferEntry { - pub real_messages: VecDeque, +pub(crate) struct LaneBufferEntry { + pub items: VecDeque, pub messages_transmitted: usize, - #[cfg(not(target_arch = "wasm32"))] - pub time_for_last_activity: time::Instant, - #[cfg(target_arch = "wasm32")] - pub time_for_last_activity: wasm_timer::Instant, + pub time_for_last_activity: Instant, } -impl LaneBufferEntry { - fn new(real_messages: Vec) -> Self { +impl LaneBufferEntry { + fn new_empty() -> Self { LaneBufferEntry { - real_messages: real_messages.into(), + items: VecDeque::new(), messages_transmitted: 0, time_for_last_activity: get_time_now(), } } - fn append(&mut self, real_messages: Vec) { - self.real_messages.append(&mut real_messages.into()); + fn new(items: VecDeque) -> Self { + LaneBufferEntry { + items, + messages_transmitted: 0, + time_for_last_activity: get_time_now(), + } + } + + fn push_item(&mut self, item: T) { + self.items.push_back(item); + // I'm not updating time here on purpose. This method is called just after `new_empty`, + // where the time is already set. Furthermore, this method is called there multiple times at once + } + + fn extend>(&mut self, items: I) { + self.items.extend(items); self.time_for_last_activity = get_time_now(); } - fn pop_front(&mut self) -> Option { - self.real_messages.pop_front() + fn pop_front(&mut self) -> Option { + self.items.pop_front() } fn is_small(&self) -> bool { - self.real_messages.len() < 100 + self.items.len() < 100 } fn is_stale(&self) -> bool { @@ -202,10 +278,10 @@ impl LaneBufferEntry { } fn len(&self) -> usize { - self.real_messages.len() + self.items.len() } fn is_empty(&self) -> bool { - self.real_messages.is_empty() + self.items.is_empty() } } diff --git a/clients/client-core/src/config/mod.rs b/clients/client-core/src/config/mod.rs index 82317a6431..97b7c85c1a 100644 --- a/clients/client-core/src/config/mod.rs +++ b/clients/client-core/src/config/mod.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use config::NymConfig; +use config::{NymConfig, OptionalSet, DB_FILE_NAME}; use nymsphinx::params::PacketSize; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; @@ -30,10 +30,36 @@ const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_00 // bandwidth bridging protocol, we can come back to a smaller timeout value const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60); +// reply-surbs related: + +// define when to request +// clients/client-core/src/client/replies/reply_storage/surb_storage.rs +const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10; +const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200; + +// define how much to request at once +// clients/client-core/src/client/replies/reply_controller.rs +const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10; +const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100; + +const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500; + +const DEFAULT_MAXIMUM_REPLY_SURB_WAITING_PERIOD: Duration = Duration::from_secs(10); + +// 12 hours +const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60); + +// 24 hours +const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60); + pub fn missing_string_value() -> String { MISSING_VALUE.to_string() } +pub trait ClientCoreConfigTrait { + fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig; +} + #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { @@ -45,30 +71,48 @@ pub struct Config { debug: DebugConfig, } +impl ClientCoreConfigTrait for Config { + fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig { + &self.client.gateway_endpoint + } +} + +impl OptionalSet for Config where T: NymConfig {} + impl Config { pub fn new>(id: S) -> Self where T: NymConfig, { - let mut cfg = Config::default(); - cfg.with_id(id); - cfg + Config::default().with_id(id) } - pub fn with_id>(&mut self, id: S) + pub fn with_id>(mut self, id: S) -> Self where T: NymConfig, { - let id = id.into(); + self.client.id = id.into(); + self.set_empty_fields_to_defaults(); + self + } + + pub fn set_empty_fields_to_defaults(&mut self) -> bool + where + T: NymConfig, + { + let id = &self.client.id; + let mut changes_made = false; // identity key setting if self.client.private_identity_key_file.as_os_str().is_empty() { + changes_made = true; self.client.private_identity_key_file = - self::Client::::default_private_identity_key_file(&id); + self::Client::::default_private_identity_key_file(id); } if self.client.public_identity_key_file.as_os_str().is_empty() { + changes_made = true; self.client.public_identity_key_file = - self::Client::::default_public_identity_key_file(&id); + self::Client::::default_public_identity_key_file(id); } // encryption key setting @@ -78,8 +122,9 @@ impl Config { .as_os_str() .is_empty() { + changes_made = true; self.client.private_encryption_key_file = - self::Client::::default_private_encryption_key_file(&id); + self::Client::::default_private_encryption_key_file(id); } if self .client @@ -87,40 +132,40 @@ impl Config { .as_os_str() .is_empty() { + changes_made = true; self.client.public_encryption_key_file = - self::Client::::default_public_encryption_key_file(&id); + self::Client::::default_public_encryption_key_file(id); } // shared gateway key setting if self.client.gateway_shared_key_file.as_os_str().is_empty() { + changes_made = true; self.client.gateway_shared_key_file = - self::Client::::default_gateway_shared_key_file(&id); + self::Client::::default_gateway_shared_key_file(id); } // ack key setting if self.client.ack_key_file.as_os_str().is_empty() { - self.client.ack_key_file = self::Client::::default_ack_key_file(&id); + changes_made = true; + self.client.ack_key_file = self::Client::::default_ack_key_file(id); } - if self - .client - .reply_encryption_key_store_path - .as_os_str() - .is_empty() - { - self.client.reply_encryption_key_store_path = - self::Client::::default_reply_encryption_key_store_path(&id); + if self.client.reply_surb_database_path.as_os_str().is_empty() { + changes_made = true; + self.client.reply_surb_database_path = + self::Client::::default_reply_surb_database_path(id); } if self.client.database_path.as_os_str().is_empty() { - self.client.database_path = self::Client::::default_database_path(&id); + changes_made = true; + self.client.database_path = self::Client::::default_database_path(id); } - - self.client.id = id; + changes_made } - pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) { + pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self { self.client.disabled_credentials_mode = disabled_credentials_mode; + self } pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) { @@ -131,18 +176,49 @@ impl Config { self.client.gateway_endpoint.gateway_id = id.into(); } - pub fn set_custom_validators(&mut self, validator_urls: Vec) { - self.client.validator_urls = validator_urls; + pub fn with_custom_nyxd(mut self, urls: Vec) -> Self { + self.client.nyxd_urls = urls; + self } - pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec) { - self.client.validator_api_urls = validator_api_urls; + pub fn set_custom_nyxd(&mut self, nyxd_urls: Vec) { + self.client.nyxd_urls = nyxd_urls; + } + + pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec) -> Self { + self.client.nym_api_urls = nym_api_urls; + self + } + + pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec) { + self.client.nym_api_urls = nym_api_urls; + } + + pub fn with_high_default_traffic_volume(mut self, enabled: bool) -> Self { + if enabled { + self.set_high_default_traffic_volume(); + } + self } pub fn set_high_default_traffic_volume(&mut self) { self.debug.average_packet_delay = Duration::from_millis(10); - self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000); // basically don't really send cover messages - self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s + // basically don't really send cover messages + self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000); + // 250 "real" messages / s + self.debug.message_sending_average_delay = Duration::from_millis(4); + } + + pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self { + if disabled { + self.set_no_cover_traffic() + } + self + } + + pub fn set_no_cover_traffic(&mut self) { + self.debug.disable_loop_cover_traffic_stream = true; + self.debug.disable_main_poisson_packet_distribution = true; } pub fn set_custom_version(&mut self, version: &str) { @@ -181,20 +257,16 @@ impl Config { self.client.gateway_shared_key_file.clone() } - pub fn get_reply_encryption_key_store_path(&self) -> PathBuf { - self.client.reply_encryption_key_store_path.clone() - } - pub fn get_ack_key_file(&self) -> PathBuf { self.client.ack_key_file.clone() } pub fn get_validator_endpoints(&self) -> Vec { - self.client.validator_urls.clone() + self.client.nyxd_urls.clone() } - pub fn get_validator_api_endpoints(&self) -> Vec { - self.client.validator_api_urls.clone() + pub fn get_nym_api_endpoints(&self) -> Vec { + self.client.nym_api_urls.clone() } pub fn get_gateway_id(&self) -> String { @@ -213,14 +285,18 @@ impl Config { &self.client.gateway_endpoint } - pub fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig { - &self.client.gateway_endpoint - } - pub fn get_database_path(&self) -> PathBuf { self.client.database_path.clone() } + pub fn get_reply_surb_database_path(&self) -> PathBuf { + self.client.reply_surb_database_path.clone() + } + + pub fn get_version(&self) -> &str { + &self.client.version + } + // Debug getters pub fn get_debug_config(&self) -> &DebugConfig { &self.debug @@ -274,8 +350,36 @@ impl Config { self.debug.use_extended_packet_size } - pub fn get_version(&self) -> &str { - &self.client.version + pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize { + self.debug.minimum_reply_surb_storage_threshold + } + + pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize { + self.debug.maximum_reply_surb_storage_threshold + } + + pub fn get_minimum_reply_surb_request_size(&self) -> u32 { + self.debug.minimum_reply_surb_request_size + } + + pub fn get_maximum_reply_surb_request_size(&self) -> u32 { + self.debug.maximum_reply_surb_request_size + } + + pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 { + self.debug.maximum_allowed_reply_surb_request_size + } + + pub fn get_maximum_reply_surb_waiting_period(&self) -> Duration { + self.debug.maximum_reply_surb_waiting_period + } + + pub fn get_maximum_reply_surb_age(&self) -> Duration { + self.debug.maximum_reply_surb_age + } + + pub fn get_maximum_reply_key_age(&self) -> Duration { + self.debug.maximum_reply_key_age } } @@ -303,6 +407,22 @@ pub struct GatewayEndpointConfig { pub gateway_listener: String, } +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] +impl GatewayEndpointConfig { + #[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))] + pub fn new( + gateway_id: String, + gateway_owner: String, + gateway_listener: String, + ) -> GatewayEndpointConfig { + GatewayEndpointConfig { + gateway_id, + gateway_owner, + gateway_listener, + } + } +} + impl From for GatewayEndpointConfig { fn from(node: topology::gateway::Node) -> GatewayEndpointConfig { let gateway_listener = node.clients_address(); @@ -328,12 +448,13 @@ pub struct Client { #[serde(default)] disabled_credentials_mode: bool, - /// Addresses to nymd validators via which the client can communicate with the chain. - #[serde(default)] - validator_urls: Vec, + /// Addresses to nyxd validators via which the client can communicate with the chain. + #[serde(alias = "validator_urls")] + nyxd_urls: Vec, /// Addresses to APIs running on validator from which the client gets the view of the network. - validator_api_urls: Vec, + #[serde(alias = "validator_api_urls")] + nym_api_urls: Vec, /// Path to file containing private identity key. private_identity_key_file: PathBuf, @@ -355,16 +476,19 @@ pub struct Client { /// acknowledgement so that nobody besides the client knows which packet it refers to. ack_key_file: PathBuf, - /// Full path to file containing reply encryption keys of all reply-SURBs we have ever - /// sent but not received back. - reply_encryption_key_store_path: PathBuf, - /// Information regarding how the client should send data to gateway. gateway_endpoint: GatewayEndpointConfig, /// Path to the database containing bandwidth credentials of this client. database_path: PathBuf, + /// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. + // this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4. + // if you're reading this message and we have already introduced some breaking changes, feel free + // to remove that attribute since at this point the client configs should have gotten regenerated + #[serde(default)] + reply_surb_database_path: PathBuf, + /// nym_home_directory specifies absolute path to the home nym Clients directory. /// It is expected to use default value and hence .toml file should not redefine this field. nym_root_directory: PathBuf, @@ -380,17 +504,17 @@ impl Default for Client { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), disabled_credentials_mode: true, - validator_urls: vec![], - validator_api_urls: vec![], + nyxd_urls: vec![], + nym_api_urls: vec![], private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), private_encryption_key_file: Default::default(), public_encryption_key_file: Default::default(), gateway_shared_key_file: Default::default(), ack_key_file: Default::default(), - reply_encryption_key_store_path: Default::default(), gateway_endpoint: Default::default(), database_path: Default::default(), + reply_surb_database_path: Default::default(), nym_root_directory: T::default_root_directory(), super_struct: Default::default(), } @@ -422,11 +546,12 @@ impl Client { T::default_data_directory(Some(id)).join("ack_key.pem") } - fn default_reply_encryption_key_store_path(id: &str) -> PathBuf { - T::default_data_directory(Some(id)).join("reply_key_store") + fn default_reply_surb_database_path(id: &str) -> PathBuf { + T::default_data_directory(Some(id)).join("persistent_reply_store.sqlite") } + fn default_database_path(id: &str) -> PathBuf { - T::default_data_directory(Some(id)).join("db.sqlite") + T::default_data_directory(Some(id)).join(DB_FILE_NAME) } } @@ -500,6 +625,37 @@ pub struct DebugConfig { /// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size. pub use_extended_packet_size: Option, + + /// Defines the minimum number of reply surbs the client wants to keep in its storage at all times. + /// It can only allow to go below that value if its to request additional reply surbs. + pub minimum_reply_surb_storage_threshold: usize, + + /// Defines the maximum number of reply surbs the client wants to keep in its storage at any times. + pub maximum_reply_surb_storage_threshold: usize, + + /// Defines the minimum number of reply surbs the client would request. + pub minimum_reply_surb_request_size: u32, + + /// Defines the maximum number of reply surbs the client would request. + pub maximum_reply_surb_request_size: u32, + + /// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once. + pub maximum_allowed_reply_surb_request_size: u32, + + /// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking + /// for more even though in theory they wouldn't need to. + #[serde(with = "humantime_serde")] + pub maximum_reply_surb_waiting_period: Duration, + + /// Defines maximum amount of time given reply surb is going to be valid for. + /// This is going to be superseded by key rotation once implemented. + #[serde(with = "humantime_serde")] + pub maximum_reply_surb_age: Duration, + + /// Defines maximum amount of time given reply key is going to be valid for. + /// This is going to be superseded by key rotation once implemented. + #[serde(with = "humantime_serde")] + pub maximum_reply_key_age: Duration, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -525,6 +681,14 @@ impl Default for DebugConfig { disable_loop_cover_traffic_stream: false, disable_main_poisson_packet_distribution: false, use_extended_packet_size: None, + minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD, + maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, + minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, + maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, + maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, + maximum_reply_surb_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_WAITING_PERIOD, + maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE, + maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE, } } } diff --git a/clients/client-core/src/error.rs b/clients/client-core/src/error.rs index e173a987fc..07bd9442ec 100644 --- a/clients/client-core/src/error.rs +++ b/clients/client-core/src/error.rs @@ -1,38 +1,69 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "reply-surb")] -use crate::client::reply_key_storage::ReplyKeyStorageError; use crypto::asymmetric::identity::Ed25519RecoveryError; use gateway_client::error::GatewayClientError; +use topology::NymTopologyError; use validator_client::ValidatorClientError; #[derive(thiserror::Error, Debug)] pub enum ClientCoreError { #[error("I/O error: {0}")] IoError(#[from] std::io::Error), + #[error("Gateway client error: {0}")] GatewayClientError(#[from] GatewayClientError), + #[error("Ed25519 error: {0}")] Ed25519RecoveryError(#[from] Ed25519RecoveryError), + #[error("Validator client error: {0}")] ValidatorClientError(#[from] ValidatorClientError), - #[cfg(feature = "reply-surb")] - #[error("Reply key storage error: {0}")] - ReplyKeyStorageError(#[from] ReplyKeyStorageError), - #[error("No gateway with id: {0}")] NoGatewayWithId(String), + #[error("No gateways on network")] NoGatewaysOnNetwork, - #[error("List of validator apis is empty")] - ListOfValidatorApisIsEmpty, + + #[error("Failed to setup gateway")] + FailedToSetupGateway, + + #[error("List of nym apis is empty")] + ListOfNymApisIsEmpty, + #[error("Could not load existing gateway configuration: {0}")] CouldNotLoadExistingGatewayConfiguration(std::io::Error), + #[error("The current network topology seem to be insufficient to route any packets through")] - InsufficientNetworkTopology, + InsufficientNetworkTopology(#[from] NymTopologyError), + + #[error("experienced a failure with our reply surb persistent storage: {source}")] + SurbStorageError { + source: Box, + }, + + #[error("The gateway id is invalid - {0}")] + UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError), + + #[error("The identity of the gateway is unknwown - did you run init?")] + GatewayIdUnknown, + + #[error("The owner of the gateway is unknown - did you run init?")] + GatewayOwnerUnknown, + + #[error("The address of the gateway is unknown - did you run init?")] + GatwayAddressUnknown, #[error("Unexpected exit")] UnexpectedExit, } + +/// Set of messages that the client can send to listeners via the task manager +#[derive(thiserror::Error, Debug)] +pub enum ClientCoreStatusMessage { + #[error("The connected gateway is slow, or the connection to it is slow")] + GatewayIsSlow, + #[error("The connected gateway is very slow, or the connection to it is very slow")] + GatewayIsVerySlow, +} diff --git a/clients/client-core/src/init.rs b/clients/client-core/src/init/helpers.rs similarity index 54% rename from clients/client-core/src/init.rs rename to clients/client-core/src/init/helpers.rs index 5e7c1e6c2a..01318e3b3c 100644 --- a/clients/client-core/src/init.rs +++ b/clients/client-core/src/init/helpers.rs @@ -1,39 +1,31 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -//! Collection of initialization steps used by client implementations - -use std::{sync::Arc, time::Duration}; - -use config::NymConfig; -use crypto::asymmetric::{encryption, identity}; -use gateway_client::GatewayClient; -use gateway_requests::registration::handshake::SharedKeys; -use nymsphinx::addressing::clients::Recipient; -use nymsphinx::addressing::nodes::NodeIdentity; -use rand::rngs::OsRng; -use rand::seq::SliceRandom; -use rand::thread_rng; -use tap::TapFallible; -use topology::{filter::VersionFilterable, gateway}; -use url::Url; - use crate::{ client::key_manager::KeyManager, config::{persistence::key_pathfinder::ClientKeyPathfinder, Config}, error::ClientCoreError, }; +use config::NymConfig; +use crypto::asymmetric::identity; +use gateway_client::GatewayClient; +use gateway_requests::registration::handshake::SharedKeys; +use rand::{rngs::OsRng, seq::SliceRandom, thread_rng}; +use std::{sync::Arc, time::Duration}; +use tap::TapFallible; +use topology::{filter::VersionFilterable, gateway}; +use url::Url; -pub async fn query_gateway_details( +pub(super) async fn query_gateway_details( validator_servers: Vec, - chosen_gateway_id: Option<&str>, + chosen_gateway_id: Option, ) -> Result { - let validator_api = validator_servers + let nym_api = validator_servers .choose(&mut thread_rng()) - .ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?; - let validator_client = validator_client::client::ApiClient::new(validator_api.clone()); + .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; + let validator_client = validator_client::client::NymApiClient::new(nym_api.clone()); - log::trace!("Fetching list of gateways from: {}", validator_api); + log::trace!("Fetching list of gateways from: {}", nym_api); let gateways = validator_client.get_cached_gateways().await?; let valid_gateways = gateways .into_iter() @@ -59,7 +51,30 @@ pub async fn query_gateway_details( } } -pub async fn register_with_gateway_and_store_keys( +async fn register_with_gateway( + gateway: &gateway::Node, + our_identity: Arc, +) -> Result, ClientCoreError> { + let timeout = Duration::from_millis(1500); + let mut gateway_client = GatewayClient::new_init( + gateway.clients_address(), + gateway.identity_key, + gateway.owner.clone(), + our_identity.clone(), + timeout, + ); + gateway_client + .establish_connection() + .await + .tap_err(|_| log::warn!("Failed to establish connection with gateway!"))?; + let shared_keys = gateway_client + .perform_initial_authentication() + .await + .tap_err(|_| log::warn!("Failed to register with the gateway!"))?; + Ok(shared_keys) +} + +pub(super) async fn register_with_gateway_and_store_keys( gateway_details: gateway::Node, config: &Config, ) -> Result<(), ClientCoreError> @@ -78,71 +93,3 @@ where .store_keys(&pathfinder) .tap_err(|err| log::error!("Failed to generate keys: {err}"))?) } - -async fn register_with_gateway( - gateway: &gateway::Node, - our_identity: Arc, -) -> Result, ClientCoreError> { - let timeout = Duration::from_millis(1500); - let mut gateway_client = GatewayClient::new_init( - gateway.clients_address(), - gateway.identity_key, - gateway.owner.clone(), - our_identity.clone(), - timeout, - None, - ); - gateway_client - .establish_connection() - .await - .tap_err(|_| log::warn!("Failed to establish connection with gateway!"))?; - let shared_keys = gateway_client - .perform_initial_authentication() - .await - .tap_err(|_| log::warn!("Failed to register with the gateway!"))?; - Ok(shared_keys) -} - -pub fn show_address(config: &Config) -> Result<(), ClientCoreError> -where - T: config::NymConfig, -{ - fn load_identity_keys( - pathfinder: &ClientKeyPathfinder, - ) -> Result { - let identity_keypair: identity::KeyPair = - pemstore::load_keypair(&pemstore::KeyPairPath::new( - pathfinder.private_identity_key().to_owned(), - pathfinder.public_identity_key().to_owned(), - )) - .tap_err(|_| log::error!("Failed to read stored identity key files"))?; - Ok(identity_keypair) - } - - fn load_sphinx_keys( - pathfinder: &ClientKeyPathfinder, - ) -> Result { - let sphinx_keypair: encryption::KeyPair = - pemstore::load_keypair(&pemstore::KeyPairPath::new( - pathfinder.private_encryption_key().to_owned(), - pathfinder.public_encryption_key().to_owned(), - )) - .tap_err(|_| log::error!("Failed to read stored sphinx key files"))?; - Ok(sphinx_keypair) - } - - let pathfinder = ClientKeyPathfinder::new_from_config(config); - let identity_keypair = load_identity_keys(&pathfinder)?; - let sphinx_keypair = load_sphinx_keys(&pathfinder)?; - - let client_recipient = Recipient::new( - *identity_keypair.public_key(), - *sphinx_keypair.public_key(), - // TODO: below only works under assumption that gateway address == gateway id - // (which currently is true) - NodeIdentity::from_base58_string(config.get_gateway_id())?, - ); - - println!("\nThe address of this client is: {}", client_recipient); - Ok(()) -} diff --git a/clients/client-core/src/init/mod.rs b/clients/client-core/src/init/mod.rs new file mode 100644 index 0000000000..4d012d366b --- /dev/null +++ b/clients/client-core/src/init/mod.rs @@ -0,0 +1,200 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +//! Collection of initialization steps used by client implementations + +use std::fmt::Display; + +use nymsphinx::addressing::{clients::Recipient, nodes::NodeIdentity}; +use serde::Serialize; +use tap::TapFallible; + +use config::NymConfig; +use crypto::asymmetric::{encryption, identity}; + +use crate::{ + config::{ + persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config, + GatewayEndpointConfig, + }, + error::ClientCoreError, + init::helpers::{query_gateway_details, register_with_gateway_and_store_keys}, +}; + +mod helpers; + +#[derive(Debug, Serialize)] +pub struct InitResults { + version: String, + id: String, + identity_key: String, + encryption_key: String, + gateway_id: String, + gateway_listener: String, +} + +impl InitResults { + pub fn new(config: &Config, address: &Recipient) -> Self + where + T: NymConfig, + { + Self { + version: config.get_version().to_string(), + id: config.get_id(), + identity_key: address.identity().to_base58_string(), + encryption_key: address.encryption_key().to_base58_string(), + gateway_id: config.get_gateway_id(), + gateway_listener: config.get_gateway_listener(), + } + } +} + +impl Display for InitResults { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Version: {}", self.version)?; + writeln!(f, "ID: {}", self.id)?; + writeln!(f, "Identity key: {}", self.identity_key)?; + writeln!(f, "Encryption: {}", self.encryption_key)?; + writeln!(f, "Gateway ID: {}", self.gateway_id)?; + write!(f, "Gateway: {}", self.gateway_listener) + } +} + +/// Convenience function for setting up the gateway for a client. Depending on the arguments given +/// it will do the sensible thing. +pub async fn setup_gateway( + register_gateway: bool, + // TODO: this should get refactored to instead take Option + user_chosen_gateway_id: Option, + config: &Config, +) -> Result +where + C: NymConfig + ClientCoreConfigTrait, + T: NymConfig, +{ + let id = config.get_id(); + if register_gateway { + register_with_gateway(user_chosen_gateway_id, config).await + } else if let Some(user_chosen_gateway_id) = user_chosen_gateway_id { + config_gateway_with_existing_keys(user_chosen_gateway_id, config).await + } else { + reuse_existing_gateway_config::(&id) + } +} + +/// 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. +/// Saves keys to disk, specified by the paths in `config`. +pub async fn register_with_gateway( + user_chosen_gateway_id: Option, + config: &Config, +) -> Result +where + T: NymConfig, +{ + println!("Configuring gateway"); + let gateway = + query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id).await?; + log::debug!("Querying gateway gives: {}", gateway); + + // Registering with gateway by setting up and writing shared keys to disk + log::trace!("Registering gateway"); + register_with_gateway_and_store_keys(gateway.clone(), config).await?; + println!("Saved all generated keys"); + + Ok(gateway.into()) +} + +/// Set the gateway using the usual procedue of querying the validator-api, but don't register or +/// create any keys. +/// This assumes that the user knows what they are doing, and that the existing keys are valid for +/// the gateway being used +pub async fn config_gateway_with_existing_keys( + user_chosen_gateway_id: String, + config: &Config, +) -> Result +where + T: NymConfig, +{ + println!("Using gateway provided by user, keeping existing keys"); + let gateway = + query_gateway_details(config.get_nym_api_endpoints(), Some(user_chosen_gateway_id)).await?; + log::debug!("Querying gateway gives: {}", gateway); + Ok(gateway.into()) +} + +/// Read and reuse the existing gateway configuration from a file that was generate earlier. +pub fn reuse_existing_gateway_config(id: &str) -> Result +where + T: NymConfig + ClientCoreConfigTrait, +{ + println!("Not registering gateway, will reuse existing config and keys"); + T::load_from_file(Some(id)) + .map(|existing_config| existing_config.get_gateway_endpoint().clone()) + .map_err(|err| { + 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." + ); + ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err) + }) +} + +/// Get the client address by loading the keys from stored files. +pub fn get_client_address_from_stored_keys( + config: &Config, +) -> Result +where + T: config::NymConfig, +{ + fn load_identity_keys( + pathfinder: &ClientKeyPathfinder, + ) -> Result { + let identity_keypair: identity::KeyPair = + pemstore::load_keypair(&pemstore::KeyPairPath::new( + pathfinder.private_identity_key().to_owned(), + pathfinder.public_identity_key().to_owned(), + )) + .tap_err(|_| log::error!("Failed to read stored identity key files"))?; + Ok(identity_keypair) + } + + fn load_sphinx_keys( + pathfinder: &ClientKeyPathfinder, + ) -> Result { + let sphinx_keypair: encryption::KeyPair = + pemstore::load_keypair(&pemstore::KeyPairPath::new( + pathfinder.private_encryption_key().to_owned(), + pathfinder.public_encryption_key().to_owned(), + )) + .tap_err(|_| log::error!("Failed to read stored sphinx key files"))?; + Ok(sphinx_keypair) + } + + let pathfinder = ClientKeyPathfinder::new_from_config(config); + let identity_keypair = load_identity_keys(&pathfinder)?; + let sphinx_keypair = load_sphinx_keys(&pathfinder)?; + + let client_recipient = Recipient::new( + *identity_keypair.public_key(), + *sphinx_keypair.public_key(), + // TODO: below only works under assumption that gateway address == gateway id + // (which currently is true) + NodeIdentity::from_base58_string(config.get_gateway_id())?, + ); + + Ok(client_recipient) +} + +pub fn output_to_json(init_results: &T, output_file: &str) { + match std::fs::File::create(output_file) { + Ok(file) => match serde_json::to_writer_pretty(file, init_results) { + Ok(_) => println!("Saved: {}", output_file), + Err(err) => eprintln!("Could not save {}: {err}", output_file), + }, + Err(err) => eprintln!("Could not save {}: {err}", output_file), + } +} diff --git a/clients/credential/Cargo.toml b/clients/credential/Cargo.toml index 81fe361ad0..1fee702592 100644 --- a/clients/credential/Cargo.toml +++ b/clients/credential/Cargo.toml @@ -6,11 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.52" bip39 = "1.0.1" cfg-if = "0.1" -clap = { version = "3.2", features = ["cargo", "derive"] } -pickledb = "0.4.1" +clap = { version = "4.0", features = ["cargo", "derive"] } rand = "0.7.3" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" @@ -25,7 +23,7 @@ credential-storage = { path = "../../common/credential-storage" } crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] } network-defaults = { path = "../../common/network-defaults" } pemstore = { path = "../../common/pemstore" } -validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] } +validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] } [features] coconut = ["credentials/coconut"] diff --git a/clients/credential/src/client.rs b/clients/credential/src/client.rs index 0eb63bf340..d4ee436698 100644 --- a/clients/credential/src/client.rs +++ b/clients/credential/src/client.rs @@ -6,27 +6,27 @@ use bip39::Mnemonic; use network_defaults::{NymNetworkDetails, VOUCHER_INFO}; use std::str::FromStr; use url::Url; -use validator_client::nymd; -use validator_client::nymd::traits::CoconutBandwidthSigningClient; -use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient}; +use validator_client::nyxd; +use validator_client::nyxd::traits::CoconutBandwidthSigningClient; +use validator_client::nyxd::{Coin, Fee, NyxdClient, SigningNyxdClient}; pub(crate) struct Client { - nymd_client: NymdClient, + nyxd_client: NyxdClient, mix_denom_base: String, } impl Client { - pub fn new(nymd_url: &str, mnemonic: &str) -> Self { - let nymd_url = Url::from_str(nymd_url).unwrap(); + pub fn new(nyxd_url: &str, mnemonic: &str) -> Self { + let nyxd_url = Url::from_str(nyxd_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) + let config = nyxd::Config::try_from_nym_network_details(&network_details) .expect("failed to construct valid validator client config with the provided network"); - let nymd_client = - NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap(); + let nyxd_client = + NyxdClient::connect_with_mnemonic(config, nyxd_url.as_ref(), mnemonic, None).unwrap(); Client { - nymd_client, + nyxd_client, mix_denom_base: network_details.chain_details.mix_denom.base, } } @@ -40,7 +40,7 @@ impl Client { ) -> Result { let amount = Coin::new(amount as u128, self.mix_denom_base.clone()); Ok(self - .nymd_client + .nyxd_client .deposit( amount, String::from(VOUCHER_INFO), diff --git a/clients/credential/src/commands.rs b/clients/credential/src/commands.rs index 7f9d3ae2e1..f697a98d7d 100644 --- a/clients/credential/src/commands.rs +++ b/clients/credential/src/commands.rs @@ -1,35 +1,29 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use async_trait::async_trait; use clap::{Args, Subcommand}; use completions::ArgShell; -use pickledb::PickleDb; use rand::rngs::OsRng; use std::str::FromStr; -use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters}; +use coconut_interface::{Base58, Parameters}; use credential_storage::storage::Storage; use credential_storage::PersistentStorage; use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; use credentials::coconut::utils::obtain_aggregate_signature; use crypto::asymmetric::{encryption, identity}; use network_defaults::{NymNetworkDetails, VOUCHER_INFO}; -use validator_client::nymd::tx::Hash; +use validator_client::nyxd::tx::Hash; use validator_client::{CoconutApiClient, Config}; use crate::client::Client; use crate::error::{CredentialClientError, Result}; -use crate::state::{KeyPair, RequestData, State}; +use crate::state::{KeyPair, State}; #[derive(Subcommand)] -pub(crate) enum Commands { - /// Deposit funds for buying coconut credential - Deposit(Deposit), - /// Lists the tx hashes of previous deposits - ListDeposits(ListDeposits), - /// Get a credential for a given deposit - GetCredential(GetCredential), +pub(crate) enum Command { + /// Run the binary + Run(Run), /// Generate shell completions Completions(ArgShell), @@ -38,169 +32,82 @@ pub(crate) enum Commands { GenerateFigSpec, } -#[async_trait] -pub(crate) trait Execute { - async fn execute(&self, db: &mut PickleDb, shared_storage: PersistentStorage) -> Result<()>; +#[derive(Args)] +pub(crate) struct Run { + /// Home directory of the client that is supposed to use the credential. + #[clap(long)] + pub(crate) client_home_directory: std::path::PathBuf, + + /// The nyxd URL that should be used + #[clap(long)] + pub(crate) nyxd_url: String, + + /// A mnemonic for the account that buys the credential + #[clap(long)] + pub(crate) mnemonic: String, + + /// The amount of utokens the credential will hold + #[clap(long)] + pub(crate) amount: u64, } -#[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, -} +pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result { + let mut rng = OsRng; + let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); + let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); -#[async_trait] -impl Execute for Deposit { - async fn execute(&self, db: &mut PickleDb, _shared_storage: PersistentStorage) -> Result<()> { - let mut rng = OsRng; - let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); - let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); - - let client = Client::new(&self.nymd_url, &self.mnemonic); - let tx_hash = client - .deposit( - self.amount, - signing_keypair.public_key.clone(), - encryption_keypair.public_key.clone(), - None, - ) - .await?; - - let state = State { - amount: self.amount, - tx_hash: tx_hash.clone(), - signing_keypair, - encryption_keypair, - blind_request_data: None, - signature: None, - }; - db.set(&tx_hash, &state).unwrap(); - - println!("{:?}", state); - - Ok(()) - } -} - -#[derive(Args, Clone)] -pub(crate) struct ListDeposits {} - -#[async_trait] -impl Execute for ListDeposits { - async fn execute(&self, db: &mut PickleDb, _shared_storage: PersistentStorage) -> Result<()> { - for kv in db.iter() { - println!("{:?}", kv.get_value::()); - } - - Ok(()) - } -} - -#[derive(Args, Clone)] -pub(crate) struct GetCredential { - /// The hash of a successful deposit transaction - #[clap(long)] - tx_hash: String, - /// The nymd URL that should be used - #[clap(long)] - nymd_url: 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))] - __no_request: bool, -} - -#[async_trait] -impl Execute for GetCredential { - async fn execute(&self, db: &mut PickleDb, shared_storage: PersistentStorage) -> Result<()> { - let mut state = db - .get::(&self.tx_hash) - .ok_or(CredentialClientError::NoDeposit)?; - - let network_details = NymNetworkDetails::new_from_env(); - let config = Config::try_from_nym_network_details(&network_details)?; - let client = validator_client::Client::new_query(config)?; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?; - - let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); - let bandwidth_credential_attributes = if self.__no_request { - if let Some(blind_request_data) = state.blind_request_data { - let serial_number = - Attribute::try_from_byte_slice(&blind_request_data.serial_number) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?; - let binding_number = - Attribute::try_from_byte_slice(&blind_request_data.binding_number) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?; - let pedersen_commitments_openings = vec![ - Attribute::try_from_byte_slice(&blind_request_data.first_attribute) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?, - Attribute::try_from_byte_slice(&blind_request_data.second_attribute) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?, - ]; - let blind_sign_request = - BlindSignRequest::from_bytes(blind_request_data.blind_sign_req.as_slice()) - .map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?; - BandwidthVoucher::new_with_blind_sign_req( - [serial_number, binding_number], - [&state.amount.to_string(), VOUCHER_INFO], - Hash::from_str(&self.tx_hash) - .map_err(|_| CredentialClientError::InvalidTxHash)?, - identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?, - encryption::PrivateKey::from_base58_string( - &state.encryption_keypair.private_key, - )?, - pedersen_commitments_openings, - blind_sign_request, - ) - } else { - return Err(CredentialClientError::NoLocalBlindSignRequest); - } - } else { - BandwidthVoucher::new( - ¶ms, - state.amount.to_string(), - VOUCHER_INFO.to_string(), - Hash::from_str(&self.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?, - identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?, - encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?, - ) - }; - - // Back up the blind sign req data, in case of sporadic failures - state.blind_request_data = Some(RequestData::new( - bandwidth_credential_attributes.get_private_attributes(), - bandwidth_credential_attributes.pedersen_commitments_openings(), - bandwidth_credential_attributes.blind_sign_request(), - )?); - db.set(&self.tx_hash, &state).unwrap(); - - let signature = obtain_aggregate_signature( - ¶ms, - &bandwidth_credential_attributes, - &coconut_api_clients, + let client = Client::new(nyxd_url, mnemonic); + let tx_hash = client + .deposit( + amount, + signing_keypair.public_key.clone(), + encryption_keypair.public_key.clone(), + None, ) .await?; - shared_storage - .insert_coconut_credential( - state.amount.to_string(), - VOUCHER_INFO.to_string(), - bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(), - bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(), - signature.to_bs58(), - ) - .await?; - state.signature = Some(signature.to_bs58()); - db.set(&self.tx_hash, &state).unwrap(); - println!("Signature: {:?}", state.signature); + let state = State { + amount, + tx_hash, + signing_keypair, + encryption_keypair, + }; - Ok(()) - } + Ok(state) +} + +pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStorage) -> Result<()> { + let network_details = NymNetworkDetails::new_from_env(); + let config = Config::try_from_nym_network_details(&network_details)?; + let client = validator_client::Client::new_query(config)?; + let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?; + + let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); + let bandwidth_credential_attributes = BandwidthVoucher::new( + ¶ms, + state.amount.to_string(), + VOUCHER_INFO.to_string(), + Hash::from_str(&state.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?, + identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?, + encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?, + ); + + let signature = obtain_aggregate_signature( + ¶ms, + &bandwidth_credential_attributes, + &coconut_api_clients, + ) + .await?; + println!("Signature: {:?}", signature.to_bs58()); + shared_storage + .insert_coconut_credential( + state.amount.to_string(), + VOUCHER_INFO.to_string(), + bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(), + bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(), + signature.to_bs58(), + ) + .await?; + + Ok(()) } diff --git a/clients/credential/src/error.rs b/clients/credential/src/error.rs index 08bd0001e9..f793957e2f 100644 --- a/clients/credential/src/error.rs +++ b/clients/credential/src/error.rs @@ -7,15 +7,15 @@ use credential_storage::error::StorageError; use credentials::error::Error as CredentialError; use crypto::asymmetric::encryption::KeyRecoveryError; use crypto::asymmetric::identity::Ed25519RecoveryError; -use validator_client::nymd::error::NymdError; +use validator_client::nyxd::error::NyxdError; use validator_client::ValidatorClientError; pub type Result = std::result::Result; #[derive(Error, Debug)] pub enum CredentialClientError { - #[error("Nymd error: {0}")] - Nymd(#[from] NymdError), + #[error("Nyxd error: {0}")] + Nyxd(#[from] NyxdError), #[error("Validator client error: {0}")] ValidatorClientError(#[from] ValidatorClientError), @@ -23,18 +23,6 @@ pub enum CredentialClientError { #[error("Credential error: {0}")] Credential(#[from] CredentialError), - #[error("No previous deposit with that tx hash")] - NoDeposit, - - #[error("Wrong number of attributes")] - WrongAttributeNumber, - - #[error("Could not find any backed up blind sign request data")] - NoLocalBlindSignRequest, - - #[error("The local blind sign request data is corrupted")] - CorruptedBlindSignRequest, - #[error("The tx hash provided is not valid")] InvalidTxHash, diff --git a/clients/credential/src/main.rs b/clients/credential/src/main.rs index cc0263e048..b27571909d 100644 --- a/clients/credential/src/main.rs +++ b/clients/credential/src/main.rs @@ -9,14 +9,13 @@ cfg_if::cfg_if! { mod error; mod state; - use commands::{Commands, Execute}; use error::Result; use network_defaults::setup_env; - use clap::CommandFactory; use completions::fig_generate; + use commands::*; + use config::{DATA_DIR, DB_FILE_NAME}; - use clap::Parser; - use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod}; + use clap::{CommandFactory, Parser}; #[derive(Parser)] #[clap(author = "Nymtech", version, about)] @@ -25,43 +24,26 @@ cfg_if::cfg_if! { #[clap(short, long)] pub(crate) config_env_file: Option, - /// 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, + pub(crate) command: Command, } #[tokio::main] async fn main() -> Result<()> { let args = Cli::parse(); - setup_env(args.config_env_file.clone()); - - let shared_storage = credential_storage::initialise_storage(args.credential_db_path.clone()).await; - let mut db = match PickleDb::load( - "credential.db", - PickleDbDumpPolicy::AutoDump, - SerializationMethod::Json, - ) { - Ok(db) => db, - Err(_) => PickleDb::new( - "credential.db", - PickleDbDumpPolicy::AutoDump, - SerializationMethod::Json, - ), - }; - + setup_env(args.config_env_file.as_ref()); let bin_name = "nym-credential-client"; - match &args.command { - Commands::Deposit(m) => m.execute(&mut db, shared_storage).await?, - Commands::ListDeposits(m) => m.execute(&mut db, shared_storage).await?, - Commands::GetCredential(m) => m.execute(&mut db, shared_storage).await?, - Commands::Completions(s) => s.generate(&mut crate::Cli::into_app(), bin_name), - Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::into_app(), bin_name) + match args.command { + Command::Run(r) => { + let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME); + let shared_storage = credential_storage::initialise_storage(db_path).await; + + let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?; + get_credential(&state, shared_storage).await?; + } + Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name), + Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name) } Ok(()) diff --git a/clients/credential/src/state.rs b/clients/credential/src/state.rs index 6da1ae6faa..98cbb307df 100644 --- a/clients/credential/src/state.rs +++ b/clients/credential/src/state.rs @@ -1,13 +1,10 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use coconut_interface::{Attribute, BlindSignRequest, Bytable, PrivateAttribute}; use serde::{Deserialize, Serialize}; use crypto::asymmetric::{encryption, identity}; -use crate::error::{CredentialClientError, Result}; - #[derive(Clone, Debug, Deserialize, Serialize)] pub(crate) struct KeyPair { pub public_key: String, @@ -38,35 +35,4 @@ pub(crate) struct State { pub tx_hash: String, pub signing_keypair: KeyPair, pub encryption_keypair: KeyPair, - pub blind_request_data: Option, - pub signature: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub(crate) struct RequestData { - pub serial_number: Vec, - pub binding_number: Vec, - pub first_attribute: Vec, - pub second_attribute: Vec, - pub blind_sign_req: Vec, -} - -impl RequestData { - pub fn new( - private_attributes: Vec, - attributes: &[Attribute], - blind_sign_request: &BlindSignRequest, - ) -> Result { - if private_attributes.len() != 2 || attributes.len() != 2 { - Err(CredentialClientError::WrongAttributeNumber) - } else { - Ok(RequestData { - serial_number: private_attributes[0].to_byte_vec(), - binding_number: private_attributes[1].to_byte_vec(), - first_attribute: attributes[0].to_byte_vec(), - second_attribute: attributes[1].to_byte_vec(), - blind_sign_req: blind_sign_request.to_bytes(), - }) - } - } } diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index eb4c722501..27f13bbbc4 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-client" -version = "1.1.1" +version = "1.1.4" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] description = "Implementation of the Nym Client" edition = "2021" @@ -20,19 +20,22 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it # and the single instance of abortable we have should really be refactored anyway url = "2.2" -clap = { version = "3.2", features = ["cargo", "derive"] } +clap = { version = "4.0", features = ["cargo", "derive"] } dirs = "4.0" +lazy_static = "1.4.0" log = "0.4" # self explanatory pretty_env_logger = "0.4" # for formatting log messages rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization -sled = "0.34" # for storage of replySURB decryption keys +serde_json = "1.0" thiserror = "1.0.34" +tap = "1.0.1" tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio-tungstenite = "0.14" # websocket ## internal -client-core = { path = "../client-core" } +build-information = { path = "../../common/build-information" } +client-core = { path = "../client-core", features = ["fs-surb-storage"] } client-connections = { path = "../../common/client-connections" } coconut-interface = { path = "../../common/coconut-interface", optional = true } config = { path = "../../common/config" } @@ -48,16 +51,12 @@ nymsphinx = { path = "../../common/nymsphinx" } pemstore = { path = "../../common/pemstore" } task = { path = "../../common/task" } topology = { path = "../../common/topology" } -validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] } +validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] } version-checker = { path = "../../common/version-checker" } websocket-requests = { path = "websocket-requests" } -tap = "1.0.1" [features] coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"] [dev-dependencies] serde_json = "1.0" # for the "textsend" example - -[build-dependencies] -vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] } diff --git a/clients/native/examples/js-examples/websocket/package-lock.json b/clients/native/examples/js-examples/websocket/package-lock.json index 4fc3fbbd83..0c20aed18c 100644 --- a/clients/native/examples/js-examples/websocket/package-lock.json +++ b/clients/native/examples/js-examples/websocket/package-lock.json @@ -477,13 +477,13 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -674,30 +674,42 @@ } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, "engines": { "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "engines": { "node": ">= 0.8" @@ -1006,23 +1018,17 @@ } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -1033,9 +1039,9 @@ } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true, "engines": { "node": ">= 0.6" @@ -1180,10 +1186,14 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-node": { "version": "2.1.0", @@ -1299,7 +1309,7 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, "node_modules/electron-to-chromium": { @@ -1310,7 +1320,7 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, "engines": { "node": ">= 0.8" @@ -1409,7 +1419,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, "engines": { "node": ">= 0.6" @@ -1453,38 +1463,39 @@ } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dev": true, "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -1499,11 +1510,23 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -1617,23 +1640,32 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/follow-redirects": { "version": "1.14.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", @@ -1666,7 +1698,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, "engines": { "node": ">= 0.6" @@ -1928,26 +1960,38 @@ "dev": true }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, "node_modules/http-parser-js": { "version": "0.5.3", @@ -2379,7 +2423,7 @@ "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "engines": { "node": ">= 0.6" @@ -2439,19 +2483,19 @@ } }, "node_modules/mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.48.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -2528,9 +2572,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "engines": { "node": ">= 0.6" @@ -2605,6 +2649,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -2637,9 +2690,9 @@ "dev": true }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { "ee-first": "1.1.1" @@ -2933,12 +2986,18 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/queue-microtask": { @@ -2979,13 +3038,13 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -2994,9 +3053,9 @@ } }, "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "engines": { "node": ">= 0.8" @@ -3239,35 +3298,53 @@ } }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -3322,24 +3399,24 @@ "dev": true }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "node_modules/shallow-clone": { @@ -3375,6 +3452,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -3642,9 +3733,9 @@ "dev": true }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "engines": { "node": ">=0.6" @@ -3671,7 +3762,7 @@ "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "engines": { "node": ">= 0.8" @@ -4793,13 +4884,13 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -4939,27 +5030,35 @@ "dev": true }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true } } @@ -5186,20 +5285,12 @@ "dev": true }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "safe-buffer": "5.2.1" } }, "content-type": { @@ -5209,9 +5300,9 @@ "dev": true }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true }, "cookie-signature": { @@ -5323,9 +5414,9 @@ "dev": true }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true }, "detect-node": { @@ -5421,7 +5512,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, "electron-to-chromium": { @@ -5432,7 +5523,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true }, "enhanced-resolve": { @@ -5503,7 +5594,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, "eventemitter3": { @@ -5535,38 +5626,39 @@ } }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dev": true, "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -5578,10 +5670,16 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -5679,18 +5777,26 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } } }, "follow-redirects": { @@ -5708,7 +5814,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, "fs-monkey": { @@ -5900,22 +6006,28 @@ "dev": true }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -6230,7 +6342,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, "memfs": { @@ -6272,16 +6384,16 @@ "dev": true }, "mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.48.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -6343,9 +6455,9 @@ "dev": true }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { @@ -6402,6 +6514,12 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, "object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -6425,9 +6543,9 @@ "dev": true }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "requires": { "ee-first": "1.1.1" @@ -6656,10 +6774,13 @@ } }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } }, "queue-microtask": { "version": "1.2.3", @@ -6682,21 +6803,21 @@ "dev": true }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true } } @@ -6868,30 +6989,42 @@ } }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -6946,21 +7079,21 @@ } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "shallow-clone": { @@ -6987,6 +7120,17 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -7181,9 +7325,9 @@ "dev": true }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, "tslib": { @@ -7204,7 +7348,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, "uri-js": { diff --git a/clients/native/examples/websocket_binarysend.rs b/clients/native/examples/websocket_binarysend.rs index f103815af2..2c22f0d03b 100644 --- a/clients/native/examples/websocket_binarysend.rs +++ b/clients/native/examples/websocket_binarysend.rs @@ -25,58 +25,59 @@ async fn get_self_address(ws_stream: &mut WebSocketStream recipient, + ServerResponse::SelfAddress(recipient) => *recipient, _ => panic!("received an unexpected response!"), } } async fn send_file_with_reply() { - let uri = "ws://localhost:1977"; - let (mut ws_stream, _) = connect_async(uri).await.unwrap(); - - let recipient = get_self_address(&mut ws_stream).await; - println!("our full address is: {}", recipient); - - let read_data = std::fs::read("examples/dummy_file").unwrap(); - - let send_request = ClientRequest::Send { - recipient, - message: read_data, - with_reply_surb: true, - connection_id: Some(0), - }; - - println!("sending content of 'dummy_file' over the mix network..."); - let response = send_message_and_get_response(&mut ws_stream, send_request.serialize()).await; - - let received = match response { - ServerResponse::Received(received) => received, - _ => panic!("received an unexpected response!"), - }; - - println!("writing the file back to the disk!"); - std::fs::write("examples/received_file_withreply", received.message).unwrap(); - - let reply_message = b"hello from reply SURB! - thanks for sending me the file!".to_vec(); - let reply_request = ClientRequest::Reply { - message: reply_message.clone(), - reply_surb: received.reply_surb.unwrap(), - }; - - println!( - "sending {:?} (using reply SURB!) over the mix network...", - String::from_utf8(reply_message).unwrap() - ); - let response = send_message_and_get_response(&mut ws_stream, reply_request.serialize()).await; - let received = match response { - ServerResponse::Received(received) => received, - _ => panic!("received an unexpected response!"), - }; - - println!( - "received {:#?} from the mix network!", - String::from_utf8(received.message).unwrap() - ); + todo!("reimplement surb usage here : )") + // let uri = "ws://localhost:1977"; + // let (mut ws_stream, _) = connect_async(uri).await.unwrap(); + // + // let recipient = get_self_address(&mut ws_stream).await; + // println!("our full address is: {}", recipient); + // + // let read_data = std::fs::read("examples/dummy_file").unwrap(); + // + // let send_request = ClientRequest::Send { + // recipient, + // message: read_data, + // with_reply_surb: true, + // connection_id: Some(0), + // }; + // + // println!("sending content of 'dummy_file' over the mix network..."); + // let response = send_message_and_get_response(&mut ws_stream, send_request.serialize()).await; + // + // let received = match response { + // ServerResponse::Received(received) => received, + // _ => panic!("received an unexpected response!"), + // }; + // + // println!("writing the file back to the disk!"); + // std::fs::write("examples/received_file_withreply", received.message).unwrap(); + // + // let reply_message = b"hello from reply SURB! - thanks for sending me the file!".to_vec(); + // let reply_request = ClientRequest::Reply { + // message: reply_message.clone(), + // reply_surb: received.reply_surb.unwrap(), + // }; + // + // println!( + // "sending {:?} (using reply SURB!) over the mix network...", + // String::from_utf8(reply_message).unwrap() + // ); + // let response = send_message_and_get_response(&mut ws_stream, reply_request.serialize()).await; + // let received = match response { + // ServerResponse::Received(received) => received, + // _ => panic!("received an unexpected response!"), + // }; + // + // println!( + // "received {:#?} from the mix network!", + // String::from_utf8(received.message).unwrap() + // ); } async fn send_file_without_reply() { @@ -91,7 +92,6 @@ async fn send_file_without_reply() { let send_request = ClientRequest::Send { recipient, message: read_data, - with_reply_surb: false, connection_id: Some(0), }; diff --git a/clients/native/src/client/config/mod.rs b/clients/native/src/client/config/mod.rs index 8ce268e9ea..e49d2e7569 100644 --- a/clients/native/src/client/config/mod.rs +++ b/clients/native/src/client/config/mod.rs @@ -2,12 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::client::config::template::config_template; -use client_core::config::Config as BaseConfig; +pub use client_core::config::Config as BaseConfig; pub use client_core::config::MISSING_VALUE; +use client_core::config::{ClientCoreConfigTrait, DebugConfig}; use config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT; -use config::NymConfig; +use config::{NymConfig, OptionalSet}; use serde::{Deserialize, Serialize}; +use std::fmt::Debug; use std::path::PathBuf; +use std::str::FromStr; mod template; @@ -73,6 +76,12 @@ impl NymConfig for Config { } } +impl ClientCoreConfigTrait for Config { + fn get_gateway_endpoint(&self) -> &client_core::config::GatewayEndpointConfig { + self.base.get_gateway_endpoint() + } +} + impl Config { pub fn new>(id: S) -> Self { Config { @@ -86,6 +95,15 @@ impl Config { self } + pub fn with_disabled_socket(mut self, disabled: bool) -> Self { + if disabled { + self.socket.socket_type = SocketType::None; + } else { + self.socket.socket_type = SocketType::WebSocket; + } + self + } + pub fn with_port(mut self, port: u16) -> Self { self.socket.listening_port = port; self @@ -104,6 +122,10 @@ impl Config { &mut self.base } + pub fn get_debug_settings(&self) -> &DebugConfig { + self.get_base().get_debug_config() + } + pub fn get_socket_type(&self) -> SocketType { self.socket.socket_type } @@ -111,6 +133,50 @@ impl Config { pub fn get_listening_port(&self) -> u16 { self.socket.listening_port } + + // poor man's 'builder' method + pub fn with_base(mut self, f: F, val: T) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + { + self.base = f(self.base, val); + self + } + + // helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config + // (plz, lets refactor it) + pub fn with_optional_ext(mut self, f: F, val: Option) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + { + self.base = self.base.with_optional(f, val); + self + } + + pub fn with_optional_env_ext(mut self, f: F, val: Option, env_var: &str) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + T: FromStr, + ::Err: Debug, + { + self.base = self.base.with_optional_env(f, val, env_var); + self + } + + pub fn with_optional_custom_env_ext( + mut self, + f: F, + val: Option, + env_var: &str, + parser: G, + ) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + G: Fn(&str) -> T, + { + self.base = self.base.with_optional_custom_env(f, val, env_var, parser); + self + } } #[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] diff --git a/clients/native/src/client/config/template.rs b/clients/native/src/client/config/template.rs index c327d286ff..15a0763ed9 100644 --- a/clients/native/src/client/config/template.rs +++ b/clients/native/src/client/config/template.rs @@ -23,16 +23,16 @@ id = '{{ client.id }}' # to claim bandwidth without presenting bandwidth credentials. disabled_credentials_mode = {{ client.disabled_credentials_mode }} -# Addresses to nymd validators via which the client can communicate with the chain. -validator_urls = [ - {{#each client.validator_urls }} +# Addresses to nyxd validators via which the client can communicate with the chain. +nyxd_urls = [ + {{#each client.nyxd_urls }} '{{this}}', {{/each}} ] # Addresses to APIs running on validator from which the client gets the view of the network. -validator_api_urls = [ - {{#each client.validator_api_urls }} +nym_api_urls = [ + {{#each client.nym_api_urls }} '{{this}}', {{/each}} ] @@ -49,13 +49,12 @@ private_encryption_key_file = '{{ client.private_encryption_key_file }}' # Path to file containing public encryption key. public_encryption_key_file = '{{ client.public_encryption_key_file }}' -# Full path to file containing reply encryption keys of all reply-SURBs we have ever -# sent but not received back. -reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}' - # Path to the database containing bandwidth credentials database_path = '{{ client.database_path }}' +# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. +reply_surb_database_path = '{{ client.reply_surb_database_path }}' + ##### additional client config options ##### # A gateway specific, optional, base58 stringified shared key used for diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index 5dc01920cc..de6aa2748d 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -1,22 +1,27 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use std::error::Error; + use crate::client::config::Config; use crate::error::ClientError; use crate::websocket; use client_connections::TransmissionLane; -use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput}; +use client_core::client::base_client::{ + non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, +}; use client_core::client::inbound_messages::InputMessage; use client_core::client::key_manager::KeyManager; use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver}; +use client_core::client::replies::reply_controller::requests::ReplyControllerSender; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use futures::channel::mpsc; use gateway_client::bandwidth::BandwidthController; use log::*; use nymsphinx::addressing::clients::Recipient; -use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use nymsphinx::receiver::ReconstructedMessage; -use task::{wait_for_signal, ShutdownNotifier}; +use task::TaskManager; pub(crate) mod config; @@ -44,8 +49,21 @@ impl SocketClient { #[cfg(feature = "coconut")] let bandwidth_controller = { let details = network_defaults::NymNetworkDetails::new_from_env(); - let client_config = validator_client::Config::try_from_nym_network_details(&details) - .expect("failed to construct validator client config"); + let mut client_config = + validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct validator client config"); + let nyxd_url = config + .get_base() + .get_validator_endpoints() + .pop() + .expect("No nyxd validator endpoint provided"); + let api_url = config + .get_base() + .get_nym_api_endpoints() + .pop() + .expect("No validator api endpoint provided"); + // overwrite env configuration with config URLs + client_config = client_config.with_urls(nyxd_url, api_url); let client = validator_client::Client::new_query(client_config) .expect("Could not construct query client"); let coconut_api_clients = @@ -69,52 +87,51 @@ impl SocketClient { config: &Config, client_input: ClientInput, client_output: ClientOutput, - self_address: Recipient, + self_address: &Recipient, + reply_controller_sender: ReplyControllerSender, + shutdown: task::TaskClient, ) { info!("Starting websocket listener..."); let ClientInput { - shared_lane_queue_lengths, connection_command_sender, input_sender, } = client_input; - let received_buffer_request_sender = client_output.received_buffer_request_sender; + let ClientOutput { + shared_lane_queue_lengths, + received_buffer_request_sender, + } = client_output; - let websocket_handler = websocket::Handler::new( + let websocket_handler = websocket::HandlerBuilder::new( input_sender, connection_command_sender, received_buffer_request_sender, self_address, shared_lane_queue_lengths, + reply_controller_sender, ); - websocket::Listener::new(config.get_listening_port()).start(websocket_handler); + websocket::Listener::new(config.get_listening_port()).start(websocket_handler, shutdown); } /// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent) - pub async fn run_socket_forever(self) -> Result<(), ClientError> { - let shutdown = self.start_socket().await?; - wait_for_signal().await; + pub async fn run_socket_forever(self) -> Result<(), Box> { + let mut shutdown = self.start_socket().await?; - println!( - "Received signal - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)." - ); + let res = task::wait_for_signal_and_error(&mut shutdown).await; log::info!("Sending shutdown"); shutdown.signal_shutdown().ok(); - // Some of these components have shutdown signalling implemented as part of socks5 work, - // but since it's not fully implemented (yet) for all the components of the native client, - // we don't try to wait and instead just stop immediately. - //log::info!("Waiting for tasks to finish... (Press ctrl-c to force)"); - //shutdown.wait_for_shutdown().await; + log::info!("Waiting for tasks to finish... (Press ctrl-c to force)"); + shutdown.wait_for_shutdown().await; log::info!("Stopping nym-client"); - Ok(()) + res } - pub async fn start_socket(self) -> Result { + pub async fn start_socket(self) -> Result { if !self.config.get_socket_type().is_websocket() { return Err(ClientError::InvalidSocketMode); } @@ -123,6 +140,11 @@ impl SocketClient { self.config.get_base(), self.key_manager, Some(Self::create_bandwidth_controller(&self.config).await), + non_wasm_helpers::setup_fs_reply_surb_backend( + self.config.get_base().get_reply_surb_database_path(), + self.config.get_debug_settings(), + ) + .await?, ); let self_address = base_builder.as_mix_recipient(); @@ -130,12 +152,19 @@ impl SocketClient { let client_input = started_client.client_input.register_producer(); let client_output = started_client.client_output.register_consumer(); - Self::start_websocket_listener(&self.config, client_input, client_output, self_address); + Self::start_websocket_listener( + &self.config, + client_input, + client_output, + &self_address, + started_client.reply_controller_sender, + started_client.task_manager.subscribe(), + ); info!("Client startup finished!"); info!("The address of this client is: {}", self_address); - Ok(started_client.shutdown_notifier) + Ok(started_client.task_manager) } pub async fn start_direct(self) -> Result { @@ -147,6 +176,11 @@ impl SocketClient { self.config.get_base(), self.key_manager, Some(Self::create_bandwidth_controller(&self.config).await), + non_wasm_helpers::setup_fs_reply_surb_backend( + self.config.get_base().get_reply_surb_database_path(), + self.config.get_debug_settings(), + ) + .await?, ); let mut started_client = base_client.start_base().await?; @@ -167,7 +201,7 @@ impl SocketClient { Ok(DirectClient { client_input, reconstructed_receiver, - _shutdown_notifier: started_client.shutdown_notifier, + _shutdown_notifier: started_client.task_manager, }) } } @@ -177,21 +211,16 @@ pub struct DirectClient { reconstructed_receiver: ReconstructedMessagesReceiver, // we need to keep reference to this guy otherwise things will start dropping - _shutdown_notifier: ShutdownNotifier, + _shutdown_notifier: TaskManager, } impl DirectClient { /// EXPERIMENTAL DIRECT RUST API /// It's untested and there are absolutely no guarantees about it (but seems to have worked /// well enough in local tests) - pub async fn send_message( - &mut self, - recipient: Recipient, - message: Vec, - with_reply_surb: bool, - ) { + pub async fn send_regular_message(&mut self, recipient: Recipient, message: Vec) { let lane = TransmissionLane::General; - let input_msg = InputMessage::new_fresh(recipient, message, with_reply_surb, lane); + let input_msg = InputMessage::new_regular(recipient, message, lane); self.client_input .input_sender @@ -203,8 +232,28 @@ impl DirectClient { /// EXPERIMENTAL DIRECT RUST API /// It's untested and there are absolutely no guarantees about it (but seems to have worked /// well enough in local tests) - pub async fn send_reply(&mut self, reply_surb: ReplySurb, message: Vec) { - let input_msg = InputMessage::new_reply(reply_surb, message); + pub async fn send_anonymous_message( + &mut self, + recipient: Recipient, + message: Vec, + reply_surbs: u32, + ) { + let lane = TransmissionLane::General; + let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane); + + self.client_input + .input_sender + .send(input_msg) + .await + .expect("InputMessageReceiver has stopped receiving!"); + } + + /// EXPERIMENTAL DIRECT RUST API + /// It's untested and there are absolutely no guarantees about it (but seems to have worked + /// well enough in local tests) + pub async fn send_reply(&mut self, recipient_tag: AnonymousSenderTag, message: Vec) { + let lane = TransmissionLane::General; + let input_msg = InputMessage::new_reply(recipient_tag, message, lane); self.client_input .input_sender diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index d97bcc8e60..8f8380349f 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -1,14 +1,18 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use clap::Args; -use client_core::{config::GatewayEndpointConfig, error::ClientCoreError}; -use config::NymConfig; - use crate::{ client::config::Config, commands::{override_config, OverrideConfig}, + error::ClientError, }; +use clap::Args; +use config::NymConfig; +use crypto::asymmetric::identity; +use nymsphinx::addressing::clients::Recipient; +use serde::Serialize; +use std::fmt::Display; +use tap::TapFallible; #[derive(Args, Clone)] pub(crate) struct Init { @@ -18,20 +22,22 @@ pub(crate) struct Init { /// Id of the gateway we are going to connect to. #[clap(long)] - gateway: Option, + gateway: Option, /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, /// potentially causing loss of access. #[clap(long)] force_register_gateway: bool, - /// Comma separated list of rest endpoints of the nymd validators - #[clap(long)] - nymd_validators: Option, + /// Comma separated list of rest endpoints of the nyxd validators + #[cfg(feature = "coconut")] + #[clap(long, alias = "nymd_validators", value_delimiter = ',')] + nyxd_urls: Option>, /// Comma separated list of rest endpoints of the API validators - #[clap(long)] - api_validators: Option, + #[clap(long, alias = "api_validators", value_delimiter = ',')] + // the alias here is included for backwards compatibility (1.1.4 and before) + nym_apis: Option>, /// Whether to not start the websocket #[clap(long)] @@ -43,32 +49,65 @@ pub(crate) struct Init { /// Mostly debug-related option to increase default traffic rate so that you would not need to /// modify config post init - #[clap(long, hidden = true)] + #[clap(long, hide = true)] fastmode: bool, + /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) + #[clap(long, hide = true)] + no_cover: bool, + /// Set this client to work in a enabled credentials mode that would attempt to use gateway /// with bandwidth credential requirement. #[cfg(feature = "coconut")] #[clap(long)] enabled_credentials_mode: bool, + + /// Save a summary of the initialization to a json file + #[clap(long)] + output_json: bool, } impl From for OverrideConfig { fn from(init_config: Init) -> Self { OverrideConfig { - nymd_validators: init_config.nymd_validators, - api_validators: init_config.api_validators, + nym_apis: init_config.nym_apis, disable_socket: init_config.disable_socket, port: init_config.port, fastmode: init_config.fastmode, + no_cover: init_config.no_cover, + #[cfg(feature = "coconut")] + nyxd_urls: init_config.nyxd_urls, #[cfg(feature = "coconut")] enabled_credentials_mode: init_config.enabled_credentials_mode, } } } -pub(crate) async fn execute(args: &Init) { +#[derive(Debug, Serialize)] +pub struct InitResults { + #[serde(flatten)] + client_core: client_core::init::InitResults, + client_listening_port: String, +} + +impl InitResults { + fn new(config: &Config, address: &Recipient) -> Self { + Self { + client_core: client_core::init::InitResults::new(config.get_base(), address), + client_listening_port: config.get_listening_port().to_string(), + } + } +} + +impl Display for InitResults { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", self.client_core)?; + write!(f, "Client listening port: {}", self.client_listening_port) + } +} + +pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> { println!("Initialising client..."); let id = &args.id; @@ -92,25 +131,44 @@ pub(crate) async fn execute(args: &Init) { let register_gateway = !already_init || user_wants_force_register; // Attempt to use a user-provided gateway, if possible - let user_chosen_gateway_id = args.gateway.as_deref(); + let user_chosen_gateway_id = args.gateway; - let mut config = Config::new(id); - let override_config_fields = OverrideConfig::from(args.clone()); - config = override_config(config, override_config_fields); + // Load and potentially override config + let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone())); + + // Setup gateway by either registering a new one, or creating a new config from the selected + // one but with keys kept, or reusing the gateway configuration. + let gateway = client_core::init::setup_gateway::( + register_gateway, + user_chosen_gateway_id.map(|id| id.to_base58_string()), + config.get_base(), + ) + .await + .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; - let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config) - .await - .unwrap_or_else(|err| { - eprintln!("Failed to setup gateway\nError: {err}"); - std::process::exit(1) - }); config.get_base_mut().with_gateway_endpoint(gateway); - let config_save_location = config.get_config_file_save_location(); - config - .save_to_file(None) - .expect("Failed to save the config file"); + config.save_to_file(None).tap_err(|_| { + log::error!("Failed to save the config file"); + })?; + print_saved_config(&config); + + let address = client_core::init::get_client_address_from_stored_keys(config.get_base())?; + let init_results = InitResults::new(&config, &address); + println!("{init_results}"); + + // Output summary to a json file, if specified + if args.output_json { + client_core::init::output_to_json(&init_results, "client_init_results.json"); + } + + println!("\nThe address of this client is: {address}\n"); + Ok(()) +} + +fn print_saved_config(config: &Config) { + let config_save_location = config.get_config_file_save_location(); println!("Saved configuration file to {:?}", config_save_location); println!("Using gateway: {}", config.get_base().get_gateway_id()); log::debug!("Gateway id: {}", config.get_base().get_gateway_id()); @@ -119,63 +177,5 @@ pub(crate) async fn execute(args: &Init) { "Gateway listener: {}", config.get_base().get_gateway_listener() ); - println!("Client configuration completed."); - - client_core::init::show_address(config.get_base()).unwrap_or_else(|err| { - eprintln!("Failed to show address\nError: {err}"); - std::process::exit(1) - }); -} - -async fn setup_gateway( - id: &str, - register: bool, - user_chosen_gateway_id: Option<&str>, - config: &Config, -) -> Result { - 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. - println!("Configuring gateway"); - let gateway = client_core::init::query_gateway_details( - config.get_base().get_validator_api_endpoints(), - user_chosen_gateway_id, - ) - .await?; - log::debug!("Querying gateway gives: {}", gateway); - - // Registering with gateway by setting up and writing shared keys to disk - log::trace!("Registering gateway"); - client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base()) - .await?; - println!("Saved all generated keys"); - - 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 - // valid for the gateway being used - println!("Using gateway provided by user, keeping existing keys"); - let gateway = client_core::init::query_gateway_details( - config.get_base().get_validator_api_endpoints(), - user_chosen_gateway_id, - ) - .await?; - log::debug!("Querying gateway gives: {}", gateway); - Ok(gateway.into()) - } else { - println!("Not registering gateway, will reuse existing config and keys"); - let existing_config = Config::load_from_file(Some(id)).map_err(|err| { - 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." - ); - ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err) - })?; - - Ok(existing_config.get_base().get_gateway_endpoint().clone()) - } + println!("Client configuration completed.\n"); } diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 648a1d6002..14ac39056f 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -1,53 +1,31 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::client::config::{Config, SocketType}; -use crate::error::ClientError; +use crate::client::config::{BaseConfig, Config}; +use build_information::BinaryBuildInformation; use clap::CommandFactory; use clap::{Parser, Subcommand}; use completions::{fig_generate, ArgShell}; +use config::OptionalSet; +use lazy_static::lazy_static; +use std::error::Error; pub(crate) mod init; pub(crate) mod run; pub(crate) mod upgrade; -fn long_version() -> String { - format!( - r#" -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -"#, - "Build Timestamp:", - env!("VERGEN_BUILD_TIMESTAMP"), - "Build Version:", - env!("VERGEN_BUILD_SEMVER"), - "Commit SHA:", - env!("VERGEN_GIT_SHA"), - "Commit Date:", - env!("VERGEN_GIT_COMMIT_TIMESTAMP"), - "Commit Branch:", - env!("VERGEN_GIT_BRANCH"), - "rustc Version:", - env!("VERGEN_RUSTC_SEMVER"), - "rustc Channel:", - env!("VERGEN_RUSTC_CHANNEL"), - "cargo Profile:", - env!("VERGEN_CARGO_PROFILE"), - ) +lazy_static! { + pub static ref PRETTY_BUILD_INFORMATION: String = + BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); } -fn long_version_static() -> &'static str { - Box::leak(long_version().into_boxed_str()) +// Helper for passing LONG_VERSION to clap +fn pretty_build_info_static() -> &'static str { + &PRETTY_BUILD_INFORMATION } #[derive(Parser)] -#[clap(author = "Nymtech", version, long_version = long_version_static(), about)] +#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] pub(crate) struct Cli { /// Path pointing to an env file that configures the client. #[clap(short, long)] @@ -75,70 +53,57 @@ pub(crate) enum Commands { // Configuration that can be overridden. pub(crate) struct OverrideConfig { - nymd_validators: Option, - api_validators: Option, + nym_apis: Option>, disable_socket: bool, port: Option, fastmode: bool, + no_cover: bool, + #[cfg(feature = "coconut")] + nyxd_urls: Option>, #[cfg(feature = "coconut")] enabled_credentials_mode: bool, } -pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> { +pub(crate) async fn execute(args: &Cli) -> Result<(), Box> { let bin_name = "nym-native-client"; match &args.command { - Commands::Init(m) => init::execute(m).await, + Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(m).await?, Commands::Upgrade(m) => upgrade::execute(m), - Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name), - Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name), + Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), + Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), } Ok(()) } pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config { - if let Some(raw_validators) = args.nymd_validators { - config - .get_base_mut() - .set_custom_validators(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::NYMD_VALIDATOR) - .expect("nymd validator not set"); - config - .get_base_mut() - .set_custom_validators(config::parse_validators(&raw_validators)); - } - if let Some(raw_validators) = args.api_validators { - config - .get_base_mut() - .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(config::parse_validators(&raw_validators)); - } - - if args.disable_socket { - config = config.with_socket(SocketType::None); - } - - if let Some(port) = args.port { - config = config.with_port(port); - } + config = config + .with_disabled_socket(args.disable_socket) + .with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode) + .with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover) + .with_optional(Config::with_port, args.port) + .with_optional_custom_env_ext( + BaseConfig::with_custom_nym_apis, + args.nym_apis, + network_defaults::var_names::NYM_API, + config::parse_urls, + ); #[cfg(feature = "coconut")] { - if args.enabled_credentials_mode { - config.get_base_mut().with_disabled_credentials(false) - } - } - - if args.fastmode { - config.get_base_mut().set_high_default_traffic_volume(); + config = config + .with_optional_custom_env_ext( + BaseConfig::with_custom_nyxd, + args.nyxd_urls, + network_defaults::var_names::NYXD, + config::parse_urls, + ) + .with_base( + BaseConfig::with_disabled_credentials, + !args.enabled_credentials_mode, + ); } config diff --git a/clients/native/src/commands/run.rs b/clients/native/src/commands/run.rs index df49471ea9..123bb827e4 100644 --- a/clients/native/src/commands/run.rs +++ b/clients/native/src/commands/run.rs @@ -1,6 +1,8 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use std::error::Error; + use crate::{ client::{config::Config, SocketClient}, commands::{override_config, OverrideConfig}, @@ -9,6 +11,7 @@ use crate::{ use clap::Args; use config::NymConfig; +use crypto::asymmetric::identity; use log::*; use version_checker::is_minor_version_compatible; @@ -18,18 +21,20 @@ pub(crate) struct Run { #[clap(long)] id: String, - /// Comma separated list of rest endpoints of the nymd validators - #[clap(long)] - nymd_validators: Option, + /// Comma separated list of rest endpoints of the nyxd validators + #[cfg(feature = "coconut")] + #[clap(long, alias = "nymd_validators", value_delimiter = ',')] + nyxd_urls: Option>, /// Comma separated list of rest endpoints of the API validators - #[clap(long)] - api_validators: Option, + #[clap(long, alias = "api_validators", value_delimiter = ',')] + // the alias here is included for backwards compatibility (1.1.4 and before) + nym_apis: Option>, /// Id of the gateway we want to connect to. If overridden, it is user's responsibility to /// ensure prior registration happened #[clap(long)] - gateway: Option, + gateway: Option, /// Whether to not start the websocket #[clap(long)] @@ -39,6 +44,15 @@ pub(crate) struct Run { #[clap(short, long)] port: Option, + /// Mostly debug-related option to increase default traffic rate so that you would not need to + /// modify config post init + #[clap(long, hide = true)] + fastmode: bool, + + /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) + #[clap(long, hide = true)] + no_cover: bool, + /// Set this client to work in a enabled credentials mode that would attempt to use gateway /// with bandwidth credential requirement. #[cfg(feature = "coconut")] @@ -49,11 +63,14 @@ pub(crate) struct Run { impl From for OverrideConfig { fn from(run_config: Run) -> Self { OverrideConfig { - nymd_validators: run_config.nymd_validators, - api_validators: run_config.api_validators, + nym_apis: run_config.nym_apis, disable_socket: run_config.disable_socket, port: run_config.port, - fastmode: false, + fastmode: run_config.fastmode, + no_cover: run_config.no_cover, + + #[cfg(feature = "coconut")] + nyxd_urls: run_config.nyxd_urls, #[cfg(feature = "coconut")] enabled_credentials_mode: run_config.enabled_credentials_mode, } @@ -79,23 +96,27 @@ fn version_check(cfg: &Config) -> bool { } } -pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> { +pub(crate) async fn execute(args: &Run) -> Result<(), Box> { let id = &args.id; let mut config = match Config::load_from_file(Some(id)) { Ok(cfg) => cfg, Err(err) => { - error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err); - return Err(ClientError::FailedToLoadConfig(id.to_string())); + error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id); + return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string()))); } }; let override_config_fields = OverrideConfig::from(args.clone()); config = override_config(config, override_config_fields); + if config.get_base_mut().set_empty_fields_to_defaults() { + warn!("some of the core config options were left unset. the default values are going to get used instead."); + } + if !version_check(&config) { error!("failed the local version check"); - return Err(ClientError::FailedLocalVersionCheck); + return Err(Box::new(ClientError::FailedLocalVersionCheck)); } SocketClient::new(config).run_socket_forever().await diff --git a/clients/native/src/commands/upgrade.rs b/clients/native/src/commands/upgrade.rs index c84073200c..6aaa732624 100644 --- a/clients/native/src/commands/upgrade.rs +++ b/clients/native/src/commands/upgrade.rs @@ -59,7 +59,7 @@ pub(crate) struct Upgrade { fn parse_config_version(config: &Config) -> Version { let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| { - eprintln!("failed to parse client version! - {:?}", err); + eprintln!("failed to parse client version! - {err}"); process::exit(1) }); @@ -110,7 +110,7 @@ fn minor_0_12_upgrade( .set_custom_version(to_version.to_string().as_ref()); config.save_to_file(None).unwrap_or_else(|err| { - eprintln!("failed to overwrite config file! - {:?}", err); + eprintln!("failed to overwrite config file! - {err}"); print_failed_upgrade(config_version, &to_version); process::exit(1); }); @@ -146,7 +146,7 @@ pub(crate) fn execute(args: &Upgrade) { let id = &args.id; let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| { - eprintln!("failed to load existing config file! - {:?}", err); + eprintln!("failed to load existing config file! - {err}"); process::exit(1) }); diff --git a/clients/native/src/error.rs b/clients/native/src/error.rs index 06c45116b6..700e6e9336 100644 --- a/clients/native/src/error.rs +++ b/clients/native/src/error.rs @@ -2,11 +2,15 @@ use client_core::error::ClientCoreError; #[derive(thiserror::Error, Debug)] pub enum ClientError { + #[error("I/O error: {0}")] + IoError(#[from] std::io::Error), + #[error("client-core error: {0}")] ClientCoreError(#[from] ClientCoreError), #[error("Failed to load config for: {0}")] FailedToLoadConfig(String), + #[error("Failed local version check, client and config mismatch")] FailedLocalVersionCheck, diff --git a/clients/native/src/main.rs b/clients/native/src/main.rs index 00529494e7..edb7c56f1a 100644 --- a/clients/native/src/main.rs +++ b/clients/native/src/main.rs @@ -1,8 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use std::error::Error; + use clap::{crate_version, Parser}; -use error::ClientError; use logging::setup_logging; use network_defaults::setup_env; @@ -12,12 +13,12 @@ pub mod error; pub mod websocket; #[tokio::main] -async fn main() -> Result<(), ClientError> { +async fn main() -> Result<(), Box> { setup_logging(); println!("{}", banner()); let args = commands::Cli::parse(); - setup_env(args.config_env_file.clone()); + setup_env(args.config_env_file.as_ref()); commands::execute(&args).await } diff --git a/clients/native/src/websocket/handler.rs b/clients/native/src/websocket/handler.rs index 6dc0d398ad..c929a907cc 100644 --- a/clients/native/src/websocket/handler.rs +++ b/clients/native/src/websocket/handler.rs @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use client_connections::{ - ConnectionCommand, ConnectionCommandSender, LaneQueueLengths, TransmissionLane, + ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane, }; +use client_core::client::replies::reply_controller::requests::ReplyControllerSender; use client_core::client::{ inbound_messages::{InputMessage, InputMessageSender}, received_buffer::{ @@ -14,9 +15,11 @@ use futures::channel::mpsc; use futures::{SinkExt, StreamExt}; use log::*; use nymsphinx::addressing::clients::Recipient; -use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use nymsphinx::receiver::ReconstructedMessage; +use std::time::Duration; use tokio::net::TcpStream; +use tokio::time::Instant; use tokio_tungstenite::{ accept_async, tungstenite::{protocol::Message as WsMessage, Error as WsError}, @@ -35,19 +38,36 @@ impl Default for ReceivedResponseType { } } -pub(crate) struct Handler { +pub(crate) struct HandlerBuilder { msg_input: InputMessageSender, client_connection_tx: ConnectionCommandSender, buffer_requester: ReceivedBufferRequestSender, self_full_address: Recipient, - socket: Option>, - received_response_type: ReceivedResponseType, lane_queue_lengths: LaneQueueLengths, + reply_controller_sender: ReplyControllerSender, } -// clone is used to use handler on a new connection, which initially is `None` -impl Clone for Handler { - fn clone(&self) -> Self { +impl HandlerBuilder { + pub(crate) fn new( + msg_input: InputMessageSender, + client_connection_tx: ConnectionCommandSender, + buffer_requester: ReceivedBufferRequestSender, + self_full_address: &Recipient, + lane_queue_lengths: LaneQueueLengths, + reply_controller_sender: ReplyControllerSender, + ) -> Self { + Self { + msg_input, + client_connection_tx, + buffer_requester, + self_full_address: *self_full_address, + lane_queue_lengths, + reply_controller_sender, + } + } + + // TODO: make sure we only ever have one active handler + pub fn create_active_handler(&self) -> Handler { Handler { msg_input: self.msg_input.clone(), client_connection_tx: self.client_connection_tx.clone(), @@ -56,106 +76,168 @@ impl Clone for Handler { socket: None, received_response_type: Default::default(), lane_queue_lengths: self.lane_queue_lengths.clone(), + reply_controller_sender: self.reply_controller_sender.clone(), } } } +pub(crate) struct Handler { + msg_input: InputMessageSender, + client_connection_tx: ConnectionCommandSender, + buffer_requester: ReceivedBufferRequestSender, + self_full_address: Recipient, + socket: Option>, + received_response_type: ReceivedResponseType, + lane_queue_lengths: LaneQueueLengths, + reply_controller_sender: ReplyControllerSender, +} + impl Drop for Handler { fn drop(&mut self) { - self.buffer_requester + if self + .buffer_requester .unbounded_send(ReceivedBufferMessage::ReceiverDisconnect) - .expect("the buffer request failed!") + .is_err() + { + error!("we failed to disconnect the receiver from the buffer! presumably the shutdown procedure has been initiated!") + } } } impl Handler { - pub(crate) fn new( - msg_input: InputMessageSender, - client_connection_tx: ConnectionCommandSender, - buffer_requester: ReceivedBufferRequestSender, - self_full_address: Recipient, - lane_queue_lengths: LaneQueueLengths, - ) -> Self { - Handler { - msg_input, - client_connection_tx, - buffer_requester, - self_full_address, - socket: None, - received_response_type: Default::default(), - lane_queue_lengths, + async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> Option { + let req_start = Instant::now(); + + // get the base queue length + // Note that this does _NOT_ take into account the packets that have been received but not + // yet reach `OutQueueControl`, so it might be a tad low. + let conn_lane = TransmissionLane::ConnectionId(connection_id); + let Ok(base_length) = self + .lane_queue_lengths + .lock() + .map(|guard| guard.get(&conn_lane).unwrap_or_default()) else { + // I'd argue we should panic here as this error it not recoverable + error!("The lane queue length lock is poisoned!!"); + return None + }; + + // get the number of pending replies waiting for reply surbs + let reply_queue_length = self + .reply_controller_sender + .get_lane_queue_length(connection_id) + .await; + + let queue_length = base_length + reply_queue_length; + + let time_taken = req_start.elapsed(); + let msg = + format!("it took {time_taken:?} to get lane length for connection {connection_id}. The length is: {queue_length} = {base_length} (already queued up) + {reply_queue_length} (waiting for reply SURBs)"); + + if time_taken > Duration::from_millis(1) { + info!("{msg}"); + } else if time_taken > Duration::from_millis(10) { + warn!("{msg}"); + } else if time_taken > Duration::from_millis(50) { + error!("{msg}"); } + + Some(ServerResponse::LaneQueueLength { + lane: connection_id, + queue_length, + }) } async fn handle_send( &mut self, - recipient: &Recipient, + recipient: Recipient, message: Vec, - with_reply_surb: bool, connection_id: Option, ) -> Option { + info!( + "Attempting to send {:.2} kiB message to {recipient} on connection_id {connection_id:?}", + message.len() as f64 / 1024.0 + ); + // We map the absence of a connection id as going into the general lane. let lane = connection_id.map_or(TransmissionLane::General, |id| { TransmissionLane::ConnectionId(id) }); // the ack control is now responsible for chunking, etc. - let input_msg = InputMessage::new_fresh(*recipient, message, with_reply_surb, lane); + let input_msg = InputMessage::new_regular(recipient, message, lane); self.msg_input .send(input_msg) .await .expect("InputMessageReceiver has stopped receiving!"); // Only reply back with a `LaneQueueLength` if the sender providided a connection id - let connection_id = match lane { - TransmissionLane::General - | TransmissionLane::Reply - | TransmissionLane::Retransmission - | TransmissionLane::Control => return None, - TransmissionLane::ConnectionId(id) => id, + let TransmissionLane::ConnectionId(connection_id) = lane else { + return None }; - // on receiving a send, we reply back the current lane queue length for that connection id. - // Note that this does _NOT_ take into account the packets that have been received but not - // yet reach `OutQueueControl`, so it might be a tad low. - let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() else { - log::warn!( - "Failed to get the lane queue length lock, \ - not responding back with the current queue length" - ); - return None; - }; - - let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0); - Some(ServerResponse::LaneQueueLength(connection_id, queue_length)) + self.get_lane_queue_length(connection_id).await } - async fn handle_reply( + async fn handle_send_anonymous( &mut self, - reply_surb: ReplySurb, + recipient: Recipient, message: Vec, + reply_surbs: u32, + connection_id: Option, ) -> Option { - if message.len() > ReplySurb::max_msg_len(Default::default()) { - return Some( - ServerResponse::new_error( - format!( - "too long message to put inside a reply SURB. Received: {} bytes and maximum is {} bytes", - message.len(), ReplySurb::max_msg_len(Default::default())) - ) - ); - } + info!( + "Attempting to anonymously send {:.2} kiB message to {recipient} on connection_id {connection_id:?} while attaching {reply_surbs} replySURBs.", + message.len() as f64 / 1024.0 + ); - let input_msg = InputMessage::new_reply(reply_surb, message); + // We map the absence of a connection id as going into the general lane. + let lane = connection_id.map_or(TransmissionLane::General, |id| { + TransmissionLane::ConnectionId(id) + }); + + let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane); self.msg_input .send(input_msg) .await .expect("InputMessageReceiver has stopped receiving!"); - None + // Only reply back with a `LaneQueueLength` if the sender providided a connection id + let TransmissionLane::ConnectionId(connection_id) = lane else { + return None + }; + + self.get_lane_queue_length(connection_id).await + } + + async fn handle_reply( + &mut self, + recipient_tag: AnonymousSenderTag, + message: Vec, + connection_id: Option, + ) -> Option { + info!("Attempting to send {:.2} kiB reply message to {recipient_tag} on connection_id {connection_id:?}", message.len() as f64 / 1024.0); + + // We map the absence of a connection id as going into the general lane. + let lane = connection_id.map_or(TransmissionLane::General, |id| { + TransmissionLane::ConnectionId(id) + }); + + let input_msg = InputMessage::new_reply(recipient_tag, message, lane); + self.msg_input + .send(input_msg) + .await + .expect("InputMessageReceiver has stopped receiving!"); + + // Only reply back with a `LaneQueueLength` if the sender providided a connection id + let TransmissionLane::ConnectionId(connection_id) = lane else { + return None + }; + + self.get_lane_queue_length(connection_id).await } fn handle_self_address(&self) -> ServerResponse { - ServerResponse::SelfAddress(self.self_full_address) + ServerResponse::SelfAddress(Box::new(self.self_full_address)) } fn handle_closed_connection(&self, connection_id: u64) -> Option { @@ -165,17 +247,8 @@ impl Handler { None } - fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option { - let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() else { - log::warn!( - "Failed to get the lane queue length lock, not responding back with the current queue length" - ); - return None; - }; - - let lane = TransmissionLane::ConnectionId(connection_id); - let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0); - Some(ServerResponse::LaneQueueLength(connection_id, queue_length)) + async fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option { + self.get_lane_queue_length(connection_id).await } async fn handle_request(&mut self, request: ClientRequest) -> Option { @@ -183,19 +256,28 @@ impl Handler { ClientRequest::Send { recipient, message, - with_reply_surb, + connection_id, + } => self.handle_send(recipient, message, connection_id).await, + + ClientRequest::SendAnonymous { + recipient, + message, + reply_surbs, connection_id, } => { - self.handle_send(&recipient, message, with_reply_surb, connection_id) + self.handle_send_anonymous(recipient, message, reply_surbs, connection_id) .await } + ClientRequest::Reply { message, - reply_surb, - } => self.handle_reply(reply_surb, message).await, + sender_tag, + connection_id, + } => self.handle_reply(sender_tag, message, connection_id).await, + ClientRequest::SelfAddress => Some(self.handle_self_address()), ClientRequest::ClosedConnection(id) => self.handle_closed_connection(id), - ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id), + ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id).await, } } @@ -276,8 +358,12 @@ impl Handler { } } - async fn listen_for_requests(&mut self, mut msg_receiver: ReconstructedMessagesReceiver) { - loop { + async fn listen_for_requests( + &mut self, + mut msg_receiver: ReconstructedMessagesReceiver, + mut task_client: task::TaskClient, + ) { + while !task_client.is_shutdown() { tokio::select! { // we can either get a client request from the websocket socket_msg = self.next_websocket_request() => { @@ -287,7 +373,7 @@ impl Handler { let socket_msg = match socket_msg.unwrap() { Ok(socket_msg) => socket_msg, Err(err) => { - warn!("failed to obtain message from websocket stream! stopping connection handler: {}", err); + warn!("failed to obtain message from websocket stream! stopping connection handler: {err}"); break; } }; @@ -299,8 +385,7 @@ impl Handler { if let Some(response) = self.handle_ws_request(socket_msg).await { if let Err(err) = self.send_websocket_response(response).await { warn!( - "Failed to send message over websocket: {}. Assuming the connection is dead.", - err + "Failed to send message over websocket: {err}. Assuming the connection is dead.", ); break; } @@ -308,24 +393,37 @@ impl Handler { } // or a reconstructed mix message that we need to push back to the client mix_messages = msg_receiver.next() => { - let mix_messages = mix_messages.expect( - "mix messages sender was unexpectedly closed! this shouldn't have ever happened!", - ); - if let Err(e) = self.push_websocket_received_plaintexts(mix_messages).await { - warn!("failed to send sphinx packets back to the client - {:?}, assuming the connection is dead", e); + let Some(mix_messages) = mix_messages else { + error!("mix messages sender was unexpectedly closed! this shouldn't have ever happened! (unless we're shutting down - TODO: implement proper graceful shutdown handler)"); + return + }; + if let Err(err) = self.push_websocket_received_plaintexts(mix_messages).await { + warn!("failed to send sphinx packets back to the client - {err}, assuming the connection is dead"); break; } } + _ = task_client.recv() => { + log::trace!("Websocket handler: Received shutdown"); + } } } + log::debug!("Websocket handler: Exiting"); } // consume self to make sure `drop` is called after this is done - pub(crate) async fn handle_connection(mut self, socket: TcpStream) { + pub(crate) async fn handle_connection( + mut self, + socket: TcpStream, + mut task_client: task::TaskClient, + ) { + // We don't want a crash in the connection handler to trigger a shutdown of the whole + // process. + task_client.mark_as_success(); + let ws_stream = match accept_async(socket).await { Ok(ws_stream) => ws_stream, Err(err) => { - warn!("error while performing the websocket handshake - {:?}", err); + warn!("error while performing the websocket handshake - {err}"); return; } }; @@ -340,7 +438,8 @@ impl Handler { )) .expect("the buffer request failed!"); - self.listen_for_requests(reconstructed_receiver).await; + self.listen_for_requests(reconstructed_receiver, task_client) + .await; } } diff --git a/clients/native/src/websocket/listener.rs b/clients/native/src/websocket/listener.rs index bdaa27d4fc..459eb1bc17 100644 --- a/clients/native/src/websocket/listener.rs +++ b/clients/native/src/websocket/listener.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::handler::Handler; +use super::handler::HandlerBuilder; use log::*; use std::{net::SocketAddr, process, sync::Arc}; use tokio::io::AsyncWriteExt; @@ -32,11 +32,11 @@ impl Listener { } } - pub(crate) async fn run(&mut self, handler: Handler) { + pub(crate) async fn run(&mut self, handler: HandlerBuilder, mut task_client: task::TaskClient) { let tcp_listener = match tokio::net::TcpListener::bind(self.address).await { Ok(listener) => listener, Err(err) => { - error!("Failed to bind to {} - {}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", self.address, err); + error!("Failed to bind to {} - {err}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", self.address); process::exit(1); } }; @@ -45,18 +45,30 @@ impl Listener { loop { tokio::select! { + // When the handler finishes we check if shutdown is signalled _ = notify.notified() => { + if task_client.is_shutdown() { + log::trace!("Websocket listener: detected shutdown after connection closed"); + break; + } // our connection terminated - we are open to a new one now! self.state = State::AwaitingConnection; } + // ... but when there is no connected client at the time of shutdown being + // signalled, we handle it here. + _ = task_client.recv() => { + if !self.state.is_connected() { + log::trace!("Not connected: shutting down"); + break; + } + } new_conn = tcp_listener.accept() => { match new_conn { Ok((mut socket, remote_addr)) => { debug!("Received connection from {:?}", remote_addr); if self.state.is_connected() { - warn!("tried to duplicate!"); + warn!("Tried to open a duplicate websocket connection. The request came from {}", remote_addr); // if we've already got a connection, don't allow another one - debug!("but there was already a connection present!"); // while we only ever want to accept a single connection, we don't want // to leave clients hanging (and also allow for reconnection if it somehow // was dropped) @@ -64,31 +76,37 @@ impl Listener { Ok(_) => trace!( "closed the connection between attempting websocket handshake" ), - Err(e) => warn!("failed to cleanly close the connection - {:?}", e), + Err(err) => warn!("failed to cleanly close the connection - {err}"), }; } else { // even though we're spawning a new task with the handler here, we will only ever spawn a single one. // it's done so that any new connections to this listener could be rejected rather than left // hanging because the executor doesn't come back here let notify_clone = Arc::clone(¬ify); - let fresh_handler = handler.clone(); + let fresh_handler = handler.create_active_handler(); + let task_client_handler = task_client.clone(); tokio::spawn(async move { - fresh_handler.handle_connection(socket).await; + fresh_handler.handle_connection(socket, task_client_handler).await; notify_clone.notify_one(); }); self.state = State::Connected; } } - Err(e) => warn!("failed to get client: {:?}", e), + Err(err) => warn!("failed to get client: {err}"), } } } } + log::debug!("Websocket listener: Exiting"); } - pub(crate) fn start(mut self, handler: Handler) -> JoinHandle<()> { + pub(crate) fn start( + mut self, + handler: HandlerBuilder, + shutdown: task::TaskClient, + ) -> JoinHandle<()> { info!("Running websocket on {:?}", self.address.to_string()); - tokio::spawn(async move { self.run(handler).await }) + tokio::spawn(async move { self.run(handler, shutdown).await }) } } diff --git a/clients/native/src/websocket/mod.rs b/clients/native/src/websocket/mod.rs index bd17b6ee08..38dadbd005 100644 --- a/clients/native/src/websocket/mod.rs +++ b/clients/native/src/websocket/mod.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub(crate) use handler::Handler; +pub(crate) use handler::HandlerBuilder; pub(crate) use listener::Listener; pub(crate) mod handler; diff --git a/clients/native/websocket-requests/src/error.rs b/clients/native/websocket-requests/src/error.rs index f9fdd029cf..2d9035c19d 100644 --- a/clients/native/websocket-requests/src/error.rs +++ b/clients/native/websocket-requests/src/error.rs @@ -24,8 +24,11 @@ impl fmt::Debug for Error { } impl Error { - pub fn new(kind: ErrorKind, message: String) -> Self { - Error { kind, message } + pub fn new>(kind: ErrorKind, message: S) -> Self { + Error { + kind, + message: message.into(), + } } } @@ -62,6 +65,31 @@ pub enum ErrorKind { Other = 0xFF, } +impl TryFrom for ErrorKind { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (ErrorKind::EmptyRequest as u8) => Ok(ErrorKind::EmptyRequest), + _ if value == (ErrorKind::TooShortRequest as u8) => Ok(ErrorKind::TooShortRequest), + _ if value == (ErrorKind::UnknownRequest as u8) => Ok(ErrorKind::UnknownRequest), + _ if value == (ErrorKind::MalformedRequest as u8) => Ok(ErrorKind::MalformedRequest), + + _ if value == (ErrorKind::EmptyResponse as u8) => Ok(ErrorKind::EmptyResponse), + _ if value == (ErrorKind::TooShortResponse as u8) => Ok(ErrorKind::TooShortResponse), + _ if value == (ErrorKind::UnknownResponse as u8) => Ok(ErrorKind::UnknownResponse), + _ if value == (ErrorKind::MalformedResponse as u8) => Ok(ErrorKind::MalformedResponse), + + _ if value == (ErrorKind::Other as u8) => Ok(ErrorKind::Other), + + n => Err(Error::new( + ErrorKind::MalformedResponse, + format!("invalid error code {}", n), + )), + } + } +} + impl ErrorKind { pub(crate) fn as_str(&self) -> &'static str { match *self { diff --git a/clients/native/websocket-requests/src/requests.rs b/clients/native/websocket-requests/src/requests.rs index 315399ae3d..637db0daba 100644 --- a/clients/native/websocket-requests/src/requests.rs +++ b/clients/native/websocket-requests/src/requests.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 // all variable size data is always prefixed with u64 length @@ -7,69 +7,115 @@ use crate::error::{self, ErrorKind}; use crate::text::ClientRequestText; use nymsphinx::addressing::clients::Recipient; -use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE}; use std::convert::{TryFrom, TryInto}; use std::mem::size_of; -/// Value tag representing [`Send`] variant of the [`ClientRequest`] -pub const SEND_REQUEST_TAG: u8 = 0x00; +#[repr(u8)] +enum ClientRequestTag { + /// Value tag representing [`Send`] variant of the [`ClientRequest`] + Send = 0x00, -/// Value tag representing [`Reply`] variant of the [`ClientRequest`] -pub const REPLY_REQUEST_TAG: u8 = 0x01; + /// Value tag representing [`SendAnonymous`] variant of the [`ClientRequest`] + SendAnonymous = 0x01, -/// Value tag representing [`SelfAddress`] variant of the [`ClientRequest`] -pub const SELF_ADDRESS_REQUEST_TAG: u8 = 0x02; + /// Value tag representing [`Reply`] variant of the [`ClientRequest`] + Reply = 0x02, -/// Value tag representing [`ClosedConnection`] variant of the [`ClientRequest`] -pub const CLOSED_CONNECTION_REQUEST_TAG: u8 = 0x03; + /// Value tag representing [`SelfAddress`] variant of the [`ClientRequest`] + SelfAddress = 0x03, -/// Value tag representing [`GetLaneQueueLength`] variant of the [`ClientRequest`] -pub const GET_LANE_QUEUE_LENGHT_TAG: u8 = 0x04; + /// Value tag representing [`ClosedConnection`] variant of the [`ClientRequest`] + ClosedConnection = 0x04, + + /// Value tag representing [`GetLaneQueueLength`] variant of the [`ClientRequest`] + GetLaneQueueLength = 0x05, +} + +impl TryFrom for ClientRequestTag { + type Error = error::Error; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (Self::Send as u8) => Ok(Self::Send), + _ if value == (Self::SendAnonymous as u8) => Ok(Self::SendAnonymous), + _ if value == (Self::Reply as u8) => Ok(Self::Reply), + _ if value == (Self::SelfAddress as u8) => Ok(Self::SelfAddress), + _ if value == (Self::ClosedConnection as u8) => Ok(Self::ClosedConnection), + _ if value == (Self::GetLaneQueueLength as u8) => Ok(Self::GetLaneQueueLength), + n => Err(error::Error::new( + ErrorKind::UnknownRequest, + format!("{n} does not correspond to any valid request tag"), + )), + } + } +} #[allow(non_snake_case)] #[derive(Debug)] pub enum ClientRequest { + /// The simplest message variant where no additional information is attached. + /// You're simply sending your `data` to specified `recipient` without any tagging. + /// + /// Ends up with `NymMessage::Plain` variant Send { recipient: Recipient, message: Vec, - // Perhaps we could change it to a number to indicate how many reply_SURBs we want to include? - with_reply_surb: bool, connection_id: Option, }, - Reply { + + /// Create a message used for a duplex anonymous communication where the recipient + /// will never learn of our true identity. This is achieved by carefully sending `reply_surbs`. + /// + /// Note that if reply_surbs is set to zero then + /// this variant requires the client having sent some reply_surbs in the past + /// (and thus the recipient also knowing our sender tag). + /// + /// Ends up with `NymMessage::Repliable` variant + SendAnonymous { + recipient: Recipient, message: Vec, - reply_surb: ReplySurb, + reply_surbs: u32, + connection_id: Option, }, + + /// Attempt to use our internally received and stored `ReplySurb` to send the message back + /// to specified recipient whilst not knowing its full identity (or even gateway). + /// + /// Ends up with `NymMessage::Reply` variant + Reply { + sender_tag: AnonymousSenderTag, + message: Vec, + connection_id: Option, + }, + SelfAddress, + ClosedConnection(u64), + GetLaneQueueLength(u64), } // we could have been parsing it directly TryFrom, but we want to retain // information about whether it came from binary or text to send appropriate response back impl ClientRequest { - // SEND_REQUEST_TAG || with_surb || recipient || conn_id || data_len || data - fn serialize_send( - recipient: Recipient, - data: Vec, - with_reply_surb: bool, - connection_id: Option, - ) -> Vec { + // SEND_REQUEST_TAG || recipient || conn_id || data_len || data + fn serialize_send(recipient: Recipient, data: Vec, connection_id: Option) -> Vec { let data_len_bytes = (data.len() as u64).to_be_bytes(); let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes(); - std::iter::once(SEND_REQUEST_TAG) - .chain(std::iter::once(with_reply_surb as u8)) - .chain(recipient.to_bytes().iter().cloned()) // will not be length prefixed because the length is constant - .chain(conn_id_bytes.iter().cloned()) - .chain(data_len_bytes.iter().cloned()) + + std::iter::once(ClientRequestTag::Send as u8) + .chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant + .chain(conn_id_bytes.into_iter()) + .chain(data_len_bytes.into_iter()) .chain(data.into_iter()) .collect() } - // SEND_REQUEST_TAG || with_reply || recipient || conn_id || data_len || data + // SEND_REQUEST_TAG || recipient || conn_id || data_len || data fn deserialize_send(b: &[u8]) -> Result { - // we need to have at least 1 (tag) + 1 (reply flag) + Recipient::LEN + 2*sizeof bytes - if b.len() < 2 + Recipient::LEN + 2 * size_of::() { + // we need to have at least 1 (tag) + Recipient::LEN + 2*sizeof bytes + if b.len() < 1 + Recipient::LEN + 2 * size_of::() { return Err(error::Error::new( ErrorKind::TooShortRequest, "not enough data provided to recover 'send'".to_string(), @@ -77,34 +123,23 @@ impl ClientRequest { } // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], SEND_REQUEST_TAG); - - let with_reply_surb = match b[1] { - 0 => false, - 1 => true, - n => { - return Err(error::Error::new( - ErrorKind::MalformedRequest, - format!("invalid reply surb flag {}", n), - )) - } - }; + debug_assert_eq!(b[0], ClientRequestTag::Send as u8); let mut recipient_bytes = [0u8; Recipient::LEN]; - recipient_bytes.copy_from_slice(&b[2..2 + Recipient::LEN]); + recipient_bytes.copy_from_slice(&b[1..1 + Recipient::LEN]); let recipient = match Recipient::try_from_bytes(recipient_bytes) { Ok(recipient) => recipient, Err(err) => { return Err(error::Error::new( ErrorKind::MalformedRequest, - format!("malformed recipient: {:?}", err), + format!("malformed recipient: {err}"), )) } }; let mut connection_id_bytes = [0u8; size_of::()]; connection_id_bytes - .copy_from_slice(&b[2 + Recipient::LEN..2 + Recipient::LEN + size_of::()]); + .copy_from_slice(&b[1 + Recipient::LEN..1 + Recipient::LEN + size_of::()]); let connection_id = u64::from_be_bytes(connection_id_bytes); let connection_id = if connection_id == 0 { None @@ -113,9 +148,9 @@ impl ClientRequest { }; let data_len_bytes = - &b[2 + Recipient::LEN + size_of::()..2 + Recipient::LEN + 2 * size_of::()]; + &b[1 + Recipient::LEN + size_of::()..1 + Recipient::LEN + 2 * size_of::()]; let data_len = u64::from_be_bytes(data_len_bytes.try_into().unwrap()); - let data = &b[2 + Recipient::LEN + 2 * size_of::()..]; + let data = &b[1 + Recipient::LEN + 2 * size_of::()..]; if data.len() as u64 != data_len { return Err(error::Error::new( ErrorKind::MalformedRequest, @@ -128,33 +163,111 @@ impl ClientRequest { } Ok(ClientRequest::Send { - with_reply_surb, recipient, message: data.to_vec(), connection_id, }) } - // REPLY_REQUEST_TAG || surb_len || surb || message_len || message - fn serialize_reply(message: Vec, reply_surb: &ReplySurb) -> Vec { - let reply_surb_bytes = reply_surb.to_bytes(); - let surb_len_bytes = (reply_surb_bytes.len() as u64).to_be_bytes(); - let message_len_bytes = (message.len() as u64).to_be_bytes(); + // SEND_ANONYMOUS_REQUEST_TAG || reply_surbs || recipient || conn_id || data_len || data + fn serialize_send_anonymous( + recipient: Recipient, + data: Vec, + reply_surbs: u32, + connection_id: Option, + ) -> Vec { + let data_len_bytes = (data.len() as u64).to_be_bytes(); + let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes(); - std::iter::once(REPLY_REQUEST_TAG) - .chain(surb_len_bytes.iter().cloned()) - .chain(reply_surb_bytes.into_iter()) - .chain(message_len_bytes.iter().cloned()) + std::iter::once(ClientRequestTag::SendAnonymous as u8) + .chain(reply_surbs.to_be_bytes().into_iter()) + .chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant + .chain(conn_id_bytes.into_iter()) + .chain(data_len_bytes.into_iter()) + .chain(data.into_iter()) + .collect() + } + + // SEND_ANONYMOUS_REQUEST_TAG || reply_surbs || recipient || data_len || data + fn deserialize_send_anonymous(b: &[u8]) -> Result { + // we need to have at least 1 (tag) + sizeof (num surbs) + Recipient::LEN + 2 *sizeof bytes + if b.len() < 1 + size_of::() + Recipient::LEN + 2 * size_of::() { + return Err(error::Error::new( + ErrorKind::TooShortRequest, + "not enough data provided to recover 'send_anonymous'".to_string(), + )); + } + + // this MUST match because it was called by 'deserialize' + debug_assert_eq!(b[0], ClientRequestTag::SendAnonymous as u8); + + let reply_surbs = u32::from_be_bytes([b[1], b[2], b[3], b[4]]); + + let mut recipient_bytes = [0u8; Recipient::LEN]; + recipient_bytes.copy_from_slice(&b[5..5 + Recipient::LEN]); + let recipient = match Recipient::try_from_bytes(recipient_bytes) { + Ok(recipient) => recipient, + Err(err) => { + return Err(error::Error::new( + ErrorKind::MalformedRequest, + format!("malformed recipient: {err}"), + )) + } + }; + + let mut connection_id_bytes = [0u8; size_of::()]; + connection_id_bytes + .copy_from_slice(&b[5 + Recipient::LEN..5 + Recipient::LEN + size_of::()]); + let connection_id = u64::from_be_bytes(connection_id_bytes); + let connection_id = if connection_id == 0 { + None + } else { + Some(connection_id) + }; + + let data_len_bytes = + &b[5 + Recipient::LEN + size_of::()..5 + Recipient::LEN + 2 * size_of::()]; + let data_len = u64::from_be_bytes(data_len_bytes.try_into().unwrap()); + let data = &b[5 + Recipient::LEN + 2 * size_of::()..]; + if data.len() as u64 != data_len { + return Err(error::Error::new( + ErrorKind::MalformedRequest, + format!( + "data len has inconsistent length. specified: {} got: {}", + data_len, + data.len() + ), + )); + } + + Ok(ClientRequest::SendAnonymous { + reply_surbs, + recipient, + message: data.to_vec(), + connection_id, + }) + } + + // REPLY_REQUEST_TAG || SENDER_TAG || conn_id || message_len || message + fn serialize_reply( + message: Vec, + sender_tag: AnonymousSenderTag, + connection_id: Option, + ) -> Vec { + let message_len_bytes = (message.len() as u64).to_be_bytes(); + let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes(); + + std::iter::once(ClientRequestTag::Reply as u8) + .chain(sender_tag.to_bytes().into_iter()) + .chain(conn_id_bytes.into_iter()) + .chain(message_len_bytes.into_iter()) .chain(message.into_iter()) .collect() } - // REPLY_REQUEST_TAG || surb_len || surb || message_len || message + // REPLY_REQUEST_TAG || SENDER_TAG || conn_id || message_len || message fn deserialize_reply(b: &[u8]) -> Result { - // we need to have at the very least 2 * sizeof bytes (in case, for some peculiar reason - // message and reply surb were 0 len - the request would still be malformed, but would in theory - // be parse'able) - if b.len() < 1 + 2 * size_of::() { + if b.len() < 1 + SENDER_TAG_SIZE + 2 * size_of::() { return Err(error::Error::new( ErrorKind::TooShortRequest, "not enough data provided to recover 'reply'".to_string(), @@ -162,42 +275,28 @@ impl ClientRequest { } // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], REPLY_REQUEST_TAG); + debug_assert_eq!(b[0], ClientRequestTag::Reply as u8); - let reply_surb_len = - u64::from_be_bytes(b[1..1 + size_of::()].as_ref().try_into().unwrap()); + // the unwrap here is fine as we're definitely using exactly SENDER_TAG_SIZE bytes + let sender_tag = + AnonymousSenderTag::from_bytes(b[1..1 + SENDER_TAG_SIZE].try_into().unwrap()); - // make sure we won't go out of bounds here - if reply_surb_len > (b.len() - 1 + 2 * size_of::()) as u64 { - return Err(error::Error::new( - ErrorKind::MalformedRequest, - format!( - "not enough data to recover reply surb with specified length {}", - reply_surb_len - ), - )); - } - - let surb_bound = 1 + size_of::() + reply_surb_len as usize; - - let reply_surb_bytes = &b[1 + size_of::()..surb_bound]; - let reply_surb = match ReplySurb::from_bytes(reply_surb_bytes) { - Ok(reply_surb) => reply_surb, - Err(err) => { - return Err(error::Error::new( - ErrorKind::MalformedRequest, - format!("malformed reply surb: {:?}", err), - )) - } + let mut connection_id_bytes = [0u8; size_of::()]; + connection_id_bytes + .copy_from_slice(&b[1 + SENDER_TAG_SIZE..1 + SENDER_TAG_SIZE + size_of::()]); + let connection_id = u64::from_be_bytes(connection_id_bytes); + let connection_id = if connection_id == 0 { + None + } else { + Some(connection_id) }; let message_len = u64::from_be_bytes( - b[surb_bound..surb_bound + size_of::()] - .as_ref() + b[1 + SENDER_TAG_SIZE + size_of::()..1 + SENDER_TAG_SIZE + 2 * size_of::()] .try_into() .unwrap(), ); - let message = &b[surb_bound + size_of::()..]; + let message = &b[1 + SENDER_TAG_SIZE + 2 * size_of::()..]; if message.len() as u64 != message_len { return Err(error::Error::new( ErrorKind::MalformedRequest, @@ -208,33 +307,32 @@ impl ClientRequest { ), )); } - // TODO: should this blow HERE, i.e. during deserialization that the data you're trying - // to send via reply is too long? Ok(ClientRequest::Reply { - reply_surb, message: message.to_vec(), + sender_tag, + connection_id, }) } // SELF_ADDRESS_REQUEST_TAG fn serialize_self_address() -> Vec { - std::iter::once(SELF_ADDRESS_REQUEST_TAG).collect() + vec![ClientRequestTag::SelfAddress as u8] } // SELF_ADDRESS_REQUEST_TAG - fn deserialize_self_address(b: &[u8]) -> Self { + fn deserialize_self_address(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], SELF_ADDRESS_REQUEST_TAG); + debug_assert_eq!(b[0], ClientRequestTag::SelfAddress as u8); - ClientRequest::SelfAddress + Ok(ClientRequest::SelfAddress) } // CLOSED_CONNECTION_REQUEST_TAG fn serialize_closed_connection(connection_id: u64) -> Vec { let conn_id_bytes = connection_id.to_be_bytes(); - std::iter::once(CLOSED_CONNECTION_REQUEST_TAG) - .chain(conn_id_bytes.iter().copied()) + std::iter::once(ClientRequestTag::ClosedConnection as u8) + .chain(conn_id_bytes.into_iter()) .collect() } @@ -243,12 +341,12 @@ impl ClientRequest { if b.len() != 1 + size_of::() { return Err(error::Error::new( ErrorKind::MalformedRequest, - "the received closed connection has invalid length".to_string(), + "The received closed connection has invalid length", )); } // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], CLOSED_CONNECTION_REQUEST_TAG); + debug_assert_eq!(b[0], ClientRequestTag::ClosedConnection as u8); let mut connection_id_bytes = [0u8; size_of::()]; connection_id_bytes.copy_from_slice(&b[1..=size_of::()]); @@ -260,8 +358,8 @@ impl ClientRequest { // GET_LANE_QUEUE_LENGHT_TAG fn serialize_get_lane_queue_lengths(connection_id: u64) -> Vec { let conn_id_bytes = connection_id.to_be_bytes(); - std::iter::once(GET_LANE_QUEUE_LENGHT_TAG) - .chain(conn_id_bytes.iter().copied()) + std::iter::once(ClientRequestTag::GetLaneQueueLength as u8) + .chain(conn_id_bytes.into_iter()) .collect() } @@ -270,12 +368,12 @@ impl ClientRequest { if b.len() != 1 + size_of::() { return Err(error::Error::new( ErrorKind::MalformedRequest, - "the received get lane queue length has invalid length".to_string(), + "The received get lane queue lengths has invalid length", )); } // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], GET_LANE_QUEUE_LENGHT_TAG); + debug_assert_eq!(b[0], ClientRequestTag::GetLaneQueueLength as u8); let mut connection_id_bytes = [0u8; size_of::()]; connection_id_bytes.copy_from_slice(&b[1..=size_of::()]); @@ -289,14 +387,21 @@ impl ClientRequest { ClientRequest::Send { recipient, message, - with_reply_surb, connection_id, - } => Self::serialize_send(recipient, message, with_reply_surb, connection_id), + } => Self::serialize_send(recipient, message, connection_id), + + ClientRequest::SendAnonymous { + recipient, + message, + reply_surbs, + connection_id, + } => Self::serialize_send_anonymous(recipient, message, reply_surbs, connection_id), ClientRequest::Reply { message, - reply_surb, - } => Self::serialize_reply(message, &reply_surb), + sender_tag, + connection_id, + } => Self::serialize_reply(message, sender_tag, connection_id), ClientRequest::SelfAddress => Self::serialize_self_address(), @@ -316,28 +421,16 @@ impl ClientRequest { )); } - if b.len() < size_of::() { - return Err(error::Error::new( - ErrorKind::TooShortRequest, - format!( - "not enough data provided to recover request tag. Provided only {} bytes", - b.len() - ), - )); - } - let request_tag = b[0]; + let request_tag = ClientRequestTag::try_from(b[0])?; // determine what kind of request that is and try to deserialize it match request_tag { - SEND_REQUEST_TAG => Self::deserialize_send(b), - REPLY_REQUEST_TAG => Self::deserialize_reply(b), - SELF_ADDRESS_REQUEST_TAG => Ok(Self::deserialize_self_address(b)), - CLOSED_CONNECTION_REQUEST_TAG => Self::deserialize_closed_connection(b), - GET_LANE_QUEUE_LENGHT_TAG => Self::deserialize_get_lane_queue_length(b), - n => Err(error::Error::new( - ErrorKind::UnknownRequest, - format!("type {n}"), - )), + ClientRequestTag::Send => Self::deserialize_send(b), + ClientRequestTag::SendAnonymous => Self::deserialize_send_anonymous(b), + ClientRequestTag::Reply => Self::deserialize_reply(b), + ClientRequestTag::SelfAddress => Self::deserialize_self_address(b), + ClientRequestTag::ClosedConnection => Self::deserialize_closed_connection(b), + ClientRequestTag::GetLaneQueueLength => Self::deserialize_get_lane_queue_length(b), } } @@ -365,50 +458,52 @@ mod tests { let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); let recipient_string = recipient.to_string(); - let send_request_no_surb = ClientRequest::Send { + let send_request = ClientRequest::Send { recipient, message: b"foomp".to_vec(), - with_reply_surb: false, connection_id: Some(42), }; - let bytes = send_request_no_surb.serialize(); + let bytes = send_request.serialize(); let recovered = ClientRequest::deserialize(&bytes).unwrap(); match recovered { ClientRequest::Send { recipient, message, - with_reply_surb, connection_id, } => { assert_eq!(recipient.to_string(), recipient_string); assert_eq!(message, b"foomp".to_vec()); - assert!(!with_reply_surb); assert_eq!(connection_id, Some(42)) } _ => unreachable!(), } + } - let send_request_surb = ClientRequest::Send { - recipient, + #[test] + fn send_anonymous_request_serialization_works() { + let original_recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); + + let send_anonymous_request = ClientRequest::SendAnonymous { + recipient: original_recipient, message: b"foomp".to_vec(), - with_reply_surb: true, - connection_id: None, + reply_surbs: 666, + connection_id: Some(42), }; - let bytes = send_request_surb.serialize(); + let bytes = send_anonymous_request.serialize(); let recovered = ClientRequest::deserialize(&bytes).unwrap(); match recovered { - ClientRequest::Send { + ClientRequest::SendAnonymous { recipient, message, - with_reply_surb, + reply_surbs, connection_id, } => { - assert_eq!(recipient.to_string(), recipient_string); + assert_eq!(recipient, original_recipient); assert_eq!(message, b"foomp".to_vec()); - assert!(with_reply_surb); - assert_eq!(connection_id, None) + assert_eq!(connection_id, Some(42)); + assert_eq!(reply_surbs, 666) } _ => unreachable!(), } @@ -416,22 +511,23 @@ mod tests { #[test] fn reply_request_serialization_works() { - let reply_surb_string = "CjfVbHbfAjbC3W1BvNHGXmM8KNAnDNYGaHMLqVDxRYeo352csAihstup9bvqXam4dTWgfHak6KYwL9STaxWJ47E8XFZbSEvs7hEsfCkxr6K9WJuSBPK84GDDEvad8ZAuMCoaXsAd5S2Lj9a5eYyzG4SL1jHzhSMni55LyJwumxo1ZTGZNXggxw1RREosvyzNrW9Rsi3owyPqLCwXpiei2tHZty8w8midVvg8vDa7ZEJD842CLv8D4ohynSG7gDpqTrhkRaqYAuz7dzqNbMXLJRM7v823Jn16fA1L7YQxmcaUdUigyRSgTdb4i9ebiLGSyJ1iDe6Acz613PQZh6Ua3bZ2zVKq3dSycpDm9ngarRK4zJrAaUxRkdih8YzW3BY4nL9eqkfKA4N1TWCLaRU7zpSaf8yMEwrAZReU3d5zLV8c5KBfa2w8R5anhQeBojduZEGEad8kkHuKU52Zg93FeWHvH1qgZaEJMHH4nN7gKXz9mvWDhYwyF4vt3Uy2NhCHC3N5pL1gMme27YcoPcTEia1fxKZtnt6rtEozzTrAgCJGswigkFbkafiV5QaJwLKTUxtzhkZ57eEuLPte9UvJHzhhXUQ2CV7R2BUkJjYZy3Zsx6YYvdYWiAFFkWUwNEGA4QpShUHciBfsQVHQ7pN41YcyYUhbywQDFnTVgEmdUZ1XCBi3gyK5U3tDQmFzP1u9m3mWrUA8qB9mRDE7ptNDm5c3c1458L6uXLUth7sdMaa1Was5LCmCdmNDtvNpCDAEt1in6q6mrZFR85aCSU9b1baNGwZoCqPpPvydkVe63gXWoi8ebvdyxARrqACFrSB3ZdY3uJBw8CTMNkKK6MvcefMkSVVsbLd36TQAtYSCqrpiMc5dQuKcEu5QfciwvWYXYx8WFNAgKwP2mv49KCTvfozNDUCbjzDwSx92Zv5zjG8HbFpB13bY9UZGeyTPvv7gGxCzjGjJGbW6FRAheRQaaje5fUgCNM95Tv7wBmAMRHHFgWafeK1sdFH7dtCX9u898HucGTaboSKLsVh8J78gbbkHErwjMh7y9YRkceq5TTYS5da4kHnyNKYWSbxgZrmFg44XGKoeYcqoHB3XTZrdsf7F5fFeNwnihkmADvhAcaxXUmVqq4rQFZH84a1iC3WBWXYcqiZH2L7ujGWV7mMDT4HBEerDYjc8rNY4xGTPfivCrBCJW1i14aqW8xRdsdgTM88eTksvC3WPJLJ7iMzfKXeL7fMW1Ek6QGyQtLBW98vEESpdcDg6DeZ5rMz6VqjTGGqcCaFGfHoqtfxMDaBAEsyQ8h7XDX6dg1wq9wH6j4Tw7Tj1MEv1b8uj5NJkozZdzVdYA2QyE2Dp8vuurQG6uVdTDNww2d88RBQ8sVgjxN8gR45y4woJLhFAaNTAtrY6wDTxyXST13ni6oyqdYxjFVk9Am4v3DzH7Y2K8iRVSHfTk4FRbPULyaeK6wt2anvMJH1XdvVRgc14h67MnBxMgMD1UFk8AErN7CDj26fppe3c5G6KozJe4cSqQUGbBjVzBnrHCruqrfZBn5hNZHTV37bQiomqhRQXohxhuKEnNrGbAe1xNvJr9X"; - let reply_surb = ReplySurb::from_base58_string(reply_surb_string).unwrap(); let reply_request = ClientRequest::Reply { + sender_tag: [8u8; SENDER_TAG_SIZE].into(), message: b"foomp".to_vec(), - reply_surb, + connection_id: Some(42), }; let bytes = reply_request.serialize(); let recovered = ClientRequest::deserialize(&bytes).unwrap(); match recovered { ClientRequest::Reply { - reply_surb, + sender_tag, message, + connection_id, } => { - assert_eq!(reply_surb.to_base58_string(), reply_surb_string); + assert_eq!(sender_tag, [8u8; SENDER_TAG_SIZE].into()); assert_eq!(message, b"foomp".to_vec()); + assert_eq!(connection_id, Some(42)); } _ => unreachable!(), } @@ -458,4 +554,15 @@ mod tests { _ => unreachable!(), } } + + #[test] + fn get_lane_queue_length_request_serialization_works() { + let close_connection_request = ClientRequest::GetLaneQueueLength(42); + let bytes = close_connection_request.serialize(); + let recovered = ClientRequest::deserialize(&bytes).unwrap(); + match recovered { + ClientRequest::GetLaneQueueLength(id) => assert_eq!(id, 42), + _ => unreachable!(), + } + } } diff --git a/clients/native/websocket-requests/src/responses.rs b/clients/native/websocket-requests/src/responses.rs index 7a32645957..8b4e60963a 100644 --- a/clients/native/websocket-requests/src/responses.rs +++ b/clients/native/websocket-requests/src/responses.rs @@ -1,36 +1,54 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 // all variable size data is always prefixed with u64 length // tags are u8 -#![allow(unknown_lints)] // due to using `clippy::branches_sharing_code` which does not exist on `stable` just yet - use crate::error::{self, ErrorKind}; use crate::text::ServerResponseText; use nymsphinx::addressing::clients::Recipient; -use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE}; use nymsphinx::receiver::ReconstructedMessage; use std::convert::TryInto; use std::mem::size_of; -/// Value tag representing [`Error`] variant of the [`ServerResponse`] -pub const ERROR_RESPONSE_TAG: u8 = 0x00; +#[repr(u8)] +enum ServerResponseTag { + /// Value tag representing [`Error`] variant of the [`ServerResponse`] + Error = 0x00, -/// Value tag representing [`Received`] variant of the [`ServerResponse`] -pub const RECEIVED_RESPONSE_TAG: u8 = 0x01; + /// Value tag representing [`Received`] variant of the [`ServerResponse`] + Received = 0x01, -/// Value tag representing [`SelfAddress`] variant of the [`ServerResponse`] -pub const SELF_ADDRESS_RESPONSE_TAG: u8 = 0x02; + /// Value tag representing [`SelfAddress`] variant of the [`ServerResponse`] + SelfAddress = 0x02, -/// Value tag representing [`LaneQueueLength`] variant of the [`ServerResponse`] -pub const LANE_QUEUE_LENGTH_RESPONSE_TAG: u8 = 0x03; + /// Value tag representing [`LaneQueueLength`] variant of the [`ServerResponse`] + LaneQueueLength = 0x03, +} + +impl TryFrom for ServerResponseTag { + type Error = error::Error; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (Self::Error as u8) => Ok(Self::Error), + _ if value == (Self::Received as u8) => Ok(Self::Received), + _ if value == (Self::SelfAddress as u8) => Ok(Self::SelfAddress), + _ if value == (Self::LaneQueueLength as u8) => Ok(Self::LaneQueueLength), + n => Err(error::Error::new( + ErrorKind::UnknownResponse, + format!("{n} does not correspond to any valid response tag"), + )), + } + } +} #[derive(Debug)] pub enum ServerResponse { Received(ReconstructedMessage), - SelfAddress(Recipient), - LaneQueueLength(u64, usize), + SelfAddress(Box), + LaneQueueLength { lane: u64, queue_length: usize }, Error(error::Error), } @@ -42,24 +60,19 @@ impl ServerResponse { }) } - // RECEIVED_RESPONSE_TAG || with_reply || (surb_len || surb) || msg_len || msg + // RECEIVED_RESPONSE_TAG || 1 | 0 indicating sender_tag || Option || msg_len || msg fn serialize_received(reconstructed_message: ReconstructedMessage) -> Vec { let message_len_bytes = (reconstructed_message.message.len() as u64).to_be_bytes(); - if let Some(reply_surb) = reconstructed_message.reply_surb { - let reply_surb_bytes = reply_surb.to_bytes(); - let surb_len_bytes = (reply_surb_bytes.len() as u64).to_be_bytes(); - // with_reply || surb_len || surb || msg_len || msg - std::iter::once(RECEIVED_RESPONSE_TAG) + if let Some(sender_tag) = reconstructed_message.sender_tag { + std::iter::once(ServerResponseTag::Received as u8) .chain(std::iter::once(true as u8)) - .chain(surb_len_bytes.iter().cloned()) - .chain(reply_surb_bytes.iter().cloned()) + .chain(sender_tag.to_bytes().into_iter()) .chain(message_len_bytes.iter().cloned()) .chain(reconstructed_message.message.into_iter()) .collect() } else { - // without_reply || msg_len || msg - std::iter::once(RECEIVED_RESPONSE_TAG) + std::iter::once(ServerResponseTag::Received as u8) .chain(std::iter::once(false as u8)) .chain(message_len_bytes.iter().cloned()) .chain(reconstructed_message.message.into_iter()) @@ -67,10 +80,9 @@ impl ServerResponse { } } - // RECEIVED_RESPONSE_TAG || with_reply || (surb_len || surb) || msg_len || msg + // RECEIVED_RESPONSE_TAG || 1 | 0 indicating sender_tag || Option || msg_len || msg fn deserialize_received(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], RECEIVED_RESPONSE_TAG); // we must be able to read at the very least if it has a reply_surb and length of some field if b.len() < 2 + size_of::() { @@ -79,101 +91,70 @@ impl ServerResponse { "not enough data provided to recover 'received'".to_string(), )); } + debug_assert_eq!(b[0], ServerResponseTag::Received as u8); - let with_reply_surb = match b[1] { + let has_sender_tag = match b[1] { 0 => false, 1 => true, n => { return Err(error::Error::new( ErrorKind::MalformedResponse, - format!("invalid reply flag {}", n), + format!("invalid sender tag flag {n}"), )) } }; - // this is a false positive as even though the code is the same, it refers to different things - #[allow(clippy::branches_sharing_code)] - if with_reply_surb { - let reply_surb_len = - u64::from_be_bytes(b[2..2 + size_of::()].as_ref().try_into().unwrap()); - - // make sure we won't go out of bounds here - if reply_surb_len > (b.len() - 2 + 2 * size_of::()) as u64 { + let mut i = 2; + let sender_tag = if has_sender_tag { + if b[2..].len() < SENDER_TAG_SIZE { return Err(error::Error::new( - ErrorKind::MalformedResponse, - "not enough bytes to read reply_surb bytes!".to_string(), + ErrorKind::TooShortResponse, + "not enough data provided to recover 'received'".to_string(), )); } - - let surb_bound = 2 + size_of::() + reply_surb_len as usize; - - let reply_surb_bytes = &b[2 + size_of::()..surb_bound]; - let reply_surb = match ReplySurb::from_bytes(reply_surb_bytes) { - Ok(reply_surb) => reply_surb, - Err(err) => { - return Err(error::Error::new( - ErrorKind::MalformedResponse, - format!("malformed reply SURB: {:?}", err), - )) - } - }; - - let message_len = u64::from_be_bytes( - b[surb_bound..surb_bound + size_of::()] - .as_ref() - .try_into() - .unwrap(), - ); - let message = &b[surb_bound + size_of::()..]; - if message.len() as u64 != message_len { - return Err(error::Error::new( - ErrorKind::MalformedResponse, - format!( - "message len has inconsistent length. specified: {} got: {}", - message_len, - message.len() - ), - )); - } - - Ok(ServerResponse::Received(ReconstructedMessage { - message: message.to_vec(), - reply_surb: Some(reply_surb), - })) + i += SENDER_TAG_SIZE; + Some(AnonymousSenderTag::from_bytes( + b[2..2 + SENDER_TAG_SIZE].try_into().unwrap(), + )) } else { - let message_len = - u64::from_be_bytes(b[2..2 + size_of::()].as_ref().try_into().unwrap()); - let message = &b[2 + size_of::()..]; - if message.len() as u64 != message_len { - return Err(error::Error::new( - ErrorKind::MalformedResponse, - format!( - "message len has inconsistent length. specified: {} got: {}", - message_len, - message.len() - ), - )); - } + None + }; - Ok(ServerResponse::Received(ReconstructedMessage { - message: message.to_vec(), - reply_surb: None, - })) + if b[i..].len() < size_of::() { + return Err(error::Error::new( + ErrorKind::TooShortResponse, + "not enough data provided to recover 'received'".to_string(), + )); } + + let message_len = u64::from_be_bytes(b[i..i + size_of::()].try_into().unwrap()); + let message = &b[i + size_of::()..]; + if message.len() as u64 != message_len { + return Err(error::Error::new( + ErrorKind::MalformedResponse, + format!( + "message len has inconsistent length. specified: {} got: {}", + message_len, + message.len() + ), + )); + } + + Ok(ServerResponse::Received(ReconstructedMessage { + message: message.to_vec(), + sender_tag, + })) } // SELF_ADDRESS_RESPONSE_TAG || self_address fn serialize_self_address(address: Recipient) -> Vec { - std::iter::once(SELF_ADDRESS_RESPONSE_TAG) - .chain(address.to_bytes().iter().cloned()) + std::iter::once(ServerResponseTag::SelfAddress as u8) + .chain(address.to_bytes().into_iter()) .collect() } // SELF_ADDRESS_RESPONSE_TAG || self_address fn deserialize_self_address(b: &[u8]) -> Result { - // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], SELF_ADDRESS_RESPONSE_TAG); - if b.len() != 1 + Recipient::LEN { return Err(error::Error::new( ErrorKind::TooShortResponse, @@ -181,6 +162,9 @@ impl ServerResponse { )); } + // this MUST match because it was called by 'deserialize' + debug_assert_eq!(b[0], ServerResponseTag::SelfAddress as u8); + let mut recipient_bytes = [0u8; Recipient::LEN]; recipient_bytes.copy_from_slice(&b[1..1 + Recipient::LEN]); @@ -189,17 +173,17 @@ impl ServerResponse { Err(err) => { return Err(error::Error::new( ErrorKind::MalformedResponse, - format!("malformed Recipient: {:?}", err), + format!("malformed Recipient: {err}"), )) } }; - Ok(ServerResponse::SelfAddress(recipient)) + Ok(ServerResponse::SelfAddress(Box::new(recipient))) } // LANE_QUEUE_LENGTH_RESPONSE_TAG || lane || queue_length fn serialize_lane_queue_length(lane: u64, queue_length: usize) -> Vec { - std::iter::once(LANE_QUEUE_LENGTH_RESPONSE_TAG) + std::iter::once(ServerResponseTag::LaneQueueLength as u8) .chain(lane.to_be_bytes().iter().cloned()) .chain(queue_length.to_be_bytes().iter().cloned()) .collect() @@ -208,7 +192,7 @@ impl ServerResponse { // LANE_QUEUE_LENGTH_RESPONSE_TAG || lane || queue_length fn deserialize_lane_queue_length(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], LANE_QUEUE_LENGTH_RESPONSE_TAG); + debug_assert_eq!(b[0], ServerResponseTag::LaneQueueLength as u8); let mut lane_bytes = [0u8; size_of::()]; lane_bytes.copy_from_slice(&b[1..=size_of::()]); @@ -219,15 +203,15 @@ impl ServerResponse { .copy_from_slice(&b[1 + size_of::()..1 + size_of::() + size_of::()]); let queue_length = usize::from_be_bytes(queue_length_bytes); - Ok(ServerResponse::LaneQueueLength(lane, queue_length)) + Ok(ServerResponse::LaneQueueLength { lane, queue_length }) } // ERROR_RESPONSE_TAG || err_code || msg_len || msg fn serialize_error(error: error::Error) -> Vec { let message_len_bytes = (error.message.len() as u64).to_be_bytes(); - std::iter::once(ERROR_RESPONSE_TAG) + std::iter::once(ServerResponseTag::Error as u8) .chain(std::iter::once(error.kind as u8)) - .chain(message_len_bytes.iter().cloned()) + .chain(message_len_bytes.into_iter()) .chain(error.message.into_bytes().into_iter()) .collect() } @@ -235,7 +219,7 @@ impl ServerResponse { // ERROR_RESPONSE_TAG || err_code || msg_len || msg fn deserialize_error(b: &[u8]) -> Result { // this MUST match because it was called by 'deserialize' - debug_assert_eq!(b[0], ERROR_RESPONSE_TAG); + debug_assert_eq!(b[0], ServerResponseTag::Error as u8); if b.len() < size_of::() + size_of::() { return Err(error::Error::new( @@ -244,26 +228,7 @@ impl ServerResponse { )); } - let error_kind = match b[1] { - _ if b[1] == (ErrorKind::EmptyRequest as u8) => ErrorKind::EmptyRequest, - _ if b[1] == (ErrorKind::TooShortRequest as u8) => ErrorKind::TooShortRequest, - _ if b[1] == (ErrorKind::UnknownRequest as u8) => ErrorKind::UnknownRequest, - _ if b[1] == (ErrorKind::MalformedRequest as u8) => ErrorKind::MalformedRequest, - - _ if b[1] == (ErrorKind::EmptyResponse as u8) => ErrorKind::EmptyResponse, - _ if b[1] == (ErrorKind::TooShortResponse as u8) => ErrorKind::TooShortResponse, - _ if b[1] == (ErrorKind::UnknownResponse as u8) => ErrorKind::UnknownResponse, - _ if b[1] == (ErrorKind::MalformedResponse as u8) => ErrorKind::MalformedResponse, - - _ if b[1] == (ErrorKind::Other as u8) => ErrorKind::Other, - - n => { - return Err(error::Error::new( - ErrorKind::MalformedResponse, - format!("invalid error code {}", n), - )) - } - }; + let error_kind = ErrorKind::try_from(b[1])?; let message_len = u64::from_be_bytes(b[2..2 + size_of::()].as_ref().try_into().unwrap()); @@ -284,7 +249,7 @@ impl ServerResponse { Err(err) => { return Err(error::Error::new( ErrorKind::MalformedResponse, - format!("malformed error message: {:?}", err), + format!("malformed error message: {err}"), )) } }; @@ -300,8 +265,8 @@ impl ServerResponse { ServerResponse::Received(reconstructed_message) => { Self::serialize_received(reconstructed_message) } - ServerResponse::SelfAddress(address) => Self::serialize_self_address(address), - ServerResponse::LaneQueueLength(lane, queue_length) => { + ServerResponse::SelfAddress(address) => Self::serialize_self_address(*address), + ServerResponse::LaneQueueLength { lane, queue_length } => { Self::serialize_lane_queue_length(lane, queue_length) } ServerResponse::Error(err) => Self::serialize_error(err), @@ -328,18 +293,14 @@ impl ServerResponse { )); } - let response_tag = b[0]; + let response_tag = ServerResponseTag::try_from(b[0])?; // determine what kind of response that is and try to deserialize it match response_tag { - RECEIVED_RESPONSE_TAG => Self::deserialize_received(b), - SELF_ADDRESS_RESPONSE_TAG => Self::deserialize_self_address(b), - LANE_QUEUE_LENGTH_RESPONSE_TAG => Self::deserialize_lane_queue_length(b), - ERROR_RESPONSE_TAG => Self::deserialize_error(b), - n => Err(error::Error::new( - ErrorKind::UnknownResponse, - format!("type {}", n), - )), + ServerResponseTag::Received => Self::deserialize_received(b), + ServerResponseTag::SelfAddress => Self::deserialize_self_address(b), + ServerResponseTag::LaneQueueLength => Self::deserialize_lane_queue_length(b), + ServerResponseTag::Error => Self::deserialize_error(b), } } @@ -361,35 +322,33 @@ mod tests { #[test] fn received_response_serialization_works() { - let reply_surb_string = "CjfVbHbfAjbC3W1BvNHGXmM8KNAnDNYGaHMLqVDxRYeo352csAihstup9bvqXam4dTWgfHak6KYwL9STaxWJ47E8XFZbSEvs7hEsfCkxr6K9WJuSBPK84GDDEvad8ZAuMCoaXsAd5S2Lj9a5eYyzG4SL1jHzhSMni55LyJwumxo1ZTGZNXggxw1RREosvyzNrW9Rsi3owyPqLCwXpiei2tHZty8w8midVvg8vDa7ZEJD842CLv8D4ohynSG7gDpqTrhkRaqYAuz7dzqNbMXLJRM7v823Jn16fA1L7YQxmcaUdUigyRSgTdb4i9ebiLGSyJ1iDe6Acz613PQZh6Ua3bZ2zVKq3dSycpDm9ngarRK4zJrAaUxRkdih8YzW3BY4nL9eqkfKA4N1TWCLaRU7zpSaf8yMEwrAZReU3d5zLV8c5KBfa2w8R5anhQeBojduZEGEad8kkHuKU52Zg93FeWHvH1qgZaEJMHH4nN7gKXz9mvWDhYwyF4vt3Uy2NhCHC3N5pL1gMme27YcoPcTEia1fxKZtnt6rtEozzTrAgCJGswigkFbkafiV5QaJwLKTUxtzhkZ57eEuLPte9UvJHzhhXUQ2CV7R2BUkJjYZy3Zsx6YYvdYWiAFFkWUwNEGA4QpShUHciBfsQVHQ7pN41YcyYUhbywQDFnTVgEmdUZ1XCBi3gyK5U3tDQmFzP1u9m3mWrUA8qB9mRDE7ptNDm5c3c1458L6uXLUth7sdMaa1Was5LCmCdmNDtvNpCDAEt1in6q6mrZFR85aCSU9b1baNGwZoCqPpPvydkVe63gXWoi8ebvdyxARrqACFrSB3ZdY3uJBw8CTMNkKK6MvcefMkSVVsbLd36TQAtYSCqrpiMc5dQuKcEu5QfciwvWYXYx8WFNAgKwP2mv49KCTvfozNDUCbjzDwSx92Zv5zjG8HbFpB13bY9UZGeyTPvv7gGxCzjGjJGbW6FRAheRQaaje5fUgCNM95Tv7wBmAMRHHFgWafeK1sdFH7dtCX9u898HucGTaboSKLsVh8J78gbbkHErwjMh7y9YRkceq5TTYS5da4kHnyNKYWSbxgZrmFg44XGKoeYcqoHB3XTZrdsf7F5fFeNwnihkmADvhAcaxXUmVqq4rQFZH84a1iC3WBWXYcqiZH2L7ujGWV7mMDT4HBEerDYjc8rNY4xGTPfivCrBCJW1i14aqW8xRdsdgTM88eTksvC3WPJLJ7iMzfKXeL7fMW1Ek6QGyQtLBW98vEESpdcDg6DeZ5rMz6VqjTGGqcCaFGfHoqtfxMDaBAEsyQ8h7XDX6dg1wq9wH6j4Tw7Tj1MEv1b8uj5NJkozZdzVdYA2QyE2Dp8vuurQG6uVdTDNww2d88RBQ8sVgjxN8gR45y4woJLhFAaNTAtrY6wDTxyXST13ni6oyqdYxjFVk9Am4v3DzH7Y2K8iRVSHfTk4FRbPULyaeK6wt2anvMJH1XdvVRgc14h67MnBxMgMD1UFk8AErN7CDj26fppe3c5G6KozJe4cSqQUGbBjVzBnrHCruqrfZBn5hNZHTV37bQiomqhRQXohxhuKEnNrGbAe1xNvJr9X"; - - let received_with_surb = ServerResponse::Received(ReconstructedMessage { + let received_with_sender_tag = ServerResponse::Received(ReconstructedMessage { message: b"foomp".to_vec(), - reply_surb: Some(ReplySurb::from_base58_string(reply_surb_string).unwrap()), + sender_tag: Some([42u8; SENDER_TAG_SIZE].into()), }); - let bytes = received_with_surb.serialize(); + let bytes = received_with_sender_tag.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::Received(reconstructed) => { assert_eq!(reconstructed.message, b"foomp".to_vec()); assert_eq!( - reconstructed.reply_surb.unwrap().to_base58_string(), - reply_surb_string + reconstructed.sender_tag, + Some([42u8; SENDER_TAG_SIZE].into()) ) } _ => unreachable!(), } - let received_without_surb = ServerResponse::Received(ReconstructedMessage { + let received_without_sender_tag = ServerResponse::Received(ReconstructedMessage { message: b"foomp".to_vec(), - reply_surb: None, + sender_tag: None, }); - let bytes = received_without_surb.serialize(); + let bytes = received_without_sender_tag.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { ServerResponse::Received(reconstructed) => { assert_eq!(reconstructed.message, b"foomp".to_vec()); - assert!(reconstructed.reply_surb.is_none()) + assert!(reconstructed.sender_tag.is_none()) } _ => unreachable!(), } @@ -400,7 +359,7 @@ mod tests { let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); let recipient_string = recipient.to_string(); - let self_address_response = ServerResponse::SelfAddress(recipient); + let self_address_response = ServerResponse::SelfAddress(Box::new(recipient)); let bytes = self_address_response.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { @@ -413,11 +372,14 @@ mod tests { #[test] fn lane_queue_length_response_serialization_works() { - let lane_queue_length_response = ServerResponse::LaneQueueLength(13, 42); + let lane_queue_length_response = ServerResponse::LaneQueueLength { + lane: 13, + queue_length: 42, + }; let bytes = lane_queue_length_response.serialize(); let recovered = ServerResponse::deserialize(&bytes).unwrap(); match recovered { - ServerResponse::LaneQueueLength(lane, queue_length) => { + ServerResponse::LaneQueueLength { lane, queue_length } => { assert_eq!(lane, 13); assert_eq!(queue_length, 42) } diff --git a/clients/native/websocket-requests/src/text.rs b/clients/native/websocket-requests/src/text.rs index 1083ecd9a8..68091dbecf 100644 --- a/clients/native/websocket-requests/src/text.rs +++ b/clients/native/websocket-requests/src/text.rs @@ -1,11 +1,11 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::error::ErrorKind; use crate::requests::ClientRequest; use crate::responses::ServerResponse; use nymsphinx::addressing::clients::Recipient; -use nymsphinx::anonymous_replies::ReplySurb; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; @@ -19,15 +19,22 @@ pub(super) enum ClientRequestText { Send { message: String, recipient: String, - with_reply_surb: bool, + connection_id: Option, + }, + #[serde(rename_all = "camelCase")] + SendAnonymous { + recipient: String, + message: String, + reply_surbs: u32, + connection_id: Option, + }, + #[serde(rename_all = "camelCase")] + Reply { + sender_tag: String, + message: String, connection_id: Option, }, SelfAddress, - #[serde(rename_all = "camelCase")] - Reply { - message: String, - reply_surb: String, - }, } impl TryFrom for ClientRequestText { @@ -46,7 +53,6 @@ impl TryInto for ClientRequestText { ClientRequestText::Send { message, recipient, - with_reply_surb, connection_id, } => { let message_bytes = message.into_bytes(); @@ -57,23 +63,42 @@ impl TryInto for ClientRequestText { Ok(ClientRequest::Send { message: message_bytes, recipient, - with_reply_surb, + connection_id, + }) + } + ClientRequestText::SendAnonymous { + recipient, + message, + reply_surbs, + connection_id, + } => { + let message_bytes = message.into_bytes(); + let recipient = Recipient::try_from_base58_string(recipient).map_err(|err| { + Self::Error::new(ErrorKind::MalformedRequest, err.to_string()) + })?; + Ok(ClientRequest::SendAnonymous { + recipient, + message: message_bytes, + reply_surbs, connection_id, }) } ClientRequestText::SelfAddress => Ok(ClientRequest::SelfAddress), ClientRequestText::Reply { + sender_tag, message, - reply_surb, + connection_id, } => { let message_bytes = message.into_bytes(); - let reply_surb = ReplySurb::from_base58_string(reply_surb).map_err(|err| { - Self::Error::new(ErrorKind::MalformedRequest, err.to_string()) - })?; + let sender_tag = + AnonymousSenderTag::try_from_base58_string(sender_tag).map_err(|err| { + Self::Error::new(ErrorKind::MalformedRequest, err.to_string()) + })?; Ok(ClientRequest::Reply { + sender_tag, message: message_bytes, - reply_surb, + connection_id, }) } } @@ -89,7 +114,7 @@ pub(super) enum ServerResponseText { #[serde(rename_all = "camelCase")] Received { message: String, - reply_surb: Option, + sender_tag: Option, }, SelfAddress { address: String, @@ -131,15 +156,13 @@ impl From for ServerResponseText { // TODO: ask DH what is more appropriate, lossy utf8 conversion or returning error and then // pure binary later message: String::from_utf8_lossy(&reconstructed.message).into_owned(), - reply_surb: reconstructed - .reply_surb - .map(|reply_surb| reply_surb.to_base58_string()), + sender_tag: reconstructed.sender_tag.map(|tag| tag.to_base58_string()), } } ServerResponse::SelfAddress(recipient) => ServerResponseText::SelfAddress { address: recipient.to_string(), }, - ServerResponse::LaneQueueLength(lane, queue_length) => { + ServerResponse::LaneQueueLength { lane, queue_length } => { ServerResponseText::LaneQueueLength { lane, queue_length } } ServerResponse::Error(err) => ServerResponseText::Error { diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index b5eb3a1a9d..c4218c45e4 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-socks5-client" -version = "1.1.1" +version = "1.1.4" authors = ["Dave Hrycyszyn "] description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" edition = "2021" @@ -11,21 +11,24 @@ name = "nym_socks5" path = "src/lib.rs" [dependencies] -clap = { version = "3.2", features = ["cargo", "derive"] } +clap = { version = "4.0", features = ["cargo", "derive"] } dirs = "4.0" futures = "0.3" +lazy_static = "1.4.0" log = "0.4" pin-project = "1.0" pretty_env_logger = "0.4" rand = { version = "0.7.3", features = ["wasm-bindgen"] } serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization -snafu = "0.6" +serde_json = "1.0.89" +tap = "1.0.1" thiserror = "1.0.34" tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } url = "2.2" # internal -client-core = { path = "../client-core" } +build-information = { path = "../../common/build-information" } +client-core = { path = "../client-core", features = ["fs-surb-storage"] } client-connections = { path = "../../common/client-connections" } coconut-interface = { path = "../../common/coconut-interface", optional = true } config = { path = "../../common/config" } @@ -44,13 +47,9 @@ proxy-helpers = { path = "../../common/socks5/proxy-helpers" } socks5-requests = { path = "../../common/socks5/requests" } task = { path = "../../common/task" } topology = { path = "../../common/topology" } -validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] } +validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] } version-checker = { path = "../../common/version-checker" } -tap = "1.0.1" [features] coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"] eth = [] - -[build-dependencies] -vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] } diff --git a/clients/socks5/build.rs b/clients/socks5/build.rs deleted file mode 100644 index 01b3a20dc6..0000000000 --- a/clients/socks5/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use vergen::{vergen, Config}; - -fn main() { - vergen(Config::default()).expect("failed to extract build metadata") -} diff --git a/clients/socks5/src/client/config/mod.rs b/clients/socks5/src/client/config/mod.rs index 4c43984f35..346eb37b34 100644 --- a/clients/socks5/src/client/config/mod.rs +++ b/clients/socks5/src/client/config/mod.rs @@ -2,16 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 use crate::client::config::template::config_template; -use client_core::config::Config as BaseConfig; +pub use client_core::config::Config as BaseConfig; pub use client_core::config::MISSING_VALUE; +use client_core::config::{ClientCoreConfigTrait, DebugConfig}; use config::defaults::DEFAULT_SOCKS5_LISTENING_PORT; -use config::NymConfig; +use config::{NymConfig, OptionalSet}; use nymsphinx::addressing::clients::Recipient; use serde::{Deserialize, Serialize}; +use std::fmt::Debug; use std::path::PathBuf; +use std::str::FromStr; mod template; +const DEFAULT_CONNECTION_START_SURBS: u32 = 20; +const DEFAULT_PER_REQUEST_SURBS: u32 = 3; + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { @@ -19,6 +25,9 @@ pub struct Config { base: BaseConfig, socks5: Socks5, + + #[serde(default)] + socks5_debug: Socks5Debug, } impl NymConfig for Config { @@ -52,25 +61,51 @@ impl NymConfig for Config { } } +impl ClientCoreConfigTrait for Config { + fn get_gateway_endpoint(&self) -> &client_core::config::GatewayEndpointConfig { + self.base.get_gateway_endpoint() + } +} + impl Config { pub fn new>(id: S, provider_mix_address: S) -> Self { Config { base: BaseConfig::new(id), socks5: Socks5::new(provider_mix_address), + socks5_debug: Socks5Debug::default(), } } + #[must_use] pub fn with_port(mut self, port: u16) -> Self { self.socks5.listening_port = port; self } + #[must_use] pub fn with_provider_mix_address(mut self, address: String) -> Self { self.socks5.provider_mix_address = address; self } + pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self { + self.socks5.send_anonymously = anonymous_replies; + self + } + // getters + pub fn get_base(&self) -> &BaseConfig { + &self.base + } + + pub fn get_base_mut(&mut self) -> &mut BaseConfig { + &mut self.base + } + + pub fn get_debug_settings(&self) -> &DebugConfig { + self.get_base().get_debug_config() + } + pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) } @@ -80,17 +115,65 @@ impl Config { .expect("malformed provider address") } - pub fn get_base(&self) -> &BaseConfig { - &self.base - } - - pub fn get_base_mut(&mut self) -> &mut BaseConfig { - &mut self.base + pub fn get_send_anonymously(&self) -> bool { + self.socks5.send_anonymously } pub fn get_listening_port(&self) -> u16 { self.socks5.listening_port } + + pub fn get_connection_start_surbs(&self) -> u32 { + self.socks5_debug.connection_start_surbs + } + + pub fn get_per_request_surbs(&self) -> u32 { + self.socks5_debug.per_request_surbs + } + + // poor man's 'builder' method + pub fn with_base(mut self, f: F, val: T) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + { + self.base = f(self.base, val); + self + } + + // helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config + // (plz, lets refactor it) + pub fn with_optional_ext(mut self, f: F, val: Option) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + { + self.base = self.base.with_optional(f, val); + self + } + + pub fn with_optional_env_ext(mut self, f: F, val: Option, env_var: &str) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + T: FromStr, + ::Err: Debug, + { + self.base = self.base.with_optional_env(f, val, env_var); + self + } + + pub fn with_optional_custom_env_ext( + mut self, + f: F, + val: Option, + env_var: &str, + parser: G, + ) -> Self + where + F: Fn(BaseConfig, T) -> BaseConfig, + G: Fn(&str) -> T, + { + self.base = self.base.with_optional_custom_env(f, val, env_var, parser); + self + } } #[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] @@ -101,6 +184,14 @@ pub struct Socks5 { /// The mix address of the provider to which all requests are going to be sent. provider_mix_address: String, + + /// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. + /// While this is going to hide its actual address information, it will make the actual communication + /// slower and consume nearly double the bandwidth as it will require sending reply SURBs. + /// + /// Note that some service providers might not support this. + #[serde(default)] + send_anonymously: bool, } impl Socks5 { @@ -108,6 +199,7 @@ impl Socks5 { Socks5 { listening_port: DEFAULT_SOCKS5_LISTENING_PORT, provider_mix_address: provider_mix_address.into(), + send_anonymously: false, } } } @@ -117,6 +209,26 @@ impl Default for Socks5 { Socks5 { listening_port: DEFAULT_SOCKS5_LISTENING_PORT, provider_mix_address: "".into(), + send_anonymously: false, + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Socks5Debug { + /// Number of reply SURBs attached to each `Request::Connect` message. + connection_start_surbs: u32, + + /// Number of reply SURBs attached to each `Request::Send` message. + per_request_surbs: u32, +} + +impl Default for Socks5Debug { + fn default() -> Self { + Socks5Debug { + connection_start_surbs: DEFAULT_CONNECTION_START_SURBS, + per_request_surbs: DEFAULT_PER_REQUEST_SURBS, } } } diff --git a/clients/socks5/src/client/config/template.rs b/clients/socks5/src/client/config/template.rs index 11d3185f56..f8c274c27c 100644 --- a/clients/socks5/src/client/config/template.rs +++ b/clients/socks5/src/client/config/template.rs @@ -23,16 +23,16 @@ id = '{{ client.id }}' # to claim bandwidth without presenting bandwidth credentials. disabled_credentials_mode = {{ client.disabled_credentials_mode }} -# Addresses to nymd validators via which the client can communicate with the chain. -validator_urls = [ - {{#each client.validator_urls }} +# Addresses to nyxd validators via which the client can communicate with the chain. +nyxd_urls = [ + {{#each client.nyxd_urls }} '{{this}}', {{/each}} ] # Addresses to APIs running on validator from which the client gets the view of the network. -validator_api_urls = [ - {{#each client.validator_api_urls }} +nym_api_urls = [ + {{#each client.nym_api_urls }} '{{this}}', {{/each}} ] @@ -49,13 +49,12 @@ private_encryption_key_file = '{{ client.private_encryption_key_file }}' # Path to file containing public encryption key. public_encryption_key_file = '{{ client.public_encryption_key_file }}' -# Full path to file containing reply encryption keys of all reply-SURBs we have ever -# sent but not received back. -reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}' - # Path to the database containing bandwidth credentials database_path = '{{ client.database_path }}' +# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. +reply_surb_database_path = '{{ client.reply_surb_database_path }}' + ##### additional client config options ##### # A gateway specific, optional, base58 stringified shared key used for @@ -92,6 +91,12 @@ provider_mix_address = '{{ socks5.provider_mix_address }}' # The port on which the client will be listening for incoming requests listening_port = {{ socks5.listening_port }} +# Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. +# While this is going to hide its actual address information, it will make the actual communication +# slower and consume nearly double the bandwidth as it will require sending reply SURBs. +# +# Note that some service providers might not support this. +send_anonymously = {{ socks5.send_anonymously }} ##### logging configuration options ##### @@ -104,6 +109,9 @@ listening_port = {{ socks5.listening_port }} # The following options should not be modified unless you know EXACTLY what you are doing # as if set incorrectly, they may impact your anonymity. +# [socks5_debug] + + [debug] average_packet_delay = '{{ debug.average_packet_delay }}' diff --git a/clients/socks5/src/client/mod.rs b/clients/socks5/src/client/mod.rs index 0784666af8..96bdf00932 100644 --- a/clients/socks5/src/client/mod.rs +++ b/clients/socks5/src/client/mod.rs @@ -3,11 +3,14 @@ use crate::client::config::Config; use crate::error::Socks5ClientError; +use crate::socks; use crate::socks::{ authentication::{AuthenticationMethods, Authenticator, User}, server::SphinxSocksServer, }; -use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput}; +use client_core::client::base_client::{ + non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, +}; use client_core::client::key_manager::KeyManager; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use futures::channel::mpsc; @@ -16,7 +19,7 @@ use gateway_client::bandwidth::BandwidthController; use log::*; use nymsphinx::addressing::clients::Recipient; use std::error::Error; -use task::{wait_for_signal_and_error, ShutdownListener, ShutdownNotifier}; +use task::{wait_for_signal_and_error, TaskClient, TaskManager}; pub mod config; @@ -54,8 +57,21 @@ impl NymClient { #[cfg(feature = "coconut")] let bandwidth_controller = { let details = network_defaults::NymNetworkDetails::new_from_env(); - let client_config = validator_client::Config::try_from_nym_network_details(&details) - .expect("failed to construct validator client config"); + let mut client_config = + validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct validator client config"); + let nyxd_url = config + .get_base() + .get_validator_endpoints() + .pop() + .expect("No nyxd validator endpoint provided"); + let api_url = config + .get_base() + .get_nym_api_endpoints() + .pop() + .expect("No validator api endpoint provided"); + // overwrite env configuration with config URLs + client_config = client_config.with_urls(nyxd_url, api_url); let client = validator_client::Client::new_query(client_config) .expect("Could not construct query client"); let coconut_api_clients = @@ -80,19 +96,21 @@ impl NymClient { client_input: ClientInput, client_output: ClientOutput, self_address: Recipient, - mut shutdown: ShutdownListener, + shutdown: TaskClient, ) { info!("Starting socks5 listener..."); let auth_methods = vec![AuthenticationMethods::NoAuth as u8]; let allowed_users: Vec = Vec::new(); let ClientInput { - shared_lane_queue_lengths, connection_command_sender, input_sender, } = client_input; - let received_buffer_request_sender = client_output.received_buffer_request_sender; + let ClientOutput { + shared_lane_queue_lengths, + received_buffer_request_sender, + } = client_output; let authenticator = Authenticator::new(auth_methods, allowed_users); let mut sphinx_socks = SphinxSocksServer::new( @@ -101,35 +119,30 @@ impl NymClient { config.get_provider_mix_address(), self_address, shared_lane_queue_lengths, + socks::client::Config::new( + config.get_send_anonymously(), + config.get_connection_start_surbs(), + config.get_per_request_surbs(), + ), shutdown.clone(), ); - tokio::spawn(async move { - // Ideally we should have a fully fledged task manager to check for errors in all - // tasks. - // However, pragmatically, we start out by at least reporting errors for some of the - // tasks that interact with the outside world and can fail in normal operation, such as - // network issues. - // TODO: replace this by a generic solution, such as a task manager that stores all - // JoinHandles of all spawned tasks. - if let Err(res) = sphinx_socks - .serve( - input_sender, - received_buffer_request_sender, - connection_command_sender, - ) - .await - { - shutdown.send_we_stopped(Box::new(res)); - } - }); + task::spawn_with_report_error( + async move { + sphinx_socks + .serve( + input_sender, + received_buffer_request_sender, + connection_command_sender, + ) + .await + }, + shutdown, + ); } /// blocking version of `start` method. Will run forever (or until SIGINT is sent) - pub async fn run_forever(self) -> Result<(), Box> { - let mut shutdown = self - .start() - .await - .map_err(|err| Box::new(err) as Box)?; + pub async fn run_forever(self) -> Result<(), Box> { + let mut shutdown = self.start().await?; let res = wait_for_signal_and_error(&mut shutdown).await; @@ -147,12 +160,13 @@ impl NymClient { pub async fn run_and_listen( self, mut receiver: Socks5ControlMessageReceiver, - ) -> Result<(), Box> { + sender: task::StatusSender, + ) -> Result<(), Box> { // Start the main task - let mut shutdown = self - .start() - .await - .map_err(|err| Box::new(err) as Box)?; + let mut shutdown = self.start().await?; + + // Listen to status messages from task, that we forward back to the caller + shutdown.start_status_listener(sender).await; let res = tokio::select! { biased; @@ -188,11 +202,16 @@ impl NymClient { res } - pub async fn start(self) -> Result { + pub async fn start(self) -> Result { let base_builder = BaseClientBuilder::new_from_base_config( self.config.get_base(), self.key_manager, Some(Self::create_bandwidth_controller(&self.config).await), + non_wasm_helpers::setup_fs_reply_surb_backend( + self.config.get_base().get_reply_surb_database_path(), + self.config.get_debug_settings(), + ) + .await?, ); let self_address = base_builder.as_mix_recipient(); @@ -205,12 +224,12 @@ impl NymClient { client_input, client_output, self_address, - started_client.shutdown_notifier.subscribe(), + started_client.task_manager.subscribe(), ); info!("Client startup finished!"); info!("The address of this client is: {}", self_address); - Ok(started_client.shutdown_notifier) + Ok(started_client.task_manager) } } diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index a1d123fc75..80087a59e6 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -1,14 +1,18 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use clap::Args; -use client_core::{config::GatewayEndpointConfig, error::ClientCoreError}; -use config::NymConfig; - use crate::{ client::config::Config, commands::{override_config, OverrideConfig}, + error::Socks5ClientError, }; +use clap::Args; +use config::NymConfig; +use crypto::asymmetric::identity; +use nymsphinx::addressing::clients::Recipient; +use serde::Serialize; +use std::fmt::Display; +use tap::TapFallible; #[derive(Args, Clone)] pub(crate) struct Init { @@ -18,24 +22,35 @@ pub(crate) struct Init { /// Address of the socks5 provider to send messages to. #[clap(long)] - provider: String, + provider: Recipient, + + /// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. + /// While this is going to hide its actual address information, it will make the actual communication + /// slower and consume nearly double the bandwidth as it will require sending reply SURBs. + /// + /// Note that some service providers might not support this. + // the alias here is included for backwards compatibility (1.1.4 and before) + #[clap(long, alias = "use_anonymous_sender_tag")] + use_reply_surbs: bool, /// Id of the gateway we are going to connect to. #[clap(long)] - gateway: Option, + gateway: Option, /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, /// potentially causing loss of access. #[clap(long)] force_register_gateway: bool, - /// Comma separated list of rest endpoints of the nymd validators - #[clap(long)] - nymd_validators: Option, + /// Comma separated list of rest endpoints of the nyxd validators + #[cfg(feature = "coconut")] + #[clap(long, alias = "nymd_validators", value_delimiter = ',')] + nyxd_urls: Option>, /// Comma separated list of rest endpoints of the API validators - #[clap(long)] - api_validators: Option, + #[clap(long, alias = "api_validators", value_delimiter = ',')] + // the alias here is included for backwards compatibility (1.1.4 and before) + nym_apis: Option>, /// Port for the socket to listen on in all subsequent runs #[clap(short, long)] @@ -43,30 +58,65 @@ pub(crate) struct Init { /// Mostly debug-related option to increase default traffic rate so that you would not need to /// modify config post init - #[clap(long, hidden = true)] + #[clap(long, hide = true)] fastmode: bool, + /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) + #[clap(long, hide = true)] + no_cover: bool, + /// Set this client to work in a enabled credentials mode that would attempt to use gateway /// with bandwidth credential requirement. #[cfg(feature = "coconut")] #[clap(long)] enabled_credentials_mode: bool, + + /// Save a summary of the initialization to a json file + #[clap(long)] + output_json: bool, } impl From for OverrideConfig { fn from(init_config: Init) -> Self { OverrideConfig { - nymd_validators: init_config.nymd_validators, - api_validators: init_config.api_validators, + nym_apis: init_config.nym_apis, port: init_config.port, + use_anonymous_replies: init_config.use_reply_surbs, fastmode: init_config.fastmode, + no_cover: init_config.no_cover, + + #[cfg(feature = "coconut")] + nyxd_urls: init_config.nyxd_urls, #[cfg(feature = "coconut")] enabled_credentials_mode: init_config.enabled_credentials_mode, } } } -pub(crate) async fn execute(args: &Init) { +#[derive(Debug, Serialize)] +pub struct InitResults { + #[serde(flatten)] + client_core: client_core::init::InitResults, + socks5_listening_port: String, +} + +impl InitResults { + fn new(config: &Config, address: &Recipient) -> Self { + Self { + client_core: client_core::init::InitResults::new(config.get_base(), address), + socks5_listening_port: config.get_listening_port().to_string(), + } + } +} + +impl Display for InitResults { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", self.client_core)?; + write!(f, "SOCKS5 listening port: {}", self.socks5_listening_port) + } +} + +pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> { println!("Initialising client..."); let id = &args.id; @@ -91,25 +141,47 @@ pub(crate) async fn execute(args: &Init) { let register_gateway = !already_init || user_wants_force_register; // Attempt to use a user-provided gateway, if possible - let user_chosen_gateway_id = args.gateway.as_deref(); + let user_chosen_gateway_id = args.gateway; - let mut config = Config::new(id, provider_address); - let override_config_fields = OverrideConfig::from(args.clone()); - config = override_config(config, override_config_fields); + // Load and potentially override config + let mut config = override_config( + Config::new(id, &provider_address.to_string()), + OverrideConfig::from(args.clone()), + ); + + // Setup gateway by either registering a new one, or creating a new config from the selected + // one but with keys kept, or reusing the gateway configuration. + let gateway = client_core::init::setup_gateway::( + register_gateway, + user_chosen_gateway_id.map(|id| id.to_base58_string()), + config.get_base(), + ) + .await + .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; - let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config) - .await - .unwrap_or_else(|err| { - eprintln!("Failed to setup gateway\nError: {err}"); - std::process::exit(1) - }); config.get_base_mut().with_gateway_endpoint(gateway); - let config_save_location = config.get_config_file_save_location(); - config - .save_to_file(None) - .expect("Failed to save the config file"); + config.save_to_file(None).tap_err(|_| { + log::error!("Failed to save the config file"); + })?; + print_saved_config(&config); + + let address = client_core::init::get_client_address_from_stored_keys(config.get_base())?; + let init_results = InitResults::new(&config, &address); + println!("{}", init_results); + + // Output summary to a json file, if specified + if args.output_json { + client_core::init::output_to_json(&init_results, "socks5_client_init_results.json"); + } + + println!("\nThe address of this client is: {}\n", address); + Ok(()) +} + +fn print_saved_config(config: &Config) { + let config_save_location = config.get_config_file_save_location(); println!("Saved configuration file to {:?}", config_save_location); println!("Using gateway: {}", config.get_base().get_gateway_id()); log::debug!("Gateway id: {}", config.get_base().get_gateway_id()); @@ -118,62 +190,5 @@ pub(crate) async fn execute(args: &Init) { "Gateway listener: {}", config.get_base().get_gateway_listener() ); - println!("Client configuration completed."); - - client_core::init::show_address(config.get_base()).unwrap_or_else(|err| { - eprintln!("Failed to show address\nError: {err}"); - std::process::exit(1) - }); -} - -async fn setup_gateway( - id: &str, - register: bool, - user_chosen_gateway_id: Option<&str>, - config: &Config, -) -> Result { - 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. - println!("Configuring gateway"); - let gateway = client_core::init::query_gateway_details( - config.get_base().get_validator_api_endpoints(), - user_chosen_gateway_id, - ) - .await?; - log::debug!("Querying gateway gives: {}", gateway); - - // Registering with gateway by setting up and writing shared keys to disk - log::trace!("Registering gateway"); - client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base()) - .await?; - println!("Saved all generated keys"); - - 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 - // valid for the gateway being used - println!("Using gateway provided by user, keeping existing keys"); - let gateway = client_core::init::query_gateway_details( - config.get_base().get_validator_api_endpoints(), - user_chosen_gateway_id, - ) - .await?; - log::debug!("Querying gateway gives: {}", gateway); - Ok(gateway.into()) - } else { - println!("Not registering gateway, will reuse existing config and keys"); - let existing_config = Config::load_from_file(Some(id)).map_err(|err| { - 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." - ); - ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err) - })?; - Ok(existing_config.get_base().get_gateway_endpoint().clone()) - } + println!("Client configuration completed.\n"); } diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index e1d42e0fb1..a825fb882e 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -1,55 +1,31 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::error::Error; - -use crate::client::config::Config; +use crate::client::config::{BaseConfig, Config}; +use build_information::BinaryBuildInformation; use clap::CommandFactory; use clap::{Parser, Subcommand}; use completions::{fig_generate, ArgShell}; -use config::parse_validators; +use config::OptionalSet; +use lazy_static::lazy_static; +use std::error::Error; pub mod init; pub(crate) mod run; pub(crate) mod upgrade; -fn long_version() -> String { - format!( - r#" -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -"#, - "Build Timestamp:", - env!("VERGEN_BUILD_TIMESTAMP"), - "Build Version:", - env!("VERGEN_BUILD_SEMVER"), - "Commit SHA:", - env!("VERGEN_GIT_SHA"), - "Commit Date:", - env!("VERGEN_GIT_COMMIT_TIMESTAMP"), - "Commit Branch:", - env!("VERGEN_GIT_BRANCH"), - "rustc Version:", - env!("VERGEN_RUSTC_SEMVER"), - "rustc Channel:", - env!("VERGEN_RUSTC_CHANNEL"), - "cargo Profile:", - env!("VERGEN_CARGO_PROFILE"), - ) +lazy_static! { + pub static ref PRETTY_BUILD_INFORMATION: String = + BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); } -fn long_version_static() -> &'static str { - Box::leak(long_version().into_boxed_str()) +// Helper for passing LONG_VERSION to clap +fn pretty_build_info_static() -> &'static str { + &PRETTY_BUILD_INFORMATION } #[derive(Parser)] -#[clap(author = "Nymtech", version, long_version = long_version_static(), about)] +#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)] pub(crate) struct Cli { /// Path pointing to an env file that configures the client. #[clap(short, long)] @@ -79,61 +55,57 @@ pub(crate) enum Commands { // Configuration that can be overridden. pub(crate) struct OverrideConfig { - nymd_validators: Option, - api_validators: Option, + nym_apis: Option>, port: Option, + use_anonymous_replies: bool, fastmode: bool, + no_cover: bool, + #[cfg(feature = "coconut")] + nyxd_urls: Option>, #[cfg(feature = "coconut")] enabled_credentials_mode: bool, } -pub(crate) async fn execute(args: &Cli) -> Result<(), Box> { +pub(crate) async fn execute(args: &Cli) -> Result<(), Box> { let bin_name = "nym-socks5-client"; match &args.command { - Commands::Init(m) => init::execute(m).await, + Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(m).await?, Commands::Upgrade(m) => upgrade::execute(m), - Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name), - Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name), + Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), + Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), } Ok(()) } pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config { - if let Some(raw_validators) = args.nymd_validators { - config - .get_base_mut() - .set_custom_validators(parse_validators(&raw_validators)); - } else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::NYMD_VALIDATOR) { - config - .get_base_mut() - .set_custom_validators(parse_validators(&raw_validators)); - } - if let Some(raw_validators) = args.api_validators { - config - .get_base_mut() - .set_custom_validator_apis(parse_validators(&raw_validators)); - } else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::API_VALIDATOR) { - config - .get_base_mut() - .set_custom_validator_apis(parse_validators(&raw_validators)); - } - - if let Some(port) = args.port { - config = config.with_port(port); - } + config = config + .with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode) + .with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover) + .with_anonymous_replies(args.use_anonymous_replies) + .with_optional(Config::with_port, args.port) + .with_optional_custom_env_ext( + BaseConfig::with_custom_nym_apis, + args.nym_apis, + network_defaults::var_names::NYM_API, + config::parse_urls, + ); #[cfg(feature = "coconut")] { - if args.enabled_credentials_mode { - config.get_base_mut().with_disabled_credentials(false) - } - } - - if args.fastmode { - config.get_base_mut().set_high_default_traffic_volume(); + config = config + .with_optional_custom_env_ext( + BaseConfig::with_custom_nyxd, + args.nyxd_urls, + network_defaults::var_names::NYXD, + config::parse_urls, + ) + .with_base( + BaseConfig::with_disabled_credentials, + !args.enabled_credentials_mode, + ); } config diff --git a/clients/socks5/src/commands/run.rs b/clients/socks5/src/commands/run.rs index d74e6678fe..9d60479e71 100644 --- a/clients/socks5/src/commands/run.rs +++ b/clients/socks5/src/commands/run.rs @@ -9,7 +9,9 @@ use crate::{ use clap::Args; use config::NymConfig; +use crypto::asymmetric::identity; use log::*; +use nymsphinx::addressing::clients::Recipient; use version_checker::is_minor_version_compatible; #[derive(Args, Clone)] @@ -22,27 +24,46 @@ pub(crate) struct Run { #[clap(long)] config: Option, + /// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. + /// While this is going to hide its actual address information, it will make the actual communication + /// slower and consume nearly double the bandwidth as it will require sending reply SURBs. + /// + /// Note that some service providers might not support this. + // the alias here is included for backwards compatibility (1.1.4 and before) + #[clap(long, alias = "use_anonymous_sender_tag")] + use_anonymous_replies: bool, + /// Address of the socks5 provider to send messages to. #[clap(long)] - provider: Option, + provider: Option, /// Id of the gateway we want to connect to. If overridden, it is user's responsibility to /// ensure prior registration happened #[clap(long)] - gateway: Option, + gateway: Option, - /// Comma separated list of rest endpoints of the nymd validators - #[clap(long)] - nymd_validators: Option, + /// Comma separated list of rest endpoints of the nyxd validators + #[cfg(feature = "coconut")] + #[clap(long, alias = "nymd_validators", value_delimiter = ',')] + nyxd_urls: Option>, - /// Comma separated list of rest endpoints of the API validators - #[clap(long)] - api_validators: Option, + /// Comma separated list of rest endpoints of the Nym APIs + #[clap(long, value_delimiter = ',')] + nym_apis: Option>, /// Port for the socket to listen on #[clap(short, long)] port: Option, + /// Mostly debug-related option to increase default traffic rate so that you would not need to + /// modify config post init + #[clap(long, hide = true)] + fastmode: bool, + + /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) + #[clap(long, hide = true)] + no_cover: bool, + /// Set this client to work in a enabled credentials mode that would attempt to use gateway /// with bandwidth credential requirement. #[cfg(feature = "coconut")] @@ -53,11 +74,14 @@ pub(crate) struct Run { impl From for OverrideConfig { fn from(run_config: Run) -> Self { OverrideConfig { - nymd_validators: run_config.nymd_validators, - api_validators: run_config.api_validators, + nym_apis: run_config.nym_apis, port: run_config.port, - fastmode: false, + use_anonymous_replies: run_config.use_anonymous_replies, + fastmode: run_config.fastmode, + no_cover: run_config.no_cover, + #[cfg(feature = "coconut")] + nyxd_urls: run_config.nyxd_urls, #[cfg(feature = "coconut")] enabled_credentials_mode: run_config.enabled_credentials_mode, } @@ -86,13 +110,13 @@ fn version_check(cfg: &Config) -> bool { } } -pub(crate) async fn execute(args: &Run) -> Result<(), Box> { +pub(crate) async fn execute(args: &Run) -> Result<(), Box> { let id = &args.id; let mut config = match Config::load_from_file(Some(id)) { Ok(cfg) => cfg, Err(err) => { - error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err); + error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id); return Err(Box::new(Socks5ClientError::FailedToLoadConfig( id.to_string(), ))); @@ -102,6 +126,10 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box Version { let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| { - eprintln!("failed to parse client version! - {:?}", err); + eprintln!("failed to parse client version! - {err}"); process::exit(1) }); @@ -109,7 +109,7 @@ fn minor_0_12_upgrade( .set_custom_version(to_version.to_string().as_ref()); config.save_to_file(None).unwrap_or_else(|err| { - eprintln!("failed to overwrite config file! - {:?}", err); + eprintln!("failed to overwrite config file! - {err}"); print_failed_upgrade(config_version, &to_version); process::exit(1); }); @@ -145,7 +145,7 @@ pub(crate) fn execute(args: &Upgrade) { let id = &args.id; let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| { - eprintln!("failed to load existing config file! - {:?}", err); + eprintln!("failed to load existing config file! - {err}"); process::exit(1) }); diff --git a/clients/socks5/src/error.rs b/clients/socks5/src/error.rs index 2ff6ffbfc2..96e98f91cc 100644 --- a/clients/socks5/src/error.rs +++ b/clients/socks5/src/error.rs @@ -1,11 +1,12 @@ -use client_core::error::ClientCoreError; - use crate::socks::types::SocksProxyError; +use client_core::error::ClientCoreError; +use socks5_requests::ConnectionId; #[derive(thiserror::Error, Debug)] pub enum Socks5ClientError { #[error("I/O error: {0}")] IoError(#[from] std::io::Error), + #[error("client-core error: {0}")] ClientCoreError(#[from] ClientCoreError), @@ -14,8 +15,16 @@ pub enum Socks5ClientError { #[error("Failed to load config for: {0}")] FailedToLoadConfig(String), + #[error("Failed local version check, client and config mismatch")] FailedLocalVersionCheck, + #[error("Fail to bind address")] FailToBindAddress, + + #[error("Network requester: connection id {connection_id}: {error}")] + NetworkRequesterError { + connection_id: ConnectionId, + error: String, + }, } diff --git a/clients/socks5/src/main.rs b/clients/socks5/src/main.rs index d41e5e4bd8..6dcd9183ab 100644 --- a/clients/socks5/src/main.rs +++ b/clients/socks5/src/main.rs @@ -13,12 +13,12 @@ pub mod error; pub mod socks; #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<(), Box> { setup_logging(); println!("{}", banner()); let args = commands::Cli::parse(); - setup_env(args.config_env_file.clone()); + setup_env(args.config_env_file.as_ref()); commands::execute(&args).await } diff --git a/clients/socks5/src/socks/client.rs b/clients/socks5/src/socks/client.rs index 3e3c24347c..ff7caa1b6b 100644 --- a/clients/socks5/src/socks/client.rs +++ b/clients/socks5/src/socks/client.rs @@ -2,8 +2,8 @@ use super::authentication::{AuthenticationMethods, Authenticator, User}; use super::request::{SocksCommand, SocksRequest}; -use super::types::{ResponseCode, SocksProxyError}; -use super::{RESERVED, SOCKS_VERSION}; +use super::types::{ResponseCodeV4, ResponseCodeV5, SocksProxyError}; +use super::{SocksVersion, RESERVED, SOCKS4_VERSION, SOCKS5_VERSION}; use client_connections::{LaneQueueLengths, TransmissionLane}; use client_core::client::inbound_messages::{InputMessage, InputMessageSender}; use futures::channel::mpsc; @@ -20,7 +20,7 @@ use socks5_requests::{ConnectionId, Message, RemoteAddress, Request}; use std::io; use std::net::SocketAddr; use std::pin::Pin; -use task::ShutdownListener; +use task::TaskClient; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; use tokio::{self, net::TcpStream}; @@ -126,23 +126,45 @@ impl AsyncWrite for StreamState { } } +#[derive(Debug, Copy, Clone)] +pub(crate) struct Config { + use_surbs_for_responses: bool, + connection_start_surbs: u32, + per_request_surbs: u32, +} + +impl Config { + pub(crate) fn new( + use_surbs_for_responses: bool, + connection_start_surbs: u32, + per_request_surbs: u32, + ) -> Self { + Self { + use_surbs_for_responses, + connection_start_surbs, + per_request_surbs, + } + } +} + /// A client connecting to the Socks proxy server, because /// it wants to make a Nym-protected outbound request. Typically, this is /// something like e.g. a wallet app running on your laptop connecting to -/// SphinxSocksServer. +/// `SphinxSocksServer`. pub(crate) struct SocksClient { + config: Config, controller_sender: ControllerSender, stream: StreamState, auth_nmethods: u8, authenticator: Authenticator, - socks_version: u8, + socks_version: Option, input_sender: InputMessageSender, connection_id: ConnectionId, service_provider: Recipient, self_address: Recipient, started_proxy: bool, lane_queue_lengths: LaneQueueLengths, - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, } impl Drop for SocksClient { @@ -158,29 +180,34 @@ impl Drop for SocksClient { } impl SocksClient { - /// Create a new SOCKClient #[allow(clippy::too_many_arguments)] pub fn new( + config: Config, stream: TcpStream, authenticator: Authenticator, input_sender: InputMessageSender, - service_provider: Recipient, + service_provider: &Recipient, controller_sender: ControllerSender, - self_address: Recipient, + self_address: &Recipient, lane_queue_lengths: LaneQueueLengths, - shutdown_listener: ShutdownListener, + mut shutdown_listener: TaskClient, ) -> Self { + // If this task fails and exits, we don't want to send shutdown signal + shutdown_listener.mark_as_success(); + let connection_id = Self::generate_random(); + SocksClient { + config, controller_sender, connection_id, stream: StreamState::Available(stream), auth_nmethods: 0, - socks_version: 0, + socks_version: None, authenticator, input_sender, - service_provider, - self_address, + service_provider: *service_provider, + self_address: *self_address, started_proxy: false, lane_queue_lengths, shutdown_listener, @@ -192,13 +219,45 @@ impl SocksClient { rng.next_u64() } + pub async fn send_error(&mut self, err: SocksProxyError) -> Result<(), SocksProxyError> { + let error_text = format!("{err}"); + let Some(ref version) = self.socks_version else { + log::error!("Trying to send error without knowing the version"); + return Ok(()); + }; + + match version { + SocksVersion::V4 => { + let response = ResponseCodeV4::RequestRejected; + self.send_error_v4(response).await + } + SocksVersion::V5 => { + let response = if error_text.contains("Host") { + ResponseCodeV5::HostUnreachable + } else if error_text.contains("Network") { + ResponseCodeV5::NetworkUnreachable + } else if error_text.contains("ttl") { + ResponseCodeV5::TtlExpired + } else { + ResponseCodeV5::Failure + }; + self.send_error_v5(response).await + } + } + } + // Send an error back to the client - pub async fn error(&mut self, r: ResponseCode) -> Result<(), SocksProxyError> { - self.stream.write_all(&[5, r as u8]).await?; + pub async fn send_error_v4(&mut self, r: ResponseCodeV4) -> Result<(), SocksProxyError> { + self.stream.write_all(&[SOCKS4_VERSION, r as u8]).await?; Ok(()) } - /// Shutdown the TcpStream to the client and end the session + pub async fn send_error_v5(&mut self, r: ResponseCodeV5) -> Result<(), SocksProxyError> { + self.stream.write_all(&[SOCKS5_VERSION, r as u8]).await?; + Ok(()) + } + + /// Shutdown the `TcpStream` to the client and end the session pub async fn shutdown(&mut self) -> Result<(), SocksProxyError> { info!("client is shutting down its TCP stream"); self.stream.shutdown().await?; @@ -210,35 +269,37 @@ impl SocksClient { /// is in use and that the client is authenticated, then runs the request. pub async fn run(&mut self) -> Result<(), SocksProxyError> { debug!("New connection from: {}", self.stream.peer_addr()?.ip()); - let mut header = [0u8; 2]; + // Read a byte from the stream and determine the version being requested + let mut header = [0u8]; self.stream.read_exact(&mut header).await?; - self.socks_version = header[0]; - self.auth_nmethods = header[1]; + self.socks_version = match SocksVersion::try_from(header[0]) { + Ok(version) => Some(version), + Err(_err) => { + warn!("Init: Unsupported version: SOCKS{}", header[0]); + return self.shutdown().await; + } + }; - // Handle SOCKS4 requests - if header[0] != SOCKS_VERSION { - warn!("Init: Unsupported version: SOCKS{}", self.socks_version); - self.shutdown().await - } - // Valid SOCKS5 - else { - // Authenticate w/ client - self.authenticate().await?; - // Handle requests - self.handle_request().await + if self.socks_version == Some(SocksVersion::V5) { + let mut auth = [0u8]; + self.stream.read_exact(&mut auth).await?; + self.auth_nmethods = auth[0]; + self.authenticate_socks5().await?; } + + self.handle_request().await } - async fn send_connect_to_mixnet(&mut self, remote_address: RemoteAddress) { - let req = Request::new_connect(self.connection_id, remote_address, self.self_address); + async fn send_anonymous_connect_to_mixnet(&mut self, remote_address: RemoteAddress) { + let req = Request::new_connect(self.connection_id, remote_address, None); let msg = Message::Request(req); - let input_message = InputMessage::new_fresh( + let input_message = InputMessage::new_anonymous( self.service_provider, msg.into_bytes(), - false, + self.config.connection_start_surbs, TransmissionLane::ConnectionId(self.connection_id), ); self.input_sender @@ -247,17 +308,48 @@ impl SocksClient { .expect("InputMessageReceiver has stopped receiving!"); } + async fn send_connect_to_mixnet_with_return_address(&mut self, remote_address: RemoteAddress) { + let req = Request::new_connect(self.connection_id, remote_address, Some(self.self_address)); + let msg = Message::Request(req); + + let input_message = InputMessage::new_regular( + self.service_provider, + msg.into_bytes(), + TransmissionLane::ConnectionId(self.connection_id), + ); + self.input_sender + .send(input_message) + .await + .expect("InputMessageReceiver has stopped receiving!"); + } + + async fn send_connect_to_mixnet(&mut self, remote_address: RemoteAddress) { + if self.config.use_surbs_for_responses { + self.send_anonymous_connect_to_mixnet(remote_address).await + } else { + self.send_connect_to_mixnet_with_return_address(remote_address) + .await + } + } + async fn run_proxy(&mut self, conn_receiver: ConnectionReceiver, remote_proxy_target: String) { self.send_connect_to_mixnet(remote_proxy_target.clone()) .await; let stream = self.stream.run_proxy(); - let local_stream_remote = stream - .peer_addr() - .expect("failed to extract peer address") - .to_string(); + let peer_addr = match stream.peer_addr() { + Ok(peer_addr) => peer_addr, + Err(err) => { + log::error!("Unable to extract the remote peer address: {err}"); + return; + } + }; + let local_stream_remote = peer_addr.to_string(); + let connection_id = self.connection_id; let input_sender = self.input_sender.clone(); + let anonymous = self.config.use_surbs_for_responses; + let per_request_surbs = self.config.per_request_surbs; let recipient = self.service_provider; let (stream, _) = ProxyRunner::new( @@ -274,7 +366,16 @@ impl SocksClient { let provider_request = Request::new_send(conn_id, read_data, socket_closed); let provider_message = Message::Request(provider_request); let lane = TransmissionLane::ConnectionId(conn_id); - InputMessage::new_fresh(recipient, provider_message.into_bytes(), false, lane) + if anonymous { + InputMessage::new_anonymous( + recipient, + provider_message.into_bytes(), + per_request_surbs, + lane, + ) + } else { + InputMessage::new_regular(recipient, provider_message.into_bytes(), lane) + } }) .await .into_inner(); @@ -286,8 +387,17 @@ impl SocksClient { async fn handle_request(&mut self) -> Result<(), SocksProxyError> { debug!("Handling CONNECT Command"); - let request = SocksRequest::from_stream(&mut self.stream).await?; - let remote_address = request.to_string(); + let version = self + .socks_version + .as_ref() + .expect("Must read version before parsing request"); + + let request = match version { + SocksVersion::V4 => SocksRequest::from_stream_socks4(&mut self.stream).await?, + SocksVersion::V5 => SocksRequest::from_stream_socks5(&mut self.stream).await?, + }; + + let remote_address = request.address_string(); // setup for receiving from the mixnet let (mix_sender, mix_receiver) = mpsc::unbounded(); @@ -296,7 +406,10 @@ impl SocksClient { // Use the Proxy to connect to the specified addr/port SocksCommand::Connect => { trace!("Connecting to: {:?}", remote_address.clone()); - self.acknowledge_socks5().await; + match version { + SocksVersion::V4 => self.acknowledge_socks4().await, + SocksVersion::V5 => self.acknowledge_socks5().await, + } self.started_proxy = true; self.controller_sender @@ -319,7 +432,6 @@ impl SocksClient { SocksCommand::UdpAssociate => unimplemented!(), // not handled }; - self.shutdown_listener.mark_as_success(); Ok(()) } @@ -328,8 +440,8 @@ impl SocksClient { async fn acknowledge_socks5(&mut self) { self.stream .write_all(&[ - SOCKS_VERSION, - ResponseCode::Success as u8, + SOCKS5_VERSION, + ResponseCodeV5::Success as u8, RESERVED, 1, 127, @@ -343,13 +455,30 @@ impl SocksClient { .unwrap(); } + /// Writes a Socks4 header back to the requesting client's TCP stream, + async fn acknowledge_socks4(&mut self) { + self.stream + .write_all(&[ + 0, //SOCKS4_VERSION, + ResponseCodeV4::Granted as u8, + 0, + 0, + 127, + 0, + 0, + 1, + ]) + .await + .unwrap(); + } + /// Authenticate the incoming request. Each request is checked for its /// authentication method. A user/password request will extract the /// username and password from the stream, then check with the Authenticator /// to see if the resulting user is allowed. /// /// A lot of this could probably be put into the `SocksRequest::from_stream()` - /// constructor, and/or cleaned up with tokio::codec. It's mostly just + /// constructor, and/or cleaned up with `tokio::codec`. It's mostly just /// read-a-byte-or-two. The bytes being extracted look like this: /// /// +----+------+----------+------+------------+ @@ -361,7 +490,7 @@ impl SocksClient { /// Pulling out the stream code into its own home, and moving the if/else logic /// into the Authenticator (where it'll be more easily testable) /// would be a good next step. - async fn authenticate(&mut self) -> Result<(), SocksProxyError> { + async fn authenticate_socks5(&mut self) -> Result<(), SocksProxyError> { debug!("Authenticating w/ {}", self.stream.peer_addr()?.ip()); // Get valid auth methods let methods = self.get_available_methods().await?; @@ -370,7 +499,7 @@ impl SocksClient { let mut response = [0u8; 2]; // Set the version in the response - response[0] = SOCKS_VERSION; + response[0] = SOCKS5_VERSION; if methods.contains(&(AuthenticationMethods::UserPass as u8)) { // Set the default auth method (NO AUTH) response[1] = AuthenticationMethods::UserPass as u8; @@ -406,11 +535,11 @@ impl SocksClient { // Authenticate passwords if self.authenticator.is_allowed(&user) { debug!("Access Granted. User: {}", user.username); - let response = [1, ResponseCode::Success as u8]; + let response = [1, ResponseCodeV5::Success as u8]; self.stream.write_all(&response).await?; } else { debug!("Access Denied. User: {}", user.username); - let response = [1, ResponseCode::Failure as u8]; + let response = [1, ResponseCodeV5::Failure as u8]; self.stream.write_all(&response).await?; // Shutdown @@ -429,7 +558,7 @@ impl SocksClient { response[1] = AuthenticationMethods::NoMethods as u8; self.stream.write_all(&response).await?; self.shutdown().await?; - Err(ResponseCode::Failure.into()) + Err(ResponseCodeV5::Failure.into()) } } diff --git a/clients/socks5/src/socks/mixnet_responses.rs b/clients/socks5/src/socks/mixnet_responses.rs index 430b4322da..85d0a7da86 100644 --- a/clients/socks5/src/socks/mixnet_responses.rs +++ b/clients/socks5/src/socks/mixnet_responses.rs @@ -9,13 +9,15 @@ use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBuffer use nymsphinx::receiver::ReconstructedMessage; use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender}; use socks5_requests::Message; -use task::ShutdownListener; +use task::TaskClient; + +use crate::error::Socks5ClientError; pub(crate) struct MixnetResponseListener { buffer_requester: ReceivedBufferRequestSender, mix_response_receiver: ReconstructedMessagesReceiver, controller_sender: ControllerSender, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl Drop for MixnetResponseListener { @@ -25,9 +27,9 @@ impl Drop for MixnetResponseListener { .unbounded_send(ReceivedBufferMessage::ReceiverDisconnect) { if self.shutdown.is_shutdown_poll() { - log::debug!("The buffer request failed: {}", err); + log::debug!("The buffer request failed: {err}"); } else { - log::error!("The buffer request failed: {}", err); + log::error!("The buffer request failed: {err}"); } } } @@ -37,7 +39,7 @@ impl MixnetResponseListener { pub(crate) fn new( buffer_requester: ReceivedBufferRequestSender, controller_sender: ControllerSender, - shutdown: ShutdownListener, + shutdown: TaskClient, ) -> Self { let (mix_response_sender, mix_response_receiver) = mpsc::unbounded(); buffer_requester @@ -52,20 +54,23 @@ impl MixnetResponseListener { } } - async fn on_message(&self, reconstructed_message: ReconstructedMessage) { + fn on_message( + &self, + reconstructed_message: ReconstructedMessage, + ) -> Result<(), Socks5ClientError> { let raw_message = reconstructed_message.message; - if reconstructed_message.reply_surb.is_some() { - warn!("this message had a surb - we didn't do anything with it"); + if reconstructed_message.sender_tag.is_some() { + warn!("this message was sent anonymously - it couldn't have come from the service provider"); } let response = match Message::try_from_bytes(&raw_message) { Err(err) => { - warn!("failed to parse received response - {:?}", err); - return; + warn!("failed to parse received response - {err}"); + return Ok(()); } Ok(Message::Request(_)) => { warn!("unexpected request"); - return; + return Ok(()); } Ok(Message::Response(data)) => data, Ok(Message::NetworkRequesterResponse(r)) => { @@ -73,7 +78,10 @@ impl MixnetResponseListener { "Network requester failed on connection id {} with error: {}", r.connection_id, r.network_requester_error ); - return; + return Err(Socks5ClientError::NetworkRequesterError { + connection_id: r.connection_id, + error: r.network_requester_error, + }); } }; @@ -84,18 +92,21 @@ impl MixnetResponseListener { response.is_closed, )) .unwrap(); + + Ok(()) } pub(crate) async fn run(&mut self) { while !self.shutdown.is_shutdown() { tokio::select! { - received_responses = self.mix_response_receiver.next() => match received_responses { - Some(received_responses) => { + received_responses = self.mix_response_receiver.next() => { + if let Some(received_responses) = received_responses { for reconstructed_message in received_responses { - self.on_message(reconstructed_message).await; + if let Err(err) = self.on_message(reconstructed_message) { + self.shutdown.send_status_msg(Box::new(err)); + } } - }, - None => { + } else { log::trace!("MixnetResponseListener: Stopping since channel closed"); break; } diff --git a/clients/socks5/src/socks/mod.rs b/clients/socks5/src/socks/mod.rs index 20dd99f125..16603839ff 100644 --- a/clients/socks5/src/socks/mod.rs +++ b/clients/socks5/src/socks/mod.rs @@ -1,7 +1,11 @@ #![forbid(unsafe_code)] +use std::convert::TryFrom; + +use self::types::SocksProxyError; + pub mod authentication; -mod client; +pub(crate) mod client; pub(crate) mod mixnet_responses; mod request; pub mod server; @@ -9,6 +13,27 @@ pub mod types; pub mod utils; /// Version of socks -const SOCKS_VERSION: u8 = 0x05; +const SOCKS4_VERSION: u8 = 0x04; +const SOCKS5_VERSION: u8 = 0x05; const RESERVED: u8 = 0x00; + +#[derive(Clone, PartialEq, Eq)] +pub enum SocksVersion { + V4 = 0x04, + V5 = 0x05, +} + +pub struct InvalidSocksVersion; + +impl TryFrom for SocksVersion { + type Error = SocksProxyError; + + fn try_from(version: u8) -> Result { + match version { + SOCKS4_VERSION => Ok(Self::V4), + SOCKS5_VERSION => Ok(Self::V5), + _ => Err(SocksProxyError::UnsupportedProxyVersion(version)), + } + } +} diff --git a/clients/socks5/src/socks/request.rs b/clients/socks5/src/socks/request.rs index 92d687aa90..ecf9b656f3 100644 --- a/clients/socks5/src/socks/request.rs +++ b/clients/socks5/src/socks/request.rs @@ -1,5 +1,7 @@ -use super::types::{AddrType, ResponseCode, SocksProxyError}; -use super::{utils as socks_utils, SOCKS_VERSION}; +use crate::socks::SOCKS4_VERSION; + +use super::types::{AddrType, ResponseCodeV5, SocksProxyError}; +use super::{utils as socks_utils, SOCKS5_VERSION}; use log::*; use std::fmt::{self, Display}; use tokio::io::{AsyncRead, AsyncReadExt}; @@ -15,80 +17,114 @@ pub(crate) struct SocksRequest { } impl SocksRequest { - /// Parse a SOCKS5 request from a TcpStream - pub async fn from_stream(stream: &mut R) -> Result + /// Parse a SOCKS4 request from a `TcpStream` + /// From documents at: + /// - SOCKS4: https://www.openssh.com/txt/socks4.protocol + /// - SOCKS4a: https://www.openssh.com/txt/socks4a.protocol + pub async fn from_stream_socks4(stream: &mut R) -> Result where R: AsyncRead + Unpin, { + log::trace!("read from stream socks4"); + + let mut packet = [0u8; 3]; + stream.read_exact(&mut packet).await?; + + // CD (command) + let Some(command) = SocksCommand::from(packet[0] as usize) else { + log::warn!("Invalid Command"); + return Err(ResponseCodeV5::CommandNotSupported.into()); + }; + + // DSTPORT + let mut port = [0u8; 2]; + port.copy_from_slice(&packet[1..]); + let port = merge_u8_into_u16(port[0], port[1]); + + // DSTIP + let mut ip = [0u8; 4]; + stream.read_exact(&mut ip).await?; + + // USERID + let _userid = read_until_zero(stream).await; + + // SOCKS4a extension + // https://www.openssh.com/txt/socks4a.protocol + // If the IP is 0.0.0.x with x nonzero, read the domain name + let (addr, addr_type) = if ip[..3] == [0, 0, 0] && ip[3] != 0 { + (read_until_zero(stream).await?, AddrType::Domain) + } else { + (ip.to_vec(), AddrType::V4) + }; + + // Return parsed request + Ok(SocksRequest { + version: SOCKS4_VERSION, + command, + addr_type, + addr, + port, + }) + } + /// Parse a SOCKS5 request from a `TcpStream` + /// From: https://www.rfc-editor.org/rfc/rfc1928 + pub async fn from_stream_socks5(stream: &mut R) -> Result + where + R: AsyncRead + Unpin, + { + log::info!("read from stream socks5"); + let mut packet = [0u8; 4]; // Read a byte from the stream and determine the version being requested stream.read_exact(&mut packet).await?; - if packet[0] != SOCKS_VERSION { - warn!("from_stream Unsupported version: SOCKS{}", packet[0]); + // VER + if packet[0] != SOCKS5_VERSION { + warn!("Unsupported version: SOCKS{}", packet[0]); return Err(SocksProxyError::UnsupportedProxyVersion(packet[0])); } - // Get command - let mut command: SocksCommand = SocksCommand::Connect; - match SocksCommand::from(packet[1] as usize) { - Some(com) => { - command = com; - Ok(()) - } - None => { - warn!("Invalid Command"); - Err(ResponseCode::CommandNotSupported) - } - }?; + // CMD + let Some(command) = SocksCommand::from(packet[1] as usize) else { + warn!("Invalid Command"); + return Err(ResponseCodeV5::CommandNotSupported.into()); + }; - // DST.address + // RSV + // packet[2] is reserved - let mut addr_type: AddrType = AddrType::V6; - match AddrType::from(packet[3] as usize) { - Some(addr) => { - addr_type = addr; - Ok(()) - } - None => { - error!("No Addr"); - Err(ResponseCode::AddrTypeNotSupported) - } - }?; + // ATYP + let Some(addr_type) = AddrType::from(packet[3] as usize) else { + error!("No Addr"); + return Err(ResponseCodeV5::AddrTypeNotSupported.into()) + }; - trace!("Getting Addr"); - // Get Addr from addr_type and stream - let addr: Result, SocksProxyError> = match addr_type { + // DST.ADDR + let addr = match addr_type { AddrType::Domain => { - let mut domain_length = [0u8; 1]; + let mut domain_length = [0u8]; stream.read_exact(&mut domain_length).await?; - let mut domain = vec![0u8; domain_length[0] as usize]; stream.read_exact(&mut domain).await?; - - Ok(domain) + domain } AddrType::V4 => { let mut addr = [0u8; 4]; stream.read_exact(&mut addr).await?; - Ok(addr.to_vec()) + addr.to_vec() } AddrType::V6 => { let mut addr = [0u8; 16]; stream.read_exact(&mut addr).await?; - Ok(addr.to_vec()) + addr.to_vec() } }; - let addr = addr?; - - // read DST.port + // DST.PORT let mut port = [0u8; 2]; stream.read_exact(&mut port).await?; - // Merge two u8s into u16 - let port = (u16::from(port[0]) << 8) | u16::from(port[1]); + let port = merge_u8_into_u16(port[0], port[1]); - // Return parsed request Ok(SocksRequest { version: packet[0], command, @@ -97,14 +133,18 @@ impl SocksRequest { port, }) } + + /// Print out the address and port to a String. + /// This might return domain:port, ipv6:port, or ipv4:port. + pub fn address_string(&self) -> String { + let address = socks_utils::pretty_print_addr(&self.addr_type, &self.addr); + format!("{}:{}", address, self.port) + } } impl Display for SocksRequest { - /// Print out the address and port to a String. - /// This might return domain:port, ipv6:port, or ipv4:port. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let address = socks_utils::pretty_print_addr(&self.addr_type, &self.addr); - write!(f, "{}:{}", address, self.port) + write!(f, "{}", self.address_string()) } } @@ -127,3 +167,23 @@ impl SocksCommand { } } } + +fn merge_u8_into_u16(a: u8, b: u8) -> u16 { + (u16::from(a) << 8) | u16::from(b) +} + +async fn read_until_zero(stream: &mut R) -> Result, SocksProxyError> +where + R: AsyncRead + Unpin, +{ + let mut result = Vec::new(); + let mut char = [0u8]; + loop { + stream.read_exact(&mut char).await?; + if char[0] == 0 { + break; + } + result.push(char[0]); + } + Ok(result) +} diff --git a/clients/socks5/src/socks/server.rs b/clients/socks5/src/socks/server.rs index 551f82b788..0b193dc9d4 100644 --- a/clients/socks5/src/socks/server.rs +++ b/clients/socks5/src/socks/server.rs @@ -1,8 +1,9 @@ use crate::error::Socks5ClientError; -use super::authentication::Authenticator; -use super::client::SocksClient; -use super::{mixnet_responses::MixnetResponseListener, types::ResponseCode}; +use super::{ + authentication::Authenticator, client::SocksClient, mixnet_responses::MixnetResponseListener, +}; +use crate::socks::client; use client_connections::{ConnectionCommandSender, LaneQueueLengths}; use client_core::client::{ inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender, @@ -12,7 +13,7 @@ use nymsphinx::addressing::clients::Recipient; use proxy_helpers::connection_controller::{BroadcastActiveConnections, Controller}; use std::net::SocketAddr; use tap::TapFallible; -use task::ShutdownListener; +use task::TaskClient; use tokio::net::TcpListener; /// A Socks5 server that listens for connections. @@ -21,8 +22,9 @@ pub struct SphinxSocksServer { listening_address: SocketAddr, service_provider: Recipient, self_address: Recipient, + client_config: client::Config, lane_queue_lengths: LaneQueueLengths, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl SphinxSocksServer { @@ -33,7 +35,8 @@ impl SphinxSocksServer { service_provider: Recipient, self_address: Recipient, lane_queue_lengths: LaneQueueLengths, - shutdown: ShutdownListener, + client_config: client::Config, + shutdown: TaskClient, ) -> Self { // hardcode ip as we (presumably) ONLY want to listen locally. If we change it, we can // just modify the config @@ -44,6 +47,7 @@ impl SphinxSocksServer { listening_address: format!("{}:{}", ip, port).parse().unwrap(), service_provider, self_address, + client_config, lane_queue_lengths, shutdown, } @@ -85,47 +89,27 @@ impl SphinxSocksServer { loop { tokio::select! { Ok((stream, _remote)) = listener.accept() => { - // TODO Optimize this let mut client = SocksClient::new( + self.client_config, stream, self.authenticator.clone(), input_sender.clone(), - self.service_provider, + &self.service_provider, controller_sender.clone(), - self.self_address, + &self.self_address, self.lane_queue_lengths.clone(), self.shutdown.clone(), ); tokio::spawn(async move { - { - match client.run().await { - Ok(_) => {} - Err(error) => { - error!("Error! {}", error); - let error_text = format!("{}", error); - - let response: ResponseCode; - - if error_text.contains("Host") { - response = ResponseCode::HostUnreachable; - } else if error_text.contains("Network") { - response = ResponseCode::NetworkUnreachable; - } else if error_text.contains("ttl") { - response = ResponseCode::TtlExpired - } else { - response = ResponseCode::Failure - } - - if client.error(response).await.is_err() { - warn!("Failed to send error code"); - }; - if client.shutdown().await.is_err() { - warn!("Failed to shutdown TcpStream"); - }; - } + if let Err(err) = client.run().await { + error!("Error! {err}"); + if client.send_error(err).await.is_err() { + warn!("Failed to send error code"); + }; + if client.shutdown().await.is_err() { + warn!("Failed to shutdown TcpStream"); }; - // client gets dropped here } }); }, diff --git a/clients/socks5/src/socks/types.rs b/clients/socks5/src/socks/types.rs index b043da5ccc..e561d0025d 100644 --- a/clients/socks5/src/socks/types.rs +++ b/clients/socks5/src/socks/types.rs @@ -1,23 +1,33 @@ -use snafu::Snafu; -#[derive(Debug, Snafu)] +/// SOCKS4 Response codes +#[allow(dead_code)] +pub(crate) enum ResponseCodeV4 { + Granted = 0x5a, + RequestRejected = 0x5b, + CannotConnectToIdent = 0x5c, + DifferentUserId = 0x5d, +} + /// Possible SOCKS5 Response Codes -pub(crate) enum ResponseCode { +#[allow(dead_code)] +#[derive(Debug, thiserror::Error)] +pub(crate) enum ResponseCodeV5 { + #[error("SOCKS5 Server Success")] Success = 0x00, - #[snafu(display("SOCKS5 Server Failure"))] + #[error("SOCKS5 Server Failure")] Failure = 0x01, - #[snafu(display("SOCKS5 Rule failure"))] + #[error("SOCKS5 Rule failure")] RuleFailure = 0x02, - #[snafu(display("network unreachable"))] + #[error("network unreachable")] NetworkUnreachable = 0x03, - #[snafu(display("host unreachable"))] + #[error("host unreachable")] HostUnreachable = 0x04, - #[snafu(display("connection refused"))] + #[error("connection refused")] ConnectionRefused = 0x05, - #[snafu(display("TTL expired"))] + #[error("TTL expired")] TtlExpired = 0x06, - #[snafu(display("Command not supported"))] + #[error("Command not supported")] CommandNotSupported = 0x07, - #[snafu(display("Addr Type not supported"))] + #[error("Addr Type not supported")] AddrTypeNotSupported = 0x08, } @@ -30,7 +40,7 @@ pub enum SocksProxyError { impl std::fmt::Display for SocksProxyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - SocksProxyError::GenericError(err) => write!(f, "GenericError - {}", err), + SocksProxyError::GenericError(err) => write!(f, "GenericError - {err}"), SocksProxyError::UnsupportedProxyVersion(version) => { write!(f, "Unsupported proxy version {}", version) } @@ -48,7 +58,7 @@ where } /// DST.addr variant types -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub(crate) enum AddrType { V4 = 0x01, Domain = 0x03, diff --git a/clients/validator/src/index.ts b/clients/validator/src/index.ts index 642fc2b077..dac29371c3 100644 --- a/clients/validator/src/index.ts +++ b/clients/validator/src/index.ts @@ -85,8 +85,8 @@ export default class ValidatorClient implements INymClient { static async connect( mnemonic: string, - nymdUrl: string, - validatorApiUrl: string, + nyxdUrl: string, + nymApiUrl: string, prefix: string, mixnetContract: string, vestingContract: string, @@ -94,19 +94,19 @@ export default class ValidatorClient implements INymClient { ): Promise { const wallet = await ValidatorClient.buildWallet(mnemonic, prefix); - const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, validatorApiUrl, prefix, denom); + const signingClient = await SigningClient.connectWithNymSigner(wallet, nyxdUrl, nymApiUrl, prefix, denom); return new ValidatorClient(signingClient, prefix, mixnetContract, vestingContract, denom); } static async connectForQuery( - nymdUrl: string, - validatorApiUrl: string, + nyxdUrl: string, + nymApiUrl: string, prefix: string, mixnetContract: string, vestingContract: string, denom: string, ): Promise { - const queryClient = await QueryClient.connectWithNym(nymdUrl, validatorApiUrl); + const queryClient = await QueryClient.connectWithNym(nyxdUrl, nymApiUrl); return new ValidatorClient(queryClient, prefix, mixnetContract, vestingContract, denom); } @@ -191,11 +191,11 @@ export default class ValidatorClient implements INymClient { return this.client.getIntervalRewardPercent(this.mixnetContract); } - public async getAllNymdMixnodes(): Promise { + public async getAllNyxdMixnodes(): Promise { let mixNodes: MixNodeBond[] = []; const limit = 50; let startAfter; - for (;;) { + for (; ;) { // eslint-disable-next-line no-await-in-loop const pagedResponse: PagedMixnodeResponse = await this.client.getMixNodesPaged(this.mixnetContract, limit); mixNodes = mixNodes.concat(pagedResponse.nodes); @@ -209,11 +209,11 @@ export default class ValidatorClient implements INymClient { return mixNodes; } - public async getAllNymdGateways(): Promise { + public async getAllNyxdGateways(): Promise { let gateways: GatewayBond[] = []; const limit = 50; let startAfter; - for (;;) { + for (; ;) { // eslint-disable-next-line no-await-in-loop const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract, limit); gateways = gateways.concat(pagedResponse.nodes); @@ -232,11 +232,11 @@ export default class ValidatorClient implements INymClient { * * @param mixIdentity identity of the node to which the delegation was sent */ - public async getAllNymdSingleMixnodeDelegations(mixIdentity: string): Promise { + public async getAllNyxdSingleMixnodeDelegations(mixIdentity: string): Promise { let delegations: Delegation[] = []; const limit = 250; let startAfter; - for (;;) { + for (; ;) { // eslint-disable-next-line no-await-in-loop const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged( this.mixnetContract, @@ -255,11 +255,11 @@ export default class ValidatorClient implements INymClient { return delegations; } - public async getAllNymdDelegatorDelegations(delegator: string): Promise { + public async getAllNyxdDelegatorDelegations(delegator: string): Promise { let delegations: Delegation[] = []; const limit = 250; let startAfter; - for (;;) { + for (; ;) { // eslint-disable-next-line no-await-in-loop const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged( this.mixnetContract, @@ -278,11 +278,11 @@ export default class ValidatorClient implements INymClient { return delegations; } - public async getAllNymdNetworkDelegations(): Promise { + public async getAllNyxdNetworkDelegations(): Promise { let delegations: Delegation[] = []; const limit = 250; let startAfter; - for (;;) { + for (; ;) { // eslint-disable-next-line no-await-in-loop const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllNetworkDelegationsPaged( this.mixnetContract, diff --git a/clients/validator/src/validator-api-querier.ts b/clients/validator/src/nym-api-querier.ts similarity index 58% rename from clients/validator/src/validator-api-querier.ts rename to clients/validator/src/nym-api-querier.ts index 7c61f91bd7..3fa0471804 100644 --- a/clients/validator/src/validator-api-querier.ts +++ b/clients/validator/src/nym-api-querier.ts @@ -6,13 +6,13 @@ import axios from 'axios'; import { GatewayBond, MixNodeBond } from './types'; -export const VALIDATOR_API_VERSION = '/v1'; -export const VALIDATOR_API_GATEWAYS_PATH = `${VALIDATOR_API_VERSION}/gateways`; -export const VALIDATOR_API_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes`; -export const VALIDATOR_API_ACTIVE_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes/active`; -export const VALIDATOR_API_REWARDED_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes/rewarded`; +export const NYM_API_VERSION = '/v1'; +export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`; +export const NYM_API_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes`; +export const NYM_API_ACTIVE_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes/active`; +export const NYM_API_REWARDED_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes/rewarded`; -export interface IValidatorApiQuery { +export interface INymApiQuery { getCachedMixnodes(): Promise; getCachedGateways(): Promise; @@ -22,16 +22,16 @@ export interface IValidatorApiQuery { getRewardedMixnodes(): Promise; } -export default class ValidatorApiQuerier implements IValidatorApiQuery { - validatorApiUrl: string; +export default class NymApiQuerier implements INymApiQuery { + nymApiUrl: string; - constructor(validatorApiUrl: string) { - this.validatorApiUrl = validatorApiUrl; + constructor(nymApiUrl: string) { + this.nymApiUrl = nymApiUrl; } async getCachedMixnodes(): Promise { - const url = new URL(this.validatorApiUrl); - url.pathname += VALIDATOR_API_MIXNODES_PATH; + const url = new URL(this.nymApiUrl); + url.pathname += NYM_API_MIXNODES_PATH; const response = await axios.get(url.toString()); if (response.status === 200) { @@ -41,8 +41,8 @@ export default class ValidatorApiQuerier implements IValidatorApiQuery { } async getCachedGateways(): Promise { - const url = new URL(this.validatorApiUrl); - url.pathname += VALIDATOR_API_GATEWAYS_PATH; + const url = new URL(this.nymApiUrl); + url.pathname += NYM_API_GATEWAYS_PATH; const response = await axios.get(url.toString()); if (response.status === 200) { @@ -52,8 +52,8 @@ export default class ValidatorApiQuerier implements IValidatorApiQuery { } async getActiveMixnodes(): Promise { - const url = new URL(this.validatorApiUrl); - url.pathname += VALIDATOR_API_ACTIVE_MIXNODES_PATH; + const url = new URL(this.nymApiUrl); + url.pathname += NYM_API_ACTIVE_MIXNODES_PATH; const response = await axios.get(url.toString()); if (response.status === 200) { @@ -63,8 +63,8 @@ export default class ValidatorApiQuerier implements IValidatorApiQuery { } async getRewardedMixnodes(): Promise { - const url = new URL(this.validatorApiUrl); - url.pathname += VALIDATOR_API_REWARDED_MIXNODES_PATH; + const url = new URL(this.nymApiUrl); + url.pathname += NYM_API_REWARDED_MIXNODES_PATH; const response = await axios.get(url.toString()); if (response.status === 200) { diff --git a/clients/validator/src/nymd-querier.ts b/clients/validator/src/nyxd-querier.ts similarity index 97% rename from clients/validator/src/nymd-querier.ts rename to clients/validator/src/nyxd-querier.ts index ee2148d56d..edd791a8fe 100644 --- a/clients/validator/src/nymd-querier.ts +++ b/clients/validator/src/nyxd-querier.ts @@ -5,7 +5,7 @@ import { JsonObject } from '@cosmjs/cosmwasm-stargate'; // eslint-disable-next-line import/no-cycle -import { INymdQuery } from './query-client'; +import { INyxdQuery } from './query-client'; import { ContractStateParams, Delegation, @@ -25,7 +25,7 @@ interface SmartContractQuery { queryContractSmart(address: string, queryMsg: Record): Promise; } -export default class NymdQuerier implements INymdQuery { +export default class NyxdQuerier implements INyxdQuery { client: SmartContractQuery; constructor(client: SmartContractQuery) { diff --git a/clients/validator/src/query-client.ts b/clients/validator/src/query-client.ts index 9e3937a89e..c78237cdb4 100644 --- a/clients/validator/src/query-client.ts +++ b/clients/validator/src/query-client.ts @@ -12,7 +12,7 @@ import { } from '@cosmjs/stargate'; import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient'; // eslint-disable-next-line import/no-cycle -import NymdQuerier from './nymd-querier'; +import NyxdQuerier from './nyxd-querier'; import { ContractStateParams, Delegation, @@ -29,7 +29,7 @@ import { PagedMixnodeResponse, RewardingStatus, } from './types'; -import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier'; +import NymApiQuerier, { INymApiQuery as INymApiQuery } from './nym-api-querier'; export interface ICosmWasmQuery { // methods exposed by `CosmWasmClient` @@ -52,7 +52,7 @@ export interface ICosmWasmQuery { queryContractSmart(address: string, queryMsg: Record): Promise; } -export interface INymdQuery { +export interface INyxdQuery { // nym-specific implemented inside NymQuerier getContractVersion(mixnetContractAddress: string): Promise; @@ -93,46 +93,46 @@ export interface INymdQuery { ): Promise; } -export interface IQueryClient extends ICosmWasmQuery, INymdQuery, IValidatorApiQuery {} +export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { } export default class QueryClient extends CosmWasmClient implements IQueryClient { - private nymdQuerier: NymdQuerier; + private nyxdQuerier: NyxdQuerier; - private validatorApiQuerier: ValidatorApiQuerier; + private nymApiQuerier: NymApiQuerier; - private constructor(tmClient: Tendermint34Client, validatorApiUrl: string) { + private constructor(tmClient: Tendermint34Client, nymApiUrl: string) { super(tmClient); - this.nymdQuerier = new NymdQuerier(this); - this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl); + this.nyxdQuerier = new NyxdQuerier(this); + this.nymApiQuerier = new NymApiQuerier(nymApiUrl); } - public static async connectWithNym(nymdUrl: string, validatorApiUrl: string): Promise { - const tmClient = await Tendermint34Client.connect(nymdUrl); - return new QueryClient(tmClient, validatorApiUrl); + public static async connectWithNym(nyxdUrl: string, nymApiUrl: string): Promise { + const tmClient = await Tendermint34Client.connect(nyxdUrl); + return new QueryClient(tmClient, nymApiUrl); } getContractVersion(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getContractVersion(mixnetContractAddress); + return this.nyxdQuerier.getContractVersion(mixnetContractAddress); } getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise { - return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter); + return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter); } getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise { - return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter); + return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter); } ownsMixNode(mixnetContractAddress: string, address: string): Promise { - return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address); + return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address); } ownsGateway(mixnetContractAddress: string, address: string): Promise { - return this.nymdQuerier.ownsGateway(mixnetContractAddress, address); + return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address); } getStateParams(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getStateParams(mixnetContractAddress); + return this.nyxdQuerier.getStateParams(mixnetContractAddress); } getAllNetworkDelegationsPaged( @@ -140,7 +140,7 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient limit?: number, startAfter?: [string, string], ): Promise { - return this.nymdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter); + return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter); } getMixNodeDelegationsPaged( @@ -149,7 +149,7 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient limit?: number, startAfter?: string, ): Promise { - return this.nymdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter); + return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter); } getDelegatorDelegationsPaged( @@ -158,31 +158,31 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient limit?: number, startAfter?: string, ): Promise { - return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter); + return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter); } getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise { - return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator); + return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator); } getLayerDistribution(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getLayerDistribution(mixnetContractAddress); + return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress); } getRewardPool(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getRewardPool(mixnetContractAddress); + return this.nyxdQuerier.getRewardPool(mixnetContractAddress); } getCirculatingSupply(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress); + return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress); } getIntervalRewardPercent(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress); + return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress); } getSybilResistancePercent(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress); + return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress); } getRewardingStatus( @@ -190,22 +190,22 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient mixIdentity: string, rewardingIntervalNonce: number, ): Promise { - return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce); + return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce); } getCachedGateways(): Promise { - return this.validatorApiQuerier.getCachedGateways(); + return this.nymApiQuerier.getCachedGateways(); } getCachedMixnodes(): Promise { - return this.validatorApiQuerier.getCachedMixnodes(); + return this.nymApiQuerier.getCachedMixnodes(); } getActiveMixnodes(): Promise { - return this.validatorApiQuerier.getActiveMixnodes(); + return this.nymApiQuerier.getActiveMixnodes(); } getRewardedMixnodes(): Promise { - return this.validatorApiQuerier.getRewardedMixnodes(); + return this.nymApiQuerier.getRewardedMixnodes(); } } diff --git a/clients/validator/src/signing-client.ts b/clients/validator/src/signing-client.ts index ef8913243d..8cb589cd0e 100644 --- a/clients/validator/src/signing-client.ts +++ b/clients/validator/src/signing-client.ts @@ -14,7 +14,7 @@ import { ChangeAdminResult } from '@cosmjs/cosmwasm-stargate/build/signingcosmwa import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; import { nymGasPrice } from './stargate-helper'; import { IQueryClient } from './query-client'; -import NymdQuerier from './nymd-querier'; +import NyxdQuerier from './nyxd-querier'; import { ContractStateParams, Delegation, @@ -33,7 +33,7 @@ import { PagedMixnodeResponse, RewardingStatus, } from './types'; -import ValidatorApiQuerier from './validator-api-querier'; +import NymApiQuerier from './nym-api-querier'; // methods exposed by `SigningCosmWasmClient` export interface ICosmWasmSigning { @@ -197,29 +197,29 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign } export default class SigningClient extends SigningCosmWasmClient implements ISigningClient { - private nymdQuerier: NymdQuerier; + private nyxdQuerier: NyxdQuerier; - private validatorApiQuerier: ValidatorApiQuerier; + private nymApiQuerier: NymApiQuerier; clientAddress: string; private constructor( clientAddress: string, - validatorApiUrl: string, + nymApiUrl: string, tmClient: Tendermint34Client, wallet: DirectSecp256k1HdWallet, signerOptions: SigningCosmWasmClientOptions, ) { super(tmClient, wallet, signerOptions); this.clientAddress = clientAddress; - this.nymdQuerier = new NymdQuerier(this); - this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl); + this.nyxdQuerier = new NyxdQuerier(this); + this.nymApiQuerier = new NymApiQuerier(nymApiUrl); } public static async connectWithNymSigner( wallet: DirectSecp256k1HdWallet, - nymdUrl: string, - validatorApiUrl: string, + nyxdUrl: string, + nymApiUrl: string, prefix: string, denom: string, ): Promise { @@ -228,34 +228,34 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig prefix, gasPrice: nymGasPrice(denom), }; - const tmClient = await Tendermint34Client.connect(nymdUrl); - return new SigningClient(address, validatorApiUrl, tmClient, wallet, signerOptions); + const tmClient = await Tendermint34Client.connect(nyxdUrl); + return new SigningClient(address, nymApiUrl, tmClient, wallet, signerOptions); } // query related: getContractVersion(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getContractVersion(mixnetContractAddress); + return this.nyxdQuerier.getContractVersion(mixnetContractAddress); } getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise { - return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter); + return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter); } getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise { - return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter); + return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter); } ownsMixNode(mixnetContractAddress: string, address: string): Promise { - return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address); + return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address); } ownsGateway(mixnetContractAddress: string, address: string): Promise { - return this.nymdQuerier.ownsGateway(mixnetContractAddress, address); + return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address); } getStateParams(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getStateParams(mixnetContractAddress); + return this.nyxdQuerier.getStateParams(mixnetContractAddress); } getAllNetworkDelegationsPaged( @@ -263,7 +263,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig limit?: number, startAfter?: [string, string], ): Promise { - return this.nymdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter); + return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter); } getMixNodeDelegationsPaged( @@ -272,7 +272,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig limit?: number, startAfter?: string, ): Promise { - return this.nymdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter); + return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter); } getDelegatorDelegationsPaged( @@ -281,31 +281,31 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig limit?: number, startAfter?: string, ): Promise { - return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter); + return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter); } getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise { - return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator); + return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator); } getLayerDistribution(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getLayerDistribution(mixnetContractAddress); + return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress); } getRewardPool(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getRewardPool(mixnetContractAddress); + return this.nyxdQuerier.getRewardPool(mixnetContractAddress); } getCirculatingSupply(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress); + return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress); } getIntervalRewardPercent(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress); + return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress); } getSybilResistancePercent(mixnetContractAddress: string): Promise { - return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress); + return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress); } getRewardingStatus( @@ -313,23 +313,23 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig mixIdentity: string, rewardingIntervalNonce: number, ): Promise { - return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce); + return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce); } getCachedGateways(): Promise { - return this.validatorApiQuerier.getCachedGateways(); + return this.nymApiQuerier.getCachedGateways(); } getCachedMixnodes(): Promise { - return this.validatorApiQuerier.getCachedMixnodes(); + return this.nymApiQuerier.getCachedMixnodes(); } getActiveMixnodes(): Promise { - return this.validatorApiQuerier.getActiveMixnodes(); + return this.nymApiQuerier.getActiveMixnodes(); } getRewardedMixnodes(): Promise { - return this.validatorApiQuerier.getRewardedMixnodes(); + return this.nymApiQuerier.getRewardedMixnodes(); } // signing related: diff --git a/clients/validator/tests/query/balance.test.ts b/clients/validator/tests/query/balance.test.ts index 254e54a745..97be47c2ce 100644 --- a/clients/validator/tests/query/balance.test.ts +++ b/clients/validator/tests/query/balance.test.ts @@ -1,4 +1,4 @@ -import ValidatorClient from '../../dist'; +import ValidatorClient from '../../validator/index'; import expect from 'expect'; describe('Query: balances', () => { diff --git a/clients/webassembly/js-example/index.js b/clients/webassembly/js-example/index.js index 4a28435219..f0890bf12e 100644 --- a/clients/webassembly/js-example/index.js +++ b/clients/webassembly/js-example/index.js @@ -65,7 +65,6 @@ async function main() { * * Message and recipient are taken from the values in the user interface. * - * @param {Client} nymClient the nym client to use for message sending */ async function sendMessageTo() { const message = document.getElementById('message').value; @@ -96,10 +95,13 @@ function displaySend(message) { /** * Display received text messages in the browser. Colour them green. * - * @param {string} message + * @param {Uint8Array} raw */ -function displayReceived(message) { - const content = message; +function displayReceived(raw, sender_tag) { + const content = new TextDecoder().decode(raw); + if (sender_tag !== undefined) { + console.log("this message also contained some surbs from", sender_tag) + } let timestamp = new Date().toISOString().substr(11, 12); let receivedDiv = document.createElement('div'); @@ -116,7 +118,7 @@ function displayReceived(message) { /** * Display the nymClient's sender address in the user interface * - * @param {Client} nymClient + * @param {String} address */ function displaySenderAddress(address) { document.getElementById('sender').value = address; diff --git a/clients/webassembly/js-example/package-lock.json b/clients/webassembly/js-example/package-lock.json index ea8023655b..2e4adc6d90 100644 --- a/clients/webassembly/js-example/package-lock.json +++ b/clients/webassembly/js-example/package-lock.json @@ -24,8 +24,7 @@ }, "../pkg": { "name": "@nymproject/nym-client-wasm", - "version": "1.1.0", - "license": "Apache-2.0" + "version": "0.0.1" }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", @@ -455,12 +454,13 @@ "license": "Apache-2.0" }, "node_modules/accepts": { - "version": "1.3.7", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, - "license": "MIT", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -636,29 +636,43 @@ } }, "node_modules/body-parser": { - "version": "1.19.0", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, - "license": "MIT", "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, "engines": { "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.0", + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -913,30 +927,51 @@ } }, "node_modules/content-disposition": { - "version": "0.5.3", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, - "license": "MIT", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.4.0", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1120,9 +1155,14 @@ } }, "node_modules/destroy": { - "version": "1.0.4", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, - "license": "MIT" + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-node": { "version": "2.1.0", @@ -1172,8 +1212,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/electron-to-chromium": { "version": "1.4.92", @@ -1185,7 +1224,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1291,7 +1329,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1338,37 +1375,39 @@ } }, "node_modules/express": { - "version": "4.17.1", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dev": true, - "license": "MIT", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -1384,6 +1423,44 @@ "dev": true, "license": "MIT" }, + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1468,22 +1545,32 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1532,7 +1619,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1734,26 +1820,38 @@ "license": "MIT" }, "node_modules/http-errors": { - "version": "1.7.2", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, - "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "license": "ISC" + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, "node_modules/http-parser-js": { "version": "0.5.3", @@ -1813,7 +1911,6 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -2175,7 +2272,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2242,7 +2338,6 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -2342,9 +2437,10 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.2", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2392,6 +2488,15 @@ "node": ">=8" } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-is": { "version": "1.1.5", "dev": true, @@ -2423,9 +2528,10 @@ "license": "MIT" }, "node_modules/on-finished": { - "version": "2.3.0", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, - "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -2705,11 +2811,18 @@ } }, "node_modules/qs": { - "version": "6.7.0", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, - "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/queue-microtask": { @@ -2754,12 +2867,13 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, - "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -2768,9 +2882,10 @@ } }, "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.0", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2960,8 +3075,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/schema-utils": { "version": "4.0.0", @@ -3002,32 +3116,52 @@ } }, "node_modules/send": { - "version": "0.17.1", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "license": "MIT" + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, "node_modules/serialize-javascript": { "version": "6.0.0", @@ -3089,23 +3223,25 @@ "license": "ISC" }, "node_modules/serve-static": { - "version": "1.14.1", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, - "license": "MIT", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "dev": true, - "license": "ISC" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true }, "node_modules/shallow-clone": { "version": "3.0.1", @@ -3143,6 +3279,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3502,9 +3652,10 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6" } @@ -3514,7 +3665,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, - "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -3528,7 +3678,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4293,11 +4442,13 @@ "dev": true }, "accepts": { - "version": "1.3.7", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -4405,23 +4556,35 @@ "dev": true }, "body-parser": { - "version": "1.19.0", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.0", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true } } @@ -4591,10 +4754,20 @@ "dev": true }, "content-disposition": { - "version": "0.5.3", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "content-type": { @@ -4604,7 +4777,9 @@ "dev": true }, "cookie": { - "version": "0.4.0", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true }, "cookie-signature": { @@ -4728,7 +4903,9 @@ "dev": true }, "destroy": { - "version": "1.0.4", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true }, "detect-node": { @@ -4884,36 +5061,39 @@ } }, "express": { - "version": "4.17.1", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dev": true, "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -4924,6 +5104,24 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true } } }, @@ -4993,16 +5191,26 @@ } }, "finalhandler": { - "version": "1.1.2", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } } }, "find-up": { @@ -5168,20 +5376,28 @@ "dev": true }, "http-errors": { - "version": "1.7.2", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -5556,7 +5772,9 @@ "dev": true }, "negotiator": { - "version": "0.6.2", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { @@ -5588,6 +5806,12 @@ "path-key": "^3.0.0" } }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, "object-is": { "version": "1.1.5", "dev": true, @@ -5607,7 +5831,9 @@ "dev": true }, "on-finished": { - "version": "2.3.0", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "requires": { "ee-first": "1.1.1" @@ -5793,8 +6019,13 @@ "dev": true }, "qs": { - "version": "6.7.0", - "dev": true + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } }, "queue-microtask": { "version": "1.2.3", @@ -5818,17 +6049,21 @@ "dev": true }, "raw-body": { - "version": "2.4.0", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.0", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true } } @@ -5978,26 +6213,42 @@ } }, "send": { - "version": "0.17.1", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, "ms": { - "version": "2.1.1", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -6053,17 +6304,21 @@ } }, "serve-static": { - "version": "1.14.1", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "setprototypeof": { - "version": "1.1.1", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "shallow-clone": { @@ -6090,6 +6345,17 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6325,7 +6591,9 @@ } }, "toidentifier": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, "type-is": { diff --git a/clients/webassembly/js-example/worker.js b/clients/webassembly/js-example/worker.js index 0cbf2fe4d6..da066d6ea2 100644 --- a/clients/webassembly/js-example/worker.js +++ b/clients/webassembly/js-example/worker.js @@ -17,38 +17,7 @@ importScripts('nym_client_wasm.js'); console.log('Initializing worker'); // wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts` -const { default_debug, get_gateway, NymClient, set_panic_hook, Config } = wasm_bindgen; - -class ClientWrapper { - constructor(config, onMessageHandler) { - this.rustClient = new NymClient(config); - this.rustClient.set_on_message(onMessageHandler); - this.rustClient.set_on_gateway_connect(this.onConnect); - } - - selfAddress = () => { - return this.rustClient.self_address(); - }; - - onConnect = () => { - console.log('Established (and authenticated) gateway connection!'); - }; - - start = async () => { - // this is current limitation of wasm in rust - for async methods you can't take self by reference... - // I'm trying to figure out if I can somehow hack my way around it, but for time being you have to re-assign - // the object (it's the same one) - this.rustClient = await this.rustClient.start(); - }; - - sendMessage = async (recipient, message) => { - this.rustClient = await this.rustClient.send_message(recipient, message); - }; - - sendBinaryMessage = async (recipient, message) => { - this.rustClient = await this.rustClient.send_binary_message(recipient, message); - }; -} +const { default_debug, NymClientBuilder, set_panic_hook, Config, GatewayEndpointConfig } = wasm_bindgen; let client = null; @@ -62,11 +31,12 @@ async function main() { set_panic_hook(); // validator server we will use to get topology from - const validator = 'https://validator.nymtech.net/api'; //"http://localhost:8081"; - const preferredGateway = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM'; - - const gatewayEndpoint = await get_gateway(validator, preferredGateway); - gatewayEndpoint.gateway_listener = "wss://gateway1.nymtech.net:443"; // this is needed if we want it to work on the web. However this gateway is a v1 gateway, we will need to change for v2 once we get there + const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; + + const gatewayId = 'EVupP2tRUeZo5Y6RpBHAbm8kSntpgNyZNL6yCr7BDEoG'; + const gatewayOwner = 'n1rmlew3euapuq7rs4s4j9apv00whrsazr764kl7'; + const gatewayListener = 'ws://176.58.120.72:9000'; + const gatewayEndpoint = new GatewayEndpointConfig(gatewayId, gatewayOwner, gatewayListener) // only really useful if you want to adjust some settings like traffic rate // (if not needed you can just pass a null) @@ -94,12 +64,17 @@ async function main() { }; console.log('Instantiating WASM client...'); - client = new ClientWrapper(config, onMessageHandler); + + let clientBuilder = new NymClientBuilder(config, onMessageHandler) console.log('Web worker creating WASM client...'); - await client.start(); + let local_client = await clientBuilder.start_client(); console.log('WASM client running!'); - - const selfAddress = client.rustClient.self_address(); + + const selfAddress = local_client.self_address(); + + // set the global (I guess we don't have to anymore?) + client = local_client; + console.log(`Client address is ${selfAddress}`); self.postMessage({ kind: 'Ready', @@ -114,7 +89,8 @@ async function main() { switch (event.data.kind) { case 'SendMessage': { const { message, recipient } = event.data.args; - await client.sendMessage(message, recipient); + let uint8Array = new TextEncoder().encode(message); + await client.send_regular_message(uint8Array, recipient); } } } diff --git a/clients/webassembly/src/client/config.rs b/clients/webassembly/src/client/config.rs index 66c6ceefe7..4efefeba21 100644 --- a/clients/webassembly/src/client/config.rs +++ b/clients/webassembly/src/client/config.rs @@ -17,7 +17,7 @@ pub struct Config { /// ID specifies the human readable ID of this particular client. pub(crate) id: String, - pub(crate) validator_api_url: Url, + pub(crate) nym_api_url: Url, pub(crate) disabled_credentials_mode: bool, @@ -38,7 +38,7 @@ impl Config { ) -> Self { Config { id, - validator_api_url: validator_server + nym_api_url: validator_server .parse() .expect("provided url was malformed"), disabled_credentials_mode: true, @@ -106,6 +106,34 @@ pub struct Debug { /// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size. pub use_extended_packet_size: bool, + + /// Defines the minimum number of reply surbs the client wants to keep in its storage at all times. + /// It can only allow to go below that value if its to request additional reply surbs. + pub minimum_reply_surb_storage_threshold: usize, + + /// Defines the maximum number of reply surbs the client wants to keep in its storage at any times. + pub maximum_reply_surb_storage_threshold: usize, + + /// Defines the minimum number of reply surbs the client would request. + pub minimum_reply_surb_request_size: u32, + + /// Defines the maximum number of reply surbs the client would request. + pub maximum_reply_surb_request_size: u32, + + /// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once. + pub maximum_allowed_reply_surb_request_size: u32, + + /// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking + /// for more even though in theory they wouldn't need to. + pub maximum_reply_surb_waiting_period_ms: u64, + + /// Defines maximum amount of time given reply surb is going to be valid for. + /// This is going to be superseded by key rotation once implemented. + pub maximum_reply_surb_age_ms: u64, + + /// Defines maximum amount of time given reply key is going to be valid for. + /// This is going to be superseded by key rotation once implemented. + pub maximum_reply_key_age_ms: u64, } impl From for ConfigDebug { @@ -135,6 +163,16 @@ impl From for ConfigDebug { disable_main_poisson_packet_distribution: debug .disable_main_poisson_packet_distribution, use_extended_packet_size, + minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold, + maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold, + minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size, + maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size, + maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size, + maximum_reply_surb_waiting_period: Duration::from_millis( + debug.maximum_reply_surb_waiting_period_ms, + ), + maximum_reply_surb_age: Duration::from_millis(debug.maximum_reply_surb_age_ms), + maximum_reply_key_age: Duration::from_millis(debug.maximum_reply_key_age_ms), } } } @@ -157,6 +195,16 @@ impl From for Debug { disable_main_poisson_packet_distribution: debug .disable_main_poisson_packet_distribution, use_extended_packet_size: debug.use_extended_packet_size.is_some(), + minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold, + maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold, + minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size, + maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size, + maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size, + maximum_reply_surb_waiting_period_ms: debug + .maximum_reply_surb_waiting_period + .as_millis() as u64, + maximum_reply_surb_age_ms: debug.maximum_reply_surb_age.as_millis() as u64, + maximum_reply_key_age_ms: debug.maximum_reply_key_age.as_millis() as u64, } } } diff --git a/clients/webassembly/src/client/helpers.rs b/clients/webassembly/src/client/helpers.rs new file mode 100644 index 0000000000..4349fd5f78 --- /dev/null +++ b/clients/webassembly/src/client/helpers.rs @@ -0,0 +1,30 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use client_core::client::base_client::ClientInput; +use client_core::client::inbound_messages::InputMessage; +use js_sys::Promise; +use std::sync::Arc; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::future_to_promise; + +// defining helper trait as we could directly call the method on the wrapper +pub(crate) trait InputSender { + fn send_message(&self, message: InputMessage) -> Promise; +} + +impl InputSender for Arc { + fn send_message(&self, message: InputMessage) -> Promise { + let this = Arc::clone(self); + future_to_promise(async move { + match this.input_sender.send(message).await { + Ok(_) => Ok(JsValue::null()), + Err(_) => { + let js_error = + js_sys::Error::new("InputMessageReceiver has stopped receiving!"); + Err(JsValue::from(js_error)) + } + } + }) + } +} diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 68a40cf99b..bd9ceab576 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -2,59 +2,65 @@ // SPDX-License-Identifier: Apache-2.0 use self::config::Config; +use crate::client::helpers::InputSender; use crate::client::response_pusher::ResponsePusher; use client_connections::TransmissionLane; use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput}; +use client_core::client::replies::reply_storage::browser_backend; use client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager}; -use crypto::asymmetric::identity; +use gateway_client::bandwidth::BandwidthController; +use js_sys::Promise; use nymsphinx::addressing::clients::Recipient; +use nymsphinx::anonymous_replies::requests::AnonymousSenderTag; use rand::rngs::OsRng; -use task::ShutdownNotifier; +use std::sync::Arc; +use task::TaskManager; use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::future_to_promise; use wasm_utils::{console_error, console_log}; pub mod config; +mod helpers; mod response_pusher; #[wasm_bindgen] pub struct NymClient { - config: Config, - - /// KeyManager object containing smart pointers to all relevant keys used by the client. - // due to disgusting workaround I had to wrap the key_manager in an Option - // so that the interface wouldn't change (i.e. both `start` and `new` would still return a `NymClient`) - key_manager: Option, - self_address: Option, - - // TODO: this should be stored somewhere persistently - // received_keys: HashSet, - /// Channel used for transforming 'raw' messages into sphinx packets and sending them - /// through the mix network. - client_input: Option, - - // callbacks - on_message: Option, - on_binary_message: Option, - on_gateway_connect: Option, + self_address: String, + client_input: Arc, // even though we don't use graceful shutdowns, other components rely on existence of this struct // and if it's dropped, everything will start going offline - _shutdown: Option, + _task_manager: TaskManager, } #[wasm_bindgen] -impl NymClient { +pub struct NymClientBuilder { + config: Config, + + /// KeyManager object containing smart pointers to all relevant keys used by the client. + key_manager: KeyManager, + + reply_surb_storage_backend: browser_backend::Backend, + + on_message: js_sys::Function, + + // unimplemented: + bandwidth_controller: Option, + disabled_credentials: bool, +} + +#[wasm_bindgen] +impl NymClientBuilder { #[wasm_bindgen(constructor)] - pub fn new(config: Config) -> Self { - Self { + pub fn new(config: Config, on_message: js_sys::Function) -> Self { + //, key_manager: Option) { + NymClientBuilder { + reply_surb_storage_backend: Self::setup_reply_surb_storage_backend(&config), config, - key_manager: Some(Self::setup_key_manager()), - on_message: None, - on_binary_message: None, - on_gateway_connect: None, - client_input: None, - self_address: None, - _shutdown: None, + key_manager: Self::setup_key_manager(), + on_message, + bandwidth_controller: None, + disabled_credentials: true, } } @@ -69,117 +75,154 @@ impl NymClient { KeyManager::new(&mut rng) } - pub fn set_on_message(&mut self, on_message: js_sys::Function) { - self.on_message = Some(on_message); - } - - pub fn set_on_binary_message(&mut self, on_binary_message: js_sys::Function) { - self.on_binary_message = Some(on_binary_message); - } - - pub fn set_on_gateway_connect(&mut self, on_connect: js_sys::Function) { - self.on_gateway_connect = Some(on_connect) - } - - fn as_mix_recipient(&self) -> Recipient { - // another disgusting (and hopefully temporary) workaround - let key_manager_ref = self - .key_manager - .as_ref() - .expect("attempting to call 'as_mix_recipient' after 'start'"); - - Recipient::new( - *key_manager_ref.identity_keypair().public_key(), - *key_manager_ref.encryption_keypair().public_key(), - identity::PublicKey::from_base58_string(&self.config.gateway_endpoint.gateway_id) - .expect("no gateway has been selected"), + // don't get too excited about the name, under the hood it's just a big fat placeholder + // with no persistence + fn setup_reply_surb_storage_backend(config: &Config) -> browser_backend::Backend { + browser_backend::Backend::new( + config.debug.minimum_reply_surb_storage_threshold, + config.debug.maximum_reply_surb_storage_threshold, ) } + fn start_reconstructed_pusher(client_output: ClientOutput, on_message: js_sys::Function) { + ResponsePusher::new(client_output, on_message).start() + } + + pub async fn start_client(self) -> Promise { + future_to_promise(async move { + console_log!("Starting the wasm client"); + + let base_builder = BaseClientBuilder::new( + &self.config.gateway_endpoint, + &self.config.debug, + self.key_manager, + self.bandwidth_controller, + self.reply_surb_storage_backend, + self.disabled_credentials, + vec![self.config.nym_api_url.clone()], + ); + + let self_address = base_builder.as_mix_recipient().to_string(); + let mut started_client = match base_builder.start_base().await { + Ok(base_client) => base_client, + Err(err) => { + let error_msg = format!("failed to start the base client components - {err}"); + console_error!("{}", error_msg); + let js_error = js_sys::Error::new(&error_msg); + return Err(JsValue::from(js_error)); + } + }; + + let client_input = started_client.client_input.register_producer(); + let client_output = started_client.client_output.register_consumer(); + + Self::start_reconstructed_pusher(client_output, self.on_message); + + Ok(JsValue::from(NymClient { + self_address, + client_input: Arc::new(client_input), + _task_manager: started_client.task_manager, + })) + }) + } +} + +#[wasm_bindgen] +impl NymClient { pub fn self_address(&self) -> String { - if let Some(address) = &self.self_address { - address.clone() - } else { - self.as_mix_recipient().to_string() + self.self_address.clone() + } + + fn parse_recipient(recipient: &str) -> Result { + match Recipient::try_from_base58_string(recipient) { + Ok(recipient) => Ok(recipient), + Err(err) => { + let error_msg = format!("{recipient} is not a valid Nym network recipient - {err}"); + console_error!("{}", error_msg); + let js_error = js_sys::Error::new(&error_msg); + Err(JsValue::from(js_error)) + } } } - // Right now it's impossible to have async exported functions to take `&mut self` rather than mut self - // TODO: try Rc> approach? - pub async fn send_message(self, message: String, recipient: String) -> Self { - console_log!("Sending {} to {}", message, recipient); - - let message_bytes = message.into_bytes(); - self.send_binary_message(message_bytes, recipient).await + fn parse_sender_tag(tag: &str) -> Result { + match AnonymousSenderTag::try_from_base58_string(tag) { + Ok(tag) => Ok(tag), + Err(err) => { + let error_msg = format!("{tag} is not a valid Nym AnonymousSenderTag - {err}"); + console_error!("{}", error_msg); + let js_error = js_sys::Error::new(&error_msg); + Err(JsValue::from(js_error)) + } + } } - pub async fn send_binary_message(self, message: Vec, recipient: String) -> Self { - console_log!("Sending {} bytes to {}", message.len(), recipient); - - let recipient = Recipient::try_from_base58_string(recipient).unwrap(); - let lane = TransmissionLane::General; - - let input_msg = InputMessage::new_fresh(recipient, message, false, lane); - - self.client_input - .as_ref() - .expect("start method was not called before!") - .input_sender - .send(input_msg) - .await - .expect("InputMessageReceiver has stopped receiving!"); - - self - } - - fn start_reconstructed_pusher( - client_output: ClientOutput, - on_message: Option, - on_binary_message: Option, - ) { - ResponsePusher::new(client_output, on_message, on_binary_message).start() - } - - pub async fn start(mut self) -> NymClient { - console_log!("Starting the wasm client"); - - let base_builder = BaseClientBuilder::new( - &self.config.gateway_endpoint, - &self.config.debug, - self.key_manager.take().unwrap(), - None, - true, - vec![self.config.validator_api_url.clone()], + /// The simplest message variant where no additional information is attached. + /// You're simply sending your `data` to specified `recipient` without any tagging. + /// + /// Ends up with `NymMessage::Plain` variant + pub fn send_regular_message(&self, message: Vec, recipient: String) -> Promise { + console_log!( + "Attempting to send {:.2} kiB message to {recipient}", + message.len() as f64 / 1024.0 ); - self.self_address = Some(base_builder.as_mix_recipient().to_string()); - let mut started_client = match base_builder.start_base().await { - Ok(base_client) => base_client, - Err(err) => { - console_error!("failed to start base client components - {}", err); - // proper error handling is left here as an exercise for the reader (hi Mark : )) - panic!("failed to start base client components - {err}") - } + let recipient = match Self::parse_recipient(&recipient) { + Ok(recipient) => recipient, + Err(err) => return Promise::reject(&err), }; - match self.on_gateway_connect.as_ref() { - Some(callback) => { - callback - .call0(&JsValue::null()) - .expect("on connect callback failed!"); - } - None => console_log!("Gateway connection established - no callback specified"), + let lane = TransmissionLane::General; + + let input_msg = InputMessage::new_regular(recipient, message, lane); + self.client_input.send_message(input_msg) + } + + /// Creates a message used for a duplex anonymous communication where the recipient + /// will never learn of our true identity. This is achieved by carefully sending `reply_surbs`. + /// + /// Note that if reply_surbs is set to zero then + /// this variant requires the client having sent some reply_surbs in the past + /// (and thus the recipient also knowing our sender tag). + /// + /// Ends up with `NymMessage::Repliable` variant + pub fn send_anonymous_message( + &self, + message: Vec, + recipient: String, + reply_surbs: u32, + ) -> Promise { + console_log!( + "Attempting to anonymously send {:.2} kiB message to {recipient} while attaching {reply_surbs} replySURBs.", + message.len() as f64 / 1024.0 + ); + + let recipient = match Self::parse_recipient(&recipient) { + Ok(recipient) => recipient, + Err(err) => return Promise::reject(&err), }; + let lane = TransmissionLane::General; - // those should be moved to a completely different struct, but I don't want to break compatibility for now - let client_input = started_client.client_input.register_producer(); - let client_output = started_client.client_output.register_consumer(); + let input_msg = InputMessage::new_anonymous(recipient, message, reply_surbs, lane); + self.client_input.send_message(input_msg) + } - let on_message = self.on_message.take(); - let on_binary_message = self.on_binary_message.take(); - Self::start_reconstructed_pusher(client_output, on_message, on_binary_message); - self.client_input = Some(client_input); - self._shutdown = Some(started_client.shutdown_notifier); + /// Attempt to use our internally received and stored `ReplySurb` to send the message back + /// to specified recipient whilst not knowing its full identity (or even gateway). + /// + /// Ends up with `NymMessage::Reply` variant + pub fn send_reply(&self, message: Vec, recipient_tag: String) -> Promise { + console_log!( + "Attempting to send {:.2} kiB reply message to {recipient_tag}", + message.len() as f64 / 1024.0 + ); - self + let sender_tag = match Self::parse_sender_tag(&recipient_tag) { + Ok(recipient) => recipient, + Err(err) => return Promise::reject(&err), + }; + let lane = TransmissionLane::General; + + let input_msg = InputMessage::new_reply(sender_tag, message, lane); + self.client_input.send_message(input_msg) } } diff --git a/clients/webassembly/src/client/response_pusher.rs b/clients/webassembly/src/client/response_pusher.rs index c5717a9bc3..fbe04c66ed 100644 --- a/clients/webassembly/src/client/response_pusher.rs +++ b/clients/webassembly/src/client/response_pusher.rs @@ -5,27 +5,18 @@ use client_core::client::base_client::ClientOutput; use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver}; use futures::channel::mpsc; use futures::StreamExt; +use js_sys::Uint8Array; use wasm_bindgen::JsValue; use wasm_bindgen_futures::spawn_local; -use wasm_utils::console_log; +use wasm_utils::console_error; pub(crate) struct ResponsePusher { reconstructed_receiver: ReconstructedMessagesReceiver, - on_message: Option, - on_binary_message: Option, + on_message: js_sys::Function, } impl ResponsePusher { - pub(crate) fn new( - client_output: ClientOutput, - on_message: Option, - on_binary_message: Option, - ) -> Self { - if on_message.is_none() && on_binary_message.is_none() { - // exercise for the reader : ) - panic!("neither 'on_message' nor 'on_binary_message' was set!") - } - + pub(crate) fn new(client_output: ClientOutput, on_message: js_sys::Function) -> Self { // register our output let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded(); @@ -40,7 +31,6 @@ impl ResponsePusher { ResponsePusher { reconstructed_receiver, on_message, - on_binary_message, } } @@ -49,23 +39,20 @@ impl ResponsePusher { let this = JsValue::null(); while let Some(reconstructed) = self.reconstructed_receiver.next().await { - for msg in reconstructed { - if let Some(ref callback_binary) = self.on_binary_message { - let arg1 = serde_wasm_bindgen::to_value(&msg.message).unwrap(); - callback_binary - .call1(&this, &arg1) - .expect("on binary message failed!"); - } - if let Some(ref callback) = self.on_message { - if msg.reply_surb.is_some() { - console_log!("the received message contained a reply-surb that we do not know how to handle (yet)") - } - let stringified = String::from_utf8_lossy(&msg.message).into_owned(); - let arg1 = serde_wasm_bindgen::to_value(&stringified).unwrap(); - callback.call1(&this, &arg1).expect("on message failed!"); - } + for reconstructed_msg in reconstructed { + let (msg, tag) = reconstructed_msg.into_inner(); + + let msg_slice: &[u8] = &msg; + let array = Uint8Array::from(msg_slice); + let arg1 = JsValue::from(array); + let arg2 = JsValue::from(tag); + self.on_message + .call2(&this, &arg1, &arg2) + .expect("on binary message failed!"); } } + + console_error!("we stopped receiving reconstructed messages!") }) } } diff --git a/clients/webassembly/src/gateway_selector.rs b/clients/webassembly/src/gateway_selector.rs index 77f5a5c6f7..d92ac987c3 100644 --- a/clients/webassembly/src/gateway_selector.rs +++ b/clients/webassembly/src/gateway_selector.rs @@ -6,7 +6,7 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] pub async fn get_gateway(api_server: String, preferred: Option) -> GatewayEndpointConfig { - let validator_client = validator_client::client::ApiClient::new(api_server.parse().unwrap()); + let validator_client = validator_client::client::NymApiClient::new(api_server.parse().unwrap()); let gateways = match validator_client.get_cached_gateways().await { Err(err) => panic!("failed to obtain list of all gateways - {err}"), diff --git a/common/build-information/Cargo.toml b/common/build-information/Cargo.toml new file mode 100644 index 0000000000..0f56bec7f1 --- /dev/null +++ b/common/build-information/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "build-information" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +vergen = { version = "7", default-features = false, features = ["build", "git", "rustc", "cargo"] } diff --git a/clients/native/build.rs b/common/build-information/build.rs similarity index 71% rename from clients/native/build.rs rename to common/build-information/build.rs index 01b3a20dc6..b909ce42c4 100644 --- a/clients/native/build.rs +++ b/common/build-information/build.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use vergen::{vergen, Config}; diff --git a/common/build-information/src/lib.rs b/common/build-information/src/lib.rs new file mode 100644 index 0000000000..f2060154f0 --- /dev/null +++ b/common/build-information/src/lib.rs @@ -0,0 +1,86 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +// TODO: at a later date this crate should probably also expose `ContractBuildInformation` +// and be used by our smart contracts + +pub struct BinaryBuildInformation { + // VERGEN_BUILD_TIMESTAMP + /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. + pub build_timestamp: &'static str, + + // VERGEN_BUILD_SEMVER + /// Provides the build version, for example `0.1.0-9-g46f83e1`. + pub build_version: &'static str, + + // VERGEN_GIT_SHA + /// Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`. + pub commit_sha: &'static str, + + // VERGEN_GIT_COMMIT_TIMESTAMP + /// Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`. + pub commit_timestamp: &'static str, + + // VERGEN_GIT_BRANCH + /// Provides the name of the git branch that was used for the build, for example `master`. + pub commit_branch: &'static str, + + // VERGEN_RUSTC_SEMVER + /// Provides the rustc version that was used for the build, for example `1.52.0-nightly`. + pub rustc_version: &'static str, + + // VERGEN_RUSTC_CHANNEL + /// Provides the rustc channel that was used for the build, for example `nightly`. + pub rustc_channel: &'static str, + + // VERGEN_CARGO_PROFILE + /// Provides the cargo profile that was used for the build, for example `debug`. + pub cargo_profile: &'static str, +} + +impl BinaryBuildInformation { + // explicitly require the build_version to be passed as it's binary specific + pub const fn new(build_version: &'static str) -> Self { + BinaryBuildInformation { + build_timestamp: env!("VERGEN_BUILD_TIMESTAMP"), + build_version, + commit_sha: env!("VERGEN_GIT_SHA"), + commit_timestamp: env!("VERGEN_GIT_COMMIT_TIMESTAMP"), + commit_branch: env!("VERGEN_GIT_BRANCH"), + rustc_version: env!("VERGEN_RUSTC_SEMVER"), + rustc_channel: env!("VERGEN_RUSTC_CHANNEL"), + cargo_profile: env!("VERGEN_CARGO_PROFILE"), + } + } + + pub fn pretty_print(&self) -> String { + format!( + r#" +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +"#, + "Build Timestamp:", + self.build_timestamp, + "Build Version:", + self.build_version, + "Commit SHA:", + self.commit_sha, + "Commit Date:", + self.commit_timestamp, + "Commit Branch:", + self.commit_branch, + "rustc Version:", + self.rustc_version, + "rustc Channel:", + self.rustc_channel, + "cargo Profile:", + self.cargo_profile, + ) + } +} diff --git a/common/client-connections/src/lib.rs b/common/client-connections/src/lib.rs index 515938ed54..b10963da79 100644 --- a/common/client-connections/src/lib.rs +++ b/common/client-connections/src/lib.rs @@ -1,18 +1,19 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::collections::HashMap; - use futures::channel::mpsc; +use std::collections::HashMap; pub type ConnectionId = u64; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum TransmissionLane { General, - Reply, + // we need to treat surb-related requests and responses at higher priority + // so that the rest of underlying communication could actually continue + ReplySurbRequest, + AdditionalReplySurbs, Retransmission, - Control, ConnectionId(ConnectionId), } diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index d1ea2abf1e..a9c14adc70 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -27,6 +27,7 @@ nymsphinx = { path = "../../nymsphinx" } pemstore = { path = "../../pemstore" } validator-client = { path = "../validator-client", optional = true } task = { path = "../../task" } +serde = { version = "1.0", features = ["derive"]} [dependencies.tungstenite] @@ -57,6 +58,7 @@ version = "0.4" [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils] path = "../../wasm-utils" +features = ["websocket"] # only import it in wasm. Prefer proper tokio timer in non-wasm [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer] diff --git a/common/client-libs/gateway-client/src/bandwidth.rs b/common/client-libs/gateway-client/src/bandwidth.rs index 00db2b1180..d6d1cb04f0 100644 --- a/common/client-libs/gateway-client/src/bandwidth.rs +++ b/common/client-libs/gateway-client/src/bandwidth.rs @@ -61,7 +61,7 @@ where #[cfg(feature = "coconut")] pub async fn prepare_coconut_credential( &self, - ) -> Result { + ) -> Result<(coconut_interface::Credential, i64), GatewayClientError> { let verification_key = obtain_aggregate_verification_key(&self.coconut_api_clients).await?; let bandwidth_credential = self.storage.get_next_coconut_credential().await?; let voucher_value = u64::from_str(&bandwidth_credential.voucher_value) @@ -75,13 +75,21 @@ where coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?; // the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff) - Ok(prepare_for_spending( - voucher_value, - voucher_info, - serial_number, - binding_number, - &signature, - &verification_key, - )?) + Ok(( + prepare_for_spending( + voucher_value, + voucher_info, + serial_number, + binding_number, + &signature, + &verification_key, + )?, + bandwidth_credential.id, + )) + } + + #[cfg(feature = "coconut")] + pub async fn consume_credential(&self, id: i64) -> Result<(), GatewayClientError> { + Ok(self.storage.consume_coconut_credential(id).await?) } } diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index 56ff03737a..23cae7e4a6 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -10,7 +10,7 @@ pub use crate::packet_router::{ use crate::socket_state::{PartiallyDelegated, SocketState}; use crate::{cleanup_socket_message, try_decrypt_binary_message}; use crypto::asymmetric::identity; -use futures::{FutureExt, SinkExt, StreamExt}; +use futures::{SinkExt, StreamExt}; use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; use gateway_requests::iv::IV; use gateway_requests::registration::handshake::{client_handshake, SharedKeys}; @@ -22,7 +22,7 @@ use rand::rngs::OsRng; use std::convert::TryFrom; use std::sync::Arc; use std::time::Duration; -use task::ShutdownListener; +use task::TaskClient; use tungstenite::protocol::Message; #[cfg(feature = "coconut")] @@ -67,9 +67,7 @@ pub struct GatewayClient { reconnection_backoff: Duration, /// Listen to shutdown messages. - // TODO: fix this - #[cfg_attr(target_arch = "wasm32", allow(dead_code))] - shutdown: Option, + shutdown: TaskClient, } impl GatewayClient { @@ -85,7 +83,7 @@ impl GatewayClient { ack_sender: AcknowledgementSender, response_timeout_duration: Duration, bandwidth_controller: Option>, - shutdown: Option, + shutdown: TaskClient, ) -> Self { GatewayClient { authenticated: false, @@ -130,7 +128,6 @@ impl GatewayClient { gateway_owner: String, local_identity: Arc, response_timeout_duration: Duration, - shutdown: Option, ) -> Self { use futures::channel::mpsc; @@ -138,6 +135,7 @@ impl GatewayClient { // perfectly fine here, because it's not meant to be used let (ack_tx, _) = mpsc::unbounded(); let (mix_tx, _) = mpsc::unbounded(); + let shutdown = TaskClient::dummy(); let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone()); GatewayClient { @@ -283,44 +281,19 @@ impl GatewayClient { // technically the `wasm_timer` also works outside wasm, but unless required, // I really prefer to just stick to tokio #[cfg(target_arch = "wasm32")] - let timeout = wasm_timer::Delay::new(self.response_timeout_duration); - - let mut fused_timeout = timeout.fuse(); - let mut fused_stream = conn.fuse(); - - // Bit of an ugly workaround for selecting on an `Option` without having access to - // `tokio::select` - #[cfg(not(target_arch = "wasm32"))] - let shutdown = { - let m_shutdown = self.shutdown.clone(); - async { - if let Some(mut s) = m_shutdown { - // TODO: fix this by marking as success _after_ the select - s.mark_as_success(); - s.recv().await - } else { - std::future::pending::<()>().await - } - } - .fuse() - }; - #[cfg(not(target_arch = "wasm32"))] - tokio::pin!(shutdown); - - #[cfg(target_arch = "wasm32")] - let mut shutdown = std::future::pending::<()>().fuse(); + let mut timeout = wasm_timer::Delay::new(self.response_timeout_duration); loop { - futures::select! { - _ = shutdown => { + tokio::select! { + _ = self.shutdown.recv() => { log::trace!("GatewayClient control response: Received shutdown"); log::debug!("GatewayClient control response: Exiting"); break Err(GatewayClientError::ConnectionClosedGatewayShutdown); } - _ = &mut fused_timeout => { + _ = &mut timeout => { break Err(GatewayClientError::Timeout); } - msg = fused_stream.next() => { + msg = conn.next() => { let ws_msg = match cleanup_socket_message(msg) { Err(err) => break Err(err), Ok(msg) => msg @@ -332,11 +305,11 @@ impl GatewayClient { if let Some(shared_keys) = &self.shared_key { if let Some(plaintext) = try_decrypt_binary_message(bin_msg, shared_keys) { if let Err(err) = self.packet_router.route_received(vec![plaintext]) { - log::warn!("Route received failed: {:?}", err); + log::warn!("Route received failed: {err}"); } } } else if let Err(err) = self.packet_router.route_received(vec![bin_msg]) { - log::warn!("Route received failed: {:?}", err); + log::warn!("Route received failed: {err}"); } } Message::Text(txt_msg) => { @@ -437,7 +410,7 @@ impl GatewayClient { match gateway_protocol { None => { warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0"); - // note: in 1.2.0 we will have to return a hard error here + // note: in +1.2.0 we will have to return a hard error here Ok(()) } Some(v) if v != PROTOCOL_VERSION => { @@ -614,7 +587,7 @@ impl GatewayClient { let _gateway_owner = self.gateway_owner.clone(); #[cfg(feature = "coconut")] - let credential = self + let (credential, credential_id) = self .bandwidth_controller .as_ref() .unwrap() @@ -624,7 +597,15 @@ impl GatewayClient { return self.try_claim_testnet_bandwidth().await; #[cfg(feature = "coconut")] - return self.claim_coconut_bandwidth(credential).await; + { + self.claim_coconut_bandwidth(credential).await?; + self.bandwidth_controller + .as_ref() + .unwrap() + .consume_credential(credential_id) + .await?; + Ok(()) + } } fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 { @@ -770,7 +751,6 @@ impl GatewayClient { .as_ref() .expect("no shared key present even though we're authenticated!"), ), - #[cfg(not(target_arch = "wasm32"))] self.shutdown.clone(), ) } diff --git a/common/client-libs/gateway-client/src/lib.rs b/common/client-libs/gateway-client/src/lib.rs index 8671a7363b..ac209f0920 100644 --- a/common/client-libs/gateway-client/src/lib.rs +++ b/common/client-libs/gateway-client/src/lib.rs @@ -16,7 +16,7 @@ pub mod client; pub mod error; pub mod packet_router; pub mod socket_state; -#[cfg(feature = "wasm")] +#[cfg(target_arch = "wasm32")] mod wasm_storage; /// Helper method for reading from websocket stream. Helps to flatten the structure. diff --git a/common/client-libs/gateway-client/src/packet_router.rs b/common/client-libs/gateway-client/src/packet_router.rs index 59d1d8add1..daeac0e548 100644 --- a/common/client-libs/gateway-client/src/packet_router.rs +++ b/common/client-libs/gateway-client/src/packet_router.rs @@ -9,7 +9,7 @@ use futures::channel::mpsc; use log::*; use nymsphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN; use nymsphinx::params::packet_sizes::PacketSize; -use task::ShutdownListener; +use task::TaskClient; pub type MixnetMessageSender = mpsc::UnboundedSender>>; pub type MixnetMessageReceiver = mpsc::UnboundedReceiver>>; @@ -21,14 +21,14 @@ pub type AcknowledgementReceiver = mpsc::UnboundedReceiver>>; pub struct PacketRouter { ack_sender: AcknowledgementSender, mixnet_message_sender: MixnetMessageSender, - shutdown: Option, + shutdown: TaskClient, } impl PacketRouter { pub fn new( ack_sender: AcknowledgementSender, mixnet_message_sender: MixnetMessageSender, - shutdown: Option, + shutdown: TaskClient, ) -> Self { PacketRouter { ack_sender, @@ -82,23 +82,21 @@ impl PacketRouter { if !received_messages.is_empty() { trace!("routing 'real'"); if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) { - if let Some(shutdown) = &mut self.shutdown { - if shutdown.is_shutdown_poll() { - // This should ideally not happen, but it's ok - log::warn!("Failed to send mixnet message due to receiver task shutdown"); - return Err(GatewayClientError::MixnetMsgSenderFailedToSend); - } + if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() { + // This should ideally not happen, but it's ok + log::warn!("Failed to send mixnet message due to receiver task shutdown"); + return Err(GatewayClientError::MixnetMsgSenderFailedToSend); } // This should never happen during ordinary operation the way it's currently used. // Abort to be on the safe side - panic!("Failed to send mixnet message: {:?}", err); + panic!("Failed to send mixnet message: {err}"); } } if !received_acks.is_empty() { trace!("routing acks"); - if let Err(e) = self.ack_sender.unbounded_send(received_acks) { - error!("failed to send ack: {:?}", e); + if let Err(err) = self.ack_sender.unbounded_send(received_acks) { + error!("failed to send ack: {err}"); }; } Ok(()) diff --git a/common/client-libs/gateway-client/src/socket_state.rs b/common/client-libs/gateway-client/src/socket_state.rs index 2d66e782e8..2bedf1e8ed 100644 --- a/common/client-libs/gateway-client/src/socket_state.rs +++ b/common/client-libs/gateway-client/src/socket_state.rs @@ -10,8 +10,7 @@ use futures::{SinkExt, StreamExt}; use gateway_requests::registration::handshake::SharedKeys; use log::*; use std::sync::Arc; -#[cfg(not(target_arch = "wasm32"))] -use task::ShutdownListener; +use task::TaskClient; use tungstenite::Message; #[cfg(not(target_arch = "wasm32"))] @@ -85,7 +84,7 @@ impl PartiallyDelegated { conn: WsConn, packet_router: PacketRouter, shared_key: Arc, - #[cfg(not(target_arch = "wasm32"))] shutdown: Option, + mut shutdown: TaskClient, ) -> Self { // when called for, it NEEDS TO yield back the stream so that we could merge it and // read control request responses. @@ -99,27 +98,9 @@ impl PartiallyDelegated { let mut chunk_stream = (&mut stream).ready_chunks(8); let mut packet_router = packet_router; - // Bit of an ugly workaround for selecting on an `Option` without having access to - // `tokio::select` - #[cfg(not(target_arch = "wasm32"))] - let shutdown = { - async { - if let Some(mut s) = shutdown { - s.recv().await - } else { - std::future::pending::<()>().await - } - } - }; - #[cfg(not(target_arch = "wasm32"))] - tokio::pin!(shutdown); - - #[cfg(target_arch = "wasm32")] - let mut shutdown = std::future::pending::<()>(); - let ret_err = loop { tokio::select! { - _ = &mut shutdown => { + _ = shutdown.recv() => { log::trace!("GatewayClient listener: Received shutdown"); log::debug!("GatewayClient listener: Exiting"); return; @@ -134,7 +115,7 @@ impl PartiallyDelegated { }; if let Err(err) = Self::route_socket_messages(ws_msgs, &mut packet_router, shared_key.as_ref()) { - log::warn!("Route socket messages failed: {:?}", err); + log::warn!("Route socket messages failed: {err}"); } } }; @@ -142,7 +123,10 @@ impl PartiallyDelegated { if match ret_err { Err(err) => stream_sender.send(Err(err)), - Ok(_) => stream_sender.send(Ok(stream)), + Ok(_) => { + shutdown.mark_as_success(); + stream_sender.send(Ok(stream)) + } } .is_err() { diff --git a/common/client-libs/gateway-client/src/wasm_storage.rs b/common/client-libs/gateway-client/src/wasm_storage.rs index 53a9a1b30e..644560b9ca 100644 --- a/common/client-libs/gateway-client/src/wasm_storage.rs +++ b/common/client-libs/gateway-client/src/wasm_storage.rs @@ -39,7 +39,7 @@ pub trait Storage: Send + Sync { async fn get_next_coconut_credential(&self) -> Result; - async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>; + async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>; } #[async_trait] @@ -59,7 +59,7 @@ impl Storage for PersistentStorage { Err(StorageError::WasmNotSupported) } - async fn remove_coconut_credential(&self, _id: i64) -> Result<(), StorageError> { + async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> { Err(StorageError::WasmNotSupported) } } diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index 600268b69b..f3a04f1099 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -122,7 +122,7 @@ impl Client { // We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care // about neither receiver nor the connection, it doesn't matter which one gets consumed if let Err(err) = receiver.map(Ok).forward(conn).await { - warn!("Failed to forward packets to {} - {:?}", address, err); + warn!("Failed to forward packets to {} - {err}", address); } debug!( diff --git a/common/client-libs/mixnet-client/src/forwarder.rs b/common/client-libs/mixnet-client/src/forwarder.rs index d92f6ad790..1aa68be8da 100644 --- a/common/client-libs/mixnet-client/src/forwarder.rs +++ b/common/client-libs/mixnet-client/src/forwarder.rs @@ -59,7 +59,7 @@ impl PacketForwarder { self.mixnet_client .send_without_response(next_hop, sphinx_packet, packet_mode) { - debug!("failed to forward the packet - {}", err) + debug!("failed to forward the packet - {err}") } } } diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index ec842828e7..0527db6954 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -29,9 +29,9 @@ futures = "0.3" coconut-interface = { path = "../../coconut-interface" } network-defaults = { path = "../../network-defaults" } -validator-api-requests = { path = "../../../validator-api/validator-api-requests", features = ["coconut"] } +nym-api-requests = { path = "../../../nym-api/nym-api-requests", features = ["coconut"] } -# required for nymd-client +# required for nyxd-client # at some point it might be possible to make it wasm-compatible # perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated) async-trait = { version = "0.1.51", optional = true } @@ -50,7 +50,7 @@ execute = { path = "../../execute" } ts-rs = "6.1.2" [features] -nymd-client = [ +nyxd-client = [ "async-trait", "bip39", "config", diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 8ddce33a4f..9ba5e6319b 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -1,54 +1,55 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{validator_api, ValidatorClientError}; +use crate::{nym_api, ValidatorClientError}; use coconut_dkg_common::types::NodeIndex; -#[cfg(feature = "nymd-client")] -use coconut_dkg_common::{ - dealer::ContractDealing, types::DealerDetails, verification_key::ContractVKShare, -}; -#[cfg(feature = "nymd-client")] -use coconut_interface::Base58; use coconut_interface::VerificationKey; use mixnet_contract_common::mixnode::MixNodeDetails; use mixnet_contract_common::MixId; use mixnet_contract_common::{GatewayBond, IdentityKeyRef}; -#[cfg(feature = "nymd-client")] -use std::str::FromStr; -use validator_api_requests::coconut::{ +use nym_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, }; -use validator_api_requests::models::{ +use nym_api_requests::models::{ GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, }; -#[cfg(feature = "nymd-client")] -use crate::nymd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient}; -#[cfg(feature = "nymd-client")] -use crate::nymd::{self, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient}; -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] +use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient}; +#[cfg(feature = "nyxd-client")] +use crate::nyxd::{self, CosmWasmClient, NyxdClient, QueryNyxdClient, SigningNyxdClient}; +#[cfg(feature = "nyxd-client")] +use coconut_dkg_common::{ + dealer::ContractDealing, types::DealerDetails, verification_key::ContractVKShare, +}; +#[cfg(feature = "nyxd-client")] +use coconut_interface::Base58; +#[cfg(feature = "nyxd-client")] use cw3::ProposalResponse; -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] use mixnet_contract_common::{ + families::{Family, FamilyHead}, mixnode::MixNodeBond, pending_events::{PendingEpochEvent, PendingIntervalEvent}, - Delegation, RewardedSetNodeStatus, UnbondedMixnode, + Delegation, IdentityKey, RewardedSetNodeStatus, UnbondedMixnode, }; -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] use network_defaults::NymNetworkDetails; +#[cfg(feature = "nyxd-client")] +use nym_api_requests::models::MixNodeBondAnnotated; +#[cfg(feature = "nyxd-client")] +use std::str::FromStr; use url::Url; -#[cfg(feature = "nymd-client")] -use validator_api_requests::models::MixNodeBondAnnotated; -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] #[must_use] #[derive(Debug, Clone)] pub struct Config { api_url: Url, - nymd_url: Url, + nyxd_url: Url, - nymd_config: nymd::Config, + nyxd_config: nyxd::Config, mixnode_page_limit: Option, gateway_page_limit: Option, @@ -59,7 +60,7 @@ pub struct Config { proposals_page_limit: Option, } -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] impl Config { pub fn try_from_nym_network_details( details: &NymNetworkDetails, @@ -77,11 +78,11 @@ impl Config { Ok(Config { api_url: api_url.pop().unwrap(), - nymd_url: details.endpoints[0] - .nymd_url + nyxd_url: details.endpoints[0] + .nyxd_url .parse() .map_err(ValidatorClientError::MalformedUrlProvided)?, - nymd_config: nymd::Config::try_from_nym_network_details(details)?, + nyxd_config: nyxd::Config::try_from_nym_network_details(details)?, mixnode_page_limit: None, gateway_page_limit: None, mixnode_delegations_page_limit: None, @@ -94,14 +95,14 @@ impl Config { // TODO: this method shouldn't really exist as all information should be included immediately // via `from_nym_network_details`, but it's here for, you guessed it, legacy compatibility - pub fn with_urls(mut self, nymd_url: Url, api_url: Url) -> Self { - self.nymd_url = nymd_url; + pub fn with_urls(mut self, nyxd_url: Url, api_url: Url) -> Self { + self.nyxd_url = nyxd_url; self.api_url = api_url; self } - pub fn with_nymd_url(mut self, nymd_url: Url) -> Self { - self.nymd_url = nymd_url; + pub fn with_nyxd_url(mut self, nyxd_url: Url) -> Self { + self.nyxd_url = nyxd_url; self } @@ -126,7 +127,7 @@ impl Config { } } -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] pub struct Client { // TODO: we really shouldn't be storing a mnemonic here, but removing it would be // non-trivial amount of work and it's out of scope of the current branch @@ -141,20 +142,20 @@ pub struct Client { proposals_page_limit: Option, // ideally they would have been read-only, but unfortunately rust doesn't have such features - pub validator_api: validator_api::Client, - pub nymd: NymdClient, + pub nym_api: nym_api::Client, + pub nyxd: NyxdClient, } -#[cfg(feature = "nymd-client")] -impl Client { +#[cfg(feature = "nyxd-client")] +impl Client { pub fn new_signing( config: Config, mnemonic: bip39::Mnemonic, - ) -> Result, ValidatorClientError> { - let validator_api_client = validator_api::Client::new(config.api_url.clone()); - let nymd_client = NymdClient::connect_with_mnemonic( - config.nymd_config.clone(), - config.nymd_url.as_str(), + ) -> Result, ValidatorClientError> { + let nym_api_client = nym_api::Client::new(config.api_url.clone()); + let nyxd_client = NyxdClient::connect_with_mnemonic( + config.nyxd_config.clone(), + config.nyxd_url.as_str(), mnemonic.clone(), None, )?; @@ -168,14 +169,14 @@ impl Client { dealers_page_limit: config.dealers_page_limit, verification_key_page_limit: config.verification_key_page_limit, proposals_page_limit: config.proposals_page_limit, - validator_api: validator_api_client, - nymd: nymd_client, + nym_api: nym_api_client, + nyxd: nyxd_client, }) } - pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { - self.nymd = NymdClient::connect_with_mnemonic( - self.nymd.current_config().clone(), + pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { + self.nyxd = NyxdClient::connect_with_mnemonic( + self.nyxd.current_config().clone(), new_endpoint.as_ref(), self.mnemonic.clone().unwrap(), None, @@ -183,17 +184,17 @@ impl Client { Ok(()) } - pub fn set_nymd_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.nymd.set_simulated_gas_multiplier(multiplier) + pub fn set_nyxd_simulated_gas_multiplier(&mut self, multiplier: f32) { + self.nyxd.set_simulated_gas_multiplier(multiplier) } } -#[cfg(feature = "nymd-client")] -impl Client { - pub fn new_query(config: Config) -> Result, ValidatorClientError> { - let validator_api_client = validator_api::Client::new(config.api_url.clone()); - let nymd_client = - NymdClient::connect(config.nymd_config.clone(), config.nymd_url.as_str())?; +#[cfg(feature = "nyxd-client")] +impl Client { + pub fn new_query(config: Config) -> Result, ValidatorClientError> { + let nym_api_client = nym_api::Client::new(config.api_url.clone()); + let nyxd_client = + NyxdClient::connect(config.nyxd_config.clone(), config.nyxd_url.as_str())?; Ok(Client { mnemonic: None, @@ -204,33 +205,84 @@ impl Client { dealers_page_limit: config.dealers_page_limit, verification_key_page_limit: config.verification_key_page_limit, proposals_page_limit: config.proposals_page_limit, - validator_api: validator_api_client, - nymd: nymd_client, + nym_api: nym_api_client, + nyxd: nyxd_client, }) } - pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { - self.nymd = NymdClient::connect(self.nymd.current_config().clone(), new_endpoint.as_ref())?; + pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { + self.nyxd = NyxdClient::connect(self.nyxd.current_config().clone(), new_endpoint.as_ref())?; Ok(()) } } -// nymd wrappers -#[cfg(feature = "nymd-client")] +// nyxd wrappers +#[cfg(feature = "nyxd-client")] impl Client { // use case: somebody initialised client without a contract in order to upload and initialise one // and now they want to actually use it without making new client + pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) { - self.nymd + self.nyxd .set_mixnet_contract_address(mixnet_contract_address) } pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId { - self.nymd.mixnet_contract_address().clone() + self.nyxd.mixnet_contract_address().clone() + } + + pub async fn get_all_node_families(&self) -> Result, ValidatorClientError> + where + C: CosmWasmClient + Sync + Send, + { + let mut families = Vec::new(); + let mut start_after = None; + + loop { + let paged_response = self + .nyxd + .get_all_node_families_paged(start_after.take(), None) + .await?; + families.extend(paged_response.families); + + if let Some(start_after_res) = paged_response.start_next_after { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(families) + } + + pub async fn get_all_family_members( + &self, + ) -> Result, ValidatorClientError> + where + C: CosmWasmClient + Sync + Send, + { + let mut members = Vec::new(); + let mut start_after = None; + + loop { + let paged_response = self + .nyxd + .get_all_family_members_paged(start_after.take(), None) + .await?; + members.extend(paged_response.members); + + if let Some(start_after_res) = paged_response.start_next_after { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(members) } // basically handles paging for us - pub async fn get_all_nymd_rewarded_set_mixnodes( + pub async fn get_all_nyxd_rewarded_set_mixnodes( &self, ) -> Result, ValidatorClientError> where @@ -241,7 +293,7 @@ impl Client { loop { let mut paged_response = self - .nymd + .nyxd .get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit) .await?; identities.append(&mut paged_response.nodes); @@ -256,7 +308,7 @@ impl Client { Ok(identities) } - pub async fn get_all_nymd_mixnode_bonds(&self) -> Result, ValidatorClientError> + pub async fn get_all_nyxd_mixnode_bonds(&self) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync + Send, { @@ -264,7 +316,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take()) .await?; mixnodes.append(&mut paged_response.nodes); @@ -279,7 +331,7 @@ impl Client { Ok(mixnodes) } - pub async fn get_all_nymd_mixnodes_detailed( + pub async fn get_all_nyxd_mixnodes_detailed( &self, ) -> Result, ValidatorClientError> where @@ -289,7 +341,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take()) .await?; mixnodes.append(&mut paged_response.nodes); @@ -304,7 +356,7 @@ impl Client { Ok(mixnodes) } - pub async fn get_all_nymd_unbonded_mixnodes( + pub async fn get_all_nyxd_unbonded_mixnodes( &self, ) -> Result, ValidatorClientError> where @@ -314,7 +366,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_unbonded_paged(self.mixnode_page_limit, start_after.take()) .await?; mixnodes.append(&mut paged_response.nodes); @@ -329,7 +381,7 @@ impl Client { Ok(mixnodes) } - pub async fn get_all_nymd_unbonded_mixnodes_by_owner( + pub async fn get_all_nyxd_unbonded_mixnodes_by_owner( &self, owner: &cosmrs::AccountId, ) -> Result, ValidatorClientError> @@ -340,7 +392,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take()) .await?; mixnodes.append(&mut paged_response.nodes); @@ -355,7 +407,7 @@ impl Client { Ok(mixnodes) } - pub async fn get_all_nymd_unbonded_mixnodes_by_identity( + pub async fn get_all_nyxd_unbonded_mixnodes_by_identity( &self, identity_key: String, ) -> Result, ValidatorClientError> @@ -366,7 +418,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_unbonded_by_identity_paged( identity_key.clone(), self.mixnode_page_limit, @@ -385,7 +437,7 @@ impl Client { Ok(mixnodes) } - pub async fn get_all_nymd_gateways(&self) -> Result, ValidatorClientError> + pub async fn get_all_nyxd_gateways(&self) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync + Send, { @@ -393,7 +445,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_gateways_paged(start_after.take(), self.gateway_page_limit) .await?; gateways.append(&mut paged_response.nodes); @@ -408,7 +460,7 @@ impl Client { Ok(gateways) } - pub async fn get_all_nymd_single_mixnode_delegations( + pub async fn get_all_nyxd_single_mixnode_delegations( &self, mix_id: MixId, ) -> Result, ValidatorClientError> @@ -419,7 +471,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_mixnode_delegations_paged( mix_id, start_after.take(), @@ -449,7 +501,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_delegator_delegations_paged( delegation_owner.to_string(), start_after.take(), @@ -476,7 +528,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_all_network_delegations_paged( start_after.take(), self.mixnode_delegations_page_limit, @@ -494,7 +546,7 @@ impl Client { Ok(delegations) } - pub async fn get_all_nymd_pending_epoch_events( + pub async fn get_all_nyxd_pending_epoch_events( &self, ) -> Result, ValidatorClientError> where @@ -505,7 +557,7 @@ impl Client { loop { let mut paged_response = self - .nymd + .nyxd .get_pending_epoch_events_paged(start_after.take(), self.rewarded_set_page_limit) .await?; events.append(&mut paged_response.events); @@ -520,7 +572,7 @@ impl Client { Ok(events) } - pub async fn get_all_nymd_pending_interval_events( + pub async fn get_all_nyxd_pending_interval_events( &self, ) -> Result, ValidatorClientError> where @@ -531,7 +583,7 @@ impl Client { loop { let mut paged_response = self - .nymd + .nyxd .get_pending_interval_events_paged(start_after.take(), self.rewarded_set_page_limit) .await?; events.append(&mut paged_response.events); @@ -546,7 +598,7 @@ impl Client { Ok(events) } - pub async fn get_all_nymd_current_dealers( + pub async fn get_all_nyxd_current_dealers( &self, ) -> Result, ValidatorClientError> where @@ -556,7 +608,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_current_dealers_paged(start_after.take(), self.dealers_page_limit) .await?; dealers.append(&mut paged_response.dealers); @@ -571,7 +623,7 @@ impl Client { Ok(dealers) } - pub async fn get_all_nymd_past_dealers( + pub async fn get_all_nyxd_past_dealers( &self, ) -> Result, ValidatorClientError> where @@ -581,7 +633,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_past_dealers_paged(start_after.take(), self.dealers_page_limit) .await?; dealers.append(&mut paged_response.dealers); @@ -596,7 +648,7 @@ impl Client { Ok(dealers) } - pub async fn get_all_nymd_epoch_dealings( + pub async fn get_all_nyxd_epoch_dealings( &self, idx: usize, ) -> Result, ValidatorClientError> @@ -607,7 +659,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_dealings_paged(idx, start_after.take(), self.dealers_page_limit) .await?; dealings.append(&mut paged_response.dealings); @@ -622,7 +674,7 @@ impl Client { Ok(dealings) } - pub async fn get_all_nymd_verification_key_shares( + pub async fn get_all_nyxd_verification_key_shares( &self, ) -> Result, ValidatorClientError> where @@ -632,7 +684,7 @@ impl Client { let mut start_after = None; loop { let mut paged_response = self - .nymd + .nyxd .get_vk_shares_paged(start_after.take(), self.verification_key_page_limit) .await?; shares.append(&mut paged_response.shares); @@ -647,7 +699,7 @@ impl Client { Ok(shares) } - pub async fn get_all_nymd_proposals( + pub async fn get_all_nyxd_proposals( &self, ) -> Result, ValidatorClientError> where @@ -658,7 +710,7 @@ impl Client { loop { let mut paged_response = self - .nymd + .nyxd .list_proposals(start_after.take(), self.proposals_page_limit) .await?; @@ -677,77 +729,77 @@ impl Client { } // validator-api wrappers -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] impl Client { - pub fn change_validator_api(&mut self, new_endpoint: Url) { - self.validator_api.change_url(new_endpoint) + pub fn change_nym_api(&mut self, new_endpoint: Url) { + self.nym_api.change_url(new_endpoint) } pub async fn get_cached_mixnodes(&self) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_mixnodes().await?) + Ok(self.nym_api.get_mixnodes().await?) } pub async fn get_cached_mixnodes_detailed( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_mixnodes_detailed().await?) + Ok(self.nym_api.get_mixnodes_detailed().await?) } pub async fn get_cached_rewarded_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_rewarded_mixnodes().await?) + Ok(self.nym_api.get_rewarded_mixnodes().await?) } pub async fn get_cached_rewarded_mixnodes_detailed( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_rewarded_mixnodes_detailed().await?) + Ok(self.nym_api.get_rewarded_mixnodes_detailed().await?) } pub async fn get_cached_active_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_active_mixnodes().await?) + Ok(self.nym_api.get_active_mixnodes().await?) } pub async fn get_cached_active_mixnodes_detailed( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_active_mixnodes_detailed().await?) + Ok(self.nym_api.get_active_mixnodes_detailed().await?) } pub async fn get_cached_gateways(&self) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_gateways().await?) + Ok(self.nym_api.get_gateways().await?) } pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, ) -> Result { - Ok(self.validator_api.blind_sign(request_body).await?) + Ok(self.nym_api.blind_sign(request_body).await?) } } #[derive(Clone)] pub struct CoconutApiClient { - pub api_client: ApiClient, + pub api_client: NymApiClient, pub verification_key: VerificationKey, pub node_id: NodeIndex, - #[cfg(feature = "nymd-client")] + #[cfg(feature = "nyxd-client")] pub cosmos_address: cosmrs::AccountId, } -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] impl CoconutApiClient { pub async fn all_coconut_api_clients( - nymd_client: &Client, + nyxd_client: &Client, ) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync + Send, { - Ok(nymd_client - .get_all_nymd_verification_key_shares() + Ok(nyxd_client + .get_all_nyxd_verification_key_shares() .await? .into_iter() .filter_map(Self::try_from) @@ -760,7 +812,7 @@ impl CoconutApiClient { if let Ok(verification_key) = VerificationKey::try_from_bs58(&share.share) { if let Ok(cosmos_address) = cosmrs::AccountId::from_str(share.owner.as_str()) { return Some(CoconutApiClient { - api_client: ApiClient::new(url_address), + api_client: NymApiClient::new(url_address), verification_key, node_id: share.node_index, cosmos_address, @@ -774,43 +826,41 @@ impl CoconutApiClient { } #[derive(Clone)] -pub struct ApiClient { - pub validator_api: validator_api::Client, +pub struct NymApiClient { + pub nym_api_client: nym_api::Client, // TODO: perhaps if we really need it at some (currently I don't see any reasons for it) // we could re-implement the communication with the REST API on port 1317 } -impl ApiClient { +impl NymApiClient { pub fn new(api_url: Url) -> Self { - let validator_api_client = validator_api::Client::new(api_url); + let nym_api_client = nym_api::Client::new(api_url); - ApiClient { - validator_api: validator_api_client, - } + NymApiClient { nym_api_client } } - pub fn change_validator_api(&mut self, new_endpoint: Url) { - self.validator_api.change_url(new_endpoint); + pub fn change_nym_api(&mut self, new_endpoint: Url) { + self.nym_api_client.change_url(new_endpoint); } pub async fn get_cached_active_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_active_mixnodes().await?) + Ok(self.nym_api_client.get_active_mixnodes().await?) } pub async fn get_cached_rewarded_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_rewarded_mixnodes().await?) + Ok(self.nym_api_client.get_rewarded_mixnodes().await?) } pub async fn get_cached_mixnodes(&self) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_mixnodes().await?) + Ok(self.nym_api_client.get_mixnodes().await?) } pub async fn get_cached_gateways(&self) -> Result, ValidatorClientError> { - Ok(self.validator_api.get_gateways().await?) + Ok(self.nym_api_client.get_gateways().await?) } pub async fn get_gateway_core_status_count( @@ -819,7 +869,7 @@ impl ApiClient { since: Option, ) -> Result { Ok(self - .validator_api + .nym_api_client .get_gateway_core_status_count(identity, since) .await?) } @@ -830,7 +880,7 @@ impl ApiClient { since: Option, ) -> Result { Ok(self - .validator_api + .nym_api_client .get_mixnode_core_status_count(mix_id, since) .await?) } @@ -839,7 +889,7 @@ impl ApiClient { &self, mix_id: MixId, ) -> Result { - Ok(self.validator_api.get_mixnode_status(mix_id).await?) + Ok(self.nym_api_client.get_mixnode_status(mix_id).await?) } pub async fn get_mixnode_reward_estimation( @@ -847,7 +897,7 @@ impl ApiClient { mix_id: MixId, ) -> Result { Ok(self - .validator_api + .nym_api_client .get_mixnode_reward_estimation(mix_id) .await?) } @@ -857,7 +907,7 @@ impl ApiClient { mix_id: MixId, ) -> Result { Ok(self - .validator_api + .nym_api_client .get_mixnode_stake_saturation(mix_id) .await?) } @@ -866,7 +916,7 @@ impl ApiClient { &self, request_body: &BlindSignRequestBody, ) -> Result { - Ok(self.validator_api.blind_sign(request_body).await?) + Ok(self.nym_api_client.blind_sign(request_body).await?) } pub async fn partial_bandwidth_credential( @@ -874,7 +924,7 @@ impl ApiClient { request_body: &str, ) -> Result { Ok(self - .validator_api + .nym_api_client .partial_bandwidth_credential(request_body) .await?) } @@ -884,7 +934,7 @@ impl ApiClient { request_body: &VerifyCredentialBody, ) -> Result { Ok(self - .validator_api + .nym_api_client .verify_bandwidth_credential(request_body) .await?) } diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index dc1acb8cd9..14f365639a 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,8 +1,8 @@ -use crate::nymd::error::NymdError; -use crate::nymd::{Config as ClientConfig, NymdClient, QueryNymdClient}; -use crate::ApiClient; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient}; +use crate::NymApiClient; -use crate::nymd::traits::MixnetQueryClient; +use crate::nyxd::traits::MixnetQueryClient; use colored::Colorize; use core::fmt; use itertools::Itertools; @@ -16,9 +16,9 @@ use url::Url; const MAX_URLS_TESTED: usize = 200; const CONNECTION_TEST_TIMEOUT_SEC: u64 = 2; -// Run connection tests for all specified nymd and api urls. These are all run concurrently. +/// Run connection tests for all specified nyxd and api urls. These are all run concurrently. pub async fn run_validator_connection_test( - nymd_urls: impl Iterator, + nyxd_urls: impl Iterator, api_urls: impl Iterator, mixnet_contract_address: HashMap, ) -> ( @@ -27,7 +27,7 @@ pub async fn run_validator_connection_test( ) { // Setup all the clients for the connection tests let connection_test_clients = - setup_connection_tests(nymd_urls, api_urls, mixnet_contract_address); + setup_connection_tests(nyxd_urls, api_urls, mixnet_contract_address); // Run all tests concurrently let connection_results = futures::future::join_all( @@ -40,28 +40,28 @@ pub async fn run_validator_connection_test( // Seperate and collect results into HashMaps ( - extract_and_collect_results_into_map(&connection_results, &UrlType::Nymd), - extract_and_collect_results_into_map(&connection_results, &UrlType::Api), + extract_and_collect_results_into_map(&connection_results, &UrlType::Nyxd), + extract_and_collect_results_into_map(&connection_results, &UrlType::NymApi), ) } fn setup_connection_tests( - nymd_urls: impl Iterator, + nyxd_urls: impl Iterator, api_urls: impl Iterator, mixnet_contract_address: HashMap, ) -> impl Iterator { - let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| { + let nyxd_connection_test_clients = nyxd_urls.filter_map(move |(network, url)| { let address = mixnet_contract_address .get(&network) .expect("No configured contract address") .clone(); let config = ClientConfig::try_from_nym_network_details(&network) - .expect("failed to create valid nymd client config"); + .expect("failed to create valid nyxd client config"); - if let Ok(mut client) = NymdClient::::connect(config, url.as_str()) { + if let Ok(mut client) = NyxdClient::::connect(config, url.as_str()) { // possibly redundant, but lets just leave it here client.set_mixnet_contract_address(address); - Some(ClientForConnectionTest::Nymd( + Some(ClientForConnectionTest::Nyxd( network, url, Box::new(client), @@ -72,10 +72,10 @@ fn setup_connection_tests( }); let api_connection_test_clients = api_urls.map(|(network, url)| { - ClientForConnectionTest::Api(network, url.clone(), ApiClient::new(url)) + ClientForConnectionTest::Api(network, url.clone(), NymApiClient::new(url)) }); - nymd_connection_test_clients.chain(api_connection_test_clients) + nyxd_connection_test_clients.chain(api_connection_test_clients) } fn extract_and_collect_results_into_map( @@ -92,10 +92,10 @@ fn extract_and_collect_results_into_map( .into_group_map() } -async fn test_nymd_connection( +async fn test_nyxd_connection( network: NymNetworkDetails, url: &Url, - client: &NymdClient, + client: &NyxdClient, ) -> ConnectionResult { let result = match timeout( Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC), @@ -103,48 +103,48 @@ async fn test_nymd_connection( ) .await { - Ok(Err(NymdError::TendermintError(e))) => { + Ok(Err(NyxdError::TendermintError(e))) => { // If we get a tendermint-rpc error, we classify the node as not contactable - log::debug!("Checking: nymd_url: {url}: {}: {}", "failed".red(), e); + log::debug!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); false } - Ok(Err(NymdError::AbciError(code, log))) => { + Ok(Err(NyxdError::AbciError { code, log, .. })) => { // We accept the mixnet contract not found as ok from a connection standpoint. This happens // for example on a pre-launch network. log::debug!( - "Checking: nymd_url: {url}: {}, but with abci error: {code}: {log}", + "Checking: nyxd url: {url}: {}, but with abci error: {code}: {log}", "success".green() ); code == 18 } - Ok(Err(error @ NymdError::NoContractAddressAvailable)) => { - log::debug!("Checking: nymd_url: {url}: {}: {error}", "failed".red()); + Ok(Err(error @ NyxdError::NoContractAddressAvailable)) => { + log::debug!("Checking: nyxd url: {url}: {}: {error}", "failed".red()); false } Ok(Err(e)) => { // For any other error, we're optimistic and just try anyway. log::debug!( - "Checking: nymd_url: {url}: {}, but with error: {e}", + "Checking: nyxd_url: {url}: {}, but with error: {e}", "success".green() ); true } Ok(Ok(_)) => { - log::debug!("Checking: nymd_url: {url}: {}", "success".green()); + log::debug!("Checking: nyxd_url: {url}: {}", "success".green()); true } Err(e) => { - log::debug!("Checking: nymd_url: {url}: {}: {e}", "failed".red()); + log::debug!("Checking: nyxd_url: {url}: {}: {e}", "failed".red()); false } }; - ConnectionResult::Nymd(network, url.clone(), result) + ConnectionResult::Nyxd(network, url.clone(), result) } -async fn test_api_connection( +async fn test_nym_api_connection( network: NymNetworkDetails, url: &Url, - client: &ApiClient, + client: &NymApiClient, ) -> ConnectionResult { let result = match timeout( Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC), @@ -169,18 +169,18 @@ async fn test_api_connection( } enum ClientForConnectionTest { - Nymd(NymNetworkDetails, Url, Box>), - Api(NymNetworkDetails, Url, ApiClient), + Nyxd(NymNetworkDetails, Url, Box>), + Api(NymNetworkDetails, Url, NymApiClient), } impl ClientForConnectionTest { async fn run_connection_check(self) -> ConnectionResult { match self { - ClientForConnectionTest::Nymd(network, ref url, ref client) => { - test_nymd_connection(network, url, client).await + ClientForConnectionTest::Nyxd(network, ref url, ref client) => { + test_nyxd_connection(network, url, client).await } ClientForConnectionTest::Api(network, ref url, ref client) => { - test_api_connection(network, url, client).await + test_nym_api_connection(network, url, client).await } } } @@ -188,37 +188,37 @@ impl ClientForConnectionTest { #[derive(Debug, PartialEq, Eq)] enum UrlType { - Nymd, - Api, + Nyxd, + NymApi, } impl fmt::Display for UrlType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - UrlType::Nymd => write!(f, "nymd"), - UrlType::Api => write!(f, "api"), + UrlType::Nyxd => write!(f, "nyxd"), + UrlType::NymApi => write!(f, "api"), } } } #[derive(Debug)] enum ConnectionResult { - Nymd(NymNetworkDetails, Url, bool), + Nyxd(NymNetworkDetails, Url, bool), Api(NymNetworkDetails, Url, bool), } impl ConnectionResult { fn result(&self) -> (&NymNetworkDetails, &Url, &bool) { match self { - ConnectionResult::Nymd(network, url, result) + ConnectionResult::Nyxd(network, url, result) | ConnectionResult::Api(network, url, result) => (network, url, result), } } fn url_type(&self) -> UrlType { match self { - ConnectionResult::Nymd(..) => UrlType::Nymd, - ConnectionResult::Api(..) => UrlType::Api, + ConnectionResult::Nyxd(..) => UrlType::Nyxd, + ConnectionResult::Api(..) => UrlType::NymApi, } } } diff --git a/common/client-libs/validator-client/src/error.rs b/common/client-libs/validator-client/src/error.rs index e832b12618..7b37ed6a14 100644 --- a/common/client-libs/validator-client/src/error.rs +++ b/common/client-libs/validator-client/src/error.rs @@ -1,23 +1,23 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::validator_api; +use crate::nym_api; use thiserror::Error; #[derive(Error, Debug)] pub enum ValidatorClientError { #[error("There was an issue with the validator api request - {source}")] - ValidatorAPIError { + NymAPIError { #[from] - source: validator_api::error::ValidatorAPIError, + source: nym_api::error::NymAPIError, }, #[error("One of the provided URLs was malformed - {0}")] MalformedUrlProvided(#[from] url::ParseError), - #[cfg(feature = "nymd-client")] - #[error("There was an issue with the Nymd client - {0}")] - NymdError(#[from] crate::nymd::error::NymdError), + #[cfg(feature = "nyxd-client")] + #[error("There was an issue with the Nyxd client - {0}")] + NyxdError(#[from] crate::nyxd::error::NyxdError), #[error("No validator API url has been provided")] NoAPIUrlAvailable, diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index 2a5e010338..1c1c3a344c 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -2,17 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 pub mod client; -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] pub mod connection_tester; mod error; -#[cfg(feature = "nymd-client")] -pub mod nymd; -pub mod validator_api; +pub mod nym_api; +#[cfg(feature = "nyxd-client")] +pub mod nyxd; -#[cfg(feature = "nymd-client")] -pub use crate::client::{ApiClient, CoconutApiClient}; +#[cfg(feature = "nyxd-client")] +pub use crate::client::{CoconutApiClient, NymApiClient}; pub use crate::error::ValidatorClientError; -pub use validator_api_requests::*; +pub use nym_api_requests::*; -#[cfg(feature = "nymd-client")] +#[cfg(feature = "nyxd-client")] pub use client::{Client, Config}; diff --git a/common/client-libs/validator-client/src/validator_api/error.rs b/common/client-libs/validator-client/src/nym_api/error.rs similarity index 60% rename from common/client-libs/validator-client/src/validator_api/error.rs rename to common/client-libs/validator-client/src/nym_api/error.rs index 9d7cd1d50d..1c3f7b11a4 100644 --- a/common/client-libs/validator-client/src/validator_api/error.rs +++ b/common/client-libs/validator-client/src/nym_api/error.rs @@ -1,8 +1,8 @@ +use nym_api_requests::models::RequestError; use thiserror::Error; -use validator_api_requests::models::RequestError; #[derive(Error, Debug)] -pub enum ValidatorAPIError { +pub enum NymAPIError { #[error("There was an issue with the REST request - {source}")] ReqwestClientError { #[from] @@ -12,6 +12,6 @@ pub enum ValidatorAPIError { #[error("Request failed with error message - {0}")] GenericRequestFailure(String), - #[error("The validator API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())] + #[error("The nym API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())] ApiRequestFailure { status: u16, error: RequestError }, } diff --git a/common/client-libs/validator-client/src/validator_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs similarity index 77% rename from common/client-libs/validator-client/src/validator_api/mod.rs rename to common/client-libs/validator-client/src/nym_api/mod.rs index 4a7b7af3cd..e1d65959c6 100644 --- a/common/client-libs/validator-client/src/validator_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -1,22 +1,23 @@ // Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::validator_api::error::ValidatorAPIError; -use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG}; +use crate::nym_api::error::NymAPIError; +use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG}; use mixnet_contract_common::mixnode::MixNodeDetails; use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId}; +use nym_api_requests::coconut::{ + BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, +}; +use nym_api_requests::models::{ + ComputeRewardEstParam, GatewayCoreStatusResponse, GatewayStatusReportResponse, + GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated, + MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse, + MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, StakeSaturationResponse, + UptimeResponse, +}; use reqwest::Response; use serde::{Deserialize, Serialize}; use url::Url; -use validator_api_requests::coconut::{ - BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, -}; -use validator_api_requests::models::{ - GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse, - InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse, - MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, - RewardEstimationResponse, StakeSaturationResponse, UptimeResponse, -}; pub mod error; pub mod routes; @@ -53,7 +54,7 @@ impl Client { &self, path: PathSegments<'_>, params: Params<'_, K, V>, - ) -> Result + ) -> Result where K: AsRef, V: AsRef, @@ -62,11 +63,11 @@ impl Client { Ok(self.reqwest_client.get(url).send().await?) } - async fn query_validator_api( + async fn query_nym_api( &self, path: PathSegments<'_>, params: Params<'_, K, V>, - ) -> Result + ) -> Result where for<'a> T: Deserialize<'a>, K: AsRef, @@ -76,16 +77,16 @@ impl Client { if res.status().is_success() { Ok(res.json().await?) } else { - Err(ValidatorAPIError::GenericRequestFailure(res.text().await?)) + Err(NymAPIError::GenericRequestFailure(res.text().await?)) } } // This works for endpoints returning Result, ErrorResponse> - async fn query_validator_api_fallible( + async fn query_nym_api_fallible( &self, path: PathSegments<'_>, params: Params<'_, K, V>, - ) -> Result + ) -> Result where for<'a> T: Deserialize<'a>, K: AsRef, @@ -97,19 +98,19 @@ impl Client { Ok(res.json().await?) } else { let request_error: RequestError = res.json().await?; - Err(ValidatorAPIError::ApiRequestFailure { + Err(NymAPIError::ApiRequestFailure { status: status.as_u16(), error: request_error, }) } } - async fn post_validator_api( + async fn post_nym_api( &self, path: PathSegments<'_>, params: Params<'_, K, V>, json_body: &B, - ) -> Result + ) -> Result where B: Serialize + ?Sized, for<'a> T: Deserialize<'a>, @@ -121,34 +122,35 @@ impl Client { if response.status().is_success() { Ok(response.json().await?) } else { - Err(ValidatorAPIError::GenericRequestFailure( - response.text().await?, - )) + Err(NymAPIError::GenericRequestFailure(response.text().await?)) } } - pub async fn get_mixnodes(&self) -> Result, ValidatorAPIError> { - self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS) + pub async fn get_mixnodes(&self) -> Result, NymAPIError> { + self.query_nym_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS) .await } - pub async fn get_mixnodes_detailed( - &self, - ) -> Result, ValidatorAPIError> { - self.query_validator_api( - &[routes::API_VERSION, routes::MIXNODES, routes::DETAILED], + pub async fn get_mixnodes_detailed(&self) -> Result, NymAPIError> { + self.query_nym_api( + &[ + routes::API_VERSION, + routes::STATUS, + routes::MIXNODES, + routes::DETAILED, + ], NO_PARAMS, ) .await } - pub async fn get_gateways(&self) -> Result, ValidatorAPIError> { - self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS) + pub async fn get_gateways(&self) -> Result, NymAPIError> { + self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS) .await } - pub async fn get_active_mixnodes(&self) -> Result, ValidatorAPIError> { - self.query_validator_api( + pub async fn get_active_mixnodes(&self) -> Result, NymAPIError> { + self.query_nym_api( &[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE], NO_PARAMS, ) @@ -157,10 +159,11 @@ impl Client { pub async fn get_active_mixnodes_detailed( &self, - ) -> Result, ValidatorAPIError> { - self.query_validator_api( + ) -> Result, NymAPIError> { + self.query_nym_api( &[ routes::API_VERSION, + routes::STATUS, routes::MIXNODES, routes::ACTIVE, routes::DETAILED, @@ -170,8 +173,8 @@ impl Client { .await } - pub async fn get_rewarded_mixnodes(&self) -> Result, ValidatorAPIError> { - self.query_validator_api( + pub async fn get_rewarded_mixnodes(&self) -> Result, NymAPIError> { + self.query_nym_api( &[routes::API_VERSION, routes::MIXNODES, routes::REWARDED], NO_PARAMS, ) @@ -181,8 +184,8 @@ impl Client { pub async fn get_mixnode_report( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api( + ) -> Result { + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS, @@ -198,8 +201,8 @@ impl Client { pub async fn get_gateway_report( &self, identity: IdentityKeyRef<'_>, - ) -> Result { - self.query_validator_api( + ) -> Result { + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS, @@ -215,8 +218,8 @@ impl Client { pub async fn get_mixnode_history( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api( + ) -> Result { + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS, @@ -232,8 +235,8 @@ impl Client { pub async fn get_gateway_history( &self, identity: IdentityKeyRef<'_>, - ) -> Result { - self.query_validator_api( + ) -> Result { + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS, @@ -248,10 +251,11 @@ impl Client { pub async fn get_rewarded_mixnodes_detailed( &self, - ) -> Result, ValidatorAPIError> { - self.query_validator_api( + ) -> Result, NymAPIError> { + self.query_nym_api( &[ routes::API_VERSION, + routes::STATUS, routes::MIXNODES, routes::REWARDED, routes::DETAILED, @@ -265,9 +269,9 @@ impl Client { &self, identity: IdentityKeyRef<'_>, since: Option, - ) -> Result { + ) -> Result { if let Some(since) = since { - self.query_validator_api( + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -279,7 +283,7 @@ impl Client { ) .await } else { - self.query_validator_api( + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -296,9 +300,9 @@ impl Client { &self, mix_id: MixId, since: Option, - ) -> Result { + ) -> Result { if let Some(since) = since { - self.query_validator_api( + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -310,7 +314,7 @@ impl Client { ) .await } else { - self.query_validator_api( + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -327,8 +331,8 @@ impl Client { pub async fn get_mixnode_status( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api( + ) -> Result { + self.query_nym_api( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -344,8 +348,8 @@ impl Client { pub async fn get_mixnode_reward_estimation( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api_fallible( + ) -> Result { + self.query_nym_api_fallible( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -358,11 +362,30 @@ impl Client { .await } + pub async fn compute_mixnode_reward_estimation( + &self, + mix_id: MixId, + request_body: &ComputeRewardEstParam, + ) -> Result { + self.post_nym_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + &mix_id.to_string(), + routes::COMPUTE_REWARD_ESTIMATION, + ], + NO_PARAMS, + request_body, + ) + .await + } + pub async fn get_mixnode_stake_saturation( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api_fallible( + ) -> Result { + self.query_nym_api_fallible( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -378,8 +401,8 @@ impl Client { pub async fn get_mixnode_inclusion_probability( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api_fallible( + ) -> Result { + self.query_nym_api_fallible( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -395,8 +418,8 @@ impl Client { pub async fn get_mixnode_avg_uptime( &self, mix_id: MixId, - ) -> Result { - self.query_validator_api_fallible( + ) -> Result { + self.query_nym_api_fallible( &[ routes::API_VERSION, routes::STATUS_ROUTES, @@ -412,8 +435,8 @@ impl Client { pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, - ) -> Result { - self.post_validator_api( + ) -> Result { + self.post_nym_api( &[ routes::API_VERSION, routes::COCONUT_ROUTES, @@ -429,8 +452,8 @@ impl Client { pub async fn partial_bandwidth_credential( &self, request_body: &str, - ) -> Result { - self.post_validator_api( + ) -> Result { + self.post_nym_api( &[ routes::API_VERSION, routes::COCONUT_ROUTES, @@ -446,8 +469,8 @@ impl Client { pub async fn verify_bandwidth_credential( &self, request_body: &VerifyCredentialBody, - ) -> Result { - self.post_validator_api( + ) -> Result { + self.post_nym_api( &[ routes::API_VERSION, routes::COCONUT_ROUTES, diff --git a/common/client-libs/validator-client/src/validator_api/routes.rs b/common/client-libs/validator-client/src/nym_api/routes.rs similarity index 87% rename from common/client-libs/validator-client/src/validator_api/routes.rs rename to common/client-libs/validator-client/src/nym_api/routes.rs index 66a822b4fd..cd8a16047a 100644 --- a/common/client-libs/validator-client/src/validator_api/routes.rs +++ b/common/client-libs/validator-client/src/nym_api/routes.rs @@ -1,9 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use network_defaults::VALIDATOR_API_VERSION; +use network_defaults::NYM_API_VERSION; -pub const API_VERSION: &str = VALIDATOR_API_VERSION; +pub const API_VERSION: &str = NYM_API_VERSION; pub const MIXNODES: &str = "mixnodes"; pub const GATEWAYS: &str = "gateways"; @@ -28,6 +28,7 @@ pub const STATUS: &str = "status"; pub const REPORT: &str = "report"; pub const HISTORY: &str = "history"; pub const REWARD_ESTIMATION: &str = "reward-estimation"; +pub const COMPUTE_REWARD_ESTIMATION: &str = "compute-reward-estimation"; pub const AVG_UPTIME: &str = "avg_uptime"; pub const STAKE_SATURATION: &str = "stake-saturation"; pub const INCLUSION_CHANCE: &str = "inclusion-probability"; diff --git a/common/client-libs/validator-client/src/nymd/coin.rs b/common/client-libs/validator-client/src/nyxd/coin.rs similarity index 100% rename from common/client-libs/validator-client/src/nymd/coin.rs rename to common/client-libs/validator-client/src/nyxd/coin.rs diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs similarity index 88% rename from common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs index 8da9d3192c..9be833c68b 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs @@ -1,13 +1,14 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::coin::Coin; -use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key}; -use crate::nymd::cosmwasm_client::types::{ +use crate::nyxd; +use crate::nyxd::coin::Coin; +use crate::nyxd::cosmwasm_client::helpers::{create_pagination, next_page_key}; +use crate::nyxd::cosmwasm_client::types::{ Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId, SequenceResponse, SimulateResponse, }; -use crate::nymd::error::NymdError; +use crate::nyxd::error::NyxdError; use async_trait::async_trait; use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; use cosmrs::proto::cosmos::bank::v1beta1::{ @@ -23,7 +24,6 @@ use cosmrs::rpc::endpoint::broadcast; use cosmrs::rpc::endpoint::tx::Response as TxResponse; use cosmrs::rpc::query::Query; use cosmrs::rpc::{self, HttpClient, Order}; -use cosmrs::tendermint::abci::Code as AbciCode; use cosmrs::tendermint::abci::Transaction; use cosmrs::tendermint::{abci, block, chain}; use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx}; @@ -57,7 +57,7 @@ pub trait CosmWasmClient: rpc::Client { &self, path: Option, req: Req, - ) -> Result + ) -> Result where Req: Message, Res: Message + Default, @@ -66,25 +66,21 @@ pub trait CosmWasmClient: rpc::Client { req.encode(&mut buf)?; let res = self.abci_query(path, buf, None, false).await?; + let res_success = nyxd::error::parse_abci_query_result(res)?; - match res.code { - AbciCode::Err(code) => return Err(NymdError::AbciError(code, res.log)), - AbciCode::Ok => (), - } - - Ok(Res::decode(res.value.as_ref())?) + Ok(Res::decode(res_success.value.as_ref())?) } - async fn get_chain_id(&self) -> Result { + async fn get_chain_id(&self) -> Result { Ok(self.status().await?.node_info.network) } - async fn get_height(&self) -> Result { + async fn get_height(&self) -> Result { Ok(self.status().await?.sync_info.latest_block_height) } // TODO: the return type should probably be changed to a non-proto, type-safe Account alternative - async fn get_account(&self, address: &AccountId) -> Result, NymdError> { + async fn get_account(&self, address: &AccountId) -> Result, NyxdError> { let path = Some("/cosmos.auth.v1beta1.Query/Account".parse().unwrap()); let req = QueryAccountRequest { @@ -98,11 +94,11 @@ pub trait CosmWasmClient: rpc::Client { res.account.map(TryFrom::try_from).transpose() } - async fn get_sequence(&self, address: &AccountId) -> Result { + async fn get_sequence(&self, address: &AccountId) -> Result { let account = self .get_account(address) .await? - .ok_or_else(|| NymdError::NonExistentAccountError(address.clone()))?; + .ok_or_else(|| NyxdError::NonExistentAccountError(address.clone()))?; let base_account = account.try_get_base_account()?; Ok(SequenceResponse { @@ -111,7 +107,7 @@ pub trait CosmWasmClient: rpc::Client { }) } - async fn get_block(&self, height: Option) -> Result { + async fn get_block(&self, height: Option) -> Result { match height { Some(height) => self.block(height).await.map_err(|err| err.into()), None => self.latest_block().await.map_err(|err| err.into()), @@ -122,7 +118,7 @@ pub trait CosmWasmClient: rpc::Client { &self, address: &AccountId, search_denom: String, - ) -> Result, NymdError> { + ) -> Result, NyxdError> { let path = Some("/cosmos.bank.v1beta1.Query/Balance".parse().unwrap()); let req = QueryBalanceRequest { @@ -137,10 +133,10 @@ pub trait CosmWasmClient: rpc::Client { res.balance .map(|proto| CosmosCoin::try_from(proto).map(Into::into)) .transpose() - .map_err(|_| NymdError::SerializationError("Coin".to_owned())) + .map_err(|_| NyxdError::SerializationError("Coin".to_owned())) } - async fn get_all_balances(&self, address: &AccountId) -> Result, NymdError> { + async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> { let path = Some("/cosmos.bank.v1beta1.Query/AllBalances".parse().unwrap()); let mut raw_balances = Vec::new(); @@ -168,10 +164,10 @@ pub trait CosmWasmClient: rpc::Client { .into_iter() .map(|proto| CosmosCoin::try_from(proto).map(Into::into)) .collect::>() - .map_err(|_| NymdError::SerializationError("Coins".to_owned())) + .map_err(|_| NyxdError::SerializationError("Coins".to_owned())) } - async fn get_total_supply(&self) -> Result, NymdError> { + async fn get_total_supply(&self) -> Result, NyxdError> { let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap()); let mut supply = Vec::new(); @@ -196,14 +192,14 @@ pub trait CosmWasmClient: rpc::Client { .into_iter() .map(|proto| CosmosCoin::try_from(proto).map(Into::into)) .collect::>() - .map_err(|_| NymdError::SerializationError("Coins".to_owned())) + .map_err(|_| NyxdError::SerializationError("Coins".to_owned())) } - async fn get_tx(&self, id: tx::Hash) -> Result { + async fn get_tx(&self, id: tx::Hash) -> Result { Ok(self.tx(id, false).await?) } - async fn search_tx(&self, query: Query) -> Result, NymdError> { + async fn search_tx(&self, query: Query) -> Result, NyxdError> { // according to https://docs.tendermint.com/master/rpc/#/Info/tx_search // the maximum entries per page is 100 and the default is 30 // so let's attempt to use the maximum @@ -238,7 +234,7 @@ pub trait CosmWasmClient: rpc::Client { async fn broadcast_tx_async( &self, tx: Transaction, - ) -> Result { + ) -> Result { Ok(rpc::Client::broadcast_tx_async(self, tx).await?) } @@ -246,7 +242,7 @@ pub trait CosmWasmClient: rpc::Client { async fn broadcast_tx_sync( &self, tx: Transaction, - ) -> Result { + ) -> Result { Ok(rpc::Client::broadcast_tx_sync(self, tx).await?) } @@ -254,16 +250,16 @@ pub trait CosmWasmClient: rpc::Client { async fn broadcast_tx_commit( &self, tx: Transaction, - ) -> Result { + ) -> Result { Ok(rpc::Client::broadcast_tx_commit(self, tx).await?) } - async fn broadcast_tx(&self, tx: Transaction) -> Result { + async fn broadcast_tx(&self, tx: Transaction) -> Result { let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?; if broadcasted.code.is_err() { let code_val = broadcasted.code.value(); - return Err(NymdError::BroadcastTxErrorDeliverTx { + return Err(NyxdError::BroadcastTxErrorDeliverTx { hash: broadcasted.hash, height: None, code: code_val, @@ -280,7 +276,7 @@ pub trait CosmWasmClient: rpc::Client { broadcasted.hash ); if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() { - return Err(NymdError::BroadcastTimeout { + return Err(NyxdError::BroadcastTimeout { hash: tx_hash, timeout: self.broadcast_timeout(), }); @@ -294,7 +290,7 @@ pub trait CosmWasmClient: rpc::Client { } } - async fn get_codes(&self) -> Result, NymdError> { + async fn get_codes(&self) -> Result, NyxdError> { let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap()); let mut raw_codes = Vec::new(); @@ -321,7 +317,7 @@ pub trait CosmWasmClient: rpc::Client { .collect::>() } - async fn get_code_details(&self, code_id: ContractCodeId) -> Result { + async fn get_code_details(&self, code_id: ContractCodeId) -> Result { let path = Some("/cosmwasm.wasm.v1.Query/Code".parse().unwrap()); let req = QueryCodeRequest { code_id }; @@ -333,10 +329,10 @@ pub trait CosmWasmClient: rpc::Client { if let Some(code_info) = res.code_info { Ok(CodeDetails::new(code_info.try_into()?, res.data)) } else { - Err(NymdError::NoCodeInformation(code_id)) + Err(NyxdError::NoCodeInformation(code_id)) } } - async fn get_contracts(&self, code_id: ContractCodeId) -> Result, NymdError> { + async fn get_contracts(&self, code_id: ContractCodeId) -> Result, NyxdError> { let path = Some("/cosmwasm.wasm.v1.Query/ContractsByCode".parse().unwrap()); let mut raw_contracts = Vec::new(); @@ -364,10 +360,10 @@ pub trait CosmWasmClient: rpc::Client { .iter() .map(|raw| raw.parse()) .collect::>() - .map_err(|_| NymdError::DeserializationError("Contract addresses".to_owned())) + .map_err(|_| NyxdError::DeserializationError("Contract addresses".to_owned())) } - async fn get_contract(&self, address: &AccountId) -> Result { + async fn get_contract(&self, address: &AccountId) -> Result { let path = Some("/cosmwasm.wasm.v1.Query/ContractInfo".parse().unwrap()); let req = QueryContractInfoRequest { @@ -382,17 +378,17 @@ pub trait CosmWasmClient: rpc::Client { if let Some(contract_info) = res.contract_info { let address = response_address .parse() - .map_err(|_| NymdError::MalformedAccountAddress(response_address))?; + .map_err(|_| NyxdError::MalformedAccountAddress(response_address))?; Ok(Contract::new(address, contract_info.try_into()?)) } else { - Err(NymdError::NoContractInformation(address.clone())) + Err(NyxdError::NoContractInformation(address.clone())) } } async fn get_contract_code_history( &self, address: &AccountId, - ) -> Result, NymdError> { + ) -> Result, NyxdError> { let path = Some("/cosmwasm.wasm.v1.Query/ContractHistory".parse().unwrap()); let mut raw_entries = Vec::new(); @@ -426,7 +422,7 @@ pub trait CosmWasmClient: rpc::Client { &self, address: &AccountId, query_data: Vec, - ) -> Result, NymdError> { + ) -> Result, NyxdError> { let path = Some("/cosmwasm.wasm.v1.Query/RawContractState".parse().unwrap()); let req = QueryRawContractStateRequest { @@ -445,7 +441,7 @@ pub trait CosmWasmClient: rpc::Client { &self, address: &AccountId, query_msg: &M, - ) -> Result + ) -> Result where M: ?Sized + Serialize + Sync, for<'a> T: Deserialize<'a>, @@ -482,7 +478,7 @@ pub trait CosmWasmClient: rpc::Client { &self, tx: Option, tx_bytes: Vec, - ) -> Result { + ) -> Result { let path = Some("/cosmos.tx.v1beta1.Service/Simulate".parse().unwrap()); let req = SimulateRequest { diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs similarity index 78% rename from common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs index ce1e93dcdb..0dd7f98ead 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/helpers.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; +use crate::nyxd::error::NyxdError; use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse}; use cosmrs::proto::cosmos::base::v1beta1::Coin as ProtoCoin; use cosmrs::rpc::endpoint::broadcast; @@ -11,13 +11,13 @@ use flate2::Compression; use std::io::Write; pub(crate) trait CheckResponse: Sized { - fn check_response(self) -> Result; + fn check_response(self) -> Result; } impl CheckResponse for broadcast::tx_commit::Response { - fn check_response(self) -> Result { + fn check_response(self) -> Result { if self.check_tx.code.is_err() { - return Err(NymdError::BroadcastTxErrorCheckTx { + return Err(NyxdError::BroadcastTxErrorCheckTx { hash: self.hash, height: Some(self.height), code: self.check_tx.code.value(), @@ -26,7 +26,7 @@ impl CheckResponse for broadcast::tx_commit::Response { } if self.deliver_tx.code.is_err() { - return Err(NymdError::BroadcastTxErrorDeliverTx { + return Err(NyxdError::BroadcastTxErrorDeliverTx { hash: self.hash, height: Some(self.height), code: self.deliver_tx.code.value(), @@ -38,10 +38,10 @@ impl CheckResponse for broadcast::tx_commit::Response { } } -impl CheckResponse for crate::nymd::TxResponse { - fn check_response(self) -> Result { +impl CheckResponse for crate::nyxd::TxResponse { + fn check_response(self) -> Result { if self.tx_result.code.is_err() { - return Err(NymdError::BroadcastTxErrorDeliverTx { + return Err(NyxdError::BroadcastTxErrorDeliverTx { hash: self.hash, height: Some(self.height), code: self.tx_result.code.value(), @@ -53,13 +53,13 @@ impl CheckResponse for crate::nymd::TxResponse { } } -pub(crate) fn compress_wasm_code(code: &[u8]) -> Result, NymdError> { +pub(crate) fn compress_wasm_code(code: &[u8]) -> Result, NyxdError> { // using compression level 9, same as cosmjs, that optimises for size let mut encoder = GzEncoder::new(Vec::new(), Compression::best()); encoder .write_all(code) - .map_err(NymdError::WasmCompressionError)?; - encoder.finish().map_err(NymdError::WasmCompressionError) + .map_err(NyxdError::WasmCompressionError)?; + encoder.finish().map_err(NyxdError::WasmCompressionError) } pub(crate) fn create_pagination(key: Vec) -> PageRequest { @@ -84,11 +84,11 @@ pub(crate) fn next_page_key(pagination_info: Option) -> Option) -> Result, NymdError> { +pub(crate) fn parse_proto_coin_vec(value: Vec) -> Result, NyxdError> { value .into_iter() .map(|proto_coin| { - Coin::try_from(&proto_coin).map_err(|_| NymdError::MalformedCoin { + Coin::try_from(&proto_coin).map_err(|_| NyxdError::MalformedCoin { coin_representation: format!("{:?}", proto_coin), }) }) diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/logs.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs similarity index 93% rename from common/client-libs/validator-client/src/nymd/cosmwasm_client/logs.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs index 6c68c306f6..fd196cf6e7 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/logs.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; +use crate::nyxd::error::NyxdError; use cosmrs::tendermint::abci; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -15,12 +15,12 @@ pub use coconut_dkg_common::event_attributes::*; pub struct Log { #[serde(default)] // weird thing is that the first msg_index seems to always be undefined on the raw logs - msg_index: usize, + pub msg_index: usize, // unless I'm missing something obvious, the "log" type in cosmjs is always an empty string // and launchpad cosmos validator was setting it to what essentially is just the raw version of what // we received (and we don't care about launchpad, we, as the time of writing this, work on the stargate) // log: String, - events: Vec, + pub events: Vec, } /// Searches in logs for the first event of the given event type and in that event @@ -39,17 +39,17 @@ pub fn find_attribute<'a>( } // those two functions were separated so that the internal logic could actually be tested -fn parse_raw_str_logs(raw: &str) -> Result, NymdError> { - let logs: Vec = serde_json::from_str(raw).map_err(|_| NymdError::MalformedLogString)?; +fn parse_raw_str_logs(raw: &str) -> Result, NyxdError> { + let logs: Vec = serde_json::from_str(raw).map_err(|_| NyxdError::MalformedLogString)?; if logs.len() != logs.iter().unique_by(|log| log.msg_index).count() { // this check is only here because I don't yet fully understand raw log string generation and // the fact the first entry does not seem to have `msg_index` defined on it. - return Err(NymdError::MalformedLogString); + return Err(NyxdError::MalformedLogString); } Ok(logs) } -pub fn parse_raw_logs(raw: abci::Log) -> Result, NymdError> { +pub fn parse_raw_logs(raw: abci::Log) -> Result, NyxdError> { parse_raw_str_logs(raw.as_ref()) } diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs similarity index 77% rename from common/client-libs/validator-client/src/nymd/cosmwasm_client/mod.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs index 2af7b52704..ef8bcdc5d7 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs @@ -1,9 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; -use crate::nymd::wallet::DirectSecp256k1HdWallet; -use crate::nymd::GasPrice; +use crate::nyxd::error::NyxdError; +use crate::nyxd::wallet::DirectSecp256k1HdWallet; +use crate::nyxd::GasPrice; use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl}; use std::convert::TryInto; @@ -13,7 +13,7 @@ pub mod logs; pub mod signing_client; pub mod types; -pub fn connect(endpoint: U) -> Result +pub fn connect(endpoint: U) -> Result where U: TryInto, { @@ -25,7 +25,7 @@ pub fn connect_with_signer( endpoint: U, signer: DirectSecp256k1HdWallet, gas_price: GasPrice, -) -> Result +) -> Result where U: TryInto, { diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs similarity index 89% rename from common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs index b1646f663e..6fbe2cec44 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs @@ -1,14 +1,14 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::cosmwasm_client::client::CosmWasmClient; -use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; -use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs}; -use crate::nymd::cosmwasm_client::types::*; -use crate::nymd::error::NymdError; -use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; -use crate::nymd::wallet::DirectSecp256k1HdWallet; -use crate::nymd::{Coin, GasAdjustable, GasPrice, TxResponse}; +use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; +use crate::nyxd::cosmwasm_client::logs::{self, parse_raw_logs}; +use crate::nyxd::cosmwasm_client::types::*; +use crate::nyxd::error::NyxdError; +use crate::nyxd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; +use crate::nyxd::wallet::DirectSecp256k1HdWallet; +use crate::nyxd::{Coin, GasAdjustable, GasPrice, TxResponse}; use async_trait::async_trait; use cosmrs::bank::MsgSend; use cosmrs::distribution::MsgWithdrawDelegatorReward; @@ -74,7 +74,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { signer_address: &AccountId, messages: Vec, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let public_key = self.signer_public_key(signer_address); let sequence_response = self.get_sequence(signer_address).await?; @@ -102,7 +102,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { wasm_code: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let compressed = compress_wasm_code(&wasm_code)?; let compressed_size = compressed.len(); let compressed_checksum = Sha256::digest(&compressed).to_vec(); @@ -115,7 +115,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { instantiate_permission: Default::default(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgStoreCode".to_owned()))?; let tx_res = self .sign_and_broadcast(sender_address, vec![upload_msg], fee, memo) @@ -161,7 +161,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { fee: Fee, memo: impl Into + Send + 'static, mut options: Option, - ) -> Result + ) -> Result where M: ?Sized + Serialize + Sync, { @@ -176,7 +176,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { funds: options.map(|options| options.funds).unwrap_or_default(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgInstantiateContract".to_owned()))?; let tx_res = self .sign_and_broadcast(sender_address, vec![init_msg], fee, memo) @@ -211,14 +211,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { new_admin: &AccountId, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let change_admin_msg = cosmwasm::MsgUpdateAdmin { sender: sender_address.clone(), new_admin: new_admin.clone(), contract: contract_address.clone(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgUpdateAdmin".to_owned()))?; let tx_res = self .sign_and_broadcast(sender_address, vec![change_admin_msg], fee, memo) @@ -240,13 +240,13 @@ pub trait SigningCosmWasmClient: CosmWasmClient { contract_address: &AccountId, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let change_admin_msg = cosmwasm::MsgClearAdmin { sender: sender_address.clone(), contract: contract_address.clone(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgClearAdmin".to_owned()))?; let tx_res = self .sign_and_broadcast(sender_address, vec![change_admin_msg], fee, memo) @@ -270,7 +270,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { fee: Fee, msg: &M, memo: impl Into + Send + 'static, - ) -> Result + ) -> Result where M: ?Sized + Serialize + Sync, { @@ -281,7 +281,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { msg: serde_json::to_vec(msg)?, } .to_any() - .map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgMigrateContract".to_owned()))?; let tx_res = self .sign_and_broadcast(sender_address, vec![migrate_msg], fee, memo) @@ -305,7 +305,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { fee: Fee, memo: impl Into + Send + 'static, funds: Vec, - ) -> Result + ) -> Result where M: ?Sized + Serialize + Sync, { @@ -316,7 +316,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { funds: funds.into_iter().map(Into::into).collect(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgExecuteContract".to_owned()))?; let tx_res = self .sign_and_broadcast(sender_address, vec![execute_msg], fee, memo) @@ -340,7 +340,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { msgs: I, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result + ) -> Result where I: IntoIterator)> + Send, M: Serialize, @@ -355,7 +355,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { funds: funds.into_iter().map(Into::into).collect(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned())) + .map_err(|_| NyxdError::SerializationError("MsgExecuteContract".to_owned())) }) .collect::>()?; @@ -381,14 +381,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { amount: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let send_msg = MsgSend { from_address: sender_address.clone(), to_address: recipient_address.clone(), amount: amount.into_iter().map(Into::into).collect(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgSend".to_owned()))?; self.sign_and_broadcast(sender_address, vec![send_msg], fee, memo) .await? @@ -401,7 +401,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { msgs: I, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result + ) -> Result where I: IntoIterator)> + Send, { @@ -414,7 +414,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { amount: amount.into_iter().map(Into::into).collect(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned())) + .map_err(|_| NyxdError::SerializationError("MsgExecuteContract".to_owned())) }) .collect::>()?; @@ -433,20 +433,20 @@ pub trait SigningCosmWasmClient: CosmWasmClient { allowed_messages: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let basic_allowance = BasicAllowance { spend_limit: spend_limit.into_iter().map(Into::into).collect(), expiration, } .to_any() - .map_err(|_| NymdError::SerializationError("BasicAllowance".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("BasicAllowance".to_owned()))?; let allowed_msg_allowance = AllowedMsgAllowance { allowance: Some(basic_allowance), allowed_messages, } .to_any() - .map_err(|_| NymdError::SerializationError("AllowedMsgAllowance".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("AllowedMsgAllowance".to_owned()))?; let grant_allowance_msg = MsgGrantAllowance { granter: granter.to_owned(), @@ -454,7 +454,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { allowance: Some(allowed_msg_allowance), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgGrantAllowance".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgGrantAllowance".to_owned()))?; self.sign_and_broadcast(granter, vec![grant_allowance_msg], fee, memo) .await? @@ -467,13 +467,13 @@ pub trait SigningCosmWasmClient: CosmWasmClient { grantee: &AccountId, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let revoke_allowance_msg = MsgRevokeAllowance { granter: granter.to_owned(), grantee: grantee.to_owned(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgRevokeAllowance".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgRevokeAllowance".to_owned()))?; self.sign_and_broadcast(granter, vec![revoke_allowance_msg], fee, memo) .await? @@ -487,14 +487,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { amount: Coin, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let delegate_msg = MsgDelegate { delegator_address: delegator_address.to_owned(), validator_address: validator_address.to_owned(), amount: amount.into(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgDelegate".to_owned()))?; self.sign_and_broadcast(delegator_address, vec![delegate_msg], fee, memo) .await? @@ -508,14 +508,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { amount: Coin, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let undelegate_msg = MsgUndelegate { delegator_address: delegator_address.to_owned(), validator_address: validator_address.to_owned(), amount: amount.into(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgUndelegate".to_owned()))?; self.sign_and_broadcast(delegator_address, vec![undelegate_msg], fee, memo) .await? @@ -528,13 +528,13 @@ pub trait SigningCosmWasmClient: CosmWasmClient { validator_address: &AccountId, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let withdraw_msg = MsgWithdrawDelegatorReward { delegator_address: delegator_address.to_owned(), validator_address: validator_address.to_owned(), } .to_any() - .map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?; self.sign_and_broadcast(delegator_address, vec![withdraw_msg], fee, memo) .await? @@ -549,7 +549,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { messages: &[Any], fee: Fee, memo: &String, - ) -> Result { + ) -> Result { let auto_fee = |multiplier: Option| async move { debug!("Trying to simulate gas costs..."); // from what I've seen in manual testing, gas estimation does not exist if transaction @@ -558,7 +558,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .simulate(signer_address, messages.to_vec(), memo.clone()) .await? .gas_info - .ok_or(NymdError::GasEstimationFailure)? + .ok_or(NyxdError::GasEstimationFailure)? .gas_used; let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER); @@ -569,7 +569,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { debug!("Final gas limit used: {}", gas); let fee = self.gas_price() * gas; - Ok::(tx::Fee::from_amount_and_gas(fee, gas)) + Ok::(tx::Fee::from_amount_and_gas(fee, gas)) }; let fee = match fee { Fee::Manual(fee) => fee, @@ -592,7 +592,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { messages: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let memo = memo.into(); let fee = self .determine_transaction_fee(signer_address, &messages, fee, &memo) @@ -600,7 +600,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let tx_raw = self.sign(signer_address, messages, fee, memo).await?; let tx_bytes = tx_raw .to_bytes() - .map_err(|_| NymdError::SerializationError("Tx".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; CosmWasmClient::broadcast_tx_async(self, tx_bytes.into()).await } @@ -612,7 +612,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { messages: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let memo = memo.into(); let fee = self .determine_transaction_fee(signer_address, &messages, fee, &memo) @@ -620,7 +620,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let tx_raw = self.sign(signer_address, messages, fee, memo).await?; let tx_bytes = tx_raw .to_bytes() - .map_err(|_| NymdError::SerializationError("Tx".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; CosmWasmClient::broadcast_tx_sync(self, tx_bytes.into()).await } @@ -632,7 +632,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { messages: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let memo = memo.into(); let fee = self .determine_transaction_fee(signer_address, &messages, fee, &memo) @@ -641,7 +641,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let tx_raw = self.sign(signer_address, messages, fee, memo).await?; let tx_bytes = tx_raw .to_bytes() - .map_err(|_| NymdError::SerializationError("Tx".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await } @@ -653,7 +653,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { messages: Vec, fee: Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { let memo = memo.into(); let fee = self .determine_transaction_fee(signer_address, &messages, fee, &memo) @@ -662,7 +662,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let tx_raw = self.sign(signer_address, messages, fee, memo).await?; let tx_bytes = tx_raw .to_bytes() - .map_err(|_| NymdError::SerializationError("Tx".to_owned()))?; + .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; self.broadcast_tx(tx_bytes.into()).await } @@ -674,12 +674,12 @@ pub trait SigningCosmWasmClient: CosmWasmClient { fee: tx::Fee, memo: impl Into + Send + 'static, signer_data: SignerData, - ) -> Result { + ) -> Result { let signer_accounts = self.signer().try_derive_accounts()?; let account_from_signer = signer_accounts .iter() .find(|account| &account.address == signer_address) - .ok_or_else(|| NymdError::SigningAccountNotFound(signer_address.clone()))?; + .ok_or_else(|| NyxdError::SigningAccountNotFound(signer_address.clone()))?; // TODO: WTF HOW IS TIMEOUT_HEIGHT SUPPOSED TO GET DETERMINED? // IT DOESNT EXIST IN COSMJS!! @@ -691,7 +691,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { SignerInfo::single_direct(Some(account_from_signer.public_key), signer_data.sequence); let auth_info = signer_info.auth_info(fee); - // ideally I'd prefer to have the entire error put into the NymdError::SigningFailure + // ideally I'd prefer to have the entire error put into the NyxdError::SigningFailure // but I'm super hesitant to trying to downcast the eyre::Report to cosmrs::error::Error let sign_doc = SignDoc::new( &tx_body, @@ -699,7 +699,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { &signer_data.chain_id, signer_data.account_number, ) - .map_err(|_| NymdError::SigningFailure)?; + .map_err(|_| NyxdError::SigningFailure)?; self.signer() .sign_direct_with_account(account_from_signer, sign_doc) @@ -711,7 +711,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { messages: Vec, fee: tx::Fee, memo: impl Into + Send + 'static, - ) -> Result { + ) -> Result { // TODO: Future optimisation: rather than grabbing current account_number and sequence // on every sign request -> just keep them cached on the struct and increment as required let sequence_response = self.get_sequence(signer_address).await?; @@ -742,7 +742,7 @@ impl Client { endpoint: U, signer: DirectSecp256k1HdWallet, gas_price: GasPrice, - ) -> Result + ) -> Result where U: TryInto, { diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs similarity index 92% rename from common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs index b680a1a8f4..96260c3bd8 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/types.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs @@ -3,9 +3,9 @@ // TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/ -use crate::nymd::cosmwasm_client::helpers::parse_proto_coin_vec; -use crate::nymd::cosmwasm_client::logs::Log; -use crate::nymd::error::NymdError; +use crate::nyxd::cosmwasm_client::helpers::parse_proto_coin_vec; +use crate::nyxd::cosmwasm_client::logs::Log; +use crate::nyxd::error::NyxdError; use cosmrs::crypto::PublicKey; use cosmrs::proto::cosmos::auth::v1beta1::{ BaseAccount as ProtoBaseAccount, ModuleAccount as ProtoModuleAccount, @@ -57,19 +57,19 @@ pub struct BaseAccount { } impl TryFrom for BaseAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoBaseAccount) -> Result { let address: AccountId = value .address .parse() - .map_err(|_| NymdError::MalformedAccountAddress(value.address.clone()))?; + .map_err(|_| NyxdError::MalformedAccountAddress(value.address.clone()))?; let pubkey = value .pub_key .map(PublicKey::try_from) .transpose() - .map_err(|_| NymdError::InvalidPublicKey(address.clone()))?; + .map_err(|_| NyxdError::InvalidPublicKey(address.clone()))?; Ok(BaseAccount { address, @@ -89,7 +89,7 @@ pub struct ModuleAccount { } impl TryFrom for ModuleAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoModuleAccount) -> Result { let base_account = value.base_account.map(TryFrom::try_from).transpose()?; @@ -114,7 +114,7 @@ pub struct BaseVestingAccount { } impl TryFrom for BaseVestingAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoBaseVestingAccount) -> Result { let base_account = value.base_account.map(TryFrom::try_from).transpose()?; @@ -142,7 +142,7 @@ pub struct ContinuousVestingAccount { } impl TryFrom for ContinuousVestingAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoContinuousVestingAccount) -> Result { let base_vesting_account = value @@ -166,7 +166,7 @@ pub struct DelayedVestingAccount { } impl TryFrom for DelayedVestingAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoDelayedVestingAccount) -> Result { let base_vesting_account = value @@ -188,7 +188,7 @@ pub struct Period { } impl TryFrom for Period { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoPeriod) -> Result { Ok(Period { @@ -208,7 +208,7 @@ pub struct PeriodicVestingAccount { } impl TryFrom for PeriodicVestingAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoPeriodicVestingAccount) -> Result { let base_vesting_account = value @@ -239,7 +239,7 @@ pub struct PermanentLockedAccount { } impl TryFrom for PermanentLockedAccount { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoPermanentLockedAccount) -> Result { let base_vesting_account = value @@ -265,43 +265,43 @@ pub enum Account { } impl Account { - pub fn try_get_base_account(&self) -> Result<&BaseAccount, NymdError> { + pub fn try_get_base_account(&self) -> Result<&BaseAccount, NyxdError> { match self { Account::Base(acc) => Ok(acc), Account::Module(acc) => acc .base_account .as_ref() - .ok_or(NymdError::NoBaseAccountInformationAvailable), + .ok_or(NyxdError::NoBaseAccountInformationAvailable), Account::BaseVesting(acc) => acc .base_account .as_ref() - .ok_or(NymdError::NoBaseAccountInformationAvailable), + .ok_or(NyxdError::NoBaseAccountInformationAvailable), Account::ContinuousVesting(acc) => acc .base_vesting_account .as_ref() .and_then(|vesting_acc| vesting_acc.base_account.as_ref()) - .ok_or(NymdError::NoBaseAccountInformationAvailable), + .ok_or(NyxdError::NoBaseAccountInformationAvailable), Account::DelayedVesting(acc) => acc .base_vesting_account .as_ref() .and_then(|vesting_acc| vesting_acc.base_account.as_ref()) - .ok_or(NymdError::NoBaseAccountInformationAvailable), + .ok_or(NyxdError::NoBaseAccountInformationAvailable), Account::PeriodicVesting(acc) => acc .base_vesting_account .as_ref() .and_then(|vesting_acc| vesting_acc.base_account.as_ref()) - .ok_or(NymdError::NoBaseAccountInformationAvailable), + .ok_or(NyxdError::NoBaseAccountInformationAvailable), Account::PermanentLockedVesting(acc) => acc .base_vesting_account .as_ref() .and_then(|vesting_acc| vesting_acc.base_account.as_ref()) - .ok_or(NymdError::NoBaseAccountInformationAvailable), + .ok_or(NyxdError::NoBaseAccountInformationAvailable), } } } impl TryFrom for Account { - type Error = NymdError; + type Error = NyxdError; fn try_from(raw_account: Any) -> Result { match raw_account.type_url.as_ref() { @@ -328,7 +328,7 @@ impl TryFrom for Account { ProtoPermanentLockedAccount::decode(raw_account.value.as_ref())?.try_into()?, )) } - _ => Err(NymdError::UnsupportedAccountType { + _ => Err(NyxdError::UnsupportedAccountType { type_url: raw_account.type_url, }), } @@ -347,7 +347,7 @@ pub struct Code { } impl TryFrom for Code { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: CodeInfoResponse) -> Result { let CodeInfoResponse { @@ -358,7 +358,7 @@ impl TryFrom for Code { let creator = creator .parse() - .map_err(|_| NymdError::MalformedAccountAddress(creator))?; + .map_err(|_| NyxdError::MalformedAccountAddress(creator))?; Ok(Code { code_id, @@ -391,7 +391,7 @@ pub(crate) struct ContractInfo { } impl TryFrom for ContractInfo { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoContractInfo) -> Result { let ProtoContractInfo { @@ -408,7 +408,7 @@ impl TryFrom for ContractInfo { Some( admin .parse() - .map_err(|_| NymdError::MalformedAccountAddress(admin))?, + .map_err(|_| NyxdError::MalformedAccountAddress(admin))?, ) }; @@ -416,7 +416,7 @@ impl TryFrom for ContractInfo { code_id, creator: creator .parse() - .map_err(|_| NymdError::MalformedAccountAddress(creator))?, + .map_err(|_| NyxdError::MalformedAccountAddress(creator))?, admin, label, }) @@ -466,14 +466,14 @@ pub struct ContractCodeHistoryEntry { } impl TryFrom for ContractCodeHistoryEntry { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoContractCodeHistoryEntry) -> Result { let operation = match ContractCodeHistoryOperationType::from_i32(value.operation) - .ok_or(NymdError::InvalidContractHistoryOperation)? + .ok_or(NyxdError::InvalidContractHistoryOperation)? { ContractCodeHistoryOperationType::Unspecified => { - return Err(NymdError::InvalidContractHistoryOperation) + return Err(NyxdError::InvalidContractHistoryOperation) } ContractCodeHistoryOperationType::Init => ContractCodeHistoryEntryOperation::Init, ContractCodeHistoryOperationType::Genesis => ContractCodeHistoryEntryOperation::Genesis, @@ -484,7 +484,7 @@ impl TryFrom for ContractCodeHistoryEntry { operation, code_id: value.code_id, msg_json: String::from_utf8(value.msg) - .map_err(|_| NymdError::DeserializationError("Contract history msg".to_owned()))?, + .map_err(|_| NyxdError::DeserializationError("Contract history msg".to_owned()))?, }) } } @@ -532,7 +532,7 @@ pub struct AbciResult { } impl TryFrom for AbciResult { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoAbciResult) -> Result { let mut events = Vec::with_capacity(value.events.len()); @@ -543,9 +543,9 @@ impl TryFrom for AbciResult { let mut attributes = Vec::with_capacity(proto_event.attributes.len()); for proto_attribute in proto_event.attributes.into_iter() { let stringified_ked = String::from_utf8(proto_attribute.key) - .map_err(|_| NymdError::DeserializationError("EventAttributeKey".to_owned()))?; + .map_err(|_| NyxdError::DeserializationError("EventAttributeKey".to_owned()))?; let stringified_value = String::from_utf8(proto_attribute.value) - .map_err(|_| NymdError::DeserializationError("EventAttributeKey".to_owned()))?; + .map_err(|_| NyxdError::DeserializationError("EventAttributeKey".to_owned()))?; attributes.push(abci::tag::Tag { key: stringified_ked.parse().unwrap(), @@ -574,7 +574,7 @@ pub struct SimulateResponse { } impl TryFrom for SimulateResponse { - type Error = NymdError; + type Error = NyxdError; fn try_from(value: ProtoSimulateResponse) -> Result { Ok(SimulateResponse { diff --git a/common/client-libs/validator-client/src/nymd/error.rs b/common/client-libs/validator-client/src/nyxd/error.rs similarity index 78% rename from common/client-libs/validator-client/src/nymd/error.rs rename to common/client-libs/validator-client/src/nyxd/error.rs index 0b6dcfe22b..846cfd13c4 100644 --- a/common/client-libs/validator-client/src/nymd/error.rs +++ b/common/client-libs/validator-client/src/nyxd/error.rs @@ -1,20 +1,27 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::cosmwasm_client::types::ContractCodeId; -use cosmrs::tendermint::{abci, block}; -use cosmrs::{bip32, tx, AccountId}; -use std::io; -use std::time::Duration; +use crate::nyxd::cosmwasm_client::types::ContractCodeId; +use cosmrs::{ + bip32, + rpc::endpoint::abci_query::AbciQuery, + tendermint::{ + abci::{self, Code as AbciCode}, + block, + }, + tx, AccountId, +}; use thiserror::Error; -pub use cosmrs::rpc::error::{ - Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail, +use std::{io, time::Duration}; + +pub use cosmrs::rpc::{ + error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail}, + response_error::{Code, ResponseError}, }; -pub use cosmrs::rpc::response_error::{Code, ResponseError}; #[derive(Debug, Error)] -pub enum NymdError { +pub enum NyxdError { #[error("No contract address is available to perform the call")] NoContractAddressAvailable, @@ -110,8 +117,12 @@ pub enum NymdError { #[error("Failed to estimate gas price for the transaction")] GasEstimationFailure, - #[error("Abci query failed with code {0} - {1}")] - AbciError(u32, abci::Log), + #[error("Abci query failed with code {code} - {log}")] + AbciError { + code: u32, + log: abci::Log, + pretty_log: Option, + }, #[error("Unsupported account type: {type_url}")] UnsupportedAccountType { type_url: String }, @@ -135,10 +146,36 @@ pub enum NymdError { UnexpectedBech32Prefix { got: String, expected: String }, } -impl NymdError { +// The purpose of parsing the abci query result is that we want to generate the `pretty_log` if +// possible. +pub fn parse_abci_query_result(query_result: AbciQuery) -> Result { + match query_result.code { + AbciCode::Ok => Ok(query_result), + AbciCode::Err(code) => Err(NyxdError::AbciError { + code, + log: query_result.log.clone(), + pretty_log: try_parse_abci_log(&query_result.log), + }), + } +} + +// Some of the error strings returned by the query are a bit too technical to present to the +// enduser. So we special case some commonly encountered errors. +fn try_parse_abci_log(log: &abci::Log) -> Option { + if log + .value() + .contains("Maximum amount of locked coins has already been pledged") + { + Some("Maximum amount of locked tokens has alredy been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string()) + } else { + None + } +} + +impl NyxdError { pub fn is_tendermint_response_timeout(&self) -> bool { match &self { - NymdError::TendermintError(TendermintRpcError( + NyxdError::TendermintError(TendermintRpcError( TendermintRpcErrorDetail::Response(err), _, )) => { @@ -165,7 +202,7 @@ impl NymdError { pub fn is_tendermint_response_duplicate(&self) -> bool { match &self { - NymdError::TendermintError(TendermintRpcError( + NyxdError::TendermintError(TendermintRpcError( TendermintRpcErrorDetail::Response(err), _, )) => { diff --git a/common/client-libs/validator-client/src/nymd/fee/gas_price.rs b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs similarity index 96% rename from common/client-libs/validator-client/src/nymd/fee/gas_price.rs rename to common/client-libs/validator-client/src/nyxd/fee/gas_price.rs index c79015ff22..13cffc92fc 100644 --- a/common/client-libs/validator-client/src/nymd/fee/gas_price.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; +use crate::nyxd::error::NyxdError; use config::defaults; use cosmrs::tx::Gas; use cosmrs::Coin; @@ -55,7 +55,7 @@ impl<'a> Mul for &'a GasPrice { } impl FromStr for GasPrice { - type Err = NymdError; + type Err = NyxdError; fn from_str(s: &str) -> Result { let possible_amount = s @@ -65,7 +65,7 @@ impl FromStr for GasPrice { let amount_len = possible_amount.len(); let amount = possible_amount .parse() - .map_err(|_| NymdError::MalformedGasPrice)?; + .map_err(|_| NyxdError::MalformedGasPrice)?; let possible_denom = s.chars().skip(amount_len).collect::(); let denom = possible_denom.trim().to_string(); @@ -74,7 +74,7 @@ impl FromStr for GasPrice { } impl GasPrice { - pub fn new_with_default_price(denom: &str) -> Result { + pub fn new_with_default_price(denom: &str) -> Result { format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse() } } diff --git a/common/client-libs/validator-client/src/nymd/fee/mod.rs b/common/client-libs/validator-client/src/nyxd/fee/mod.rs similarity index 99% rename from common/client-libs/validator-client/src/nymd/fee/mod.rs rename to common/client-libs/validator-client/src/nyxd/fee/mod.rs index 411cac2365..b887c08128 100644 --- a/common/client-libs/validator-client/src/nymd/fee/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/mod.rs @@ -1,8 +1,8 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::Coin; -use crate::nymd::Gas; +use crate::nyxd::Coin; +use crate::nyxd::Gas; use cosmrs::{tx, AccountId}; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs similarity index 89% rename from common/client-libs/validator-client/src/nymd/mod.rs rename to common/client-libs/validator-client/src/nyxd/mod.rs index 49d237ddd2..abdbc61a03 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -1,14 +1,14 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::cosmwasm_client::signing_client; -use crate::nymd::cosmwasm_client::types::{ +use crate::nyxd::cosmwasm_client::signing_client; +use crate::nyxd::cosmwasm_client::types::{ Account, ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, MigrateResult, SequenceResponse, SimulateResponse, UploadResult, }; -use crate::nymd::error::NymdError; -use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; -use crate::nymd::wallet::DirectSecp256k1HdWallet; +use crate::nyxd::error::NyxdError; +use crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; +use crate::nyxd::wallet::DirectSecp256k1HdWallet; use cosmrs::cosmwasm; use cosmrs::rpc::endpoint::block::Response as BlockResponse; use cosmrs::rpc::query::Query; @@ -23,14 +23,14 @@ use std::convert::TryInto; use std::time::SystemTime; use vesting_contract_common::ExecuteMsg as VestingExecuteMsg; -pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; -pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; -pub use crate::nymd::fee::Fee; +pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; +pub use crate::nyxd::fee::Fee; pub use coin::Coin; pub use cosmrs::bank::MsgSend; pub use cosmrs::rpc::endpoint::tx::Response as TxResponse; pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse; -pub use cosmrs::rpc::HttpClient as QueryNymdClient; +pub use cosmrs::rpc::HttpClient as QueryNyxdClient; pub use cosmrs::rpc::Paging; pub use cosmrs::tendermint::abci::responses::{DeliverTx, Event}; pub use cosmrs::tendermint::abci::tag::Tag; @@ -44,7 +44,7 @@ pub use cosmrs::{bip32, AccountId, Decimal, Denom}; pub use cosmwasm_std::Coin as CosmWasmCoin; pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment}; use mixnet_contract_common::MixId; -pub use signing_client::Client as SigningNymdClient; +pub use signing_client::Client as SigningNyxdClient; pub use traits::{VestingQueryClient, VestingSigningClient}; use vesting_contract_common::PledgeCap; @@ -75,7 +75,7 @@ impl Config { fn parse_optional_account( raw: Option<&String>, expected_prefix: &str, - ) -> Result, NymdError> { + ) -> Result, NyxdError> { if let Some(address) = raw { debug!("Raw address:{:?}", raw); debug!("Expected prefix:{:?}", expected_prefix); @@ -84,7 +84,7 @@ impl Config { .map_err(|_| NymdError::MalformedAccountAddress(address.clone()))?; debug!("Parsed prefix:{:?}", parsed); if parsed.prefix() != expected_prefix { - Err(NymdError::UnexpectedBech32Prefix { + Err(NyxdError::UnexpectedBech32Prefix { got: parsed.prefix().into(), expected: expected_prefix.into(), }) @@ -96,7 +96,7 @@ impl Config { } } - pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result { + pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result { let prefix = &details.chain_details.bech32_account_prefix; Ok(Config { chain_details: details.chain_details.clone(), @@ -132,20 +132,20 @@ impl Config { } #[derive(Debug)] -pub struct NymdClient { +pub struct NyxdClient { client: C, config: Config, client_address: Option>, simulated_gas_multiplier: f32, } -impl NymdClient { - pub fn connect(config: Config, endpoint: U) -> Result, NymdError> +impl NyxdClient { + pub fn connect(config: Config, endpoint: U) -> Result, NyxdError> where U: TryInto, { - Ok(NymdClient { - client: QueryNymdClient::new(endpoint)?, + Ok(NyxdClient { + client: QueryNyxdClient::new(endpoint)?, config, client_address: None, simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, @@ -153,7 +153,7 @@ impl NymdClient { } } -impl NymdClient { +impl NyxdClient { // maybe the wallet could be made into a generic, but for now, let's just have this one implementation pub fn connect_with_signer( config: Config, @@ -161,7 +161,7 @@ impl NymdClient { endpoint: U, signer: DirectSecp256k1HdWallet, gas_price: Option, - ) -> Result, NymdError> + ) -> Result, NyxdError> where U: TryInto, { @@ -173,8 +173,8 @@ impl NymdClient { .collect(); let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(&denom)?); - Ok(NymdClient { - client: SigningNymdClient::connect_with_signer(endpoint, signer, gas_price)?, + Ok(NyxdClient { + client: SigningNyxdClient::connect_with_signer(endpoint, signer, gas_price)?, config, client_address: Some(client_address), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, @@ -186,7 +186,7 @@ impl NymdClient { endpoint: U, mnemonic: bip39::Mnemonic, gas_price: Option, - ) -> Result, NymdError> + ) -> Result, NyxdError> where U: TryInto, { @@ -200,8 +200,8 @@ impl NymdClient { .collect(); let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?); - Ok(NymdClient { - client: SigningNymdClient::connect_with_signer(endpoint, wallet, gas_price)?, + Ok(NyxdClient { + client: SigningNyxdClient::connect_with_signer(endpoint, wallet, gas_price)?, config, client_address: Some(client_address), simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, @@ -209,7 +209,7 @@ impl NymdClient { } } -impl NymdClient { +impl NyxdClient { pub fn current_config(&self) -> &Config { &self.config } @@ -238,7 +238,7 @@ impl NymdClient { self.config.multisig_contract_address = Some(address); } - // TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, // so it's not introducing new source of failure (just moves it) @@ -246,7 +246,7 @@ impl NymdClient { self.config.mixnet_contract_address.as_ref().unwrap() } - // TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, // so it's not introducing new source of failure (just moves it) @@ -254,7 +254,7 @@ impl NymdClient { self.config.vesting_contract_address.as_ref().unwrap() } - // TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, // so it's not introducing new source of failure (just moves it) @@ -265,7 +265,7 @@ impl NymdClient { .unwrap() } - // TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, // so it's not introducing new source of failure (just moves it) @@ -276,7 +276,7 @@ impl NymdClient { .unwrap() } - // TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, // so it's not introducing new source of failure (just moves it) @@ -284,7 +284,7 @@ impl NymdClient { self.config.multisig_contract_address.as_ref().unwrap() } - // TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits // note: what unwrap is doing here is just moving a failure that would have normally // occurred in `connect` when attempting to parse an empty address, // so it's not introducing new source of failure (just moves it) @@ -300,7 +300,7 @@ impl NymdClient { &self, contract: &AccountId, query_msg: &M, - ) -> Result + ) -> Result where C: CosmWasmClient + Sync, M: ?Sized + Serialize + Sync, @@ -313,7 +313,7 @@ impl NymdClient { &self, contract: &AccountId, query_data: Vec, - ) -> Result, NymdError> + ) -> Result, NyxdError> where C: CosmWasmClient + Sync, { @@ -325,7 +325,7 @@ impl NymdClient { contract_address: &AccountId, msg: &M, funds: Vec, - ) -> Result + ) -> Result where C: SigningCosmWasmClient, M: ?Sized + Serialize, @@ -370,7 +370,7 @@ impl NymdClient { // CHAIN QUERIES - pub async fn account_sequence(&self) -> Result + pub async fn account_sequence(&self) -> Result where C: SigningCosmWasmClient + Sync, { @@ -380,7 +380,7 @@ impl NymdClient { pub async fn get_account_details( &self, address: &AccountId, - ) -> Result, NymdError> + ) -> Result, NyxdError> where C: CosmWasmClient + Sync, { @@ -390,7 +390,7 @@ impl NymdClient { pub async fn get_account_public_key( &self, address: &AccountId, - ) -> Result, NymdError> + ) -> Result, NyxdError> where C: CosmWasmClient + Sync, { @@ -402,7 +402,7 @@ impl NymdClient { Ok(None) } - pub async fn get_current_block_timestamp(&self) -> Result + pub async fn get_current_block_timestamp(&self) -> Result where C: CosmWasmClient + Sync, { @@ -412,21 +412,21 @@ impl NymdClient { pub async fn get_block_timestamp( &self, height: Option, - ) -> Result + ) -> Result where C: CosmWasmClient + Sync, { Ok(self.client.get_block(height).await?.block.header.time) } - pub async fn get_block(&self, height: Option) -> Result + pub async fn get_block(&self, height: Option) -> Result where C: CosmWasmClient + Sync, { self.client.get_block(height).await } - pub async fn get_current_block_height(&self) -> Result + pub async fn get_current_block_height(&self) -> Result where C: CosmWasmClient + Sync, { @@ -438,7 +438,7 @@ impl NymdClient { /// # Arguments /// /// * `height`: height of the block for which we want to obtain the hash. - pub async fn get_block_hash(&self, height: u32) -> Result + pub async fn get_block_hash(&self, height: u32) -> Result where C: CosmWasmClient + Sync, { @@ -452,7 +452,7 @@ impl NymdClient { &self, height: u64, paging: Paging, - ) -> Result + ) -> Result where C: CosmWasmClient + Sync, { @@ -463,42 +463,42 @@ impl NymdClient { &self, address: &AccountId, denom: String, - ) -> Result, NymdError> + ) -> Result, NyxdError> where C: CosmWasmClient + Sync, { self.client.get_balance(address, denom).await } - pub async fn get_all_balances(&self, address: &AccountId) -> Result, NymdError> + pub async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> where C: CosmWasmClient + Sync, { self.client.get_all_balances(address).await } - pub async fn get_tx(&self, id: tx::Hash) -> Result + pub async fn get_tx(&self, id: tx::Hash) -> Result where C: CosmWasmClient + Sync, { self.client.get_tx(id).await } - pub async fn search_tx(&self, query: Query) -> Result, NymdError> + pub async fn search_tx(&self, query: Query) -> Result, NyxdError> where C: CosmWasmClient + Sync, { self.client.search_tx(query).await } - pub async fn get_total_supply(&self) -> Result, NymdError> + pub async fn get_total_supply(&self) -> Result, NyxdError> where C: CosmWasmClient + Sync, { self.client.get_total_supply().await } - pub async fn simulate(&self, messages: I) -> Result + pub async fn simulate(&self, messages: I) -> Result where C: SigningCosmWasmClient + Sync, I: IntoIterator + Send, @@ -512,7 +512,7 @@ impl NymdClient { .map(|msg| msg.into_any()) .collect::, _>>() .map_err(|_| { - NymdError::SerializationError("custom simulate messages".to_owned()) + NyxdError::SerializationError("custom simulate messages".to_owned()) })?, "simulating execution of transactions", ) @@ -526,7 +526,7 @@ impl NymdClient { amount: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -542,7 +542,7 @@ impl NymdClient { msgs: Vec<(AccountId, Vec)>, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -561,7 +561,7 @@ impl NymdClient { allowed_messages: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -585,7 +585,7 @@ impl NymdClient { grantee: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -602,7 +602,7 @@ impl NymdClient { fee: Option, memo: impl Into + Send + 'static, funds: Vec, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, @@ -619,7 +619,7 @@ impl NymdClient { msgs: I, fee: Option, memo: impl Into + Send + 'static, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, I: IntoIterator)> + Send, @@ -636,7 +636,7 @@ impl NymdClient { wasm_code: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -654,7 +654,7 @@ impl NymdClient { memo: impl Into + Send + 'static, options: Option, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, @@ -671,7 +671,7 @@ impl NymdClient { new_admin: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -686,7 +686,7 @@ impl NymdClient { contract_address: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, { @@ -703,7 +703,7 @@ impl NymdClient { msg: &M, memo: impl Into + Send + 'static, fee: Option, - ) -> Result + ) -> Result where C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, diff --git a/common/client-libs/validator-client/src/nymd/traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs similarity index 79% rename from common/client-libs/validator-client/src/nymd/traits/coconut_bandwidth_query_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs index b2e38e12a5..73c13d9d41 100644 --- a/common/client-libs/validator-client/src/nymd/traits/coconut_bandwidth_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs @@ -1,8 +1,8 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; -use crate::nymd::{CosmWasmClient, NymdClient}; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{CosmWasmClient, NyxdClient}; use coconut_bandwidth_contract_common::msg::QueryMsg; use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse; @@ -14,15 +14,15 @@ pub trait CoconutBandwidthQueryClient { async fn get_spent_credential( &self, blinded_serial_number: String, - ) -> Result; + ) -> Result; } #[async_trait] -impl CoconutBandwidthQueryClient for NymdClient { +impl CoconutBandwidthQueryClient for NyxdClient { async fn get_spent_credential( &self, blinded_serial_number: String, - ) -> Result { + ) -> Result { let request = QueryMsg::GetSpentCredential { blinded_serial_number, }; diff --git a/common/client-libs/validator-client/src/nymd/traits/coconut_bandwidth_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs similarity index 84% rename from common/client-libs/validator-client/src/nymd/traits/coconut_bandwidth_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs index 0fc0d0e126..0d0e11e9e8 100644 --- a/common/client-libs/validator-client/src/nymd/traits/coconut_bandwidth_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs @@ -1,10 +1,10 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nymd::cosmwasm_client::types::ExecuteResult; -use crate::nymd::error::NymdError; -use crate::nymd::{Coin, Fee, NymdClient}; +pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, NyxdClient}; use coconut_bandwidth_contract_common::spend_credential::SpendCredentialData; use coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg}; @@ -19,18 +19,18 @@ pub trait CoconutBandwidthSigningClient { verification_key: String, encryption_key: String, fee: Option, - ) -> Result; + ) -> Result; async fn spend_credential( &self, funds: Coin, blinded_serial_number: String, gateway_cosmos_address: String, fee: Option, - ) -> Result; + ) -> Result; } #[async_trait] -impl CoconutBandwidthSigningClient for NymdClient { +impl CoconutBandwidthSigningClient for NyxdClient { async fn deposit( &self, amount: Coin, @@ -38,7 +38,7 @@ impl CoconutBandwidthSigningClient for N verification_key: String, encryption_key: String, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::DepositFunds { data: DepositData::new(info.to_string(), verification_key, encryption_key), @@ -60,7 +60,7 @@ impl CoconutBandwidthSigningClient for N blinded_serial_number: String, gateway_cosmos_address: String, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::SpendCredential { data: SpendCredentialData::new( diff --git a/common/client-libs/validator-client/src/nymd/traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs similarity index 72% rename from common/client-libs/validator-client/src/nymd/traits/dkg_query_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs index 561b9f44ef..c9477fedd6 100644 --- a/common/client-libs/validator-client/src/nymd/traits/dkg_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs @@ -1,63 +1,70 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; -use crate::nymd::{CosmWasmClient, NymdClient}; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{CosmWasmClient, NyxdClient}; use async_trait::async_trait; use coconut_dkg_common::dealer::{ DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse, }; use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; -use coconut_dkg_common::types::EpochState; +use coconut_dkg_common::types::Epoch; use coconut_dkg_common::verification_key::PagedVKSharesResponse; use cosmrs::AccountId; #[async_trait] pub trait DkgQueryClient { - async fn get_current_epoch_state(&self) -> Result; + async fn get_current_epoch(&self) -> Result; + async fn get_current_epoch_threshold(&self) -> Result, NyxdError>; async fn get_dealer_details( &self, address: &AccountId, - ) -> Result; + ) -> Result; async fn get_current_dealers_paged( &self, start_after: Option, page_limit: Option, - ) -> Result; + ) -> Result; async fn get_past_dealers_paged( &self, start_after: Option, page_limit: Option, - ) -> Result; + ) -> Result; async fn get_dealings_paged( &self, idx: usize, start_after: Option, page_limit: Option, - ) -> Result; + ) -> Result; async fn get_vk_shares_paged( &self, start_after: Option, page_limit: Option, - ) -> Result; + ) -> Result; } #[async_trait] -impl DkgQueryClient for NymdClient +impl DkgQueryClient for NyxdClient where C: CosmWasmClient + Send + Sync, { - async fn get_current_epoch_state(&self) -> Result { + async fn get_current_epoch(&self) -> Result { let request = DkgQueryMsg::GetCurrentEpochState {}; self.client .query_contract_smart(self.coconut_dkg_contract_address(), &request) .await } + async fn get_current_epoch_threshold(&self) -> Result, NyxdError> { + let request = DkgQueryMsg::GetCurrentEpochThreshold {}; + self.client + .query_contract_smart(self.coconut_dkg_contract_address(), &request) + .await + } async fn get_dealer_details( &self, address: &AccountId, - ) -> Result { + ) -> Result { let request = DkgQueryMsg::GetDealerDetails { dealer_address: address.to_string(), }; @@ -70,7 +77,7 @@ where &self, start_after: Option, page_limit: Option, - ) -> Result { + ) -> Result { let request = DkgQueryMsg::GetCurrentDealers { start_after, limit: page_limit, @@ -84,7 +91,7 @@ where &self, start_after: Option, page_limit: Option, - ) -> Result { + ) -> Result { let request = DkgQueryMsg::GetPastDealers { start_after, limit: page_limit, @@ -99,7 +106,7 @@ where idx: usize, start_after: Option, page_limit: Option, - ) -> Result { + ) -> Result { let request = DkgQueryMsg::GetDealing { idx: idx as u64, limit: page_limit, @@ -114,7 +121,7 @@ where &self, start_after: Option, page_limit: Option, - ) -> Result { + ) -> Result { let request = DkgQueryMsg::GetVerificationKeys { limit: page_limit, start_after, diff --git a/common/client-libs/validator-client/src/nymd/traits/dkg_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs similarity index 71% rename from common/client-libs/validator-client/src/nymd/traits/dkg_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs index b0d1dfaf08..f04730adb1 100644 --- a/common/client-libs/validator-client/src/nymd/traits/dkg_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs @@ -1,9 +1,9 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::cosmwasm_client::types::ExecuteResult; -use crate::nymd::error::NymdError; -use crate::nymd::{Fee, NymdClient, SigningCosmWasmClient}; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; use async_trait::async_trait; use coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; use coconut_dkg_common::types::EncodedBTEPublicKeyWithProof; @@ -12,37 +12,53 @@ use contracts_common::dealings::ContractSafeBytes; #[async_trait] pub trait DkgSigningClient { + async fn advance_dkg_epoch_state(&self, fee: Option) -> Result; async fn register_dealer( &self, bte_key: EncodedBTEPublicKeyWithProof, announce_address: String, fee: Option, - ) -> Result; + ) -> Result; async fn submit_dealing_bytes( &self, commitment: ContractSafeBytes, fee: Option, - ) -> Result; + ) -> Result; async fn submit_verification_key_share( &self, share: VerificationKeyShare, fee: Option, - ) -> Result; + ) -> Result; } #[async_trait] -impl DkgSigningClient for NymdClient +impl DkgSigningClient for NyxdClient where C: SigningCosmWasmClient + Send + Sync, { + async fn advance_dkg_epoch_state(&self, fee: Option) -> Result { + let req = DkgExecuteMsg::AdvanceEpochState {}; + + self.client + .execute( + self.address(), + self.coconut_dkg_contract_address(), + &req, + fee.unwrap_or_default(), + "advancing DKG state", + vec![], + ) + .await + } + async fn register_dealer( &self, bte_key: EncodedBTEPublicKeyWithProof, announce_address: String, fee: Option, - ) -> Result { + ) -> Result { let req = DkgExecuteMsg::RegisterDealer { bte_key_with_proof: bte_key, announce_address, @@ -64,7 +80,7 @@ where &self, dealing_bytes: ContractSafeBytes, fee: Option, - ) -> Result { + ) -> Result { let req = DkgExecuteMsg::CommitDealing { dealing_bytes }; self.client @@ -83,7 +99,7 @@ where &self, share: VerificationKeyShare, fee: Option, - ) -> Result { + ) -> Result { let req = DkgExecuteMsg::CommitVerificationKeyShare { share }; self.client diff --git a/common/client-libs/validator-client/src/nymd/traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs similarity index 76% rename from common/client-libs/validator-client/src/nymd/traits/mixnet_query_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs index 675c15c5d1..e589719fa3 100644 --- a/common/client-libs/validator-client/src/nymd/traits/mixnet_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs @@ -1,12 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; -use crate::nymd::error::NymdError; -use crate::nymd::NymdClient; +pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::error::NyxdError; +use crate::nyxd::NyxdClient; use async_trait::async_trait; use cosmrs::AccountId; use mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey}; +use mixnet_contract_common::families::Family; use mixnet_contract_common::mixnode::{ MixNodeDetails, MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse, PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse, @@ -20,46 +21,46 @@ use mixnet_contract_common::{ CurrentIntervalResponse, EpochEventId, GatewayBondResponse, GatewayOwnershipResponse, IdentityKey, IntervalEventId, LayerDistribution, MixId, MixOwnershipResponse, MixnodeDetailsResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, - PagedGatewayResponse, PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, - PagedRewardedSetResponse, PendingEpochEventsResponse, PendingIntervalEventsResponse, - QueryMsg as MixnetQueryMsg, + PagedFamiliesResponse, PagedGatewayResponse, PagedMembersResponse, + PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, PagedRewardedSetResponse, + PendingEpochEventsResponse, PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg, }; use serde::Deserialize; #[async_trait] pub trait MixnetQueryClient { - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where for<'a> T: Deserialize<'a>; // state/sys-params-related - async fn get_mixnet_contract_version(&self) -> Result { + async fn get_mixnet_contract_version(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {}) .await } - async fn get_rewarding_validator_address(&self) -> Result { + async fn get_rewarding_validator_address(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {}) .await } - async fn get_mixnet_contract_settings(&self) -> Result { + async fn get_mixnet_contract_settings(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) .await } - async fn get_mixnet_contract_state(&self) -> Result { + async fn get_mixnet_contract_state(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetState {}) .await } - async fn get_rewarding_parameters(&self) -> Result { + async fn get_rewarding_parameters(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {}) .await } - async fn get_current_interval_details(&self) -> Result { + async fn get_current_interval_details(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {}) .await } @@ -68,18 +69,36 @@ pub trait MixnetQueryClient { &self, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after }) .await } + async fn get_all_node_families_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after }) + .await + } + + async fn get_all_family_members_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after }) + .await + } + // mixnode-related: async fn get_mixnode_bonds_paged( &self, limit: Option, start_after: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after }) .await } @@ -88,7 +107,7 @@ pub trait MixnetQueryClient { &self, limit: Option, start_after: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after }) .await } @@ -97,7 +116,7 @@ pub trait MixnetQueryClient { &self, limit: Option, start_after: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after }) .await } @@ -107,7 +126,7 @@ pub trait MixnetQueryClient { owner: &AccountId, limit: Option, start_after: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner { owner: owner.to_string(), limit, @@ -121,7 +140,7 @@ pub trait MixnetQueryClient { identity_key: String, limit: Option, start_after: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { identity_key, limit, @@ -133,7 +152,7 @@ pub trait MixnetQueryClient { async fn get_owned_mixnode( &self, address: &AccountId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode { address: address.to_string(), }) @@ -143,7 +162,7 @@ pub trait MixnetQueryClient { async fn get_mixnode_details( &self, mix_id: MixId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id }) .await } @@ -151,7 +170,7 @@ pub trait MixnetQueryClient { async fn get_mixnode_rewarding_details( &self, mix_id: MixId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id }) .await } @@ -159,7 +178,7 @@ pub trait MixnetQueryClient { async fn get_mixnode_stake_saturation( &self, mix_id: MixId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id }) .await } @@ -167,12 +186,12 @@ pub trait MixnetQueryClient { async fn get_unbonded_mixnode_information( &self, mix_id: MixId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id }) .await } - async fn get_layer_distribution(&self) -> Result { + async fn get_layer_distribution(&self) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {}) .await } @@ -183,7 +202,7 @@ pub trait MixnetQueryClient { &self, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit }) .await } @@ -192,7 +211,7 @@ pub trait MixnetQueryClient { async fn get_gateway_bond( &self, identity: IdentityKey, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity }) .await } @@ -201,7 +220,7 @@ pub trait MixnetQueryClient { async fn get_owned_gateway( &self, address: &AccountId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway { address: address.to_string(), }) @@ -216,7 +235,7 @@ pub trait MixnetQueryClient { mix_id: MixId, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations { mix_id, start_after, @@ -231,7 +250,7 @@ pub trait MixnetQueryClient { delegator: String, start_after: Option<(MixId, OwnerProxySubKey)>, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations { delegator, start_after, @@ -246,7 +265,7 @@ pub trait MixnetQueryClient { mix_id: MixId, delegator: &AccountId, proxy: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails { mix_id, delegator: delegator.to_string(), @@ -260,7 +279,7 @@ pub trait MixnetQueryClient { &self, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit }) .await } @@ -269,7 +288,7 @@ pub trait MixnetQueryClient { async fn get_pending_operator_reward( &self, operator: &AccountId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward { address: operator.to_string(), }) @@ -279,7 +298,7 @@ pub trait MixnetQueryClient { async fn get_pending_mixnode_operator_reward( &self, mix_id: MixId, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id }) .await } @@ -289,7 +308,7 @@ pub trait MixnetQueryClient { delegator: &AccountId, mix_id: MixId, proxy: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward { address: delegator.to_string(), mix_id, @@ -303,7 +322,7 @@ pub trait MixnetQueryClient { &self, mix_id: MixId, estimated_performance: Performance, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { mix_id, estimated_performance, @@ -318,7 +337,7 @@ pub trait MixnetQueryClient { mix_id: MixId, proxy: Option, estimated_performance: Performance, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { address: delegator.to_string(), mix_id, @@ -334,7 +353,7 @@ pub trait MixnetQueryClient { &self, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit }) .await } @@ -343,7 +362,7 @@ pub trait MixnetQueryClient { &self, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit }) .await } @@ -351,20 +370,34 @@ pub trait MixnetQueryClient { async fn get_mixnode_details_by_identity( &self, mix_identity: IdentityKey, - ) -> Result, NymdError> { + ) -> Result, NyxdError> { self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity, }) .await } + + async fn get_node_family_by_label(&self, label: &str) -> Result, NyxdError> { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { + label: label.to_string(), + }) + .await + } + + async fn get_node_family_by_head(&self, head: &str) -> Result, NyxdError> { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { + head: head.to_string(), + }) + .await + } } #[async_trait] -impl MixnetQueryClient for NymdClient +impl MixnetQueryClient for NyxdClient where C: CosmWasmClient + Sync + Send, { - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { @@ -379,10 +412,10 @@ impl MixnetQueryClient for crate::Client where C: CosmWasmClient + Sync + Send, { - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.nymd.query_mixnet_contract(query).await + self.nyxd.query_mixnet_contract(query).await } } diff --git a/common/client-libs/validator-client/src/nymd/traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs similarity index 65% rename from common/client-libs/validator-client/src/nymd/traits/mixnet_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs index 41fa7818a8..f059674683 100644 --- a/common/client-libs/validator-client/src/nymd/traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs @@ -1,17 +1,17 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::coin::Coin; -pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; -use crate::nymd::cosmwasm_client::types::ExecuteResult; -use crate::nymd::error::NymdError; -use crate::nymd::{Fee, NymdClient, SigningCosmWasmClient}; +use crate::nyxd::coin::Coin; +pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; use async_trait::async_trait; use cosmrs::AccountId; use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance}; use mixnet_contract_common::{ - ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, MixId, MixNode, + ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, LayerAssignment, MixId, MixNode, }; #[async_trait] @@ -21,7 +21,7 @@ pub trait MixnetSigningClient { fee: Option, msg: MixnetExecuteMsg, funds: Vec, - ) -> Result; + ) -> Result; // state/sys-params-related @@ -29,7 +29,7 @@ pub trait MixnetSigningClient { &self, address: AccountId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateRewardingValidatorAddress { @@ -44,7 +44,7 @@ pub trait MixnetSigningClient { &self, updated_parameters: ContractStateParams, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateContractStateParams { updated_parameters }, @@ -58,7 +58,7 @@ pub trait MixnetSigningClient { active_set_size: u32, force_immediately: bool, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateActiveSetSize { @@ -75,7 +75,7 @@ pub trait MixnetSigningClient { updated_params: IntervalRewardingParamsUpdate, force_immediately: bool, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateRewardingParams { @@ -93,7 +93,7 @@ pub trait MixnetSigningClient { epoch_duration_secs: u64, force_immediately: bool, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateIntervalConfig { @@ -108,10 +108,10 @@ pub trait MixnetSigningClient { async fn advance_current_epoch( &self, - new_rewarded_set: Vec, + new_rewarded_set: Vec, expected_active_set_size: u32, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::AdvanceCurrentEpoch { @@ -127,7 +127,7 @@ pub trait MixnetSigningClient { &self, limit: Option, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::ReconcileEpochEvents { limit }, @@ -136,6 +136,147 @@ pub trait MixnetSigningClient { .await } + // family related + async fn create_family( + &self, + owner_signature: String, + label: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::CreateFamily { + owner_signature, + label, + }, + vec![], + ) + .await + } + + async fn create_family_on_behalf( + &self, + owner_address: String, + owner_signature: String, + label: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::CreateFamilyOnBehalf { + owner_address, + owner_signature, + label, + }, + vec![], + ) + .await + } + + async fn join_family( + &self, + signature: String, + family_head: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::JoinFamily { + signature, + family_head, + }, + vec![], + ) + .await + } + + async fn join_family_on_behalf( + &self, + member_address: String, + signature: String, + family_head: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::JoinFamilyOnBehalf { + member_address, + signature, + family_head, + }, + vec![], + ) + .await + } + + async fn leave_family( + &self, + signature: String, + family_head: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::LeaveFamily { + signature, + family_head, + }, + vec![], + ) + .await + } + + async fn leave_family_on_behalf( + &self, + member_address: String, + signature: String, + family_head: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::LeaveFamilyOnBehalf { + member_address, + signature, + family_head, + }, + vec![], + ) + .await + } + + async fn kick_family_member( + &self, + signature: String, + member: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::KickFamilyMember { signature, member }, + vec![], + ) + .await + } + + async fn kick_family_member_on_behalf( + &self, + head_address: String, + signature: String, + member: String, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::KickFamilyMemberOnBehalf { + head_address, + signature, + member, + }, + vec![], + ) + .await + } // mixnode-related: async fn bond_mixnode( @@ -145,7 +286,7 @@ pub trait MixnetSigningClient { owner_signature: String, pledge: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::BondMixnode { @@ -166,7 +307,7 @@ pub trait MixnetSigningClient { owner_signature: String, pledge: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::BondMixnodeOnBehalf { @@ -180,7 +321,36 @@ pub trait MixnetSigningClient { .await } - async fn unbond_mixnode(&self, fee: Option) -> Result { + async fn pledge_more( + &self, + additional_pledge: Coin, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::PledgeMore {}, + vec![additional_pledge], + ) + .await + } + + async fn pledge_more_on_behalf( + &self, + owner: AccountId, + additional_pledge: Coin, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::PledgeMoreOnBehalf { + owner: owner.to_string(), + }, + vec![additional_pledge], + ) + .await + } + + async fn unbond_mixnode(&self, fee: Option) -> Result { self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondMixnode {}, vec![]) .await } @@ -189,7 +359,7 @@ pub trait MixnetSigningClient { &self, owner: AccountId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UnbondMixnodeOnBehalf { @@ -204,7 +374,7 @@ pub trait MixnetSigningClient { &self, new_costs: MixNodeCostParams, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs }, @@ -218,7 +388,7 @@ pub trait MixnetSigningClient { owner: AccountId, new_costs: MixNodeCostParams, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf { @@ -234,7 +404,7 @@ pub trait MixnetSigningClient { &self, new_config: MixNodeConfigUpdate, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateMixnodeConfig { new_config }, @@ -248,7 +418,7 @@ pub trait MixnetSigningClient { owner: AccountId, new_config: MixNodeConfigUpdate, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf { @@ -268,7 +438,7 @@ pub trait MixnetSigningClient { owner_signature: String, pledge: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::BondGateway { @@ -287,7 +457,7 @@ pub trait MixnetSigningClient { owner_signature: String, pledge: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::BondGatewayOnBehalf { @@ -300,7 +470,7 @@ pub trait MixnetSigningClient { .await } - async fn unbond_gateway(&self, fee: Option) -> Result { + async fn unbond_gateway(&self, fee: Option) -> Result { self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondGateway {}, vec![]) .await } @@ -309,7 +479,7 @@ pub trait MixnetSigningClient { &self, owner: AccountId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UnbondGatewayOnBehalf { @@ -327,7 +497,7 @@ pub trait MixnetSigningClient { mix_id: MixId, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::DelegateToMixnode { mix_id }, @@ -342,7 +512,7 @@ pub trait MixnetSigningClient { mix_id: MixId, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::DelegateToMixnodeOnBehalf { @@ -358,7 +528,7 @@ pub trait MixnetSigningClient { &self, mix_id: MixId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UndelegateFromMixnode { mix_id }, @@ -372,7 +542,7 @@ pub trait MixnetSigningClient { delegate: AccountId, mix_id: MixId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::UndelegateFromMixnodeOnBehalf { @@ -391,7 +561,7 @@ pub trait MixnetSigningClient { mix_id: MixId, performance: Performance, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::RewardMixnode { @@ -403,7 +573,7 @@ pub trait MixnetSigningClient { .await } - async fn withdraw_operator_reward(&self, fee: Option) -> Result { + async fn withdraw_operator_reward(&self, fee: Option) -> Result { self.execute_mixnet_contract(fee, MixnetExecuteMsg::WithdrawOperatorReward {}, vec![]) .await } @@ -412,7 +582,7 @@ pub trait MixnetSigningClient { &self, owner: AccountId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf { @@ -427,7 +597,7 @@ pub trait MixnetSigningClient { &self, mix_id: MixId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::WithdrawDelegatorReward { mix_id }, @@ -441,7 +611,7 @@ pub trait MixnetSigningClient { owner: AccountId, mix_id: MixId, fee: Option, - ) -> Result { + ) -> Result { self.execute_mixnet_contract( fee, MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { @@ -455,7 +625,7 @@ pub trait MixnetSigningClient { } #[async_trait] -impl MixnetSigningClient for NymdClient +impl MixnetSigningClient for NyxdClient where C: SigningCosmWasmClient + Sync + Send, { @@ -464,7 +634,7 @@ where fee: Option, msg: MixnetExecuteMsg, funds: Vec, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let memo = msg.default_memo(); self.client @@ -490,7 +660,7 @@ where fee: Option, msg: MixnetExecuteMsg, funds: Vec, - ) -> Result { - self.nymd.execute_mixnet_contract(fee, msg, funds).await + ) -> Result { + self.nyxd.execute_mixnet_contract(fee, msg, funds).await } } diff --git a/common/client-libs/validator-client/src/nymd/traits/mod.rs b/common/client-libs/validator-client/src/nyxd/traits/mod.rs similarity index 100% rename from common/client-libs/validator-client/src/nymd/traits/mod.rs rename to common/client-libs/validator-client/src/nyxd/traits/mod.rs diff --git a/common/client-libs/validator-client/src/nymd/traits/multisig_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs similarity index 81% rename from common/client-libs/validator-client/src/nymd/traits/multisig_query_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs index 89643e8136..51eac0532b 100644 --- a/common/client-libs/validator-client/src/nymd/traits/multisig_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs @@ -1,8 +1,8 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; -use crate::nymd::{CosmWasmClient, NymdClient}; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{CosmWasmClient, NyxdClient}; use cw3::{ProposalListResponse, ProposalResponse}; use multisig_contract_common::msg::QueryMsg; @@ -11,17 +11,17 @@ use async_trait::async_trait; #[async_trait] pub trait MultisigQueryClient { - async fn get_proposal(&self, proposal_id: u64) -> Result; + async fn get_proposal(&self, proposal_id: u64) -> Result; async fn list_proposals( &self, start_after: Option, limit: Option, - ) -> Result; + ) -> Result; } #[async_trait] -impl MultisigQueryClient for NymdClient { - async fn get_proposal(&self, proposal_id: u64) -> Result { +impl MultisigQueryClient for NyxdClient { + async fn get_proposal(&self, proposal_id: u64) -> Result { let request = QueryMsg::Proposal { proposal_id }; self.client .query_contract_smart(self.multisig_contract_address(), &request) @@ -32,7 +32,7 @@ impl MultisigQueryClient for NymdClient { &self, start_after: Option, limit: Option, - ) -> Result { + ) -> Result { let request = QueryMsg::ListProposals { start_after, limit }; self.client .query_contract_smart(self.multisig_contract_address(), &request) diff --git a/common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs similarity index 86% rename from common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs index a715c85075..9051cfa79d 100644 --- a/common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs @@ -1,10 +1,10 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nymd::cosmwasm_client::types::ExecuteResult; -use crate::nymd::error::NymdError; -use crate::nymd::{Fee, NymdClient}; +pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Fee, NyxdClient}; use coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; use cw3::Vote; @@ -21,31 +21,31 @@ pub trait MultisigSigningClient { blinded_serial_number: String, voucher_value: u128, fee: Option, - ) -> Result; + ) -> Result; async fn vote_proposal( &self, proposal_id: u64, yes: bool, fee: Option, - ) -> Result; + ) -> Result; async fn execute_proposal( &self, proposal_id: u64, fee: Option, - ) -> Result; + ) -> Result; } #[async_trait] -impl MultisigSigningClient for NymdClient { +impl MultisigSigningClient for NyxdClient { async fn propose_release_funds( &self, title: String, blinded_serial_number: String, voucher_value: u128, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { funds: Coin::new( @@ -81,7 +81,7 @@ impl MultisigSigningClient for NymdClien proposal_id: u64, vote_yes: bool, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let vote = if vote_yes { Vote::Yes } else { Vote::No }; let req = ExecuteMsg::Vote { proposal_id, vote }; @@ -101,7 +101,7 @@ impl MultisigSigningClient for NymdClien &self, proposal_id: u64, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = ExecuteMsg::Execute { proposal_id }; self.client diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs similarity index 85% rename from common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs index 598818197a..5aadd0a608 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs @@ -1,10 +1,10 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::coin::Coin; -pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; -use crate::nymd::error::NymdError; -use crate::nymd::NymdClient; +use crate::nyxd::coin::Coin; +pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::error::NyxdError; +use crate::nyxd::NyxdClient; use async_trait::async_trait; use contracts_common::ContractBuildInformation; use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp}; @@ -18,11 +18,11 @@ use vesting_contract_common::{ #[async_trait] pub trait VestingQueryClient { - async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result + async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where for<'a> T: Deserialize<'a>; - async fn get_vesting_contract_version(&self) -> Result { + async fn get_vesting_contract_version(&self) -> Result { self.query_vesting_contract(VestingQueryMsg::GetContractVersion {}) .await } @@ -31,72 +31,72 @@ pub trait VestingQueryClient { &self, address: &str, block_time: Option, - ) -> Result; + ) -> Result; async fn spendable_coins( &self, vesting_account_address: &str, block_time: Option, - ) -> Result; + ) -> Result; async fn vested_coins( &self, vesting_account_address: &str, block_time: Option, - ) -> Result; + ) -> Result; async fn vesting_coins( &self, vesting_account_address: &str, block_time: Option, - ) -> Result; + ) -> Result; async fn vesting_start_time( &self, vesting_account_address: &str, - ) -> Result; + ) -> Result; async fn vesting_end_time(&self, vesting_account_address: &str) - -> Result; + -> Result; async fn original_vesting( &self, vesting_account_address: &str, - ) -> Result; + ) -> Result; async fn delegated_free( &self, vesting_account_address: &str, block_time: Option, - ) -> Result; + ) -> Result; async fn delegated_vesting( &self, vesting_account_address: &str, block_time: Option, - ) -> Result; + ) -> Result; - async fn get_account(&self, address: &str) -> Result; - async fn get_mixnode_pledge(&self, address: &str) -> Result, NymdError>; - async fn get_gateway_pledge(&self, address: &str) -> Result, NymdError>; + async fn get_account(&self, address: &str) -> Result; + async fn get_mixnode_pledge(&self, address: &str) -> Result, NyxdError>; + async fn get_gateway_pledge(&self, address: &str) -> Result, NyxdError>; async fn get_current_vesting_period( &self, vesting_account_address: &str, - ) -> Result; + ) -> Result; async fn get_delegation_timestamps( &self, address: &str, mix_id: MixId, - ) -> Result; + ) -> Result; async fn get_all_vesting_delegations_paged( &self, start_after: Option<(u32, MixId, u64)>, limit: Option, - ) -> Result; + ) -> Result; - async fn get_all_vesting_delegations(&self) -> Result, NymdError> { + async fn get_all_vesting_delegations(&self) -> Result, NyxdError> { let mut delegations = Vec::new(); let mut start_after = None; loop { @@ -117,8 +117,8 @@ pub trait VestingQueryClient { } #[async_trait] -impl VestingQueryClient for NymdClient { - async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result +impl VestingQueryClient for NyxdClient { + async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { @@ -131,7 +131,7 @@ impl VestingQueryClient for NymdClient { &self, vesting_account_address: &str, block_time: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::LockedCoins { vesting_account_address: vesting_account_address.to_string(), block_time, @@ -146,7 +146,7 @@ impl VestingQueryClient for NymdClient { &self, vesting_account_address: &str, block_time: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::SpendableCoins { vesting_account_address: vesting_account_address.to_string(), block_time, @@ -160,7 +160,7 @@ impl VestingQueryClient for NymdClient { &self, vesting_account_address: &str, block_time: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetVestedCoins { vesting_account_address: vesting_account_address.to_string(), block_time, @@ -174,7 +174,7 @@ impl VestingQueryClient for NymdClient { &self, vesting_account_address: &str, block_time: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetVestingCoins { vesting_account_address: vesting_account_address.to_string(), block_time, @@ -188,7 +188,7 @@ impl VestingQueryClient for NymdClient { async fn vesting_start_time( &self, vesting_account_address: &str, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetStartTime { vesting_account_address: vesting_account_address.to_string(), }; @@ -200,7 +200,7 @@ impl VestingQueryClient for NymdClient { async fn vesting_end_time( &self, vesting_account_address: &str, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetEndTime { vesting_account_address: vesting_account_address.to_string(), }; @@ -212,7 +212,7 @@ impl VestingQueryClient for NymdClient { async fn original_vesting( &self, vesting_account_address: &str, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetOriginalVesting { vesting_account_address: vesting_account_address.to_string(), }; @@ -225,7 +225,7 @@ impl VestingQueryClient for NymdClient { &self, vesting_account_address: &str, block_time: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetDelegatedFree { vesting_account_address: vesting_account_address.to_string(), block_time, @@ -241,7 +241,7 @@ impl VestingQueryClient for NymdClient { &self, vesting_account_address: &str, block_time: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetDelegatedVesting { vesting_account_address: vesting_account_address.to_string(), block_time, @@ -252,7 +252,7 @@ impl VestingQueryClient for NymdClient { .map(Into::into) } - async fn get_account(&self, address: &str) -> Result { + async fn get_account(&self, address: &str) -> Result { let request = VestingQueryMsg::GetAccount { address: address.to_string(), }; @@ -260,7 +260,7 @@ impl VestingQueryClient for NymdClient { .query_contract_smart(self.vesting_contract_address(), &request) .await } - async fn get_mixnode_pledge(&self, address: &str) -> Result, NymdError> { + async fn get_mixnode_pledge(&self, address: &str) -> Result, NyxdError> { let request = VestingQueryMsg::GetMixnode { address: address.to_string(), }; @@ -268,7 +268,7 @@ impl VestingQueryClient for NymdClient { .query_contract_smart(self.vesting_contract_address(), &request) .await } - async fn get_gateway_pledge(&self, address: &str) -> Result, NymdError> { + async fn get_gateway_pledge(&self, address: &str) -> Result, NyxdError> { let request = VestingQueryMsg::GetGateway { address: address.to_string(), }; @@ -277,7 +277,7 @@ impl VestingQueryClient for NymdClient { .await } - async fn get_current_vesting_period(&self, address: &str) -> Result { + async fn get_current_vesting_period(&self, address: &str) -> Result { let request = VestingQueryMsg::GetCurrentVestingPeriod { address: address.to_string(), }; @@ -290,7 +290,7 @@ impl VestingQueryClient for NymdClient { &self, address: &str, mix_id: MixId, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetDelegationTimes { address: address.to_string(), mix_id, @@ -304,7 +304,7 @@ impl VestingQueryClient for NymdClient { &self, start_after: Option<(u32, MixId, u64)>, limit: Option, - ) -> Result { + ) -> Result { let request = VestingQueryMsg::GetAllDelegations { start_after, limit }; self.client .query_contract_smart(self.vesting_contract_address(), &request) diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs similarity index 84% rename from common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs index eebf0adfa3..68b4bcc53a 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs @@ -1,10 +1,10 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nymd::cosmwasm_client::types::ExecuteResult; -use crate::nymd::error::NymdError; -use crate::nymd::{Coin, Fee, NymdClient}; +pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, NyxdClient}; use async_trait::async_trait; use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use mixnet_contract_common::{Gateway, MixId, MixNode}; @@ -18,25 +18,25 @@ pub trait VestingSigningClient { fee: Option, msg: VestingExecuteMsg, funds: Vec, - ) -> Result; + ) -> Result; async fn vesting_update_mixnode_cost_params( &self, new_costs: MixNodeCostParams, fee: Option, - ) -> Result; + ) -> Result; async fn vesting_update_mixnode_config( &self, new_config: MixNodeConfigUpdate, fee: Option, - ) -> Result; + ) -> Result; async fn update_mixnet_address( &self, address: &str, fee: Option, - ) -> Result; + ) -> Result; async fn vesting_bond_gateway( &self, @@ -44,16 +44,16 @@ pub trait VestingSigningClient { owner_signature: &str, pledge: Coin, fee: Option, - ) -> Result; + ) -> Result; - async fn vesting_unbond_gateway(&self, fee: Option) -> Result; + async fn vesting_unbond_gateway(&self, fee: Option) -> Result; async fn vesting_track_unbond_gateway( &self, owner: &str, amount: Coin, fee: Option, - ) -> Result; + ) -> Result; async fn vesting_bond_mixnode( &self, @@ -62,22 +62,37 @@ pub trait VestingSigningClient { owner_signature: &str, pledge: Coin, fee: Option, - ) -> Result; + ) -> Result; - async fn vesting_unbond_mixnode(&self, fee: Option) -> Result; + async fn vesting_pledge_more( + &self, + additional_pledge: Coin, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::PledgeMore { + amount: additional_pledge.into(), + }, + vec![], + ) + .await + } + + async fn vesting_unbond_mixnode(&self, fee: Option) -> Result; async fn vesting_track_unbond_mixnode( &self, owner: &str, amount: Coin, fee: Option, - ) -> Result; + ) -> Result; async fn withdraw_vested_coins( &self, amount: Coin, fee: Option, - ) -> Result; + ) -> Result; async fn vesting_track_undelegation( &self, @@ -85,20 +100,20 @@ pub trait VestingSigningClient { mix_id: MixId, amount: Coin, fee: Option, - ) -> Result; + ) -> Result; async fn vesting_delegate_to_mixnode( &self, mix_id: MixId, amount: Coin, fee: Option, - ) -> Result; + ) -> Result; async fn vesting_undelegate_from_mixnode( &self, mix_id: MixId, fee: Option, - ) -> Result; + ) -> Result; async fn create_periodic_vesting_account( &self, @@ -108,17 +123,17 @@ pub trait VestingSigningClient { amount: Coin, cap: Option, fee: Option, - ) -> Result; + ) -> Result; } #[async_trait] -impl VestingSigningClient for NymdClient { +impl VestingSigningClient for NyxdClient { async fn execute_vesting_contract( &self, fee: Option, msg: VestingExecuteMsg, funds: Vec, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let memo = msg.name().to_string(); self.client @@ -137,7 +152,7 @@ impl VestingSigningClient for NymdClient &self, new_costs: MixNodeCostParams, fee: Option, - ) -> Result { + ) -> Result { self.execute_vesting_contract( fee, VestingExecuteMsg::UpdateMixnodeCostParams { new_costs }, @@ -150,7 +165,7 @@ impl VestingSigningClient for NymdClient &self, new_config: MixNodeConfigUpdate, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config }; self.client @@ -169,7 +184,7 @@ impl VestingSigningClient for NymdClient &self, address: &str, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UpdateMixnetAddress { address: address.to_string(), @@ -192,7 +207,7 @@ impl VestingSigningClient for NymdClient owner_signature: &str, pledge: Coin, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::BondGateway { gateway, @@ -211,7 +226,7 @@ impl VestingSigningClient for NymdClient .await } - async fn vesting_unbond_gateway(&self, fee: Option) -> Result { + async fn vesting_unbond_gateway(&self, fee: Option) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UnbondGateway {}; self.client @@ -231,7 +246,7 @@ impl VestingSigningClient for NymdClient owner: &str, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::TrackUnbondGateway { owner: owner.to_string(), @@ -256,7 +271,7 @@ impl VestingSigningClient for NymdClient owner_signature: &str, pledge: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_vesting_contract( fee, VestingExecuteMsg::BondMixnode { @@ -270,7 +285,7 @@ impl VestingSigningClient for NymdClient .await } - async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { + async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::UnbondMixnode {}; self.client @@ -290,7 +305,7 @@ impl VestingSigningClient for NymdClient owner: &str, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::TrackUnbondMixnode { owner: owner.to_string(), @@ -312,7 +327,7 @@ impl VestingSigningClient for NymdClient &self, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::WithdrawVestedCoins { amount: amount.into(), @@ -335,7 +350,7 @@ impl VestingSigningClient for NymdClient mix_id: MixId, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_vesting_contract( fee, VestingExecuteMsg::TrackUndelegation { @@ -353,7 +368,7 @@ impl VestingSigningClient for NymdClient mix_id: MixId, amount: Coin, fee: Option, - ) -> Result { + ) -> Result { self.execute_vesting_contract( fee, VestingExecuteMsg::DelegateToMixnode { @@ -369,7 +384,7 @@ impl VestingSigningClient for NymdClient &self, mix_id: MixId, fee: Option, - ) -> Result { + ) -> Result { self.execute_vesting_contract( fee, VestingExecuteMsg::UndelegateFromMixnode { mix_id }, @@ -386,7 +401,7 @@ impl VestingSigningClient for NymdClient amount: Coin, cap: Option, fee: Option, - ) -> Result { + ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let req = VestingExecuteMsg::CreateAccount { owner_address: owner_address.to_string(), diff --git a/common/client-libs/validator-client/src/nymd/wallet/mod.rs b/common/client-libs/validator-client/src/nyxd/wallet/mod.rs similarity index 92% rename from common/client-libs/validator-client/src/nymd/wallet/mod.rs rename to common/client-libs/validator-client/src/nyxd/wallet/mod.rs index c78aacde70..8983176692 100644 --- a/common/client-libs/validator-client/src/nymd/wallet/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/wallet/mod.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nymd::error::NymdError; +use crate::nyxd::error::NyxdError; use config::defaults; use cosmrs::bip32::{DerivationPath, XPrv}; use cosmrs::crypto::secp256k1::{Signature, SigningKey}; @@ -62,16 +62,16 @@ impl DirectSecp256k1HdWallet { } /// Restores a wallet from the given BIP39 mnemonic using default options. - pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Result { + pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Result { DirectSecp256k1HdWalletBuilder::new(prefix).build(mnemonic) } - pub fn generate(prefix: &str, word_count: usize) -> Result { + pub fn generate(prefix: &str, word_count: usize) -> Result { let mneomonic = bip39::Mnemonic::generate(word_count)?; Self::from_mnemonic(prefix, mneomonic) } - fn derive_keypair(&self, hd_path: &DerivationPath) -> Result { + fn derive_keypair(&self, hd_path: &DerivationPath) -> Result { let extended_private_key = XPrv::derive_from_path(self.seed, hd_path)?; let private_key: SigningKey = extended_private_key.into(); @@ -80,7 +80,7 @@ impl DirectSecp256k1HdWallet { Ok((private_key, public_key)) } - pub fn try_derive_accounts(&self) -> Result, NymdError> { + pub fn try_derive_accounts(&self) -> Result, NyxdError> { let mut accounts = Vec::with_capacity(self.accounts.len()); for derivation_info in &self.accounts { let keypair = self.derive_keypair(&derivation_info.hd_path)?; @@ -89,7 +89,7 @@ impl DirectSecp256k1HdWallet { let address = keypair .1 .account_id(&derivation_info.prefix) - .map_err(|_| NymdError::AccountDerivationError)?; + .map_err(|_| NyxdError::AccountDerivationError)?; accounts.push(AccountData { address, @@ -109,36 +109,36 @@ impl DirectSecp256k1HdWallet { &self, signer: &AccountData, message: &[u8], - ) -> Result { + ) -> Result { signer .private_key .sign(message) - .map_err(|_| NymdError::SigningFailure) + .map_err(|_| NyxdError::SigningFailure) } pub fn sign_direct_with_account( &self, signer: &AccountData, sign_doc: SignDoc, - ) -> Result { - // ideally I'd prefer to have the entire error put into the NymdError::SigningFailure + ) -> Result { + // ideally I'd prefer to have the entire error put into the NyxdError::SigningFailure // but I'm super hesitant to trying to downcast the eyre::Report to cosmrs::error::Error sign_doc .sign(&signer.private_key) - .map_err(|_| NymdError::SigningFailure) + .map_err(|_| NyxdError::SigningFailure) } pub fn sign_direct( &self, signer_address: &AccountId, sign_doc: SignDoc, - ) -> Result { + ) -> Result { // I hate deriving accounts at every sign here so much : ( let accounts = self.try_derive_accounts()?; let account = accounts .iter() .find(|account| &account.address == signer_address) - .ok_or_else(|| NymdError::SigningAccountNotFound(signer_address.clone()))?; + .ok_or_else(|| NyxdError::SigningAccountNotFound(signer_address.clone()))?; self.sign_direct_with_account(account, sign_doc) } @@ -185,7 +185,7 @@ impl DirectSecp256k1HdWalletBuilder { self } - pub fn build(self, mnemonic: bip39::Mnemonic) -> Result { + pub fn build(self, mnemonic: bip39::Mnemonic) -> Result { let seed = mnemonic.to_seed(&self.bip39_password); let prefix = self.prefix; let accounts = self diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index 21a1534c5d..084f3956cd 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -10,7 +10,7 @@ bip39 = "1.0.1" bs58 = "0.4" comfy-table = "6.0.0" cfg-if = "1.0.0" -clap = { version = "3.2", features = ["derive"] } +clap = { version = "4.0", features = ["derive"] } cw-utils = { version = "0.13.4" } handlebars = "3.0.1" humantime-serde = "1.0" @@ -28,7 +28,7 @@ tap = "1" cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmwasm-std = { version = "1.0.0" } -validator-client = { path = "../client-libs/validator-client", features = ["nymd-client"] } +validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] } network-defaults = { path = "../network-defaults" } mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" } diff --git a/common/commands/src/context/errors.rs b/common/commands/src/context/errors.rs index 44a99f49ed..c385e96626 100644 --- a/common/commands/src/context/errors.rs +++ b/common/commands/src/context/errors.rs @@ -11,14 +11,14 @@ pub enum ContextError { #[error("failed to parse mnemonic - {0}")] Bip39Error(#[from] bip39::Error), - // there are lots of error that can occur in the nymd client, so just pass through their display details + // there are lots of error that can occur in the nyxd client, so just pass through their display details // TODO: improve this to return known errors #[error("failed to create client - {0}")] - NymdError(String), + NyxdError(String), - #[error("{0}")] - NymdErrorPassthrough(#[from] validator_client::nymd::error::NymdError), + #[error(transparent)] + NyxdErrorPassthrough(#[from] validator_client::nyxd::error::NyxdError), - #[error("{0}")] + #[error(transparent)] ValidatorClientError(#[from] validator_client::ValidatorClientError), } diff --git a/common/commands/src/context/mod.rs b/common/commands/src/context/mod.rs index 4f55aa1847..ff0656e2f4 100644 --- a/common/commands/src/context/mod.rs +++ b/common/commands/src/context/mod.rs @@ -3,27 +3,27 @@ use network_defaults::{ setup_env, - var_names::{API_VALIDATOR, MIXNET_CONTRACT_ADDRESS, NYMD_VALIDATOR, VESTING_CONTRACT_ADDRESS}, + var_names::{MIXNET_CONTRACT_ADDRESS, NYM_API, NYXD, VESTING_CONTRACT_ADDRESS}, NymNetworkDetails, }; use tap::prelude::*; -use validator_client::nymd::{self, AccountId, NymdClient, QueryNymdClient, SigningNymdClient}; -pub use validator_client::validator_api::Client as ValidatorApiClient; +pub use validator_client::nym_api::Client as NymApiClient; +use validator_client::nyxd::{self, AccountId, NyxdClient, QueryNyxdClient, SigningNyxdClient}; use crate::context::errors::ContextError; pub mod errors; -pub type SigningClient = validator_client::nymd::NymdClient; -pub type QueryClient = validator_client::nymd::NymdClient; -pub type SigningClientWithValidatorAPI = validator_client::Client; -pub type QueryClientWithValidatorAPI = validator_client::Client; +pub type SigningClient = validator_client::nyxd::NyxdClient; +pub type QueryClient = validator_client::nyxd::NyxdClient; +pub type SigningClientWithNyxd = validator_client::Client; +pub type QueryClientWithNyxd = validator_client::Client; #[derive(Debug)] pub struct ClientArgs { pub config_env_file: Option, - pub nymd_url: Option, - pub validator_api_url: Option, + pub nyxd_url: Option, + pub nym_api_url: Option, pub mnemonic: Option, pub mixnet_contract_address: Option, pub vesting_contract_address: Option, @@ -32,14 +32,14 @@ pub struct ClientArgs { pub fn get_network_details(args: &ClientArgs) -> Result { // let the network defaults crate handle setting up the env vars if the file arg is set, otherwise // it will default to what is already in env vars, falling back to mainnet - setup_env(args.config_env_file.clone()); + setup_env(args.config_env_file.as_ref()); // override the env vars with user supplied arguments, if set - if let Some(nymd_url) = args.nymd_url.as_ref() { - std::env::set_var(NYMD_VALIDATOR, nymd_url); + if let Some(nyxd_url) = args.nyxd_url.as_ref() { + std::env::set_var(NYXD, nyxd_url); } - if let Some(validator_api_url) = args.validator_api_url.as_ref() { - std::env::set_var(API_VALIDATOR, validator_api_url); + if let Some(nym_api_url) = args.nym_api_url.as_ref() { + std::env::set_var(NYM_API, nym_api_url); } if let Some(mixnet_contract_address) = args.mixnet_contract_address.as_ref() { std::env::set_var(MIXNET_CONTRACT_ADDRESS, mixnet_contract_address.to_string()); @@ -58,8 +58,8 @@ pub fn create_signing_client( args: ClientArgs, network_details: &NymNetworkDetails, ) -> Result { - let client_config = nymd::Config::try_from_nym_network_details(network_details) - .tap_err(|e| log::error!("Failed to get client config - {:?}", e))?; + let client_config = nyxd::Config::try_from_nym_network_details(network_details) + .tap_err(|err| log::error!("Failed to get client config - {err}"))?; // get mnemonic let mnemonic = match std::env::var("MNEMONIC") { @@ -71,44 +71,44 @@ pub fn create_signing_client( }, }; - let nymd_url = network_details + let nyxd_url = network_details .endpoints .first() .expect("network details are not defined") - .nymd_url + .nyxd_url .as_str(); - match NymdClient::connect_with_mnemonic(client_config, nymd_url, mnemonic, None) { + match NyxdClient::connect_with_mnemonic(client_config, nyxd_url, mnemonic, None) { Ok(client) => Ok(client), - Err(e) => Err(ContextError::NymdError(format!("{:?}", e))), + Err(e) => Err(ContextError::NyxdError(format!("{:?}", e))), } } pub fn create_query_client( network_details: &NymNetworkDetails, ) -> Result { - let client_config = nymd::Config::try_from_nym_network_details(network_details) - .tap_err(|e| log::error!("Failed to get client config - {:?}", e))?; + let client_config = nyxd::Config::try_from_nym_network_details(network_details) + .tap_err(|err| log::error!("Failed to get client config - {err}"))?; - let nymd_url = network_details + let nyxd_url = network_details .endpoints .first() .expect("network details are not defined") - .nymd_url + .nyxd_url .as_str(); - match NymdClient::connect(client_config, nymd_url) { + match NyxdClient::connect(client_config, nyxd_url) { Ok(client) => Ok(client), - Err(e) => Err(ContextError::NymdError(format!("{:?}", e))), + Err(e) => Err(ContextError::NyxdError(format!("{:?}", e))), } } -pub fn create_signing_client_with_validator_api( +pub fn create_signing_client_with_nym_api( args: ClientArgs, network_details: &NymNetworkDetails, -) -> Result { +) -> Result { let client_config = validator_client::Config::try_from_nym_network_details(network_details) - .tap_err(|e| log::error!("Failed to get client config - {:?}", e))?; + .tap_err(|err| log::error!("Failed to get client config - {err}"))?; // get mnemonic let mnemonic = match std::env::var("MNEMONIC") { @@ -122,18 +122,18 @@ pub fn create_signing_client_with_validator_api( match validator_client::client::Client::new_signing(client_config, mnemonic) { Ok(client) => Ok(client), - Err(e) => Err(ContextError::NymdError(format!("{:?}", e))), + Err(e) => Err(ContextError::NyxdError(format!("{:?}", e))), } } -pub fn create_query_client_with_validator_api( +pub fn create_query_client_with_nym_api( network_details: &NymNetworkDetails, -) -> Result { +) -> Result { let client_config = validator_client::Config::try_from_nym_network_details(network_details) - .tap_err(|e| log::error!("Failed to get client config - {:?}", e))?; + .tap_err(|err| log::error!("Failed to get client config - {err}"))?; match validator_client::client::Client::new_query(client_config) { Ok(client) => Ok(client), - Err(e) => Err(ContextError::NymdError(format!("{:?}", e))), + Err(e) => Err(ContextError::NyxdError(format!("{:?}", e))), } } diff --git a/common/commands/src/utils.rs b/common/commands/src/utils.rs index 2e042da300..59fee99f4e 100644 --- a/common/commands/src/utils.rs +++ b/common/commands/src/utils.rs @@ -6,7 +6,7 @@ use std::fmt::Display; use cosmwasm_std::{Coin as CosmWasmCoin, Decimal}; use log::error; -use validator_client::nymd::Coin; +use validator_client::nyxd::Coin; pub fn pretty_coin(coin: &Coin) -> String { let amount = Decimal::from_ratio(coin.amount, 1_000_000u128); diff --git a/common/commands/src/validator/account/balance.rs b/common/commands/src/validator/account/balance.rs index eb8e2dfa8f..3f926891a5 100644 --- a/common/commands/src/validator/account/balance.rs +++ b/common/commands/src/validator/account/balance.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::{error, info}; -use validator_client::nymd::AccountId; +use validator_client::nyxd::AccountId; use crate::context::QueryClient; use crate::utils::{pretty_coin, show_error}; diff --git a/common/commands/src/validator/account/create.rs b/common/commands/src/validator/account/create.rs index 3003b9a9d5..e321d5b41d 100644 --- a/common/commands/src/validator/account/create.rs +++ b/common/commands/src/validator/account/create.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::Parser; -use validator_client::nymd::wallet::DirectSecp256k1HdWallet; +use validator_client::nyxd::wallet::DirectSecp256k1HdWallet; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/account/pubkey.rs b/common/commands/src/validator/account/pubkey.rs index 83d15f25e3..8ecf98c406 100644 --- a/common/commands/src/validator/account/pubkey.rs +++ b/common/commands/src/validator/account/pubkey.rs @@ -4,8 +4,8 @@ use clap::Parser; use log::{error, info}; -use validator_client::nymd::wallet::DirectSecp256k1HdWallet; -use validator_client::nymd::AccountId; +use validator_client::nyxd::wallet::DirectSecp256k1HdWallet; +use validator_client::nyxd::AccountId; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/account/send.rs b/common/commands/src/validator/account/send.rs index 90816258bd..ac80de8169 100644 --- a/common/commands/src/validator/account/send.rs +++ b/common/commands/src/validator/account/send.rs @@ -5,7 +5,7 @@ use clap::Parser; use log::info; use serde_json::json; -use validator_client::nymd::{AccountId, Coin}; +use validator_client::nyxd::{AccountId, Coin}; use crate::context::SigningClient; diff --git a/common/commands/src/validator/cosmwasm/execute_contract.rs b/common/commands/src/validator/cosmwasm/execute_contract.rs index f387b83903..d271121a49 100644 --- a/common/commands/src/validator/cosmwasm/execute_contract.rs +++ b/common/commands/src/validator/cosmwasm/execute_contract.rs @@ -6,7 +6,7 @@ use clap::Parser; use cosmrs::AccountId; use log::{error, info}; use serde_json::{json, Value}; -use validator_client::nymd::Coin; +use validator_client::nyxd::Coin; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/cosmwasm/init_contract.rs b/common/commands/src/validator/cosmwasm/init_contract.rs index c624a81a94..6ee55b7791 100644 --- a/common/commands/src/validator/cosmwasm/init_contract.rs +++ b/common/commands/src/validator/cosmwasm/init_contract.rs @@ -6,8 +6,8 @@ use clap::Parser; use cosmrs::{AccountId, Coin as CosmosCoin}; use log::info; use network_defaults::NymNetworkDetails; -use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, InstantiateOptions}; -use validator_client::nymd::Coin; +use validator_client::nyxd::cosmwasm_client::types::{ContractCodeId, InstantiateOptions}; +use validator_client::nyxd::Coin; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/cosmwasm/migrate_contract.rs b/common/commands/src/validator/cosmwasm/migrate_contract.rs index fa21062ee7..4abf101ae8 100644 --- a/common/commands/src/validator/cosmwasm/migrate_contract.rs +++ b/common/commands/src/validator/cosmwasm/migrate_contract.rs @@ -6,7 +6,7 @@ use crate::utils::show_error_passthrough; use clap::Parser; use cosmrs::AccountId; use log::info; -use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, EmptyMsg}; +use validator_client::nyxd::cosmwasm_client::types::{ContractCodeId, EmptyMsg}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs index 50b4880c6e..696a2e977d 100644 --- a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use mixnet_contract_common::{Coin, MixId}; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs b/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs index d816e65a0c..68a347a1fc 100644 --- a/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs +++ b/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::info; -use crate::context::SigningClientWithValidatorAPI; +use crate::context::SigningClientWithNyxd; use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough}; use comfy_table::Table; @@ -14,19 +14,19 @@ use mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventKin #[derive(Debug, Parser)] pub struct Args {} -pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) { +pub async fn execute(_args: Args, client: SigningClientWithNyxd) { info!( "Getting delegations for account {}...", - client.nymd.address() + client.nyxd.address() ); let delegations = client - .get_all_delegator_delegations(client.nymd.address()) + .get_all_delegator_delegations(client.nyxd.address()) .await .map_err(show_error_passthrough); let mixnet_contract_events = client - .get_all_nymd_pending_epoch_events() + .get_all_nyxd_pending_epoch_events() .await .map_err(show_error_passthrough); @@ -48,14 +48,14 @@ pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) { } } -async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidatorAPI) -> String { - match client.nymd.get_block_timestamp(Some(block_height)).await { +async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithNyxd) -> String { + match client.nyxd.get_block_timestamp(Some(block_height)).await { Ok(res) => res.to_rfc3339(), Err(_e) => "-".to_string(), } } -async fn print_delegations(delegations: Vec, client: &SigningClientWithValidatorAPI) { +async fn print_delegations(delegations: Vec, client: &SigningClientWithNyxd) { let mut table = Table::new(); table.set_header(vec!["Timestamp", "Mix Id", "Delegation", "Proxy"]); @@ -75,10 +75,7 @@ async fn print_delegations(delegations: Vec, client: &SigningClientW println!("{table}"); } -async fn print_delegation_events( - events: Vec, - client: &SigningClientWithValidatorAPI, -) { +async fn print_delegation_events(events: Vec, client: &SigningClientWithNyxd) { let mut table = Table::new(); table.set_header(vec![ @@ -97,7 +94,7 @@ async fn print_delegation_events( amount, proxy, } => { - if owner.as_str() == client.nymd.address().as_ref() { + if owner.as_str() == client.nyxd.address().as_ref() { table.add_row(vec![ "not-sure-if-applicable".into(), mix_id.to_string(), @@ -112,7 +109,7 @@ async fn print_delegation_events( mix_id, proxy, } => { - if owner.as_str() == client.nymd.address().as_ref() { + if owner.as_str() == client.nyxd.address().as_ref() { table.add_row(vec![ "not-sure-if-applicable".into(), mix_id.to_string(), diff --git a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs index 1858b7cebb..474d6d4233 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use mixnet_contract_common::MixId; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs index 6eca5cbf7a..a3a9fbc385 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use mixnet_contract_common::MixId; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs index 063d4a523f..1bdc5f5305 100644 --- a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use mixnet_contract_common::MixId; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs index ba33a49037..b4cef52bf3 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs @@ -5,8 +5,8 @@ use clap::Parser; use log::info; use mixnet_contract_common::{Coin, MixId}; -use validator_client::nymd::traits::MixnetQueryClient; -use validator_client::nymd::VestingSigningClient; +use validator_client::nyxd::traits::MixnetQueryClient; +use validator_client::nyxd::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs index 59fb6b6c3d..29b77c8a3b 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs @@ -4,8 +4,8 @@ use clap::Parser; use log::info; use mixnet_contract_common::MixId; -use validator_client::nymd::traits::MixnetQueryClient; -use validator_client::nymd::VestingSigningClient; +use validator_client::nyxd::traits::MixnetQueryClient; +use validator_client::nyxd::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs index e9482dde34..34b652a567 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs @@ -6,7 +6,7 @@ use clap::Parser; use log::{info, warn}; use mixnet_contract_common::Coin; use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use validator_client::nymd::traits::MixnetSigningClient; +use validator_client::nyxd::traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs index 90cae1623c..e0e7ba68f1 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use validator_client::nymd::traits::MixnetSigningClient; +use validator_client::nyxd::traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs index 6c26b09eeb..c4e3854fd4 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs @@ -6,7 +6,7 @@ use clap::Parser; use log::{info, warn}; use mixnet_contract_common::{Coin, Gateway}; use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use validator_client::nymd::VestingSigningClient; +use validator_client::nyxd::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs index 0c0eae2fe6..c9b95a3169 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use validator_client::nymd::traits::MixnetSigningClient; +use validator_client::nyxd::traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs index 4d672a5322..0c04845a15 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs @@ -9,8 +9,8 @@ use mixnet_contract_common::{Coin, MixNodeCostParams, Percent}; use network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use validator_client::nymd::traits::MixnetSigningClient; -use validator_client::nymd::CosmWasmCoin; +use validator_client::nyxd::traits::MixnetSigningClient; +use validator_client::nyxd::CosmWasmCoin; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs index 773f4cb3d7..e689f91f73 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use validator_client::nymd::traits::MixnetSigningClient; +use validator_client::nyxd::traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs index d4895e83f2..f91506ad02 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use validator_client::nymd::traits::MixnetSigningClient; +use validator_client::nyxd::traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs index 84cf9f2f4e..cb793c61f5 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use mixnet_contract_common::MixNodeConfigUpdate; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs index bca3642c97..044764c642 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use mixnet_contract_common::MixNodeConfigUpdate; -use validator_client::nymd::traits::MixnetQueryClient; -use validator_client::nymd::VestingSigningClient; +use validator_client::nyxd::traits::MixnetQueryClient; +use validator_client::nyxd::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs index 04fa9a55cd..8f5e881048 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs @@ -3,7 +3,7 @@ use clap::Parser; use log::info; -use validator_client::nymd::traits::MixnetSigningClient; +use validator_client::nyxd::traits::MixnetSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs index 6f20996460..3aa8da5969 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs @@ -10,7 +10,7 @@ use mixnet_contract_common::{MixNode, Percent}; use network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use validator_client::nymd::{CosmWasmCoin, VestingSigningClient}; +use validator_client::nyxd::{CosmWasmCoin, VestingSigningClient}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs index 0824cc9342..e99c4baf83 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use validator_client::nymd::VestingSigningClient; +use validator_client::nyxd::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/query/query_all_gateways.rs b/common/commands/src/validator/mixnet/query/query_all_gateways.rs index 679ac08bcd..17f1d4d48d 100644 --- a/common/commands/src/validator/mixnet/query/query_all_gateways.rs +++ b/common/commands/src/validator/mixnet/query/query_all_gateways.rs @@ -4,7 +4,7 @@ use clap::Parser; use comfy_table::Table; -use crate::context::QueryClientWithValidatorAPI; +use crate::context::QueryClientWithNyxd; use crate::utils::{pretty_cosmwasm_coin, show_error}; #[derive(Debug, Parser)] @@ -14,8 +14,8 @@ pub struct Args { pub identity_key: Option, } -pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) { - match client.validator_api.get_gateways().await { +pub async fn query(args: Args, client: &QueryClientWithNyxd) { + match client.nym_api.get_gateways().await { Ok(res) => match args.identity_key { Some(identity_key) => { let node = res.iter().find(|node| { diff --git a/common/commands/src/validator/mixnet/query/query_all_mixnodes.rs b/common/commands/src/validator/mixnet/query/query_all_mixnodes.rs index 4ec6b25b7b..3b23f2092e 100644 --- a/common/commands/src/validator/mixnet/query/query_all_mixnodes.rs +++ b/common/commands/src/validator/mixnet/query/query_all_mixnodes.rs @@ -4,7 +4,7 @@ use clap::Parser; use comfy_table::Table; -use crate::context::QueryClientWithValidatorAPI; +use crate::context::QueryClientWithNyxd; use crate::utils::{pretty_decimal_with_denom, show_error}; #[derive(Debug, Parser)] @@ -14,8 +14,8 @@ pub struct Args { pub identity_key: Option, } -pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) { - match client.validator_api.get_mixnodes().await { +pub async fn query(args: Args, client: &QueryClientWithNyxd) { + match client.nym_api.get_mixnodes().await { Ok(res) => match args.identity_key { Some(identity_key) => { let node = res.iter().find(|node| { diff --git a/common/commands/src/validator/signature/errors.rs b/common/commands/src/validator/signature/errors.rs index 2369e06d66..d67c3d387b 100644 --- a/common/commands/src/validator/signature/errors.rs +++ b/common/commands/src/validator/signature/errors.rs @@ -11,6 +11,6 @@ pub enum Errors { #[error("signature error - {0}")] SignatureError(#[from] k256::ecdsa::signature::Error), - #[error("{0}")] + #[error(transparent)] CosmrsError(#[from] cosmrs::ErrorReport), } diff --git a/common/commands/src/validator/signature/sign.rs b/common/commands/src/validator/signature/sign.rs index cd4c169ae1..87dfc0303e 100644 --- a/common/commands/src/validator/signature/sign.rs +++ b/common/commands/src/validator/signature/sign.rs @@ -7,7 +7,7 @@ use cosmrs::crypto::PublicKey; use log::error; use serde::Serialize; use serde_json::json; -use validator_client::nymd::wallet::DirectSecp256k1HdWallet; +use validator_client::nyxd::wallet::DirectSecp256k1HdWallet; #[derive(Debug, Serialize)] pub struct SignatureOutputJson { diff --git a/common/commands/src/validator/signature/verify.rs b/common/commands/src/validator/signature/verify.rs index b29f93ca9a..8f84251a1a 100644 --- a/common/commands/src/validator/signature/verify.rs +++ b/common/commands/src/validator/signature/verify.rs @@ -8,7 +8,7 @@ use cosmrs::crypto::PublicKey; use log::{error, info}; use serde_json::json; -use validator_client::nymd::AccountId; +use validator_client::nyxd::AccountId; use crate::context::QueryClient; use crate::validator::signature::helpers::secp256k1_verify_with_public_key; diff --git a/common/commands/src/validator/vesting/balance.rs b/common/commands/src/validator/vesting/balance.rs index 0dff560b9f..5263d106c3 100644 --- a/common/commands/src/validator/vesting/balance.rs +++ b/common/commands/src/validator/vesting/balance.rs @@ -5,7 +5,7 @@ use clap::Parser; use cosmrs::AccountId; use log::{error, info}; -use validator_client::nymd::{Coin, VestingQueryClient}; +use validator_client::nyxd::{Coin, VestingQueryClient}; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/vesting/create_vesting_schedule.rs b/common/commands/src/validator/vesting/create_vesting_schedule.rs index c7c133673d..d022f2ce3a 100644 --- a/common/commands/src/validator/vesting/create_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/create_vesting_schedule.rs @@ -8,9 +8,9 @@ use log::info; use mixnet_contract_common::Coin; use network_defaults::NymNetworkDetails; -use validator_client::nymd::AccountId; -use validator_client::nymd::VestingSigningClient; -use validator_client::nymd::{CosmosCoin, Denom}; +use validator_client::nyxd::AccountId; +use validator_client::nyxd::VestingSigningClient; +use validator_client::nyxd::{CosmosCoin, Denom}; use vesting_contract_common::messages::VestingSpecification; use vesting_contract_common::PledgeCap; diff --git a/common/commands/src/validator/vesting/query_vesting_schedule.rs b/common/commands/src/validator/vesting/query_vesting_schedule.rs index 10a4dc41d3..3d486124ef 100644 --- a/common/commands/src/validator/vesting/query_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/query_vesting_schedule.rs @@ -6,7 +6,7 @@ use cosmrs::AccountId; use cosmwasm_std::Coin as CosmWasmCoin; use log::{error, info}; -use validator_client::nymd::{Coin, VestingQueryClient}; +use validator_client::nyxd::{Coin, VestingQueryClient}; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/vesting/withdraw_vested.rs b/common/commands/src/validator/vesting/withdraw_vested.rs index 972c5290d9..c203bbc63d 100644 --- a/common/commands/src/validator/vesting/withdraw_vested.rs +++ b/common/commands/src/validator/vesting/withdraw_vested.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::info; -use validator_client::nymd::{Coin, VestingQueryClient, VestingSigningClient}; +use validator_client::nyxd::{Coin, VestingQueryClient, VestingSigningClient}; use crate::context::SigningClient; use crate::utils::show_error; diff --git a/common/completions/Cargo.toml b/common/completions/Cargo.toml index ace1f76e7f..53a5fa1eed 100644 --- a/common/completions/Cargo.toml +++ b/common/completions/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "3.2", features = ["derive"] } -clap_complete = "3.2" -clap_complete_fig = "3.2" \ No newline at end of file +clap = { version = "4.0", features = ["derive"] } +clap_complete = "4.0" +clap_complete_fig = "4.0" \ No newline at end of file diff --git a/common/completions/src/lib.rs b/common/completions/src/lib.rs index 8526c18d4f..15344ec834 100644 --- a/common/completions/src/lib.rs +++ b/common/completions/src/lib.rs @@ -1,5 +1,5 @@ use clap::builder::Command; -use clap::clap_derive::ArgEnum; +use clap::clap_derive::ValueEnum; use clap::Args; use clap_complete::generator::generate; use clap_complete::Shell as ClapShell; @@ -14,7 +14,7 @@ pub fn fig_generate(command: &mut Command, name: &str) { ) } -#[derive(ArgEnum, Copy, Clone)] +#[derive(ValueEnum, Copy, Clone)] pub enum Shell { Bash, Elvish, @@ -25,7 +25,7 @@ pub enum Shell { #[derive(Args, Copy, Clone)] pub struct ArgShell { - #[clap(arg_enum, value_name = "SHELL")] + #[clap(value_enum, value_name = "SHELL")] shell: Shell, } diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 1f72896d9c..9e778840cd 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -2,15 +2,24 @@ // SPDX-License-Identifier: Apache-2.0 use handlebars::Handlebars; +use network_defaults::mainnet::read_var_if_not_default; +use network_defaults::var_names::CONFIGURED; use serde::de::DeserializeOwned; use serde::Serialize; +use std::any::type_name; +use std::fmt::Debug; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use std::str::FromStr; use std::{fs, io}; pub mod defaults; +pub const CONFIG_DIR: &str = "config"; +pub const DATA_DIR: &str = "data"; +pub const DB_FILE_NAME: &str = "db.sqlite"; + pub trait NymConfig: Default + Serialize + DeserializeOwned { fn template() -> &'static str; @@ -23,17 +32,17 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { // default, most probable, implementations; can be easily overridden where required fn default_config_directory(id: Option<&str>) -> PathBuf { if let Some(id) = id { - Self::default_root_directory().join(id).join("config") + Self::default_root_directory().join(id).join(CONFIG_DIR) } else { - Self::default_root_directory().join("config") + Self::default_root_directory().join(CONFIG_DIR) } } fn default_data_directory(id: Option<&str>) -> PathBuf { if let Some(id) = id { - Self::default_root_directory().join(id).join("data") + Self::default_root_directory().join(id).join(DATA_DIR) } else { - Self::default_root_directory().join("data") + Self::default_root_directory().join(DATA_DIR) } } @@ -47,17 +56,17 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { fn try_default_config_directory(id: Option<&str>) -> Option { if let Some(id) = id { - Self::try_default_root_directory().map(|d| d.join(id).join("config")) + Self::try_default_root_directory().map(|d| d.join(id).join(CONFIG_DIR)) } else { - Self::try_default_root_directory().map(|d| d.join("config")) + Self::try_default_root_directory().map(|d| d.join(CONFIG_DIR)) } } fn try_default_data_directory(id: Option<&str>) -> Option { if let Some(id) = id { - Self::try_default_root_directory().map(|d| d.join(id).join("data")) + Self::try_default_root_directory().map(|d| d.join(id).join(DATA_DIR)) } else { - Self::try_default_root_directory().map(|d| d.join("data")) + Self::try_default_root_directory().map(|d| d.join(DATA_DIR)) } } @@ -114,13 +123,78 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { } } -pub fn parse_validators(raw: &str) -> Vec { +// this function is only used for parsing values from the network defaults and thus the "expect" there are fine +pub fn parse_urls(raw: &str) -> Vec { raw.split(',') - .map(|raw_validator| { - raw_validator + .map(|raw_url| { + raw_url .trim() .parse() - .expect("one of the provided validator api urls is invalid") + .expect("one of the provided nym api urls is invalid") }) .collect() } + +pub trait OptionalSet { + fn with_optional(self, f: F, val: Option) -> Self + where + F: Fn(Self, T) -> Self, + Self: Sized, + { + if let Some(val) = val { + f(self, val) + } else { + self + } + } + + fn with_optional_env(self, f: F, val: Option, env_var: &str) -> Self + where + F: Fn(Self, T) -> Self, + T: FromStr, + ::Err: Debug, + Self: Sized, + { + if let Some(val) = val { + return f(self, val); + } else if std::env::var(CONFIGURED).is_ok() { + if let Some(raw) = read_var_if_not_default(env_var) { + return f( + self, + raw.parse().unwrap_or_else(|err| { + panic!( + "failed to parse value of {raw} into type {}. the error was {:?}", + type_name::(), + err + ) + }), + ); + } + } + self + } + + fn with_optional_custom_env( + self, + f: F, + val: Option, + env_var: &str, + parser: G, + ) -> Self + where + F: Fn(Self, T) -> Self, + G: Fn(&str) -> T, + Self: Sized, + { + if let Some(val) = val { + return f(self, val); + } else if std::env::var(CONFIGURED).is_ok() { + if let Some(raw) = read_var_if_not_default(env_var) { + return f(self, parser(&raw)); + } + } + self + } +} + +impl OptionalSet for T where T: NymConfig {} diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs index 357e56b968..7a85486eb0 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs @@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize}; pub struct InstantiateMsg { pub group_addr: String, pub multisig_addr: String, - pub admin: String, pub mix_denom: String, } @@ -42,6 +41,7 @@ pub enum ExecuteMsg { #[serde(rename_all = "snake_case")] pub enum QueryMsg { GetCurrentEpochState {}, + GetCurrentEpochThreshold {}, GetDealerDetails { dealer_address: String, }, diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs index ffb1653919..4de725cebf 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs @@ -6,15 +6,48 @@ use std::fmt::{Display, Formatter}; pub use crate::dealer::{DealerDetails, PagedDealerResponse}; pub use contracts_common::dealings::ContractSafeBytes; -pub use cosmwasm_std::{Addr, Coin}; +pub use cosmwasm_std::{Addr, Coin, Timestamp}; pub type EncodedBTEPublicKeyWithProof = String; pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str; pub type NodeIndex = u64; +// The time sign-up is open for dealers to join (2 minutes) +pub const PUBLIC_KEY_SUBMISSION_TIME_SECS: u64 = 60 * 2; +pub const DEALING_EXCHANGE_TIME_SECS: u64 = 60 * 5; +pub const VERIFICATION_KEY_SUBMISSION_TIME_SECS: u64 = 60 * 5; +pub const VERIFICATION_KEY_VALIDATION_TIME_SECS: u64 = 60; +pub const VERIFICATION_KEY_FINALIZATION_TIME_SECS: u64 = 60; +// The time an epoch lasts (2 weeks) +pub const IN_PROGRESS_TIME_SECS: u64 = 60 * 60 * 24 * 14; + // 2 public attributes, 2 private attributes, 1 fixed for coconut credential pub const TOTAL_DEALINGS: usize = 2 + 2 + 1; +#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] +#[serde(rename_all = "snake_case")] +pub struct Epoch { + pub state: EpochState, + pub finish_timestamp: Timestamp, +} + +impl Epoch { + pub fn new(state: EpochState, current_timestamp: Timestamp) -> Self { + let duration = match state { + EpochState::PublicKeySubmission => PUBLIC_KEY_SUBMISSION_TIME_SECS, + EpochState::DealingExchange => DEALING_EXCHANGE_TIME_SECS, + EpochState::VerificationKeySubmission => VERIFICATION_KEY_SUBMISSION_TIME_SECS, + EpochState::VerificationKeyValidation => VERIFICATION_KEY_VALIDATION_TIME_SECS, + EpochState::VerificationKeyFinalization => VERIFICATION_KEY_FINALIZATION_TIME_SECS, + EpochState::InProgress => IN_PROGRESS_TIME_SECS, + }; + Epoch { + state, + finish_timestamp: current_timestamp.plus_seconds(duration), + } + } +} + // currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints), // the epoch can be in the following states (in order): // 1. PublicKeySubmission -> potential dealers are submitting their BTE and ed25519 public keys to participate in dealing exchange diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs b/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs index 8beedd4a42..98e587737e 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs @@ -15,7 +15,7 @@ pub const MAX_DISPLAY_SIZE: usize = 128; // TODO: if we are to use this for different types, it might make sense to introduce something like // CommitmentTypeId field on the below for distinguishing different ones. it would somehow become part of the trait #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, JsonSchema)] -pub struct ContractSafeBytes(Vec); +pub struct ContractSafeBytes(pub Vec); impl Deref for ContractSafeBytes { type Target = Vec; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs index e0d369f933..6eff27c315 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs @@ -1,7 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::MixId; +use crate::{IdentityKey, MixId}; use cosmwasm_std::{Addr, Coin, Decimal}; use thiserror::Error; @@ -133,4 +133,40 @@ pub enum MixnetContractError { #[error("Mixnode {mix_id} appears multiple times in the provided rewarded set update!")] DuplicateRewardedSetNode { mix_id: MixId }, + + #[error("Family with head {head} does not exist!")] + FamilyDoesNotExist { head: String }, + + #[error("Family with label '{0}' already exists")] + FamilyWithLabelExists(String), + + #[error("Invalid layer expected 1, 2 or 3, got {0}")] + InvalidLayer(u8), + + #[error("Head already has a family")] + FamilyCanHaveOnlyOne, + + #[error("Already member of family {0}")] + AlreadyMemberOfFamily(String), + + #[error("Can't join own family, family head {head}, member {member}")] + CantJoinOwnFamily { + head: IdentityKey, + member: IdentityKey, + }, + + #[error("Can't leave own family, family head {head}, member {member}")] + CantLeaveOwnFamily { + head: IdentityKey, + member: IdentityKey, + }, + + #[error("{member} is not a member of family {head}")] + NotAMember { + head: IdentityKey, + member: IdentityKey, + }, + + #[error("Feature is not yet implemented")] + NotImplemented, } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs index bba85e3ccd..f9f5de1802 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs @@ -12,6 +12,8 @@ pub const EVENT_VERSION_PREFIX: &str = "v2_"; pub enum MixnetEventType { MixnodeBonding, + PendingPledgeIncrease, + PledgeIncrease, GatewayBonding, GatewayUnbonding, PendingMixnodeUnbonding, @@ -51,6 +53,8 @@ impl ToString for MixnetEventType { fn to_string(&self) -> String { let event_name = match self { MixnetEventType::MixnodeBonding => "mixnode_bonding", + MixnetEventType::PendingPledgeIncrease => "pending_pledge_increase", + MixnetEventType::PledgeIncrease => "pledge_increase", MixnetEventType::GatewayBonding => "gateway_bonding", MixnetEventType::GatewayUnbonding => "gateway_unbonding", MixnetEventType::PendingMixnodeUnbonding => "pending_mixnode_unbonding", @@ -330,6 +334,19 @@ pub fn new_mixnode_bonding_event( .add_attribute(AMOUNT_KEY, amount.to_string()) } +pub fn new_pending_pledge_increase_event(mix_id: MixId, amount: &Coin) -> Event { + Event::new(MixnetEventType::PendingPledgeIncrease) + .add_attribute(MIX_ID_KEY, mix_id.to_string()) + .add_attribute(AMOUNT_KEY, amount.to_string()) +} + +pub fn new_pledge_increase_event(created_at: BlockHeight, mix_id: MixId, amount: &Coin) -> Event { + Event::new(MixnetEventType::PledgeIncrease) + .add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string()) + .add_attribute(MIX_ID_KEY, mix_id.to_string()) + .add_attribute(AMOUNT_KEY, amount.to_string()) +} + pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Event { Event::new(MixnetEventType::MixnodeUnbonding) .add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string()) diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs new file mode 100644 index 0000000000..0ab73f6085 --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs @@ -0,0 +1,62 @@ +use crate::{IdentityKey, IdentityKeyRef}; +use cosmwasm_std::Addr; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] +#[cfg_attr( + feature = "generate-ts", + ts(export_to = "ts-packages/types/src/types/rust/NodeFamily.ts") +)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, JsonSchema)] +pub struct Family { + head: FamilyHead, + proxy: Option, + label: String, +} + +#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] +#[cfg_attr( + feature = "generate-ts", + ts(export_to = "ts-packages/types/src/types/rust/NodeFamilyHead.ts") +)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, JsonSchema)] +pub struct FamilyHead(IdentityKey); + +impl FamilyHead { + pub fn new(identity: IdentityKeyRef<'_>) -> Self { + FamilyHead(identity.to_string()) + } + + pub fn identity(&self) -> IdentityKeyRef<'_> { + &self.0 + } +} + +impl Family { + pub fn new(head: FamilyHead, proxy: Option, label: &str) -> Self { + Family { + head, + proxy: proxy.map(|p| p.to_string()), + label: label.to_string(), + } + } + + #[allow(dead_code)] + pub fn head(&self) -> &FamilyHead { + &self.head + } + + pub fn head_identity(&self) -> IdentityKeyRef<'_> { + self.head.identity() + } + + #[allow(dead_code)] + pub fn proxy(&self) -> Option<&String> { + self.proxy.as_ref() + } + + pub fn label(&self) -> &str { + &self.label + } +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index 351828585a..adabf7de30 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -8,6 +8,7 @@ mod constants; pub mod delegation; pub mod error; pub mod events; +pub mod families; pub mod gateway; pub mod helpers; mod interval; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index 9bccf2d1ff..90dacbea30 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -36,7 +36,6 @@ impl RewardedSetNodeStatus { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNodeDetails { pub bond_information: MixNodeBond, - pub rewarding_details: MixNodeRewarding, } @@ -325,6 +324,14 @@ impl MixNodeRewarding { Ok(()) } + pub fn increase_operator_uint128( + &mut self, + amount: Uint128, + ) -> Result<(), MixnetContractError> { + self.operator += amount.into_base_decimal()?; + Ok(()) + } + pub fn increase_delegates_uint128( &mut self, amount: Uint128, @@ -571,6 +578,29 @@ impl From for String { } } +impl TryFrom for Layer { + type Error = MixnetContractError; + + fn try_from(i: u8) -> Result { + match i { + 1 => Ok(Layer::One), + 2 => Ok(Layer::Two), + 3 => Ok(Layer::Three), + _ => Err(MixnetContractError::InvalidLayer(i)), + } + } +} + +impl From for u8 { + fn from(layer: Layer) -> u8 { + match layer { + Layer::One => 1, + Layer::Two => 2, + Layer::Three => 3, + } + } +} + #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 1c00c64a9c..cc86757367 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -8,7 +8,7 @@ use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use crate::reward_params::{ IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams, }; -use crate::{delegation, ContractStateParams, MixId, Percent}; +use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent}; use crate::{Gateway, IdentityKey, MixNode}; use cosmwasm_std::Decimal; use schemars::JsonSchema; @@ -73,6 +73,51 @@ impl InitialRewardingParams { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + AssignNodeLayer { + mix_id: MixId, + layer: Layer, + }, + // Families + /// Only owner of the node can crate the family with node as head + CreateFamily { + owner_signature: String, + label: String, + }, + /// Family head needs to sign the joining node IdentityKey + JoinFamily { + signature: String, + family_head: IdentityKey, + }, + LeaveFamily { + signature: String, + family_head: IdentityKey, + }, + KickFamilyMember { + signature: String, + member: IdentityKey, + }, + CreateFamilyOnBehalf { + owner_address: String, + owner_signature: String, + label: String, + }, + /// Family head needs to sign the joining node IdentityKey + JoinFamilyOnBehalf { + member_address: String, + signature: String, + family_head: IdentityKey, + }, + LeaveFamilyOnBehalf { + member_address: String, + signature: String, + family_head: IdentityKey, + }, + KickFamilyMemberOnBehalf { + head_address: String, + signature: String, + member: IdentityKey, + }, + // state/sys-params-related UpdateRewardingValidatorAddress { address: String, @@ -94,7 +139,8 @@ pub enum ExecuteMsg { force_immediately: bool, }, AdvanceCurrentEpoch { - new_rewarded_set: Vec, + new_rewarded_set: Vec, + // families_in_layer: HashMap, expected_active_set_size: u32, }, ReconcileEpochEvents { @@ -113,6 +159,10 @@ pub enum ExecuteMsg { owner_signature: String, owner: String, }, + PledgeMore {}, + PledgeMoreOnBehalf { + owner: String, + }, UnbondMixnode {}, UnbondMixnodeOnBehalf { owner: String, @@ -190,6 +240,29 @@ pub enum ExecuteMsg { impl ExecuteMsg { pub fn default_memo(&self) -> String { match self { + ExecuteMsg::AssignNodeLayer { mix_id, layer } => { + format!("assigning mix {} for layer {:?}", mix_id, layer) + } + ExecuteMsg::CreateFamily { .. } => "crating node family with".to_string(), + ExecuteMsg::JoinFamily { family_head, .. } => { + format!("joining family {}", family_head) + } + ExecuteMsg::LeaveFamily { family_head, .. } => { + format!("leaving family {}", family_head) + } + ExecuteMsg::KickFamilyMember { member, .. } => { + format!("kicking {} from family", member) + } + ExecuteMsg::CreateFamilyOnBehalf { .. } => "crating node family with".to_string(), + ExecuteMsg::JoinFamilyOnBehalf { family_head, .. } => { + format!("joining family {}", family_head) + } + ExecuteMsg::LeaveFamilyOnBehalf { family_head, .. } => { + format!("leaving family {}", family_head) + } + ExecuteMsg::KickFamilyMemberOnBehalf { member, .. } => { + format!("kicking {} from family", member) + } ExecuteMsg::UpdateRewardingValidatorAddress { address } => { format!("updating rewarding validator to {}", address) } @@ -223,6 +296,8 @@ impl ExecuteMsg { ExecuteMsg::BondMixnodeOnBehalf { mix_node, .. } => { format!("bonding mixnode {} on behalf", mix_node.identity_key) } + ExecuteMsg::PledgeMore {} => "pledging additional tokens".into(), + ExecuteMsg::PledgeMoreOnBehalf { .. } => "pledging additional tokens on behalf".into(), ExecuteMsg::UnbondMixnode { .. } => "unbonding mixnode".into(), ExecuteMsg::UnbondMixnodeOnBehalf { .. } => "unbonding mixnode on behalf".into(), ExecuteMsg::UpdateMixnodeCostParams { .. } => "updating mixnode cost parameters".into(), @@ -280,6 +355,27 @@ impl ExecuteMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { + // families + GetAllFamiliesPaged { + limit: Option, + start_after: Option, + }, + GetAllMembersPaged { + limit: Option, + start_after: Option, + }, + GetFamilyByHead { + head: String, + }, + GetFamilyByLabel { + label: String, + }, + GetFamilyMembersByHead { + head: String, + }, + GetFamilyMembersByLabel { + label: String, + }, // state/sys-params-related GetContractVersion {}, GetRewardingValidatorAddress {}, @@ -334,7 +430,6 @@ pub enum QueryMsg { mix_identity: IdentityKey, }, GetLayerDistribution {}, - // gateway-related: GetGateways { start_after: Option, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs index fce8bd8cf0..3fe817fcb6 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs @@ -34,6 +34,10 @@ pub enum PendingEpochEventKind { mix_id: MixId, proxy: Option, }, + PledgeMore { + mix_id: MixId, + amount: Coin, + }, UnbondMixnode { mix_id: MixId, }, @@ -78,7 +82,6 @@ pub enum PendingIntervalEventKind { mix_id: MixId, new_costs: MixNodeCostParams, }, - UpdateRewardingParams { update: IntervalRewardingParamsUpdate, }, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs index 09223a8e8a..c17520a2b6 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::MixnetContractError; +use crate::families::{Family, FamilyHead}; use crate::{Layer, RewardedSetNodeStatus}; use cosmwasm_std::Addr; use cosmwasm_std::Coin; @@ -21,6 +22,27 @@ pub type BlockHeight = u64; pub type EpochEventId = u32; pub type IntervalEventId = u32; +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq)] +pub struct LayerAssignment { + mix_id: MixId, + layer: Layer, +} + +impl LayerAssignment { + pub fn new(mix_id: MixId, layer: Layer) -> Self { + LayerAssignment { mix_id, layer } + } + + pub fn mix_id(&self) -> MixId { + self.mix_id + } + + pub fn layer(&self) -> Layer { + self.layer + } +} + #[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)] pub struct LayerDistribution { pub layer1: u64, @@ -126,3 +148,15 @@ pub struct PagedRewardedSetResponse { pub nodes: Vec<(MixId, RewardedSetNodeStatus)>, pub start_next_after: Option, } + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +pub struct PagedFamiliesResponse { + pub families: Vec, + pub start_next_after: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +pub struct PagedMembersResponse { + pub members: Vec<(IdentityKey, FamilyHead)>, + pub start_next_after: Option, +} diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs index 896ead2c9e..4e5809beee 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs @@ -14,6 +14,7 @@ pub const VESTING_UNDELEGATION_EVENT_TYPE: &str = "vesting_undelegation"; pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding"; pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding"; pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding"; +pub const VESTING_PLEDGE_MORE_EVENT_TYPE: &str = "vesting_pledge_more"; pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding"; pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config"; pub const VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE: &str = @@ -112,6 +113,10 @@ pub fn new_vesting_mixnode_bonding_event() -> Event { Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE) } +pub fn new_vesting_pledge_more_event() -> Event { + Event::new(VESTING_PLEDGE_MORE_EVENT_TYPE) +} + pub fn new_vesting_update_mixnode_config_event() -> Event { Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE) } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 696df628aa..3edc6c2303 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{Coin, Timestamp}; use mixnet_contract_common::{ mixnode::{MixNodeConfigUpdate, MixNodeCostParams}, - Gateway, MixId, MixNode, + Gateway, IdentityKey, MixId, MixNode, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -57,6 +57,25 @@ impl VestingSpecification { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + // Families + /// Only owner of the node can crate the family with node as head + CreateFamily { + owner_signature: String, + label: String, + }, + /// Family head needs to sign the joining node IdentityKey + JoinFamily { + signature: String, + family_head: IdentityKey, + }, + LeaveFamily { + signature: String, + family_head: IdentityKey, + }, + KickFamilyMember { + signature: String, + member: IdentityKey, + }, TrackReward { amount: Coin, address: String, @@ -101,6 +120,9 @@ pub enum ExecuteMsg { owner_signature: String, amount: Coin, }, + PledgeMore { + amount: Coin, + }, UnbondMixnode {}, TrackUnbondMixnode { owner: String, @@ -131,6 +153,10 @@ pub enum ExecuteMsg { impl ExecuteMsg { pub fn name(&self) -> &str { match self { + ExecuteMsg::CreateFamily { .. } => "VestingExecuteMsg::CreateFamily", + ExecuteMsg::JoinFamily { .. } => "VestingExecuteMsg::JoinFamily", + ExecuteMsg::LeaveFamily { .. } => "VestingExecuteMsg::LeaveFamily", + ExecuteMsg::KickFamilyMember { .. } => "VestingExecuteMsg::KickFamilyMember", ExecuteMsg::TrackReward { .. } => "VestingExecuteMsg::TrackReward", ExecuteMsg::ClaimOperatorReward { .. } => "VestingExecuteMsg::ClaimOperatorReward", ExecuteMsg::ClaimDelegatorReward { .. } => "VestingExecuteMsg::ClaimDelegatorReward", @@ -145,6 +171,7 @@ impl ExecuteMsg { ExecuteMsg::WithdrawVestedCoins { .. } => "VestingExecuteMsg::WithdrawVestedCoins", ExecuteMsg::TrackUndelegation { .. } => "VestingExecuteMsg::TrackUndelegation", ExecuteMsg::BondMixnode { .. } => "VestingExecuteMsg::BondMixnode", + ExecuteMsg::PledgeMore { .. } => "VestingExecuteMsg::PledgeMore", ExecuteMsg::UnbondMixnode { .. } => "VestingExecuteMsg::UnbondMixnode", ExecuteMsg::TrackUnbondMixnode { .. } => "VestingExecuteMsg::TrackUnbondMixnode", ExecuteMsg::BondGateway { .. } => "VestingExecuteMsg::BondGateway", diff --git a/common/credential-storage/migrations/20221208120000_add_consumed_to_coconut.sql b/common/credential-storage/migrations/20221208120000_add_consumed_to_coconut.sql new file mode 100644 index 0000000000..f3994cf2b6 --- /dev/null +++ b/common/credential-storage/migrations/20221208120000_add_consumed_to_coconut.sql @@ -0,0 +1,16 @@ +/* + * Copyright 2022 - Nym Technologies SA + * SPDX-License-Identifier: Apache-2.0 + */ + +DROP TABLE coconut_credentials; +CREATE TABLE coconut_credentials +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + voucher_value TEXT NOT NULL, + voucher_info TEXT NOT NULL, + serial_number TEXT NOT NULL, + binding_number TEXT NOT NULL, + signature TEXT NOT NULL UNIQUE, + consumed BOOLEAN NOT NULL +); \ No newline at end of file diff --git a/common/credential-storage/src/coconut.rs b/common/credential-storage/src/coconut.rs index e6512dd7fc..103f8760d2 100644 --- a/common/credential-storage/src/coconut.rs +++ b/common/credential-storage/src/coconut.rs @@ -36,8 +36,8 @@ impl CoconutCredentialManager { signature: String, ) -> Result<(), sqlx::Error> { sqlx::query!( - "INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature) VALUES (?, ?, ?, ?, ?)", - voucher_value, voucher_info, serial_number, binding_number, signature + "INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature, consumed) VALUES (?, ?, ?, ?, ?, ?)", + voucher_value, voucher_info, serial_number, binding_number, signature, false ) .execute(&self.connection_pool) .await?; @@ -48,20 +48,26 @@ impl CoconutCredentialManager { pub(crate) async fn get_next_coconut_credential( &self, ) -> Result { - sqlx::query_as!(CoconutCredential, "SELECT * FROM coconut_credentials") - .fetch_one(&self.connection_pool) - .await + sqlx::query_as!( + CoconutCredential, + "SELECT * FROM coconut_credentials WHERE NOT consumed" + ) + .fetch_one(&self.connection_pool) + .await } - /// Removes from the database the specified credential. + /// Consumes in the database the specified credential. /// /// # Arguments /// /// * `id`: Database id. - pub(crate) async fn remove_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> { - sqlx::query!("DELETE FROM coconut_credentials WHERE id = ?", id) - .execute(&self.connection_pool) - .await?; + pub(crate) async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> { + sqlx::query!( + "UPDATE coconut_credentials SET consumed = TRUE WHERE id = ?", + id + ) + .execute(&self.connection_pool) + .await?; Ok(()) } } diff --git a/common/credential-storage/src/lib.rs b/common/credential-storage/src/lib.rs index 7a4ecb5358..d0a0992894 100644 --- a/common/credential-storage/src/lib.rs +++ b/common/credential-storage/src/lib.rs @@ -45,13 +45,13 @@ impl PersistentStorage { let connection_pool = match sqlx::SqlitePool::connect_with(opts).await { Ok(db) => db, Err(err) => { - error!("Failed to connect to SQLx database: {}", err); + error!("Failed to connect to SQLx database: {err}"); return Err(err.into()); } }; if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await { - error!("Failed to perform migration on the SQLx database: {}", err); + error!("Failed to perform migration on the SQLx database: {err}"); return Err(err.into()); } @@ -93,9 +93,9 @@ impl Storage for PersistentStorage { Ok(credential) } - async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError> { + async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> { self.coconut_credential_manager - .remove_coconut_credential(id) + .consume_coconut_credential(id) .await?; Ok(()) @@ -104,7 +104,7 @@ impl Storage for PersistentStorage { pub async fn initialise_storage(path: PathBuf) -> PersistentStorage { match PersistentStorage::init(path).await { - Err(err) => panic!("failed to initialise credential storage - {}", err), + Err(err) => panic!("failed to initialise credential storage - {err}"), Ok(storage) => storage, } } diff --git a/common/credential-storage/src/models.rs b/common/credential-storage/src/models.rs index 9684df056f..6ce7a9dd4b 100644 --- a/common/credential-storage/src/models.rs +++ b/common/credential-storage/src/models.rs @@ -9,4 +9,5 @@ pub struct CoconutCredential { pub serial_number: String, pub binding_number: String, pub signature: String, + pub consumed: bool, } diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index 892e561442..77df96f4c8 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -25,10 +25,10 @@ pub trait Storage: Send + Sync { /// Tries to retrieve one of the stored, unused credentials. async fn get_next_coconut_credential(&self) -> Result; - /// Removes from the database the specified credential. + /// Marks as consumed in the database the specified credential. /// /// # Arguments /// - /// * `signature`: Coconut credential in the form of a signature. - async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>; + /// * `id`: Id of the credential to be consumed. + async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>; } diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 58d43af03f..2f754253cc 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -13,7 +13,7 @@ thiserror = "1.0" # I guess temporarily until we get serde support in coconut up and running coconut-interface = { path = "../coconut-interface" } crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] } -validator-api-requests = { path = "../../validator-api/validator-api-requests" } +nym-api-requests = { path = "../../nym-api/nym-api-requests" } validator-client = { path = "../client-libs/validator-client" } [dev-dependencies] diff --git a/common/credentials/src/coconut/params.rs b/common/credentials/src/coconut/params.rs index 42b9205ebb..7e2260bc10 100644 --- a/common/credentials/src/coconut/params.rs +++ b/common/credentials/src/coconut/params.rs @@ -9,7 +9,7 @@ type Aes128Ctr = ctr::Ctr64LE; /// Hashing algorithm used during hkdf for ephemeral shared key generation per blinded signature /// response encryption. -pub type ValidatorApiCredentialHkdfAlgorithm = blake3::Hasher; +pub type NymApiCredentialHkdfAlgorithm = blake3::Hasher; /// Encryption algorithm used for end-to-end encryption of blinded signature response -pub type ValidatorApiCredentialEncryptionAlgorithm = Aes128Ctr; +pub type NymApiCredentialEncryptionAlgorithm = Aes128Ctr; diff --git a/common/credentials/src/coconut/utils.rs b/common/credentials/src/coconut/utils.rs index f701938d59..53e01437af 100644 --- a/common/credentials/src/coconut/utils.rs +++ b/common/credentials/src/coconut/utils.rs @@ -8,13 +8,11 @@ use coconut_interface::{ use crypto::asymmetric::encryption::PublicKey; use crypto::shared_key::recompute_shared_key; use crypto::symmetric::stream_cipher; -use validator_api_requests::coconut::BlindSignRequestBody; +use nym_api_requests::coconut::BlindSignRequestBody; use validator_client::client::CoconutApiClient; use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES}; -use crate::coconut::params::{ - ValidatorApiCredentialEncryptionAlgorithm, ValidatorApiCredentialHkdfAlgorithm, -}; +use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm}; use crate::error::Error; pub async fn obtain_aggregate_verification_key( @@ -39,7 +37,7 @@ pub async fn obtain_aggregate_verification_key( async fn obtain_partial_credential( params: &Parameters, attributes: &BandwidthVoucher, - client: &validator_client::client::ApiClient, + client: &validator_client::client::NymApiClient, validator_vk: &VerificationKey, ) -> Result { let public_attributes = attributes.get_public_attributes(); @@ -66,11 +64,11 @@ async fn obtain_partial_credential( let remote_key = PublicKey::from_bytes(&response.remote_key)?; let encryption_key = recompute_shared_key::< - ValidatorApiCredentialEncryptionAlgorithm, - ValidatorApiCredentialHkdfAlgorithm, + NymApiCredentialEncryptionAlgorithm, + NymApiCredentialHkdfAlgorithm, >(&remote_key, attributes.encryption_key()); - let zero_iv = stream_cipher::zero_iv::(); - let blinded_signature_bytes = stream_cipher::decrypt::( + let zero_iv = stream_cipher::zero_iv::(); + let blinded_signature_bytes = stream_cipher::decrypt::( &encryption_key, &zero_iv, &encrypted_signature, diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index a8e749f2e0..94d3d2820b 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -22,6 +22,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true } serde_bytes = { version = "0.11.6", optional = true } serde_crate = { version = "1.0", optional = true, default_features = false, package = "serde" } subtle-encoding = { version = "0.5", features = ["bech32-preview"]} +thiserror = "1.0.37" # internal nymsphinx-types = { path = "../nymsphinx/types" } diff --git a/common/crypto/dkg/src/bte/keys.rs b/common/crypto/dkg/src/bte/keys.rs index 9412790db8..098e9638c7 100644 --- a/common/crypto/dkg/src/bte/keys.rs +++ b/common/crypto/dkg/src/bte/keys.rs @@ -53,7 +53,7 @@ impl PublicKey { } } -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub struct PublicKeyWithProof { pub(crate) key: PublicKey, diff --git a/common/crypto/dkg/src/bte/proof_discrete_log.rs b/common/crypto/dkg/src/bte/proof_discrete_log.rs index 5dbdbba702..489eb13244 100644 --- a/common/crypto/dkg/src/bte/proof_discrete_log.rs +++ b/common/crypto/dkg/src/bte/proof_discrete_log.rs @@ -13,7 +13,7 @@ use zeroize::Zeroize; const DISCRETE_LOG_DOMAIN: &[u8] = b"NYM_COCONUT_NIDKG_V01_CS01_WITH_BLS12381_XMD:SHA-256_SSWU_RO_PROOF_DISCRETE_LOG"; -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub struct ProofOfDiscreteLog { pub(crate) rand_commitment: G1Projective, diff --git a/common/crypto/dkg/src/dealing.rs b/common/crypto/dkg/src/dealing.rs index 817bffdf12..d92a9ba6b4 100644 --- a/common/crypto/dkg/src/dealing.rs +++ b/common/crypto/dkg/src/dealing.rs @@ -9,19 +9,79 @@ use crate::interpolation::polynomial::{Polynomial, PublicCoefficients}; use crate::interpolation::{ perform_lagrangian_interpolation_at_origin, perform_lagrangian_interpolation_at_x, }; +use crate::utils::deserialize_g2; use crate::{NodeIndex, Share, Threshold}; use bls12_381::{G2Projective, Scalar}; +use group::GroupEncoding; use rand_core::RngCore; use std::collections::BTreeMap; use zeroize::Zeroize; -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub struct RecoveredVerificationKeys { pub recovered_master: G2Projective, pub recovered_partials: Vec, } +impl RecoveredVerificationKeys { + pub fn to_bytes(&self) -> Vec { + let partials = self.recovered_partials.len(); + let mut bytes = Vec::with_capacity(96 + 4 + 96 * partials); + bytes.extend_from_slice(self.recovered_master.to_bytes().as_ref()); + bytes.extend_from_slice(&((partials as u32).to_be_bytes())); + for partial in &self.recovered_partials { + bytes.extend_from_slice(partial.to_bytes().as_ref()); + } + + bytes + } + + pub fn try_from_bytes(b: &[u8]) -> Result { + if b.len() < 96 + 4 { + return Err(DkgError::new_deserialization_failure( + "RecoveredVerificationKeys", + "insufficient number of bytes provided", + )); + } + + let recovered_master = deserialize_g2(&b[..96]).ok_or_else(|| { + DkgError::new_deserialization_failure( + "RecoveredVerificationKeys.recovered_master", + "invalid curve point", + ) + })?; + + let partials = u32::from_be_bytes([b[96], b[97], b[98], b[99]]) as usize; + let mut recovered_partials = Vec::with_capacity(partials); + + if b.len() != 96 + 4 + 96 * partials { + return Err(DkgError::new_deserialization_failure( + "RecoveredVerificationKeys", + "insufficient number of bytes provided", + )); + } + + let mut i = 96 + 4; + for _ in 0..partials { + let partial = deserialize_g2(&b[i..i + 96]).ok_or_else(|| { + DkgError::new_deserialization_failure( + "RecoveredVerificationKeys.recovered_partials", + "invalid curve point", + ) + })?; + + recovered_partials.push(partial); + i += 96; + } + + Ok(RecoveredVerificationKeys { + recovered_master, + recovered_partials, + }) + } +} + #[derive(Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub struct Dealing { @@ -266,11 +326,6 @@ pub fn try_recover_verification_keys( return Err(DkgError::MismatchedDealings); } - // currently we expect every dealer to also be a receiver. This restriction might be relaxed in the future - if dealings.len() != receivers.len() { - return Err(DkgError::MismatchedDealings); - } - let indices = receivers.keys().collect::>(); // Compute A0, ..., A_{t-1} @@ -355,6 +410,17 @@ mod tests { use crate::combine_shares; use rand_core::SeedableRng; + #[test] + fn recovered_verification_keys_serde() { + let keys = RecoveredVerificationKeys { + recovered_master: Default::default(), + recovered_partials: vec![Default::default(), Default::default()], + }; + let bytes = keys.to_bytes(); + let recovered_keys = RecoveredVerificationKeys::try_from_bytes(&bytes).unwrap(); + assert_eq!(keys, recovered_keys); + } + #[test] #[ignore] // expensive test fn recovering_partial_verification_keys() { diff --git a/common/crypto/dkg/tests/integration.rs b/common/crypto/dkg/tests/integration.rs index 2d25c31d40..4ac55df1c7 100644 --- a/common/crypto/dkg/tests/integration.rs +++ b/common/crypto/dkg/tests/integration.rs @@ -254,3 +254,125 @@ fn full_threshold_secret_resharing() { assert_ne!(derived_secrets, reshared_secrets); assert_ne!(recovered_partials, reshared_partials); } + +#[test] +#[ignore] // expensive test +fn full_threshold_secret_resharing_left_party() { + let dummy_seed = [42u8; 32]; + let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); + let params = setup(); + + // the simplest possible case + let threshold = 2; + + // the indices are going to get assigned externally, so for test sake, use non-consecutive ones + let mut node_indices = vec![15u64, 248, 33521]; + + let mut receivers = BTreeMap::new(); + let mut full_keys = Vec::new(); + for index in &node_indices { + let (dk, pk) = keygen(¶ms, &mut rng); + receivers.insert(*index, *pk.public_key()); + full_keys.push((dk, pk)) + } + + let first_dealings = node_indices + .iter() + .map(|&dealer_index| { + Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0 + }) + .collect::>(); + + // recover verification keys + let RecoveredVerificationKeys { + recovered_master: public_original_master, + recovered_partials, + } = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap(); + + let mut derived_secrets = Vec::new(); + for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() { + let shares = first_dealings + .iter() + .map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap()) + .collect(); + + let recovered_secret = + combine_shares(shares, &receivers.keys().copied().collect::>()).unwrap(); + + derived_secrets.push(recovered_secret) + } + + let original_master = perform_lagrangian_interpolation_at_origin(&[ + (Scalar::from(node_indices[0]), derived_secrets[0]), + (Scalar::from(node_indices[1]), derived_secrets[1]), + ]) + .unwrap(); + + // one party leaves the process + let left_party_index = node_indices.pop().unwrap(); + receivers.remove(&left_party_index); + full_keys.pop(); + // and another one joins, but we're still over the threshold value of initial parties + let join_party_index = 100000; + let (dk, pk) = keygen(¶ms, &mut rng); + receivers.insert(join_party_index, *pk.public_key()); + full_keys.push((dk, pk)); + + // only initial parties attempt to create resharing dealings! + let resharing_dealings = node_indices + .iter() + .zip(derived_secrets.iter().take(2)) + .map(|(&dealer_index, prior_secret)| { + Dealing::create( + &mut rng, + ¶ms, + dealer_index, + threshold, + &receivers, + Some(*prior_secret), + ) + .0 + }) + .collect::>(); + + for (reshared_dealing, prior_vk) in resharing_dealings + .iter() + .zip(recovered_partials.iter().take(2)) + { + reshared_dealing + .verify(¶ms, threshold, &receivers, Some(*prior_vk)) + .unwrap(); + } + + // recover verification keys + let RecoveredVerificationKeys { + recovered_master: public_reshared_master, + recovered_partials: reshared_partials, + } = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap(); + + let mut reshared_secrets = Vec::new(); + for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() { + let shares = resharing_dealings + .iter() + .map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap()) + .collect(); + + let recovered_secret = combine_shares(shares, &node_indices).unwrap(); + + reshared_secrets.push(recovered_secret) + } + + let reshared_master = perform_lagrangian_interpolation_at_origin(&[ + (Scalar::from(node_indices[0]), reshared_secrets[0]), + (Scalar::from(join_party_index), reshared_secrets[2]), + ]) + .unwrap(); + + // the master secret and public values didn't change + assert_eq!(original_master, reshared_master); + assert_eq!(public_original_master, public_reshared_master); + + // but partials did + assert_ne!(derived_secrets, reshared_secrets); + assert_ne!(recovered_partials, reshared_partials); +} diff --git a/common/crypto/src/asymmetric/encryption/mod.rs b/common/crypto/src/asymmetric/encryption/mod.rs index 5c7b5398ad..28ff92c07e 100644 --- a/common/crypto/src/asymmetric/encryption/mod.rs +++ b/common/crypto/src/asymmetric/encryption/mod.rs @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use pemstore::traits::{PemStorableKey, PemStorableKeyPair}; +use std::fmt::{self, Display, Formatter}; +use thiserror::Error; + #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::{self, Display, Formatter}; /// Size of a X25519 private key pub const PRIVATE_KEY_SIZE: usize = 32; @@ -17,31 +19,26 @@ pub const PUBLIC_KEY_SIZE: usize = 32; /// Size of a X25519 shared secret pub const SHARED_SECRET_SIZE: usize = 32; -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, Error)] pub enum KeyRecoveryError { - InvalidPublicKeyBytes, - InvalidPrivateKeyBytes, - MalformedString(bs58::decode::Error), -} + #[error("received public key of invalid size. Got: {received}, expected: {expected}")] + InvalidSizePublicKey { received: usize, expected: usize }, -impl From for KeyRecoveryError { - fn from(err: bs58::decode::Error) -> Self { - KeyRecoveryError::MalformedString(err) - } -} + #[error("received private key of invalid size. Got: {received}, expected: {expected}")] + InvalidSizePrivateKey { received: usize, expected: usize }, -// required for std::error::Error -impl Display for KeyRecoveryError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - KeyRecoveryError::InvalidPrivateKeyBytes => write!(f, "Invalid private key bytes"), - KeyRecoveryError::InvalidPublicKeyBytes => write!(f, "Invalid public key bytes"), - KeyRecoveryError::MalformedString(err) => write!(f, "malformed string - {}", err), - } - } -} + #[error("the base58 representation of the public key was malformed - {source}")] + MalformedPublicKeyString { + #[source] + source: bs58::decode::Error, + }, -impl std::error::Error for KeyRecoveryError {} + #[error("the base58 representation of the private key was malformed - {source}")] + MalformedPrivateKeyString { + #[source] + source: bs58::decode::Error, + }, +} pub struct KeyPair { pub(crate) private_key: PrivateKey, @@ -112,7 +109,10 @@ impl PublicKey { pub fn from_bytes(b: &[u8]) -> Result { if b.len() != PUBLIC_KEY_SIZE { - return Err(KeyRecoveryError::InvalidPublicKeyBytes); + return Err(KeyRecoveryError::InvalidSizePublicKey { + received: b.len(), + expected: PUBLIC_KEY_SIZE, + }); } let mut bytes = [0; PUBLIC_KEY_SIZE]; bytes.copy_from_slice(&b[..PUBLIC_KEY_SIZE]); @@ -124,7 +124,9 @@ impl PublicKey { } pub fn from_base58_string>(val: I) -> Result { - let bytes = bs58::decode(val).into_vec()?; + let bytes = bs58::decode(val) + .into_vec() + .map_err(|source| KeyRecoveryError::MalformedPublicKeyString { source })?; Self::from_bytes(&bytes) } } @@ -188,7 +190,10 @@ impl PrivateKey { pub fn from_bytes(b: &[u8]) -> Result { if b.len() != PRIVATE_KEY_SIZE { - return Err(KeyRecoveryError::InvalidPrivateKeyBytes); + return Err(KeyRecoveryError::InvalidSizePrivateKey { + received: b.len(), + expected: PRIVATE_KEY_SIZE, + }); } let mut bytes = [0; 32]; bytes.copy_from_slice(&b[..PRIVATE_KEY_SIZE]); @@ -200,7 +205,9 @@ impl PrivateKey { } pub fn from_base58_string>(val: I) -> Result { - let bytes = bs58::decode(val).into_vec()?; + let bytes = bs58::decode(val) + .into_vec() + .map_err(|source| KeyRecoveryError::MalformedPrivateKeyString { source })?; Self::from_bytes(&bytes) } diff --git a/common/crypto/src/asymmetric/identity/mod.rs b/common/crypto/src/asymmetric/identity/mod.rs index 8ba1de8ec6..d8f6e2983c 100644 --- a/common/crypto/src/asymmetric/identity/mod.rs +++ b/common/crypto/src/asymmetric/identity/mod.rs @@ -6,10 +6,12 @@ pub use ed25519_dalek::SignatureError; pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH}; use nymsphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH}; use pemstore::traits::{PemStorableKey, PemStorableKeyPair}; +use std::fmt::{self, Display, Formatter}; +use std::str::FromStr; +use thiserror::Error; + #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; -use std::fmt::{self, Display, Formatter}; - #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] @@ -17,35 +19,30 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum Ed25519RecoveryError { - MalformedBytes(SignatureError), - MalformedString(bs58::decode::Error), -} + #[error(transparent)] + MalformedBytes(#[from] SignatureError), -impl From for Ed25519RecoveryError { - fn from(err: SignatureError) -> Self { - Ed25519RecoveryError::MalformedBytes(err) - } -} + #[error("the base58 representation of the public key was malformed - {source}")] + MalformedPublicKeyString { + #[source] + source: bs58::decode::Error, + }, -impl From for Ed25519RecoveryError { - fn from(err: bs58::decode::Error) -> Self { - Ed25519RecoveryError::MalformedString(err) - } -} + #[error("the base58 representation of the private key was malformed - {source}")] + MalformedPrivateKeyString { + #[source] + source: bs58::decode::Error, + }, -impl fmt::Display for Ed25519RecoveryError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Ed25519RecoveryError::MalformedBytes(err) => write!(f, "malformed bytes - {}", err), - Ed25519RecoveryError::MalformedString(err) => write!(f, "malformed string - {}", err), - } - } + #[error("the base58 representation of the signature was malformed - {source}")] + MalformedSignatureString { + #[source] + source: bs58::decode::Error, + }, } -impl std::error::Error for Ed25519RecoveryError {} - /// Keypair for usage in ed25519 EdDSA. #[derive(Debug)] pub struct KeyPair { @@ -134,7 +131,9 @@ impl PublicKey { } pub fn from_base58_string>(val: I) -> Result { - let bytes = bs58::decode(val).into_vec()?; + let bytes = bs58::decode(val) + .into_vec() + .map_err(|source| Ed25519RecoveryError::MalformedPublicKeyString { source })?; Self::from_bytes(&bytes) } @@ -143,6 +142,14 @@ impl PublicKey { } } +impl FromStr for PublicKey { + type Err = Ed25519RecoveryError; + + fn from_str(s: &str) -> Result { + PublicKey::from_base58_string(s) + } +} + #[cfg(feature = "serde")] impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result @@ -211,7 +218,9 @@ impl PrivateKey { } pub fn from_base58_string>(val: I) -> Result { - let bytes = bs58::decode(val).into_vec()?; + let bytes = bs58::decode(val) + .into_vec() + .map_err(|source| Ed25519RecoveryError::MalformedPrivateKeyString { source })?; Self::from_bytes(&bytes) } @@ -277,7 +286,9 @@ impl Signature { } pub fn from_base58_string>(val: I) -> Result { - let bytes = bs58::decode(val).into_vec()?; + let bytes = bs58::decode(val) + .into_vec() + .map_err(|source| Ed25519RecoveryError::MalformedSignatureString { source })?; Self::from_bytes(&bytes) } diff --git a/common/execute/src/lib.rs b/common/execute/src/lib.rs index 795a72822d..c05a2e8748 100644 --- a/common/execute/src/lib.rs +++ b/common/execute/src/lib.rs @@ -53,13 +53,13 @@ pub fn execute(attr: TokenStream, item: TokenStream) -> TokenStream { ); let execute_output = quote! { - -> Result + -> Result }; let o_ts = proc_macro::TokenStream::from(execute_output); execute_f.sig.output = parse_macro_input!(o_ts as ReturnType); let simulate_output = quote! { - -> Result + -> Result }; let o_ts = proc_macro::TokenStream::from(simulate_output); simulate_f.sig.output = parse_macro_input!(o_ts as ReturnType); diff --git a/common/mixnode-common/Cargo.toml b/common/mixnode-common/Cargo.toml index 53676023af..d2e20ef29d 100644 --- a/common/mixnode-common/Cargo.toml +++ b/common/mixnode-common/Cargo.toml @@ -16,6 +16,7 @@ serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.21.2", features = ["time", "macros", "rt", "net", "io-util"] } tokio-util = { version = "0.7.3", features = ["codec"] } url = "2.2" +thiserror = "1.0.37" crypto = { path = "../crypto" } nymsphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" } @@ -25,5 +26,5 @@ nymsphinx-framing = { path = "../nymsphinx/framing" } nymsphinx-params = { path = "../nymsphinx/params" } nymsphinx-types = { path = "../nymsphinx/types" } task = { path = "../task" } -validator-client = { path = "../client-libs/validator-client" } +validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"]} version-checker = { path = "../version-checker" } diff --git a/common/mixnode-common/src/packet_processor/error.rs b/common/mixnode-common/src/packet_processor/error.rs index 38be92079d..084ce708a8 100644 --- a/common/mixnode-common/src/packet_processor/error.rs +++ b/common/mixnode-common/src/packet_processor/error.rs @@ -4,63 +4,22 @@ use nymsphinx_acknowledgements::surb_ack::SurbAckRecoveryError; use nymsphinx_addressing::nodes::NymNodeRoutingAddressError; use nymsphinx_types::Error as SphinxError; -use std::fmt::{self, Display, Formatter}; +use thiserror::Error; -#[derive(Debug)] +#[derive(Error, Debug)] pub enum MixProcessingError { - SphinxProcessingError(SphinxError), - InvalidHopAddress(NymNodeRoutingAddressError), - NoSurbAckInFinalHop, - MalformedSurbAck(SurbAckRecoveryError), + #[error("failed to process received packet: {0}")] + SphinxProcessingError(#[from] SphinxError), + #[error("the forward hop address was malformed: {0}")] + InvalidForwardHopAddress(#[from] NymNodeRoutingAddressError), + + #[error("the final hop did not contain a SURB-Ack")] + NoSurbAckInFinalHop, + + #[error("failed to recover the expected SURB-Ack packet: {0}")] + MalformedSurbAck(#[from] SurbAckRecoveryError), + + #[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")] ReceivedOldTypeVpnPacket, } - -impl From for MixProcessingError { - // for the time being just have a single error instance for all possible results of SphinxError - fn from(err: SphinxError) -> Self { - use MixProcessingError::*; - - SphinxProcessingError(err) - } -} - -impl From for MixProcessingError { - fn from(err: NymNodeRoutingAddressError) -> Self { - use MixProcessingError::*; - - InvalidHopAddress(err) - } -} - -impl From for MixProcessingError { - fn from(err: SurbAckRecoveryError) -> Self { - use MixProcessingError::*; - - MalformedSurbAck(err) - } -} - -impl Display for MixProcessingError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - MixProcessingError::SphinxProcessingError(sphinx_err) => { - write!(f, "Sphinx Processing Error - {}", sphinx_err) - } - MixProcessingError::InvalidHopAddress(address_err) => { - write!(f, "Invalid Hop Address - {:?}", address_err) - } - MixProcessingError::NoSurbAckInFinalHop => { - write!(f, "No SURBAck present in the final hop data") - } - MixProcessingError::MalformedSurbAck(surb_ack_err) => { - write!(f, "Malformed SURBAck - {:?}", surb_ack_err) - } - MixProcessingError::ReceivedOldTypeVpnPacket => { - write!(f, "Received an old-type unsafe 'VPN' mode packet") - } - } - } -} - -impl std::error::Error for MixProcessingError {} diff --git a/common/mixnode-common/src/packet_processor/processor.rs b/common/mixnode-common/src/packet_processor/processor.rs index 918e001fec..84e543d8bd 100644 --- a/common/mixnode-common/src/packet_processor/processor.rs +++ b/common/mixnode-common/src/packet_processor/processor.rs @@ -51,7 +51,7 @@ impl SphinxPacketProcessor { packet: SphinxPacket, ) -> Result { packet.process(&self.sphinx_key).map_err(|err| { - debug!("Failed to unwrap Sphinx packet: {:?}", err); + debug!("Failed to unwrap Sphinx packet: {err}"); MixProcessingError::SphinxProcessingError(err) }) } @@ -161,7 +161,7 @@ impl SphinxPacketProcessor { ) -> Result { match packet { ProcessedPacket::ForwardHop(packet, address, delay) => { - self.process_forward_hop(packet, address, delay, packet_mode) + self.process_forward_hop(*packet, address, delay, packet_mode) } // right now there's no use for the surb_id included in the header - probably it should get removed from the // sphinx all together? diff --git a/common/mixnode-common/src/verloc/error.rs b/common/mixnode-common/src/verloc/error.rs index 44a408c77a..0268059d7c 100644 --- a/common/mixnode-common/src/verloc/error.rs +++ b/common/mixnode-common/src/verloc/error.rs @@ -53,13 +53,13 @@ impl Display for RttError { write!(f, "The received reply packet had invalid signature") } RttError::UnreachableNode(id, err) => { - write!(f, "Could not establish connection to {} - {}", id, err) + write!(f, "Could not establish connection to {} - {err}", id) } RttError::UnexpectedConnectionFailureWrite(id, err) => { - write!(f, "Failed to write echo packet to {} - {}", id, err) + write!(f, "Failed to write echo packet to {} - {err}", id) } RttError::UnexpectedConnectionFailureRead(id, err) => { - write!(f, "Failed to read reply packet from {} - {}", id, err) + write!(f, "Failed to read reply packet from {} - {err}", id) } RttError::ConnectionReadTimeout(id) => { write!(f, "Timed out while trying to read reply packet from {}", id) diff --git a/common/mixnode-common/src/verloc/listener.rs b/common/mixnode-common/src/verloc/listener.rs index 8f49779130..812358e330 100644 --- a/common/mixnode-common/src/verloc/listener.rs +++ b/common/mixnode-common/src/verloc/listener.rs @@ -11,7 +11,7 @@ use std::fmt::{Display, Formatter}; use std::net::SocketAddr; use std::sync::Arc; use std::{fmt, io, process}; -use task::ShutdownListener; +use task::TaskClient; use tokio::io::AsyncWriteExt; use tokio::net::{TcpListener, TcpStream}; use tokio_util::codec::{Decoder, Encoder, Framed}; @@ -19,14 +19,14 @@ use tokio_util::codec::{Decoder, Encoder, Framed}; pub(crate) struct PacketListener { address: SocketAddr, connection_handler: Arc, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl PacketListener { pub(crate) fn new( address: SocketAddr, identity: Arc, - shutdown: ShutdownListener, + shutdown: TaskClient, ) -> Self { PacketListener { address, @@ -66,7 +66,7 @@ impl PacketListener { tokio::spawn(connection_handler.handle_connection(socket, remote_addr, handler_shutdown_listener)); } - Err(err) => warn!("Failed to accept incoming connection - {:?}", err), + Err(err) => warn!("Failed to accept incoming connection - {err}"), } }, _ = shutdown_listener.recv() => { @@ -91,7 +91,7 @@ impl ConnectionHandler { self: Arc, conn: TcpStream, remote: SocketAddr, - mut shutdown_listener: ShutdownListener, + mut shutdown_listener: TaskClient, ) { debug!("Starting connection handler for {:?}", remote); @@ -141,9 +141,9 @@ enum EchoPacketCodecError { impl Display for EchoPacketCodecError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - EchoPacketCodecError::IoError(err) => write!(f, "encountered io error - {}", err), + EchoPacketCodecError::IoError(err) => write!(f, "encountered io error - {err}"), EchoPacketCodecError::PacketRecoveryError(err) => { - write!(f, "failed to correctly decode an echo packet - {}", err) + write!(f, "failed to correctly decode an echo packet - {err}") } } } diff --git a/common/mixnode-common/src/verloc/mod.rs b/common/mixnode-common/src/verloc/mod.rs index 48e9a7b483..07fc80b4ba 100644 --- a/common/mixnode-common/src/verloc/mod.rs +++ b/common/mixnode-common/src/verloc/mod.rs @@ -9,10 +9,11 @@ use futures::StreamExt; use log::*; use rand::seq::SliceRandom; use rand::thread_rng; -use std::net::{SocketAddr, ToSocketAddrs}; +use std::net::SocketAddr; +use std::net::ToSocketAddrs; use std::sync::Arc; use std::time::Duration; -use task::ShutdownListener; +use task::TaskClient; use tokio::task::JoinHandle; use tokio::time::sleep; use url::Url; @@ -69,8 +70,8 @@ pub struct Config { /// due to being unable to get the list of nodes. retry_timeout: Duration, - /// URLs to the validator apis for obtaining network topology. - validator_api_urls: Vec, + /// URLs to the nym apis for obtaining network topology. + nym_api_urls: Vec, } impl Config { @@ -132,15 +133,15 @@ impl ConfigBuilder { self } - pub fn validator_api_urls(mut self, validator_api_urls: Vec) -> Self { - self.0.validator_api_urls = validator_api_urls; + pub fn nym_api_urls(mut self, nym_api_urls: Vec) -> Self { + self.0.nym_api_urls = nym_api_urls; self } pub fn build(self) -> Config { // panics here are fine as those are only ever constructed at the initial setup assert!( - !self.0.validator_api_urls.is_empty(), + !self.0.nym_api_urls.is_empty(), "at least one validator endpoint must be provided", ); self.0 @@ -159,7 +160,7 @@ impl Default for ConfigBuilder { tested_nodes_batch_size: DEFAULT_BATCH_SIZE, testing_interval: DEFAULT_TESTING_INTERVAL, retry_timeout: DEFAULT_RETRY_TIMEOUT, - validator_api_urls: vec![], + nym_api_urls: vec![], }) } } @@ -168,7 +169,7 @@ pub struct VerlocMeasurer { config: Config, packet_sender: Arc, packet_listener: Arc, - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, currently_used_api: usize, @@ -176,7 +177,7 @@ pub struct VerlocMeasurer { // It only does bunch of REST queries. If we update it at some point to a more sophisticated (maybe signing) client, // then it definitely cannot be constructed here and probably will need to be passed from outside, // as mixnodes/gateways would already be using an instance of said client. - validator_client: validator_client::ApiClient, + validator_client: validator_client::NymApiClient, results: AtomicVerlocResult, } @@ -184,9 +185,9 @@ impl VerlocMeasurer { pub fn new( mut config: Config, identity: Arc, - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, ) -> Self { - config.validator_api_urls.shuffle(&mut thread_rng()); + config.nym_api_urls.shuffle(&mut thread_rng()); VerlocMeasurer { packet_sender: Arc::new(PacketSender::new( @@ -204,24 +205,21 @@ impl VerlocMeasurer { )), shutdown_listener, currently_used_api: 0, - validator_client: validator_client::ApiClient::new( - config.validator_api_urls[0].clone(), - ), + validator_client: validator_client::NymApiClient::new(config.nym_api_urls[0].clone()), config, results: AtomicVerlocResult::new(), } } - fn use_next_validator_api(&mut self) { - if self.config.validator_api_urls.len() == 1 { + fn use_next_nym_api(&mut self) { + if self.config.nym_api_urls.len() == 1 { warn!("There's only a single validator API available - it won't be possible to use a different one"); return; } - self.currently_used_api = - (self.currently_used_api + 1) % self.config.validator_api_urls.len(); + self.currently_used_api = (self.currently_used_api + 1) % self.config.nym_api_urls.len(); self.validator_client - .change_validator_api(self.config.validator_api_urls[self.currently_used_api].clone()) + .change_nym_api(self.config.nym_api_urls[self.currently_used_api].clone()) } pub fn get_verloc_results_pointer(&self) -> AtomicVerlocResult { @@ -308,7 +306,7 @@ impl VerlocMeasurer { "failed to obtain list of mixnodes from the validator - {}. Going to attempt to use another validator API in the next run", err ); - self.use_next_validator_api(); + self.use_next_nym_api(); sleep(self.config.retry_timeout).await; continue; } diff --git a/common/mixnode-common/src/verloc/sender.rs b/common/mixnode-common/src/verloc/sender.rs index bed34f4474..91f7ee9974 100644 --- a/common/mixnode-common/src/verloc/sender.rs +++ b/common/mixnode-common/src/verloc/sender.rs @@ -11,7 +11,7 @@ use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use std::{fmt, io}; -use task::ShutdownListener; +use task::TaskClient; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tokio::time::sleep; @@ -45,7 +45,7 @@ pub(crate) struct PacketSender { packet_timeout: Duration, connection_timeout: Duration, delay_between_packets: Duration, - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, } impl PacketSender { @@ -55,7 +55,7 @@ impl PacketSender { packet_timeout: Duration, connection_timeout: Duration, delay_between_packets: Duration, - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, ) -> Self { PacketSender { identity, diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index d7b01622b1..dd16eb18d7 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -1,6 +1,7 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD}; use serde::{Deserialize, Serialize}; use std::{env::var, ops::Not, path::PathBuf}; use url::Url; @@ -77,8 +78,8 @@ impl NymNetworkDetails { .expect("denomination exponent is not u32"), }) .with_validator_endpoint(ValidatorDetails::new( - var(var_names::NYMD_VALIDATOR).expect("nymd validator not set"), - Some(var(var_names::API_VALIDATOR).expect("api validator not set")), + var(var_names::NYXD).expect("nyxd validator not set"), + Some(var(var_names::NYM_API).expect("nym api not set")), )) .with_mixnet_contract(Some( var(var_names::MIXNET_CONTRACT_ADDRESS).expect("mixnet contract not set"), @@ -254,7 +255,7 @@ impl DenomDetailsOwned { #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ValidatorDetails { // it is assumed those values are always valid since they're being provided in our defaults file - pub nymd_url: String, + pub nyxd_url: String, // Right now api_url is optional as we are not running the api reliably on all validators // however, later on it should be a mandatory field pub api_url: Option, @@ -262,24 +263,24 @@ pub struct ValidatorDetails { } impl ValidatorDetails { - pub fn new>(nymd_url: S, api_url: Option) -> Self { + pub fn new>(nyxd_url: S, api_url: Option) -> Self { ValidatorDetails { - nymd_url: nymd_url.into(), + nyxd_url: nyxd_url.into(), api_url: api_url.map(Into::into), } } - pub fn new_nymd_only>(nymd_url: S) -> Self { + pub fn new_nyxd_only>(nyxd_url: S) -> Self { ValidatorDetails { - nymd_url: nymd_url.into(), + nyxd_url: nyxd_url.into(), api_url: None, } } - pub fn nymd_url(&self) -> Url { - self.nymd_url + pub fn nyxd_url(&self) -> Url { + self.nyxd_url .parse() - .expect("the provided nymd url is invalid!") + .expect("the provided nyxd url is invalid!") } pub fn api_url(&self) -> Option { @@ -289,13 +290,28 @@ impl ValidatorDetails { } } -pub fn setup_env(config_env_file: Option) { +fn fix_deprecated_environmental_variables() { + // if we're using the outdated environmental variables, set the updated ones to preserve compatibility + if let Ok(nyxd) = std::env::var(DEPRECATED_NYMD_VALIDATOR) { + if std::env::var(NYXD).is_err() { + std::env::set_var(NYXD, nyxd) + } + } + if let Ok(nym_apis) = std::env::var(DEPRECATED_API_VALIDATOR) { + if std::env::var(NYM_API).is_err() { + std::env::set_var(NYM_API, nym_apis) + } + } +} + +pub fn setup_env(config_env_file: Option<&PathBuf>) { match std::env::var(var_names::CONFIGURED) { // if the configuration is not already set in the env vars Err(std::env::VarError::NotPresent) => { if let Some(config_env_file) = config_env_file { dotenv::from_path(config_env_file) .expect("Invalid path to environment configuration file"); + fix_deprecated_environmental_variables(); } else { // if nothing is set, the use mainnet defaults // if the user has not set `CONFIGURED`, then even if they set any of the env variables, @@ -304,7 +320,9 @@ pub fn setup_env(config_env_file: Option) { } } Err(_) => crate::mainnet::export_to_env(), - _ => {} + _ => { + fix_deprecated_environmental_variables(); + } } // if we haven't explicitly defined any of the constants, fallback to defaults @@ -355,14 +373,14 @@ pub const DEFAULT_WEBSOCKET_LISTENING_PORT: u16 = 1977; // 'SOCKS5' CLIENT pub const DEFAULT_SOCKS5_LISTENING_PORT: u16 = 1080; -// VALIDATOR-API -pub const DEFAULT_VALIDATOR_API_PORT: u16 = 8080; +// NYM-API +pub const DEFAULT_NYM_API_PORT: u16 = 8080; -pub const VALIDATOR_API_VERSION: &str = "v1"; +pub const NYM_API_VERSION: &str = "v1"; // REWARDING -/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms +/// We'll be assuming a few more things, profit margin and cost function. Since we don't have reliable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms // pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$ // pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 55_556; // 40$/1hr at 1 Nym == 1$ // pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 9259; // 40$/1hr/6 at 1 Nym == 1$ diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs index 59242376f9..4f8352c1c6 100644 --- a/common/network-defaults/src/mainnet.rs +++ b/common/network-defaults/src/mainnet.rs @@ -9,7 +9,7 @@ pub(crate) const BECH32_PREFIX: &str = "n"; pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6); pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6); -pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = +pub const MIXNET_CONTRACT_ADDRESS: &str = "n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr"; pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw"; @@ -25,11 +25,11 @@ 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 = "https://mainnet-stats.nymte.ch:8090/"; -pub const NYMD_VALIDATOR: &str = "https://rpc.nymtech.net"; -pub const API_VALIDATOR: &str = "https://validator.nymtech.net/api/"; +pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/"; +pub const NYXD_URL: &str = "https://rpc.nymtech.net"; +pub const NYM_API: &str = "https://validator.nymtech.net/api/"; pub(crate) fn validators() -> Vec { - vec![ValidatorDetails::new(NYMD_VALIDATOR, Some(API_VALIDATOR))] + vec![ValidatorDetails::new(NYXD_URL, Some(NYM_API))] } const DEFAULT_SUFFIX: &str = "_MAINNET_DEFAULT"; @@ -97,8 +97,8 @@ pub fn export_to_env() { var_names::STATISTICS_SERVICE_DOMAIN_ADDRESS, STATISTICS_SERVICE_DOMAIN_ADDRESS, ); - set_var_to_default(var_names::NYMD_VALIDATOR, NYMD_VALIDATOR); - set_var_to_default(var_names::API_VALIDATOR, API_VALIDATOR); + set_var_to_default(var_names::NYXD, NYXD_URL); + set_var_to_default(var_names::NYM_API, NYM_API); } pub fn export_to_env_if_not_set() { @@ -141,6 +141,6 @@ pub fn export_to_env_if_not_set() { var_names::STATISTICS_SERVICE_DOMAIN_ADDRESS, STATISTICS_SERVICE_DOMAIN_ADDRESS, ); - set_var_conditionally_to_default(var_names::NYMD_VALIDATOR, NYMD_VALIDATOR); - set_var_conditionally_to_default(var_names::API_VALIDATOR, API_VALIDATOR); + set_var_conditionally_to_default(var_names::NYXD, NYXD_URL); + set_var_conditionally_to_default(var_names::NYM_API, NYM_API); } diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs index 4e62e444cc..c6e4a450a2 100644 --- a/common/network-defaults/src/var_names.rs +++ b/common/network-defaults/src/var_names.rs @@ -18,5 +18,9 @@ pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS"; pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS"; pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS"; pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_ADDRESS"; -pub const NYMD_VALIDATOR: &str = "NYMD_VALIDATOR"; -pub const API_VALIDATOR: &str = "API_VALIDATOR"; +pub const NYXD: &str = "NYXD"; +pub const NYM_API: &str = "NYM_API"; + +// we don't want to explicitly tag those with `#[deprecated]` because then our CI would be red and sad : ( +pub const DEPRECATED_NYMD_VALIDATOR: &str = "NYMD_VALIDATOR"; +pub const DEPRECATED_API_VALIDATOR: &str = "API_VALIDATOR"; diff --git a/common/nymcoconut/src/tests/e2e.rs b/common/nymcoconut/src/tests/e2e.rs index 235b2adbab..db950d0c3f 100644 --- a/common/nymcoconut/src/tests/e2e.rs +++ b/common/nymcoconut/src/tests/e2e.rs @@ -45,6 +45,7 @@ fn keygen() -> Result<(), CoconutError> { } #[test] +#[ignore] // expensive test fn dkg() -> Result<(), CoconutError> { let params = setup(5)?; let node_indices = vec![15u64, 248, 33521]; diff --git a/common/nymsphinx/Cargo.toml b/common/nymsphinx/Cargo.toml index f0f24ac665..d9d81aecd4 100644 --- a/common/nymsphinx/Cargo.toml +++ b/common/nymsphinx/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand_distr = "0.3" +thiserror = "1.0.37" nymsphinx-acknowledgements = { path = "acknowledgements" } nymsphinx-addressing = { path = "addressing" } diff --git a/common/nymsphinx/acknowledgements/Cargo.toml b/common/nymsphinx/acknowledgements/Cargo.toml index f105e0c544..c05976ade7 100644 --- a/common/nymsphinx/acknowledgements/Cargo.toml +++ b/common/nymsphinx/acknowledgements/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] rand = { version = "0.7.3", features = ["wasm-bindgen"] } +thiserror = "1.0.37" crypto = { path = "../../crypto", features = ["symmetric", "rand"] } nymsphinx-addressing = { path = "../addressing" } diff --git a/common/nymsphinx/acknowledgements/src/surb_ack.rs b/common/nymsphinx/acknowledgements/src/surb_ack.rs index 22bd4bc468..912ed4bc32 100644 --- a/common/nymsphinx/acknowledgements/src/surb_ack.rs +++ b/common/nymsphinx/acknowledgements/src/surb_ack.rs @@ -4,10 +4,13 @@ use crate::identifier::prepare_identifier; use crate::AckKey; use nymsphinx_addressing::clients::Recipient; -use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN}; +use nymsphinx_addressing::nodes::{ + NymNodeRoutingAddress, NymNodeRoutingAddressError, MAX_NODE_ADDRESS_UNPADDED_LEN, +}; use nymsphinx_params::packet_sizes::PacketSize; use nymsphinx_params::DEFAULT_NUM_MIX_HOPS; use nymsphinx_types::builder::SphinxPacketBuilder; +use nymsphinx_types::Error as SphinxError; use nymsphinx_types::{ delays::{self, Delay}, SphinxPacket, @@ -15,6 +18,7 @@ use nymsphinx_types::{ use rand::{CryptoRng, RngCore}; use std::convert::TryFrom; use std::time; +use thiserror::Error; use topology::{NymTopology, NymTopologyError}; pub struct SurbAck { @@ -23,11 +27,16 @@ pub struct SurbAck { expected_total_delay: Delay, } -#[derive(Debug)] +#[derive(Debug, Error)] pub enum SurbAckRecoveryError { - InvalidPacketSize, - InvalidAddress, - InvalidSphinxPacket, + #[error("received an invalid number of bytes to deserialize the SURB-Ack. Got {received}, expected {expected}")] + InvalidPacketSize { received: usize, expected: usize }, + + #[error("could not extract first hop address information - {0}")] + InvalidAddress(#[from] NymNodeRoutingAddressError), + + #[error("the contained sphinx packet was not correctly formed - {0}")] + InvalidSphinxPacket(#[from] SphinxError), } impl SurbAck { @@ -72,6 +81,10 @@ impl SurbAck { PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN } + pub fn expected_total_delay(&self) -> Delay { + self.expected_total_delay + } + pub fn prepare_for_sending(self) -> (Delay, Vec) { // SURB_FIRST_HOP || SURB_ACK let surb_bytes: Vec<_> = self @@ -88,20 +101,17 @@ impl SurbAck { b: &[u8], ) -> Result<(NymNodeRoutingAddress, SphinxPacket), SurbAckRecoveryError> { if b.len() != Self::len() { - Err(SurbAckRecoveryError::InvalidPacketSize) + Err(SurbAckRecoveryError::InvalidPacketSize { + received: b.len(), + expected: Self::len(), + }) } else { - let address = match NymNodeRoutingAddress::try_from_bytes(b) { - Ok(address) => address, - Err(_) => return Err(SurbAckRecoveryError::InvalidAddress), - }; + let address = NymNodeRoutingAddress::try_from_bytes(b)?; // TODO: this will be variable once/if we decide to introduce optimization described // in common/nymsphinx/chunking/src/lib.rs:available_plaintext_size() let address_offset = MAX_NODE_ADDRESS_UNPADDED_LEN; - let packet = match SphinxPacket::from_bytes(&b[address_offset..]) { - Ok(packet) => packet, - Err(_) => return Err(SurbAckRecoveryError::InvalidSphinxPacket), - }; + let packet = SphinxPacket::from_bytes(&b[address_offset..])?; Ok((address, packet)) } diff --git a/common/nymsphinx/addressing/Cargo.toml b/common/nymsphinx/addressing/Cargo.toml index a67849b128..7a85486e4f 100644 --- a/common/nymsphinx/addressing/Cargo.toml +++ b/common/nymsphinx/addressing/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys nymsphinx-types = { path = "../types" } # we need to be able to refer to some types defined inside sphinx crate serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient` +thiserror = "1.0.37" [dev-dependencies] rand = "0.7" diff --git a/common/nymsphinx/addressing/src/clients.rs b/common/nymsphinx/addressing/src/clients.rs index 819f272efd..29f7051c6c 100644 --- a/common/nymsphinx/addressing/src/clients.rs +++ b/common/nymsphinx/addressing/src/clients.rs @@ -10,6 +10,8 @@ use nymsphinx_types::Destination; use serde::de::{Error as SerdeError, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Formatter}; +use std::str::FromStr; +use thiserror::Error; // Not entirely sure whether this is the correct place for those, but let's see how it's going // to work out @@ -19,46 +21,25 @@ const CLIENT_ENCRYPTION_KEY_SIZE: usize = encryption::PUBLIC_KEY_SIZE; pub type ClientIdentity = identity::PublicKey; const CLIENT_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH; -#[derive(Debug)] +pub type RecipientBytes = [u8; Recipient::LEN]; + +#[derive(Debug, Error)] pub enum RecipientFormattingError { - MalformedRecipientError, + #[error("recipient is malformed - {reason} ")] + MalformedRecipientError { reason: String }, + + #[error("recipient's identity key is malformed: {0}")] MalformedIdentityError(identity::Ed25519RecoveryError), - MalformedEncryptionKeyError(encryption::KeyRecoveryError), + + #[error("recipient's encryption key is malformed: {0}")] + MalformedEncryptionKeyError(#[from] encryption::KeyRecoveryError), + + #[error("recipient gateway's identity key is malformed: {0}")] MalformedGatewayError(identity::Ed25519RecoveryError), } -impl fmt::Display for RecipientFormattingError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { - match self { - RecipientFormattingError::MalformedRecipientError => { - write!(f, "recipient is malformed") - } - RecipientFormattingError::MalformedIdentityError(id_err) => { - write!(f, "recipient's identity key is malformed: {}", id_err) - } - RecipientFormattingError::MalformedEncryptionKeyError(enc_err) => { - write!(f, "recipient's encryption key is malformed: {}", enc_err) - } - RecipientFormattingError::MalformedGatewayError(id_err) => write!( - f, - "recipient gateway's identity key is malformed: {}", - id_err - ), - } - } -} - -// since we have Debug and Display might as well slap Error on top of it too -impl std::error::Error for RecipientFormattingError {} - -impl From for RecipientFormattingError { - fn from(err: encryption::KeyRecoveryError) -> Self { - RecipientFormattingError::MalformedEncryptionKeyError(err) - } -} - // TODO: this should a different home... somewhere, but where? -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Recipient { client_identity: ClientIdentity, client_encryption_key: ClientEncryptionKey, @@ -154,7 +135,7 @@ impl Recipient { &self.gateway } - pub fn to_bytes(self) -> [u8; Self::LEN] { + pub fn to_bytes(self) -> RecipientBytes { let mut out = [0u8; Self::LEN]; out[..CLIENT_IDENTITY_SIZE].copy_from_slice(&self.client_identity.to_bytes()); out[CLIENT_IDENTITY_SIZE..CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE] @@ -165,7 +146,7 @@ impl Recipient { out } - pub fn try_from_bytes(bytes: [u8; Self::LEN]) -> Result { + pub fn try_from_bytes(bytes: RecipientBytes) -> Result { let identity_bytes = &bytes[..CLIENT_IDENTITY_SIZE]; let enc_key_bytes = &bytes[CLIENT_IDENTITY_SIZE..CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE]; @@ -196,14 +177,20 @@ impl Recipient { let string_address = full_address.into(); let split: Vec<_> = string_address.split('@').collect(); if split.len() != 2 { - return Err(RecipientFormattingError::MalformedRecipientError); + return Err(RecipientFormattingError::MalformedRecipientError { + reason: "the string address does not contain exactly a single '@' character" + .to_string(), + }); } let client_half = split[0]; let gateway_half = split[1]; let split_client: Vec<_> = client_half.split('.').collect(); if split_client.len() != 2 { - return Err(RecipientFormattingError::MalformedRecipientError); + return Err(RecipientFormattingError::MalformedRecipientError { + reason: "the string address does not contain exactly a single '.' character" + .to_string(), + }); } let client_identity = match ClientIdentity::from_base58_string(split_client[0]) { @@ -239,6 +226,14 @@ impl std::fmt::Display for Recipient { } } +impl FromStr for Recipient { + type Err = RecipientFormattingError; + + fn from_str(s: &str) -> Result { + Recipient::try_from_base58_string(s) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/common/nymsphinx/addressing/src/nodes.rs b/common/nymsphinx/addressing/src/nodes.rs index a01f7202bd..cf43625c5a 100644 --- a/common/nymsphinx/addressing/src/nodes.rs +++ b/common/nymsphinx/addressing/src/nodes.rs @@ -5,6 +5,7 @@ use crypto::asymmetric::identity; use nymsphinx_types::{NodeAddressBytes, NODE_ADDRESS_LENGTH}; use std::convert::{TryFrom, TryInto}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use thiserror::Error; // Not entirely sure whether this is the correct place for those, but let's see how it's going // to work out @@ -20,10 +21,23 @@ pub const NODE_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH; /// In this case it's an ipv6 socket address (with version prefix) pub const MAX_NODE_ADDRESS_UNPADDED_LEN: usize = 19; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum NymNodeRoutingAddressError { - InsufficientNumberOfBytesAvailableError, - InvalidIpVersion, + #[error("Attempted to deserialize NymNodeRoutingAddress without providing any bytes")] + NoBytesProvided, + + #[error("Provided insufficient amount of few bytes to deserialize a valid NymNodeRoutingAddress for IPv{protocol_version} variant. Received {received} and required {required}")] + TooFewBytesProvided { + protocol_version: u8, + received: usize, + required: usize, + }, + + #[error("{received} is not a valid version of the Internet Protocol (IP). Expected either '4' or '6'")] + InvalidIpVersion { received: u8 }, + + #[error("Could not serialize NymNodeRoutingAddress into NodeAddressBytes as that requires using at least {required} bytes and only {NODE_ADDRESS_LENGTH} are available")] + TooSmallBytesRepresentation { required: usize }, } /// Current representation of Node routing information used in Nym system. @@ -84,28 +98,39 @@ impl NymNodeRoutingAddress { /// Tries to recover `Self` from a bytes slice. /// Does not care if it's zero-padded or not. pub fn try_from_bytes(b: &[u8]) -> Result { - // the bare minimum to represent `Self` is 7 bytes (for the shorter V4 version) - if b.len() < 7 { - return Err(NymNodeRoutingAddressError::InsufficientNumberOfBytesAvailableError); + if b.is_empty() { + return Err(NymNodeRoutingAddressError::NoBytesProvided); } let ip_version = b[0]; - let port: u16 = u16::from_be_bytes([b[1], b[2]]); let ip = match ip_version { - 4 => IpAddr::V4(Ipv4Addr::new(b[3], b[4], b[5], b[6])), + 4 => { + if b.len() < 7 { + return Err(NymNodeRoutingAddressError::TooFewBytesProvided { + protocol_version: 4, + received: b.len(), + required: 7, + }); + } + IpAddr::V4(Ipv4Addr::new(b[3], b[4], b[5], b[6])) + } 6 => { if b.len() < 19 { - return Err( - NymNodeRoutingAddressError::InsufficientNumberOfBytesAvailableError, - ); + return Err(NymNodeRoutingAddressError::TooFewBytesProvided { + protocol_version: 6, + received: b.len(), + required: 19, + }); } let mut address_octets = [0u8; 16]; address_octets.copy_from_slice(&b[3..19]); IpAddr::V6(Ipv6Addr::from(address_octets)) } - _ => return Err(NymNodeRoutingAddressError::InvalidIpVersion), + v => return Err(NymNodeRoutingAddressError::InvalidIpVersion { received: v }), }; + let port: u16 = u16::from_be_bytes([b[1], b[2]]); + Ok(Self(SocketAddr::new(ip, port))) } @@ -148,7 +173,9 @@ impl TryInto for NymNodeRoutingAddress { fn try_into(self) -> Result { // first check if we have enough bytes to represent `self`: if self.bytes_min_len() > NODE_ADDRESS_LENGTH { - return Err(NymNodeRoutingAddressError::InsufficientNumberOfBytesAvailableError); + return Err(NymNodeRoutingAddressError::TooSmallBytesRepresentation { + required: self.bytes_min_len(), + }); } let padded_address = self.as_zero_padded_bytes(NODE_ADDRESS_LENGTH); diff --git a/common/nymsphinx/anonymous-replies/Cargo.toml b/common/nymsphinx/anonymous-replies/Cargo.toml index 246f6de509..5ddb069e19 100644 --- a/common/nymsphinx/anonymous-replies/Cargo.toml +++ b/common/nymsphinx/anonymous-replies/Cargo.toml @@ -7,12 +7,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = {version = "0.7.3", features = ["wasm-bindgen"]} +rand = { version = "0.7.3", features = ["wasm-bindgen"] } bs58 = "0.4" serde = "1.0" +thiserror = "1" crypto = { path = "../../crypto" } nymsphinx-addressing = { path = "../addressing" } nymsphinx-params = { path = "../params" } nymsphinx-types = { path = "../types" } topology = { path = "../../topology" } + +[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] +version = "0.2.83" \ No newline at end of file diff --git a/common/nymsphinx/anonymous-replies/src/encryption_key.rs b/common/nymsphinx/anonymous-replies/src/encryption_key.rs index b52b8cea60..7a6689f4a7 100644 --- a/common/nymsphinx/anonymous-replies/src/encryption_key.rs +++ b/common/nymsphinx/anonymous-replies/src/encryption_key.rs @@ -16,7 +16,7 @@ pub type EncryptionKeyDigest = pub type SurbEncryptionKeySize = ::KeySize; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct SurbEncryptionKey(CipherKey); #[derive(Debug)] diff --git a/common/nymsphinx/anonymous-replies/src/lib.rs b/common/nymsphinx/anonymous-replies/src/lib.rs index ce93c4099b..3e48d0ea1b 100644 --- a/common/nymsphinx/anonymous-replies/src/lib.rs +++ b/common/nymsphinx/anonymous-replies/src/lib.rs @@ -3,6 +3,7 @@ pub mod encryption_key; pub mod reply_surb; +pub mod requests; pub use encryption_key::{SurbEncryptionKey, SurbEncryptionKeySize}; pub use reply_surb::{ReplySurb, ReplySurbError}; diff --git a/common/nymsphinx/anonymous-replies/src/reply_surb.rs b/common/nymsphinx/anonymous-replies/src/reply_surb.rs index b8b8ec04cf..1fb29b2910 100644 --- a/common/nymsphinx/anonymous-replies/src/reply_surb.rs +++ b/common/nymsphinx/anonymous-replies/src/reply_surb.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::encryption_key::{SurbEncryptionKey, SurbEncryptionKeyError, SurbEncryptionKeySize}; @@ -14,44 +14,22 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::TryFrom; use std::fmt::{self, Formatter}; use std::time; +use thiserror::Error; use topology::{NymTopology, NymTopologyError}; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum ReplySurbError { + #[error("tried to use reply SURB with an unpadded message")] UnpaddedMessageError, - MalformedStringError(bs58::decode::Error), - RecoveryError(SphinxError), - InvalidEncryptionKeyData(SurbEncryptionKeyError), -} -impl fmt::Display for ReplySurbError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { - match self { - ReplySurbError::UnpaddedMessageError => { - write!(f, "tried to use reply SURB with an unpadded message") - } - ReplySurbError::MalformedStringError(decode_err) => { - write!(f, "reply SURB is incorrectly formatted: {}", decode_err) - } - ReplySurbError::RecoveryError(sphinx_err) => { - write!(f, "failed to recover reply SURB from bytes: {}", sphinx_err) - } - ReplySurbError::InvalidEncryptionKeyData(surb_key_err) => write!( - f, - "failed to recover reply SURB encryption key from bytes: {}", - surb_key_err - ), - } - } -} + #[error("reply SURB is incorrectly formatted: {0}")] + MalformedStringError(#[from] bs58::decode::Error), -// since we have Debug and Display might as well slap Error on top of it too -impl std::error::Error for ReplySurbError {} + #[error("failed to recover reply SURB from bytes: {0}")] + RecoveryError(#[from] SphinxError), -impl From for ReplySurbError { - fn from(err: SurbEncryptionKeyError) -> Self { - ReplySurbError::InvalidEncryptionKeyData(err) - } + #[error("failed to recover reply SURB encryption key from bytes: {0}")] + InvalidEncryptionKeyData(#[from] SurbEncryptionKeyError), } #[derive(Debug)] @@ -157,6 +135,8 @@ impl ReplySurb { } pub fn from_bytes(bytes: &[u8]) -> Result { + // TODO: introduce bound checks to guard us against out of bound reads + let encryption_key = SurbEncryptionKey::try_from_bytes(&bytes[..SurbEncryptionKeySize::USIZE])?; @@ -189,21 +169,22 @@ impl ReplySurb { // - surb-ack // - key digest // - encrypted plaintext with padding to constant length - pub fn apply_surb( + pub fn apply_surb>( self, - message: &[u8], + message: M, packet_size: Option, ) -> Result<(SphinxPacket, NymNodeRoutingAddress), ReplySurbError> { let packet_size = packet_size.unwrap_or_default(); - if message.len() != packet_size.plaintext_size() { + let message_bytes = message.as_ref(); + if message_bytes.len() != packet_size.plaintext_size() { return Err(ReplySurbError::UnpaddedMessageError); } // this can realistically only fail on too long messages and we just checked for that let (packet, first_hop) = self .surb - .use_surb(message, packet_size.payload_size()) + .use_surb(message_bytes, packet_size.payload_size()) .expect("this error indicates inconsistent message length checking - it shouldn't have happened!"); let first_hop_address = NymNodeRoutingAddress::try_from(first_hop).unwrap(); diff --git a/common/nymsphinx/anonymous-replies/src/requests.rs b/common/nymsphinx/anonymous-replies/src/requests.rs new file mode 100644 index 0000000000..686f65aa79 --- /dev/null +++ b/common/nymsphinx/anonymous-replies/src/requests.rs @@ -0,0 +1,476 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ReplySurb, ReplySurbError}; +use nymsphinx_addressing::clients::{Recipient, RecipientFormattingError}; +use rand::{CryptoRng, RngCore}; +use std::fmt::{Display, Formatter}; +use std::mem; +use thiserror::Error; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +pub const SENDER_TAG_SIZE: usize = 16; + +#[derive(Debug, Error)] +pub enum InvalidAnonymousSenderTagRepresentation { + #[error("Failed to decode the base58-encoded string - {0}")] + MalformedString(#[from] bs58::decode::Error), + + #[error( + "Decoded AnonymousSenderTag has invalid length. Expected {expected}, but got {received}" + )] + InvalidLength { received: usize, expected: usize }, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] +pub struct AnonymousSenderTag([u8; SENDER_TAG_SIZE]); + +impl From<[u8; SENDER_TAG_SIZE]> for AnonymousSenderTag { + fn from(bytes: [u8; SENDER_TAG_SIZE]) -> Self { + AnonymousSenderTag(bytes) + } +} + +impl Display for AnonymousSenderTag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_base58_string()) + } +} + +impl AnonymousSenderTag { + pub fn new_random(rng: &mut R) -> Self { + let mut bytes = [0u8; SENDER_TAG_SIZE]; + rng.fill_bytes(&mut bytes); + AnonymousSenderTag(bytes) + } + + pub fn to_bytes(&self) -> [u8; SENDER_TAG_SIZE] { + self.0 + } + + pub fn from_bytes(bytes: [u8; SENDER_TAG_SIZE]) -> Self { + AnonymousSenderTag(bytes) + } + + pub fn to_base58_string(self) -> String { + bs58::encode(self.to_bytes()).into_string() + } + + pub fn try_from_base58_string>( + val: I, + ) -> Result { + let bytes = bs58::decode(val).into_vec()?; + if bytes.len() != SENDER_TAG_SIZE { + return Err(InvalidAnonymousSenderTagRepresentation::InvalidLength { + received: bytes.len(), + expected: SENDER_TAG_SIZE, + }); + } + + // the unwrap here is fine as we just asserted the bytes are of exactly SENDER_TAG_SIZE length + let byte_array: [u8; SENDER_TAG_SIZE] = bytes.try_into().unwrap(); + Ok(AnonymousSenderTag::from_bytes(byte_array)) + } +} + +#[derive(Debug, Error)] +pub enum InvalidReplyRequestError { + #[error("Did not provide sufficient number of bytes to deserialize a valid request")] + RequestTooShortToDeserialize, + + #[error("{received} is not a valid content tag for a repliable message")] + InvalidRepliableContentTag { received: u8 }, + + #[error("{received} is not a valid content tag for a reply message")] + InvalidReplyContentTag { received: u8 }, + + #[error("failed to deserialize recipient information - {0}")] + MalformedRecipient(#[from] RecipientFormattingError), + + #[error("failed to deserialize replySURB - {0}")] + MalformedReplySurb(#[from] ReplySurbError), +} + +#[derive(Debug)] +pub struct RepliableMessage { + pub sender_tag: AnonymousSenderTag, + pub content: RepliableMessageContent, +} + +impl Display for RepliableMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.content { + RepliableMessageContent::Data { + message, + reply_surbs, + } => write!( + f, + "repliable {:.2} kiB data message with {} reply surbs attached from {}", + message.len() as f64 / 1024.0, + reply_surbs.len(), + self.sender_tag, + ), + RepliableMessageContent::AdditionalSurbs { reply_surbs } => write!( + f, + "repliable additional surbs message ({} reply surbs attached) from {}", + reply_surbs.len(), + self.sender_tag, + ), + RepliableMessageContent::Heartbeat { + additional_reply_surbs, + } => { + write!( + f, + "repliable heartbeat message ({} reply surbs attached) from {}", + additional_reply_surbs.len(), + self.sender_tag, + ) + } + } + } +} + +impl RepliableMessage { + pub fn new_data( + data: Vec, + sender_tag: AnonymousSenderTag, + reply_surbs: Vec, + ) -> Self { + RepliableMessage { + sender_tag, + content: RepliableMessageContent::Data { + message: data, + reply_surbs, + }, + } + } + + pub fn new_additional_surbs( + sender_tag: AnonymousSenderTag, + reply_surbs: Vec, + ) -> Self { + RepliableMessage { + sender_tag, + content: RepliableMessageContent::AdditionalSurbs { reply_surbs }, + } + } + + pub fn into_bytes(self) -> Vec { + let content_tag = self.content.tag(); + + self.sender_tag + .to_bytes() + .into_iter() + .chain(std::iter::once(content_tag as u8)) + .chain(self.content.into_bytes()) + .collect() + } + + pub fn try_from_bytes( + bytes: &[u8], + num_mix_hops: u8, + ) -> Result { + if bytes.len() < SENDER_TAG_SIZE + 1 { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + let sender_tag = + AnonymousSenderTag::from_bytes(bytes[..SENDER_TAG_SIZE].try_into().unwrap()); + let content_tag = RepliableMessageContentTag::try_from(bytes[SENDER_TAG_SIZE])?; + + let content = RepliableMessageContent::try_from_bytes( + &bytes[SENDER_TAG_SIZE + 1..], + num_mix_hops, + content_tag, + )?; + + Ok(RepliableMessage { + sender_tag, + content, + }) + } +} + +// this recovery code is shared between all variants containing reply surbs +fn recover_reply_surbs( + bytes: &[u8], + num_mix_hops: u8, +) -> Result<(Vec, usize), InvalidReplyRequestError> { + let mut consumed = mem::size_of::(); + if bytes.len() < consumed { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + let num_surbs = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); + let surb_size = ReplySurb::serialized_len(num_mix_hops); + if bytes[consumed..].len() < num_surbs as usize * surb_size { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + + let mut reply_surbs = Vec::with_capacity(num_surbs as usize); + for _ in 0..num_surbs as usize { + let surb_bytes = &bytes[consumed..consumed + surb_size]; + let reply_surb = ReplySurb::from_bytes(surb_bytes)?; + reply_surbs.push(reply_surb); + + consumed += surb_size; + } + + Ok((reply_surbs, consumed)) +} + +#[repr(u8)] +enum RepliableMessageContentTag { + Data = 0, + AdditionalSurbs = 1, + Heartbeat = 2, +} + +impl TryFrom for RepliableMessageContentTag { + type Error = InvalidReplyRequestError; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (RepliableMessageContentTag::Data as u8) => Ok(Self::Data), + _ if value == (RepliableMessageContentTag::AdditionalSurbs as u8) => { + Ok(Self::AdditionalSurbs) + } + _ if value == (RepliableMessageContentTag::Heartbeat as u8) => Ok(Self::Heartbeat), + val => Err(InvalidReplyRequestError::InvalidRepliableContentTag { received: val }), + } + } +} + +// sent by original sender that initialised the communication that knows address of the remote +#[derive(Debug)] +pub enum RepliableMessageContent { + Data { + message: Vec, + reply_surbs: Vec, + }, + AdditionalSurbs { + reply_surbs: Vec, + }, + Heartbeat { + additional_reply_surbs: Vec, + }, +} + +impl RepliableMessageContent { + pub fn into_bytes(self) -> Vec { + match self { + RepliableMessageContent::Data { + message, + reply_surbs, + } => { + let num_surbs = reply_surbs.len() as u32; + + num_surbs + .to_be_bytes() + .into_iter() + .chain(reply_surbs.into_iter().flat_map(|s| s.to_bytes())) + .chain(message.into_iter()) + .collect() + } + RepliableMessageContent::AdditionalSurbs { reply_surbs } => { + let num_surbs = reply_surbs.len() as u32; + + num_surbs + .to_be_bytes() + .into_iter() + .chain(reply_surbs.into_iter().flat_map(|s| s.to_bytes())) + .collect() + } + RepliableMessageContent::Heartbeat { + additional_reply_surbs, + } => { + let num_surbs = additional_reply_surbs.len() as u32; + + num_surbs + .to_be_bytes() + .into_iter() + .chain( + additional_reply_surbs + .into_iter() + .flat_map(|s| s.to_bytes()), + ) + .collect() + } + } + } + + fn try_from_bytes( + bytes: &[u8], + num_mix_hops: u8, + tag: RepliableMessageContentTag, + ) -> Result { + if bytes.is_empty() { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + + let (reply_surbs, n) = recover_reply_surbs(bytes, num_mix_hops)?; + + match tag { + RepliableMessageContentTag::Data => Ok(RepliableMessageContent::Data { + message: bytes[n..].to_vec(), + reply_surbs, + }), + RepliableMessageContentTag::AdditionalSurbs => { + Ok(RepliableMessageContent::AdditionalSurbs { reply_surbs }) + } + RepliableMessageContentTag::Heartbeat => Ok(RepliableMessageContent::Heartbeat { + additional_reply_surbs: reply_surbs, + }), + } + } + + fn tag(&self) -> RepliableMessageContentTag { + match self { + RepliableMessageContent::Data { .. } => RepliableMessageContentTag::Data, + RepliableMessageContent::AdditionalSurbs { .. } => { + RepliableMessageContentTag::AdditionalSurbs + } + RepliableMessageContent::Heartbeat { .. } => RepliableMessageContentTag::Heartbeat, + } + } +} + +// sent by the remote party who does **NOT** know the original sender's identity +#[derive(Debug)] +pub struct ReplyMessage { + pub content: ReplyMessageContent, +} + +impl Display for ReplyMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.content { + ReplyMessageContent::Data { message } => write!( + f, + "{:.2} kiB reply data message", + message.len() as f64 / 1024.0 + ), + ReplyMessageContent::SurbRequest { recipient, amount } => write!( + f, + "request for {amount} additional reply SURBs from {recipient}", + ), + } + } +} + +impl ReplyMessage { + pub fn new_data_message(message: Vec) -> Self { + ReplyMessage { + content: ReplyMessageContent::Data { message }, + } + } + + pub fn new_surb_request_message(recipient: Recipient, amount: u32) -> Self { + ReplyMessage { + content: ReplyMessageContent::SurbRequest { + recipient: Box::new(recipient), + amount, + }, + } + } + + pub fn into_bytes(self) -> Vec { + let content_tag = self.content.tag(); + + std::iter::once(content_tag as u8) + .chain(self.content.into_bytes()) + .collect() + } + + pub fn try_from_bytes(bytes: &[u8]) -> Result { + if bytes.is_empty() { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + let tag = ReplyMessageContentTag::try_from(bytes[0])?; + let content = ReplyMessageContent::try_from_bytes(&bytes[1..], tag)?; + + Ok(ReplyMessage { content }) + } +} + +#[repr(u8)] +enum ReplyMessageContentTag { + Data = 0, + SurbRequest = 1, +} + +impl TryFrom for ReplyMessageContentTag { + type Error = InvalidReplyRequestError; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (ReplyMessageContentTag::Data as u8) => Ok(Self::Data), + _ if value == (ReplyMessageContentTag::SurbRequest as u8) => Ok(Self::SurbRequest), + val => Err(InvalidReplyRequestError::InvalidReplyContentTag { received: val }), + } + } +} + +#[derive(Debug)] +pub enum ReplyMessageContent { + // TODO: later allow to request surbs whilst sending data + Data { + message: Vec, + }, + SurbRequest { + recipient: Box, + amount: u32, + }, +} + +impl ReplyMessageContent { + pub fn into_bytes(self) -> Vec { + match self { + ReplyMessageContent::Data { message } => message, + ReplyMessageContent::SurbRequest { recipient, amount } => recipient + .to_bytes() + .into_iter() + .chain(amount.to_be_bytes().into_iter()) + .collect(), + } + } + + fn try_from_bytes( + bytes: &[u8], + tag: ReplyMessageContentTag, + ) -> Result { + if bytes.is_empty() { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + + match tag { + ReplyMessageContentTag::Data => Ok(ReplyMessageContent::Data { + message: bytes.to_vec(), + }), + ReplyMessageContentTag::SurbRequest => { + if bytes.len() != Recipient::LEN + std::mem::size_of::() { + return Err(InvalidReplyRequestError::RequestTooShortToDeserialize); + } + let mut recipient_bytes = [0u8; Recipient::LEN]; + recipient_bytes.copy_from_slice(&bytes[..Recipient::LEN]); + + Ok(ReplyMessageContent::SurbRequest { + recipient: Box::new(Recipient::try_from_bytes(recipient_bytes)?), + amount: u32::from_be_bytes([ + bytes[Recipient::LEN], + bytes[Recipient::LEN + 1], + bytes[Recipient::LEN + 2], + bytes[Recipient::LEN + 3], + ]), + }) + } + } + } + + fn tag(&self) -> ReplyMessageContentTag { + match self { + ReplyMessageContent::Data { .. } => ReplyMessageContentTag::Data, + ReplyMessageContent::SurbRequest { .. } => ReplyMessageContentTag::SurbRequest, + } + } +} diff --git a/common/nymsphinx/chunking/Cargo.toml b/common/nymsphinx/chunking/Cargo.toml index 5f47ab5358..3d6bacab61 100644 --- a/common/nymsphinx/chunking/Cargo.toml +++ b/common/nymsphinx/chunking/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] log = "0.4.8" rand = { version = "0.7.3", features = ["wasm-bindgen"] } +thiserror = "1.0.37" nymsphinx-addressing = { path = "../addressing" } nymsphinx-params = { path = "../params" } diff --git a/common/nymsphinx/chunking/src/fragment.rs b/common/nymsphinx/chunking/src/fragment.rs index 58c1baa78a..8d909982eb 100644 --- a/common/nymsphinx/chunking/src/fragment.rs +++ b/common/nymsphinx/chunking/src/fragment.rs @@ -1,12 +1,10 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::set::generate_set_id; use crate::ChunkingError; use nymsphinx_params::{SerializedFragmentIdentifier, FRAG_ID_LEN}; -use rand::Rng; use std::convert::TryInto; -use std::fmt::{self, Formatter}; +use std::fmt::{self, Debug, Formatter}; // Personal reflection: In hindsight I've spent too much time on relatively too little // gain here, as even though I might have saved couple of bytes per packet, the gain @@ -60,7 +58,7 @@ pub const COVER_FRAG_ID: FragmentIdentifier = FragmentIdentifier { /// and u8 position of the `Fragment` in the set. // TODO: this should really be redesigned, especially how cover and reply messages are really // "abusing" this. They should work with it natively instead. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct FragmentIdentifier { set_id: i32, fragment_position: u8, @@ -77,20 +75,6 @@ impl fmt::Display for FragmentIdentifier { } impl FragmentIdentifier { - // I really dislike how 'hacky' this function seems - // refer to: https://github.com/nymtech/nym/issues/294 for further discussion - pub fn new_reply(rng: &mut R) -> Self { - FragmentIdentifier { - set_id: generate_set_id(rng), - fragment_position: 0, - } - } - - // and this one - pub fn is_reply(self) -> bool { - self.set_id > 0 && self.fragment_position == 0 - } - pub fn to_bytes(self) -> SerializedFragmentIdentifier { debug_assert_eq!(FRAG_ID_LEN, 5); @@ -110,7 +94,7 @@ impl FragmentIdentifier { let set_id = i32::from_be_bytes([b[0], b[1], b[2], b[3]]); // set_id == 0 is valid for COVER_FRAG_ID and replies if set_id < 0 { - return Err(ChunkingError::MalformedFragmentIdentifier); + return Err(ChunkingError::MalformedFragmentIdentifier { received: set_id }); } Ok(FragmentIdentifier { @@ -124,12 +108,22 @@ impl FragmentIdentifier { /// Each `Fragment` after being marshaled is guaranteed to fit into a single sphinx packet. /// The `Fragment` itself consists of part, or whole of, message to be sent as well as additional /// header used to reconstruct the message after being received. -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Clone)] pub struct Fragment { header: FragmentHeader, payload: Vec, } +// manual implementation to hide detailed payload that we don't care about +impl Debug for Fragment { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Fragment") + .field("header", &self.header) + .field("payload length", &self.payload.len()) + .finish() + } +} + impl Fragment { /// Tries to encapsulate provided payload slice and metadata into a `Fragment`. /// It can fail if payload would not fully fit in a single `Fragment` or some of the metadata @@ -153,24 +147,42 @@ impl Fragment { // check for whether payload has expected length, which depend on whether fragment is linked // and if it's the only one or the last one in the set (then lower bound is removed) + let max_linked_len = linked_fragment_payload_max_len(max_plaintext_size); + let max_unlinked_len = unlinked_fragment_payload_max_len(max_plaintext_size); + if previous_fragments_set_id.is_some() { if total_fragments > 1 { - if payload.len() != linked_fragment_payload_max_len(max_plaintext_size) { - return Err(ChunkingError::InvalidPayloadLengthError); + if payload.len() != max_linked_len { + return Err(ChunkingError::InvalidPayloadLengthError { + received: payload.len(), + expected: max_linked_len, + }); } - } else if payload.len() > linked_fragment_payload_max_len(max_plaintext_size) { - return Err(ChunkingError::InvalidPayloadLengthError); + } else if payload.len() > max_linked_len { + return Err(ChunkingError::TooLongPayloadLengthError { + received: payload.len(), + expected_at_most: max_linked_len, + }); } } else if next_fragments_set_id.is_some() { - if payload.len() != linked_fragment_payload_max_len(max_plaintext_size) { - return Err(ChunkingError::InvalidPayloadLengthError); + if payload.len() != max_linked_len { + return Err(ChunkingError::InvalidPayloadLengthError { + received: payload.len(), + expected: max_linked_len, + }); } } else if total_fragments != current_fragment { - if payload.len() != unlinked_fragment_payload_max_len(max_plaintext_size) { - return Err(ChunkingError::InvalidPayloadLengthError); + if payload.len() != max_unlinked_len { + return Err(ChunkingError::InvalidPayloadLengthError { + received: payload.len(), + expected: max_unlinked_len, + }); } - } else if payload.len() > unlinked_fragment_payload_max_len(max_plaintext_size) { - return Err(ChunkingError::InvalidPayloadLengthError); + } else if payload.len() > max_unlinked_len { + return Err(ChunkingError::TooLongPayloadLengthError { + received: payload.len(), + expected_at_most: max_unlinked_len, + }); } Ok(Fragment { @@ -196,6 +208,11 @@ impl Fragment { } } + /// Gets the size of payload contained in this `Fragment`. + pub fn payload_size(&self) -> usize { + self.payload.len() + } + /// Extracts id of this `Fragment`. pub fn id(&self) -> i32 { self.header.id @@ -347,7 +364,10 @@ impl FragmentHeader { fn try_from_bytes(b: &[u8]) -> Result<(Self, usize), ChunkingError> { // header needs to be at least 7 bytes long if b.len() < UNLINKED_FRAGMENTED_HEADER_LEN { - return Err(ChunkingError::TooShortFragmentData); + return Err(ChunkingError::TooShortFragmentHeader { + received: b.len(), + expected: UNLINKED_FRAGMENTED_HEADER_LEN, + }); } let frag_id = i32::from_be_bytes(b[0..4].try_into().unwrap()); // sanity check for the fragmentation flag @@ -370,7 +390,10 @@ impl FragmentHeader { let read_bytes = if b[6] != 0 { // there's linking ID supposedly attached, make sure we have enough bytes to parse if b.len() < LINKED_FRAGMENTED_HEADER_LEN { - return Err(ChunkingError::TooShortFragmentData); + return Err(ChunkingError::TooShortFragmentHeader { + received: b.len(), + expected: LINKED_FRAGMENTED_HEADER_LEN, + }); } let flagged_linked_id = i32::from_be_bytes(b[6..10].try_into().unwrap()); diff --git a/common/nymsphinx/chunking/src/lib.rs b/common/nymsphinx/chunking/src/lib.rs index 62a246955b..60316edb15 100644 --- a/common/nymsphinx/chunking/src/lib.rs +++ b/common/nymsphinx/chunking/src/lib.rs @@ -3,6 +3,7 @@ use crate::fragment::{linked_fragment_payload_max_len, unlinked_fragment_payload_max_len}; pub use set::split_into_sets; +use thiserror::Error; // Future consideration: currently in a lot of places, the payloads have randomised content // which is not a perfect testing strategy as it might not detect some edge cases I never would @@ -45,18 +46,26 @@ pub mod set; /// Both of those concepts as well as their structures, i.e. `Set` and `Fragment` /// are further explained in the respective files. -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Error)] pub enum ChunkingError { - InvalidPayloadLengthError, - TooBigMessageToSplit, + #[error("Received payload is too long. Got {received}, expected {expected}")] + InvalidPayloadLengthError { received: usize, expected: usize }, + + #[error("Received payload is too long. Got {received}, expected at most {expected_at_most}")] + TooLongPayloadLengthError { + received: usize, + expected_at_most: usize, + }, + + // this should really be split into multiple variants to provide better error information + #[error("Provided header was malformed or contained self-contradicting fields")] MalformedHeaderError, - NoValidProvidersError, - NoValidRoutesAvailableError, - InvalidTopologyError, - TooShortFragmentData, - MalformedFragmentData, - UnexpectedFragmentCount, - MalformedFragmentIdentifier, + + #[error("Received too few bytes to deserialize fragment header. Got {received}, expected {expected}")] + TooShortFragmentHeader { received: usize, expected: usize }, + + #[error("Received fragment identifier ({received}) is not a valid value!")] + MalformedFragmentIdentifier { received: i32 }, } /// Returns number of fragments the message will be split to as well as number of available @@ -94,7 +103,7 @@ pub fn number_of_required_fragments( // we know for sure that all fragments in all but last set are definitely full // (last one has single 'linked' fragment) - let without_last = (n - 1) * (u8::max_value() as usize); + let without_last = (n - 1) * (u8::MAX as usize); let linked_fragments_without_last = (2 * n - 2) - 1; let unlinked_fragments_without_last = without_last - linked_fragments_without_last; diff --git a/common/nymsphinx/cover/Cargo.toml b/common/nymsphinx/cover/Cargo.toml index 6fd629ade7..064ebf1298 100644 --- a/common/nymsphinx/cover/Cargo.toml +++ b/common/nymsphinx/cover/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] rand = { version = "0.7.3", features = ["wasm-bindgen"] } +thiserror = "1.0.37" crypto = { path = "../../crypto" } nymsphinx-acknowledgements = { path = "../acknowledgements" } diff --git a/common/nymsphinx/cover/src/lib.rs b/common/nymsphinx/cover/src/lib.rs index aaaff5a9b5..2ce9454d5c 100644 --- a/common/nymsphinx/cover/src/lib.rs +++ b/common/nymsphinx/cover/src/lib.rs @@ -6,7 +6,7 @@ use crypto::symmetric::stream_cipher; use nymsphinx_acknowledgements::surb_ack::SurbAck; use nymsphinx_acknowledgements::AckKey; use nymsphinx_addressing::clients::Recipient; -use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError}; +use nymsphinx_addressing::nodes::NymNodeRoutingAddress; use nymsphinx_chunking::fragment::COVER_FRAG_ID; use nymsphinx_forwarding::packet::MixPacket; use nymsphinx_params::packet_sizes::PacketSize; @@ -18,35 +18,18 @@ use nymsphinx_types::{delays, Error as SphinxError}; use rand::{CryptoRng, RngCore}; use std::convert::TryFrom; use std::time; +use thiserror::Error; use topology::{NymTopology, NymTopologyError}; pub const LOOP_COVER_MESSAGE_PAYLOAD: &[u8] = b"The cake is a lie!"; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum CoverMessageError { - NoValidProvidersError, - InvalidTopologyError, - SphinxError(SphinxError), - InvalidFirstMixAddress, -} + #[error("Could not construct cover message due to invalid topology - {0}")] + InvalidTopologyError(#[from] NymTopologyError), -impl From for CoverMessageError { - fn from(err: SphinxError) -> Self { - CoverMessageError::SphinxError(err) - } -} - -impl From for CoverMessageError { - fn from(_: NymNodeRoutingAddressError) -> Self { - use CoverMessageError::*; - InvalidFirstMixAddress - } -} - -impl From for CoverMessageError { - fn from(_: NymTopologyError) -> Self { - CoverMessageError::InvalidTopologyError - } + #[error("Could not construct a valid sphinx packet - {0}")] + SphinxError(#[from] SphinxError), } pub fn generate_loop_cover_surb_ack( diff --git a/common/nymsphinx/framing/Cargo.toml b/common/nymsphinx/framing/Cargo.toml index 6921a8cf69..fc46ece44a 100644 --- a/common/nymsphinx/framing/Cargo.toml +++ b/common/nymsphinx/framing/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] bytes = "1.0" tokio-util = { version = "0.7.3", features = ["codec"] } +thiserror = "1.0.37" nymsphinx-types = { path = "../types" } nymsphinx-params = { path = "../params" } diff --git a/common/nymsphinx/framing/src/codec.rs b/common/nymsphinx/framing/src/codec.rs index 53bd77c3b7..f3986d2ece 100644 --- a/common/nymsphinx/framing/src/codec.rs +++ b/common/nymsphinx/framing/src/codec.rs @@ -1,57 +1,48 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::packet::{FramedSphinxPacket, Header}; use bytes::{Buf, BufMut, BytesMut}; use nymsphinx_params::packet_modes::InvalidPacketMode; use nymsphinx_params::packet_sizes::{InvalidPacketSize, PacketSize}; +use nymsphinx_types::Error as SphinxError; use nymsphinx_types::SphinxPacket; use std::io; +use thiserror::Error; use tokio_util::codec::{Decoder, Encoder}; -#[derive(Debug)] +#[derive(Error, Debug)] pub enum SphinxCodecError { - InvalidPacketSize, - InvalidPacketMode, - MalformedSphinxPacket, - IoError(io::Error), -} + #[error("the packet size information was malformed - {0}")] + InvalidPacketSize(#[from] InvalidPacketSize), -impl From for SphinxCodecError { - fn from(err: io::Error) -> Self { - SphinxCodecError::IoError(err) - } + #[error("the packet mode information was malformed - {0}")] + InvalidPacketMode(#[from] InvalidPacketMode), + + #[error("the actual sphinx packet was malformed - {0}")] + MalformedSphinxPacket(#[from] SphinxError), + + #[error("encountered an IO error - {0}")] + IoError(#[from] io::Error), } impl From for io::Error { fn from(err: SphinxCodecError) -> Self { match err { - SphinxCodecError::InvalidPacketSize => { - io::Error::new(io::ErrorKind::InvalidInput, "invalid packet size") + SphinxCodecError::InvalidPacketSize(source) => { + io::Error::new(io::ErrorKind::InvalidInput, source) } - SphinxCodecError::InvalidPacketMode => { - io::Error::new(io::ErrorKind::InvalidInput, "invalid packet mode") + SphinxCodecError::InvalidPacketMode(source) => { + io::Error::new(io::ErrorKind::InvalidInput, source) } - SphinxCodecError::MalformedSphinxPacket => { - io::Error::new(io::ErrorKind::InvalidData, "malformed packet") + SphinxCodecError::MalformedSphinxPacket(source) => { + io::Error::new(io::ErrorKind::InvalidData, source) } SphinxCodecError::IoError(err) => err, } } } -impl From for SphinxCodecError { - fn from(_: InvalidPacketSize) -> Self { - SphinxCodecError::InvalidPacketSize - } -} - -impl From for SphinxCodecError { - fn from(_: InvalidPacketMode) -> Self { - SphinxCodecError::InvalidPacketMode - } -} - // TODO: in the future it could be extended to have state containing symmetric encryption key // so that all data could be encrypted easily (alternatively we could just slap TLS) pub struct SphinxCodec; @@ -97,17 +88,11 @@ impl Decoder for SphinxCodec { // advance buffer past the header - at this point we have enough bytes src.advance(header.size()); let sphinx_packet_bytes = src.split_to(sphinx_packet_size); - let sphinx_packet = match SphinxPacket::from_bytes(&sphinx_packet_bytes) { - Ok(sphinx_packet) => sphinx_packet, - // here it could be debatable whether stream is corrupt or not, - // but let's go with the safer approach and assume it is. - Err(_) => return Err(SphinxCodecError::MalformedSphinxPacket), - }; - let nymsphinx_packet = FramedSphinxPacket { - header, - packet: sphinx_packet, - }; + // here it could be debatable whether stream is corrupt or not, + // but let's go with the safer approach and assume it is. + let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?; + let nymsphinx_packet = FramedSphinxPacket { header, packet }; // As per docs: // Before returning from the function, implementations should ensure that the buffer @@ -178,7 +163,7 @@ mod packet_encoding { ]; SphinxPacketBuilder::new() .with_payload_size(size.payload_size()) - .build_packet(b"foomp".to_vec(), &route, &destination, &delays) + .build_packet(b"foomp", &route, &destination, &delays) .unwrap() } diff --git a/common/nymsphinx/params/Cargo.toml b/common/nymsphinx/params/Cargo.toml index a06f897f22..ebca914742 100644 --- a/common/nymsphinx/params/Cargo.toml +++ b/common/nymsphinx/params/Cargo.toml @@ -7,5 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +thiserror = "1.0.37" + crypto = { path = "../../crypto", features = ["hashing", "symmetric"] } nymsphinx-types = { path = "../types" } diff --git a/common/nymsphinx/params/src/packet_modes.rs b/common/nymsphinx/params/src/packet_modes.rs index 819b32b3af..3fb7ac6358 100644 --- a/common/nymsphinx/params/src/packet_modes.rs +++ b/common/nymsphinx/params/src/packet_modes.rs @@ -1,10 +1,14 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use std::convert::TryFrom; +use thiserror::Error; -#[derive(Debug)] -pub struct InvalidPacketMode; +#[derive(Error, Debug)] +#[error("{received} is not a valid packet mode tag")] +pub struct InvalidPacketMode { + received: u8, +} #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -31,11 +35,11 @@ impl PacketMode { impl TryFrom for PacketMode { type Error = InvalidPacketMode; - fn try_from(value: u8) -> std::result::Result { + fn try_from(value: u8) -> Result { match value { _ if value == (PacketMode::Mix as u8) => Ok(Self::Mix), _ if value == (PacketMode::Vpn as u8) => Ok(Self::Vpn), - _ => Err(InvalidPacketMode), + v => Err(InvalidPacketMode { received: v }), } } } diff --git a/common/nymsphinx/params/src/packet_sizes.rs b/common/nymsphinx/params/src/packet_sizes.rs index aa573ffe11..ef9976bd92 100644 --- a/common/nymsphinx/params/src/packet_sizes.rs +++ b/common/nymsphinx/params/src/packet_sizes.rs @@ -6,6 +6,7 @@ use nymsphinx_types::header::HEADER_SIZE; use nymsphinx_types::PAYLOAD_OVERHEAD_SIZE; use std::convert::TryFrom; use std::str::FromStr; +use thiserror::Error; // it's up to the smart people to figure those values out : ) const REGULAR_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 2 * 1024; @@ -20,11 +21,17 @@ const EXTENDED_PACKET_SIZE_8: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 8 * const EXTENDED_PACKET_SIZE_16: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 16 * 1024; const EXTENDED_PACKET_SIZE_32: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 32 * 1024; -#[derive(Debug)] -pub struct InvalidPacketSize; +#[derive(Debug, Error)] +pub enum InvalidPacketSize { + #[error("{received} is not a valid packet size tag")] + UnknownPacketTag { received: u8 }, -#[derive(Debug)] -pub struct InvalidExtendedPacketSize; + #[error("{received} is not a valid extended packet size variant")] + UnknownExtendedPacketVariant { received: String }, + + #[error("{received} does not correspond with any known packet size")] + UnknownPacketSize { received: usize }, +} #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -55,7 +62,9 @@ impl FromStr for PacketSize { "extended8" => Ok(Self::ExtendedPacket8), "extended16" => Ok(Self::ExtendedPacket16), "extended32" => Ok(Self::ExtendedPacket32), - _ => Err(InvalidPacketSize), + s => Err(InvalidPacketSize::UnknownExtendedPacketVariant { + received: s.to_string(), + }), } } } @@ -63,14 +72,14 @@ impl FromStr for PacketSize { impl TryFrom for PacketSize { type Error = InvalidPacketSize; - fn try_from(value: u8) -> std::result::Result { + fn try_from(value: u8) -> Result { match value { _ if value == (PacketSize::RegularPacket as u8) => Ok(Self::RegularPacket), _ if value == (PacketSize::AckPacket as u8) => Ok(Self::AckPacket), _ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8), _ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16), _ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32), - _ => Err(InvalidPacketSize), + v => Err(InvalidPacketSize::UnknownPacketTag { received: v }), } } } @@ -94,7 +103,7 @@ impl PacketSize { self.size() - HEADER_SIZE } - pub fn get_type(size: usize) -> std::result::Result { + pub fn get_type(size: usize) -> Result { if PacketSize::RegularPacket.size() == size { Ok(PacketSize::RegularPacket) } else if PacketSize::AckPacket.size() == size { @@ -106,7 +115,7 @@ impl PacketSize { } else if PacketSize::ExtendedPacket32.size() == size { Ok(PacketSize::ExtendedPacket32) } else { - Err(InvalidPacketSize) + Err(InvalidPacketSize::UnknownPacketSize { received: size }) } } diff --git a/common/nymsphinx/src/lib.rs b/common/nymsphinx/src/lib.rs index e3e7c51fac..bbc69c2682 100644 --- a/common/nymsphinx/src/lib.rs +++ b/common/nymsphinx/src/lib.rs @@ -1,6 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +pub mod message; pub mod preparer; pub mod receiver; pub mod utils; @@ -16,3 +17,6 @@ pub use nymsphinx_forwarding as forwarding; pub use nymsphinx_framing as framing; pub use nymsphinx_params as params; pub use nymsphinx_types::*; + +// TEMP UNTIL FURTHER REFACTORING +pub use preparer::payload::NymsphinxPayloadBuilder; diff --git a/common/nymsphinx/src/message.rs b/common/nymsphinx/src/message.rs new file mode 100644 index 0000000000..f9b32daa35 --- /dev/null +++ b/common/nymsphinx/src/message.rs @@ -0,0 +1,238 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::chunking; +use crypto::asymmetric::encryption; +use crypto::Digest; +use nymsphinx_addressing::clients::Recipient; +use nymsphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN; +use nymsphinx_anonymous_replies::requests::{ + InvalidReplyRequestError, RepliableMessage, RepliableMessageContent, ReplyMessage, + ReplyMessageContent, +}; +use nymsphinx_chunking::fragment::Fragment; +use nymsphinx_params::{PacketSize, ReplySurbKeyDigestAlgorithm}; +use rand::Rng; +use std::fmt::{Display, Formatter}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum NymMessageError { + #[error("{received} is not a valid type tag for a NymMessage")] + InvalidMessageType { received: u8 }, + + #[error(transparent)] + InvalidReplyRequest(#[from] InvalidReplyRequestError), + + #[error("The received message seems to have incorrect zero padding (no '1' byte found)")] + InvalidMessagePadding, + + #[error("Received empty message for deserialization")] + EmptyMessage, +} + +#[repr(u8)] +enum NymMessageType { + Plain = 0, + Repliable = 1, + Reply = 2, +} + +impl TryFrom for NymMessageType { + type Error = NymMessageError; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (NymMessageType::Plain as u8) => Ok(Self::Plain), + _ if value == (NymMessageType::Repliable as u8) => Ok(Self::Repliable), + _ if value == (NymMessageType::Reply as u8) => Ok(Self::Reply), + val => Err(NymMessageError::InvalidMessageType { received: val }), + } + } +} + +pub type PlainMessage = Vec; + +#[derive(Debug)] +pub enum NymMessage { + Plain(PlainMessage), + Repliable(RepliableMessage), + Reply(ReplyMessage), +} + +impl Display for NymMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + NymMessage::Plain(plain_message) => write!( + f, + "plain {:.2} kiB message", + plain_message.len() as f64 / 1024.0 + ), + NymMessage::Repliable(repliable_message) => repliable_message.fmt(f), + NymMessage::Reply(reply_message) => reply_message.fmt(f), + } + } +} + +impl NymMessage { + pub fn new_additional_surbs_request(recipient: Recipient, amount: u32) -> Self { + NymMessage::Reply(ReplyMessage { + content: ReplyMessageContent::SurbRequest { + recipient: Box::new(recipient), + amount, + }, + }) + } + + pub fn new_plain(msg: Vec) -> Self { + NymMessage::Plain(msg) + } + + pub fn new_repliable(msg: RepliableMessage) -> Self { + NymMessage::Repliable(msg) + } + + pub fn new_reply(msg: ReplyMessage) -> Self { + NymMessage::Reply(msg) + } + + pub fn is_reply_surb_request(&self) -> bool { + match self { + NymMessage::Reply(reply_msg) => { + matches!(reply_msg.content, ReplyMessageContent::SurbRequest { .. }) + } + _ => false, + } + } + + pub fn into_inner_data(self) -> Vec { + match self { + NymMessage::Plain(data) => data, + NymMessage::Repliable(repliable) => match repliable.content { + RepliableMessageContent::Data { message, .. } => message, + _ => Vec::new(), + }, + NymMessage::Reply(reply) => match reply.content { + ReplyMessageContent::Data { message } => message, + _ => Vec::new(), + }, + } + } + + fn typ(&self) -> NymMessageType { + match self { + NymMessage::Plain(_) => NymMessageType::Plain, + NymMessage::Repliable(_) => NymMessageType::Repliable, + NymMessage::Reply(_) => NymMessageType::Reply, + } + } + + fn inner_bytes(self) -> Vec { + match self { + NymMessage::Plain(msg) => msg, + NymMessage::Repliable(msg) => msg.into_bytes(), + NymMessage::Reply(msg) => msg.into_bytes(), + } + } + + // the message is in the format of: + // typ || msg + fn into_bytes(self) -> Vec { + let typ = self.typ(); + + std::iter::once(typ as u8) + .chain(self.inner_bytes()) + .collect() + } + + fn try_from_bytes(bytes: &[u8], num_mix_hops: u8) -> Result { + if bytes.is_empty() { + return Err(NymMessageError::EmptyMessage); + } + + let typ_tag = NymMessageType::try_from(bytes[0])?; + match typ_tag { + NymMessageType::Plain => Ok(NymMessage::Plain(bytes[1..].to_vec())), + NymMessageType::Repliable => Ok(NymMessage::Repliable( + RepliableMessage::try_from_bytes(&bytes[1..], num_mix_hops)?, + )), + NymMessageType::Reply => Ok(NymMessage::Reply(ReplyMessage::try_from_bytes( + &bytes[1..], + )?)), + } + } + + /// Length of plaintext (from the sphinx point of view) data that is available per sphinx + /// packet. + pub fn available_plaintext_per_packet(&self, packet_size: PacketSize) -> usize { + let ack_overhead = MAX_NODE_ADDRESS_UNPADDED_LEN + PacketSize::AckPacket.size(); + + let variant_overhead = match self { + // each plain or repliable packet attaches an ephemeral public key so that the recipient + // could perform diffie-hellman with its own keys followed by a kdf to re-derive + // the packet encryption key + NymMessage::Plain(_) | NymMessage::Repliable(_) => encryption::PUBLIC_KEY_SIZE, + // each reply attaches the digest of the encryption key so that the recipient could + // lookup correct key for decryption, + NymMessage::Reply(_) => ReplySurbKeyDigestAlgorithm::output_size(), + }; + + packet_size.plaintext_size() - ack_overhead - variant_overhead + } + + /// Pads the message so that after it gets chunked, it will occupy exactly N sphinx packets. + /// Produces new_message = message || 1 || 0000.... + pub fn pad_to_full_packet_lengths(self, plaintext_per_packet: usize) -> PaddedMessage { + let bytes = self.into_bytes(); + + // 1 is added as there will always have to be at least a single byte of padding (1) added + // to be able to later distinguish the actual padding from the underlying message + let (_, space_left) = + chunking::number_of_required_fragments(bytes.len() + 1, plaintext_per_packet); + + bytes + .into_iter() + .chain(std::iter::once(1u8)) + .chain(std::iter::repeat(0u8).take(space_left)) + .collect::>() + .into() + } +} + +pub struct PaddedMessage(Vec); + +impl PaddedMessage { + pub fn new_reconstructed(bytes: Vec) -> Self { + PaddedMessage(bytes) + } + + /// Splits the padded message into [`Fragment`] that when serialized are going to become + /// sphinx packet payloads. + pub fn split_into_fragments( + self, + rng: &mut R, + plaintext_per_packet: usize, + ) -> Vec { + chunking::split_into_sets(rng, &self.0, plaintext_per_packet) + .into_iter() + .flat_map(|fragment_set| fragment_set.into_iter()) + .collect() + } + + // reverse of NymMessage::pad_to_full_packet_lengths + pub fn remove_padding(self, num_mix_hops: u8) -> Result { + // we are looking for first occurrence of 1 in the tail and we get its index + if let Some(padding_end) = self.0.iter().rposition(|b| *b == 1) { + // and now we only take bytes until that point (but not including it) + NymMessage::try_from_bytes(&self.0[..padding_end], num_mix_hops) + } else { + Err(NymMessageError::InvalidMessagePadding) + } + } +} + +impl From> for PaddedMessage { + fn from(bytes: Vec) -> Self { + PaddedMessage(bytes) + } +} diff --git a/common/nymsphinx/src/preparer/mod.rs b/common/nymsphinx/src/preparer/mod.rs index d91a7c85a5..19f68e30bc 100644 --- a/common/nymsphinx/src/preparer/mod.rs +++ b/common/nymsphinx/src/preparer/mod.rs @@ -1,24 +1,17 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::chunking; -use crypto::asymmetric::encryption; -use crypto::shared_key::new_ephemeral_shared_key; -use crypto::symmetric::stream_cipher; -use crypto::Digest; +use crate::message::NymMessage; +use crate::NymsphinxPayloadBuilder; use nymsphinx_acknowledgements::surb_ack::SurbAck; use nymsphinx_acknowledgements::AckKey; use nymsphinx_addressing::clients::Recipient; -use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN}; -use nymsphinx_anonymous_replies::encryption_key::SurbEncryptionKey; +use nymsphinx_addressing::nodes::NymNodeRoutingAddress; use nymsphinx_anonymous_replies::reply_surb::ReplySurb; use nymsphinx_chunking::fragment::{Fragment, FragmentIdentifier}; use nymsphinx_forwarding::packet::MixPacket; use nymsphinx_params::packet_sizes::PacketSize; -use nymsphinx_params::{ - PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm, - ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS, -}; +use nymsphinx_params::DEFAULT_NUM_MIX_HOPS; use nymsphinx_types::builder::SphinxPacketBuilder; use nymsphinx_types::{delays, Delay}; use rand::{CryptoRng, Rng}; @@ -26,6 +19,8 @@ use std::convert::TryFrom; use std::time::Duration; use topology::{NymTopology, NymTopologyError}; +pub(crate) mod payload; + /// Represents fully packed and prepared [`Fragment`] that can be sent through the mix network. pub struct PreparedFragment { /// Indicates the total expected round-trip time, i.e. delay from the sending of this message @@ -36,27 +31,17 @@ pub struct PreparedFragment { /// address of the node to which the message should be sent, the actual 'chunk' of the message /// going through the mix network and also the 'mode' of the packet, i.e. VPN or Mix. pub mix_packet: MixPacket, -} -#[derive(Debug)] -pub enum PreparationError { - TopologyError(NymTopologyError), - TooLongReplyMessageError, -} - -impl From for PreparationError { - fn from(err: NymTopologyError) -> Self { - PreparationError::TopologyError(err) - } + /// Identifier to uniquely identify a fragment. + pub fragment_identifier: FragmentIdentifier, } /// Prepares the message that is to be sent through the mix network by attaching /// an optional reply-SURB, padding it to appropriate length, encrypting its content, /// and chunking into appropriate size [`Fragment`]s. -// #[cfg_attr(not(target_arch = "wasm32"), derive(Clone))] #[derive(Clone)] #[must_use] -pub struct MessagePreparer { +pub struct MessagePreparer { /// Instance of a cryptographically secure random number generator. rng: R, @@ -115,86 +100,70 @@ where self.sender_address = sender_address; } - /// Length of plaintext (from the sphinx point of view) data that is available per sphinx - /// packet. - fn available_plaintext_per_packet(&self) -> usize { - // we need to put first hop's destination alongside the actual ack data - // TODO: a possible optimization way down the line: currently we're always assuming that - // the addresses will have `MAX_NODE_ADDRESS_UNPADDED_LEN`, i.e. be ipv6. In most cases - // they're actually going to be ipv4 hence wasting few bytes every packet. - // To fully utilise all available space, I guess first we'd need to generate routes for ACKs - // and only then perform the chunking with `available_plaintext_size` being called per chunk. - // However this will probably introduce bunch of complexity - // for relatively not a lot of gain, so it shouldn't be done just yet. - let ack_overhead = MAX_NODE_ADDRESS_UNPADDED_LEN + PacketSize::AckPacket.size(); - let ephemeral_public_key_overhead = encryption::PUBLIC_KEY_SIZE; - - self.packet_size.plaintext_size() - ack_overhead - ephemeral_public_key_overhead - } - - /// Pads the message so that after it gets chunked, it will occupy exactly N sphinx packets. - /// Produces new_message = message || 1 || 0000.... - fn pad_message(&self, message: Vec) -> Vec { - // 1 is added as there will always have to be at least a single byte of padding (1) added - // to be able to later distinguish the actual padding from the underlying message - let (_, space_left) = chunking::number_of_required_fragments( - message.len() + 1, - self.available_plaintext_per_packet(), - ); - - message - .into_iter() - .chain(std::iter::once(1u8)) - .chain(std::iter::repeat(0u8).take(space_left)) - .collect() - } - - /// Attaches reply-SURB to the message alongside the reply key. - /// Results in: - /// new_message = 0 || message - /// OR - /// new_message = 1 || REPLY_KEY || REPLY_SURB || message - fn optionally_attach_reply_surb( + pub fn generate_reply_surbs( &mut self, - message: Vec, - should_attach: bool, + amount: usize, topology: &NymTopology, - ) -> Result<(Vec, Option), PreparationError> { - if should_attach { + ) -> Result, NymTopologyError> { + let mut reply_surbs = Vec::with_capacity(amount); + for _ in 0..amount { let reply_surb = ReplySurb::construct( &mut self.rng, &self.sender_address, self.average_packet_delay, topology, )?; - - let reply_key = reply_surb.encryption_key(); - // if there's a reply surb, the message takes form of `1 || REPLY_KEY || REPLY_SURB || MSG` - Ok(( - std::iter::once(true as u8) - .chain(reply_surb.to_bytes().iter().cloned()) - .chain(message.into_iter()) - .collect(), - Some(reply_key.clone()), - )) - } else { - // but if there's no reply surb, the message takes form of `0 || MSG` - Ok(( - std::iter::once(false as u8) - .chain(message.into_iter()) - .collect(), - None, - )) + reply_surbs.push(reply_surb) } + + Ok(reply_surbs) } - /// Splits the message into [`Fragment`] that are going to be put later put into sphinx packets. - fn split_message(&mut self, message: Vec) -> Vec { - let plaintext_per_packet = self.available_plaintext_per_packet(); - chunking::split_into_sets(&mut self.rng, &message, plaintext_per_packet) - .into_iter() - .flat_map(|fragment_set| fragment_set.into_iter()) - .collect() + /// The procedure is as follows: + /// For each fragment: + /// - compute SURB_ACK + /// - generate (x, g^x) + /// - obtain key k from the reply-surb which was computed as follows: + /// k = KDF(remote encryption key ^ x) this is equivalent to KDF( dh(remote, x) ) + /// - compute v_b = AES-128-CTR(k, serialized_fragment) + /// - compute vk_b = H(k) || v_b + /// - compute sphinx_plaintext = SURB_ACK || H(k) || v_b + /// - compute sphinx_packet by applying the reply surb on the sphinx_plaintext + pub fn prepare_reply_chunk_for_sending( + &mut self, + fragment: Fragment, + topology: &NymTopology, + ack_key: &AckKey, + reply_surb: ReplySurb, + ) -> Result { + // this is not going to be accurate by any means. but that's the best estimation we can do + let expected_forward_delay = Delay::new_from_millis( + (self.average_packet_delay.as_millis() * self.num_mix_hops as u128) as u64, + ); + + let fragment_identifier = fragment.fragment_identifier(); + + // create an ack + let surb_ack = self.generate_surb_ack(fragment_identifier, topology, ack_key)?; + let ack_delay = surb_ack.expected_total_delay(); + + let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack) + .build_reply(reply_surb.encryption_key()); + + // the unwrap here is fine as the failures can only originate from attempting to use invalid payload lenghts + // and we just very carefully constructed a (presumably) valid one + let (sphinx_packet, first_hop_address) = reply_surb + .apply_surb(packet_payload, Some(self.packet_size)) + .unwrap(); + + Ok(PreparedFragment { + // the round-trip delay is the sum of delays of all hops on the forward route as + // well as the total delay of the ack packet. + // we don't know the delays inside the reply surbs so we use best-effort estimation from our poisson distribution + total_delay: expected_forward_delay + ack_delay, + mix_packet: MixPacket::new(first_hop_address, sphinx_packet, Default::default()), + fragment_identifier, + }) } /// Tries to convert this [`Fragment`] into a [`SphinxPacket`] that can be sent through the Nym mix-network, @@ -221,46 +190,14 @@ where ack_key: &AckKey, packet_recipient: &Recipient, ) -> Result { + let fragment_identifier = fragment.fragment_identifier(); + // create an ack - let (ack_delay, surb_ack_bytes) = self - .generate_surb_ack(fragment.fragment_identifier(), topology, ack_key)? - .prepare_for_sending(); + let surb_ack = self.generate_surb_ack(fragment_identifier, topology, ack_key)?; + let ack_delay = surb_ack.expected_total_delay(); - // TODO: - // TODO: - // TODO: - // TODO: - // TODO: ASK @AP AND @DH WHETHER THOSE KEYS CAN/SHOULD ALSO BE REUSED IN VPN MODE!! - // TODO: - // TODO: - // TODO: - // TODO: - - // create keys for 'payload' encryption - let (ephemeral_keypair, shared_key) = - new_ephemeral_shared_key::( - &mut self.rng, - packet_recipient.encryption_key(), - ); - - // serialize fragment and encrypt its content - let mut chunk_data = fragment.into_bytes(); - - let zero_iv = stream_cipher::zero_iv::(); - stream_cipher::encrypt_in_place::( - &shared_key, - &zero_iv, - &mut chunk_data, - ); - - // combine it together as follows: - // SURB_ACK_FIRST_HOP || SURB_ACK_DATA || EPHEMERAL_KEY || CHUNK_DATA - // (note: surb_ack_bytes contains SURB_ACK_FIRST_HOP || SURB_ACK_DATA ) - let packet_payload: Vec<_> = surb_ack_bytes - .into_iter() - .chain(ephemeral_keypair.public_key().to_bytes().iter().cloned()) - .chain(chunk_data.into_iter()) - .collect(); + let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack) + .build_regular(&mut self.rng, packet_recipient.encryption_key()); // generate pseudorandom route for the packet let route = topology.random_route_to_gateway( @@ -290,6 +227,7 @@ where // note that the last hop of the packet is a gateway that does not do any delays total_delay: delays.iter().take(delays.len() - 1).sum::() + ack_delay, mix_packet: MixPacket::new(first_hop_address, sphinx_packet, Default::default()), + fragment_identifier, }) } @@ -310,113 +248,12 @@ where ) } - /// Attaches an optional reply-surb and correct padding to the underlying message - /// and splits it into [`Fragment`] that can be later packed into sphinx packets to be - /// sent through the mix network. - pub fn prepare_and_split_message( - &mut self, - message: Vec, - with_reply_surb: bool, - topology: &NymTopology, - ) -> Result<(Vec, Option), PreparationError> { - let (message, reply_key) = - self.optionally_attach_reply_surb(message, with_reply_surb, topology)?; + pub fn pad_and_split_message(&mut self, message: NymMessage) -> Vec { + let plaintext_per_packet = message.available_plaintext_per_packet(self.packet_size); - let message = self.pad_message(message); - - Ok((self.split_message(message), reply_key)) - } - - // TODO: perhaps the return type could somehow be combined with [`PreparedFragment`] ? - pub async fn prepare_reply_for_use( - &mut self, - message: Vec, - reply_surb: ReplySurb, - topology: &NymTopology, - ack_key: &AckKey, - ) -> Result<(MixPacket, FragmentIdentifier), PreparationError> { - // there's no chunking in reply-surbs so there's a hard limit on message, - // we also need to put the key digest into the message (same size as ephemeral key) - // and need 1 byte to indicate padding length (this is not the case for 'normal' messages - // as there the padding is added for the whole message) - // so before doing any processing, let's see if we have enough space for it all - let ack_overhead = MAX_NODE_ADDRESS_UNPADDED_LEN + PacketSize::AckPacket.size(); - if message.len() - > self.packet_size.plaintext_size() - - ack_overhead - - ReplySurbKeyDigestAlgorithm::output_size() - - 1 - { - return Err(PreparationError::TooLongReplyMessageError); - } - - let reply_id = FragmentIdentifier::new_reply(&mut self.rng); - - // create an ack - // even though it won't be used for retransmission, it must be present so that - // 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)? - .prepare_for_sending(); - - let zero_pad_len = self.packet_size.plaintext_size() - - message.len() - - ack_overhead - - ReplySurbKeyDigestAlgorithm::output_size() - - 1; - - // create reply message that will reach the recipient: - let mut reply_content: Vec<_> = message - .into_iter() - .chain(std::iter::once(1)) - .chain(std::iter::repeat(0).take(zero_pad_len)) - .collect(); - - // encrypt the reply message - let zero_iv = stream_cipher::zero_iv::(); - stream_cipher::encrypt_in_place::( - reply_surb.encryption_key().inner(), - &zero_iv, - &mut reply_content, - ); - - // combine it together as follows: - // SURB_ACK_FIRST_HOP || SURB_ACK_DATA || KEY_DIGEST || E (REPLY_MESSAGE || 1 || 0*) - // (note: surb_ack_bytes contains SURB_ACK_FIRST_HOP || SURB_ACK_DATA ) - let packet_payload: Vec<_> = surb_ack_bytes - .into_iter() - .chain(reply_surb.encryption_key().compute_digest().iter().copied()) - .chain(reply_content.into_iter()) - .collect(); - - // finally put it all inside a sphinx packet - // this can only fail if packet payload has incorrect size, but if it does, it means - // there's a bug in the above code - let (packet, first_hop) = reply_surb - .apply_surb(&packet_payload, Some(self.packet_size)) - .unwrap(); - - Ok(( - MixPacket::new(first_hop, packet, Default::default()), - reply_id, - )) - } - - #[allow(dead_code)] - #[cfg(test)] - pub(crate) fn test_fixture() -> MessagePreparer { - let rng = rand::rngs::OsRng; - let dummy_address = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); - - MessagePreparer { - rng, - packet_size: Default::default(), - sender_address: dummy_address, - average_packet_delay: Default::default(), - average_ack_delay: Default::default(), - num_mix_hops: DEFAULT_NUM_MIX_HOPS, - } + message + .pad_to_full_packet_lengths(plaintext_per_packet) + .split_into_fragments(&mut self.rng, plaintext_per_packet) } } diff --git a/common/nymsphinx/src/preparer/payload.rs b/common/nymsphinx/src/preparer/payload.rs new file mode 100644 index 0000000000..94acde03c8 --- /dev/null +++ b/common/nymsphinx/src/preparer/payload.rs @@ -0,0 +1,97 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crypto::aes::cipher::{KeyIvInit, StreamCipher}; +use crypto::asymmetric::encryption; +use crypto::shared_key::new_ephemeral_shared_key; +use crypto::symmetric::stream_cipher; +use crypto::symmetric::stream_cipher::CipherKey; +use nymsphinx_acknowledgements::surb_ack::SurbAck; +use nymsphinx_anonymous_replies::SurbEncryptionKey; +use nymsphinx_chunking::fragment::Fragment; +use nymsphinx_params::{ + PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm, +}; +use rand::{CryptoRng, RngCore}; + +pub struct NymsphinxPayloadBuilder { + fragment: Fragment, + surb_ack: SurbAck, +} + +impl NymsphinxPayloadBuilder { + pub fn new(fragment: Fragment, surb_ack: SurbAck) -> Self { + NymsphinxPayloadBuilder { fragment, surb_ack } + } + + fn build( + self, + packet_encryption_key: &CipherKey, + variant_data: impl IntoIterator, + ) -> NymsphinxPayload + where + C: StreamCipher + KeyIvInit, + { + let (_, surb_ack_bytes) = self.surb_ack.prepare_for_sending(); + + let mut fragment_data = self.fragment.into_bytes(); + stream_cipher::encrypt_in_place::( + packet_encryption_key, + &stream_cipher::zero_iv::(), + &mut fragment_data, + ); + + // combines all the data as follows: + // SURB_ACK || VARIANT_SPECIFIC_DATA || CHUNK_DATA + // where variant-specific data is as follows: + // for replies it would be the digest of the encryption key used + // for 'regular' messages it would be the public component used in DH later used in the KDF + NymsphinxPayload( + surb_ack_bytes + .into_iter() + .chain(variant_data.into_iter()) + .chain(fragment_data.into_iter()) + .collect(), + ) + } + + pub fn build_reply(self, packet_encryption_key: &SurbEncryptionKey) -> NymsphinxPayload { + let key_digest = packet_encryption_key.compute_digest(); + self.build::( + packet_encryption_key.inner(), + key_digest.into_iter(), + ) + } + + pub fn build_regular( + self, + rng: &mut R, + recipient_encryption_key: &encryption::PublicKey, + ) -> NymsphinxPayload + where + R: RngCore + CryptoRng, + { + // create keys for 'payload' encryption + let (ephemeral_keypair, shared_key) = new_ephemeral_shared_key::< + PacketEncryptionAlgorithm, + PacketHkdfAlgorithm, + _, + >(rng, recipient_encryption_key); + + self.build::( + &shared_key, + ephemeral_keypair.public_key().to_bytes(), + ) + } +} + +// the actual byte data that will be put into the sphinx packet paylaod. +// no more transformations are going to happen to it +// TODO: use that fact for some better compile time assertions +pub struct NymsphinxPayload(Vec); + +impl AsRef<[u8]> for NymsphinxPayload { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index 0b6a8e96dd..453f632f76 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -1,13 +1,22 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::message::{NymMessage, NymMessageError, PaddedMessage, PlainMessage}; +use crypto::aes::cipher::{KeyIvInit, StreamCipher}; use crypto::asymmetric::encryption; use crypto::shared_key::recompute_shared_key; use crypto::symmetric::stream_cipher; -use nymsphinx_anonymous_replies::reply_surb::{ReplySurb, ReplySurbError}; +use crypto::symmetric::stream_cipher::CipherKey; +use nymsphinx_anonymous_replies::requests::AnonymousSenderTag; +use nymsphinx_anonymous_replies::SurbEncryptionKey; use nymsphinx_chunking::fragment::Fragment; use nymsphinx_chunking::reconstruction::MessageReconstructor; -use nymsphinx_params::{PacketEncryptionAlgorithm, PacketHkdfAlgorithm, DEFAULT_NUM_MIX_HOPS}; +use nymsphinx_chunking::ChunkingError; +use nymsphinx_params::{ + PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm, + DEFAULT_NUM_MIX_HOPS, +}; +use thiserror::Error; // TODO: should this live in this file? #[derive(Debug)] @@ -15,31 +24,53 @@ pub struct ReconstructedMessage { /// The actual plaintext message that was received. pub message: Vec, - /// Optional ReplySURB to allow for an anonymous reply to the sender. - pub reply_surb: Option, + /// Optional ephemeral sender tag indicating pseudo-identity of the party who sent us the message + /// (alongside any reply SURBs) + pub sender_tag: Option, } -#[derive(Debug)] +impl From for (Vec, Option) { + fn from(msg: ReconstructedMessage) -> Self { + (msg.message, msg.sender_tag) + } +} + +impl ReconstructedMessage { + pub fn new(message: Vec, sender_tag: AnonymousSenderTag) -> Self { + Self { + message, + sender_tag: Some(sender_tag), + } + } + + pub fn into_inner(self) -> (Vec, Option) { + self.into() + } +} + +impl From for ReconstructedMessage { + fn from(message: PlainMessage) -> Self { + ReconstructedMessage { + message, + sender_tag: None, + } + } +} + +#[derive(Debug, Error)] pub enum MessageRecoveryError { - InvalidSurbPrefixError, - MalformedSurbError(ReplySurbError), - InvalidRemoteEphemeralKey(encryption::KeyRecoveryError), - MalformedFragmentError, - InvalidMessagePaddingError, - MalformedReconstructedMessage(Vec), - TooShortMessageError, -} + #[error("Recovered remote x25519 public key is invalid - {0}")] + InvalidRemoteEphemeralKey(#[from] encryption::KeyRecoveryError), -impl From for MessageRecoveryError { - fn from(err: ReplySurbError) -> Self { - MessageRecoveryError::MalformedSurbError(err) - } -} + #[error("The reconstructed message was malformed - {source}")] + MalformedReconstructedMessage { + #[source] + source: NymMessageError, + used_sets: Vec, + }, -impl From for MessageRecoveryError { - fn from(err: encryption::KeyRecoveryError) -> Self { - MessageRecoveryError::InvalidRemoteEphemeralKey(err) - } + #[error("Failed to recover message fragment - {0}")] + FragmentRecoveryError(#[from] ChunkingError), } pub struct MessageReceiver { @@ -64,36 +95,34 @@ impl MessageReceiver { self } - /// Parses the message to strip and optionally recover reply SURB. - fn recover_reply_surb_from_message( - &self, - message: &mut Vec, - ) -> Result, MessageRecoveryError> { - match message[0] { - n if n == false as u8 => { - message.remove(0); - Ok(None) - } - n if n == true as u8 => { - let surb_len: usize = ReplySurb::serialized_len(self.num_mix_hops); - // note the extra +1 (due to 0/1 message prefix) - let surb_bytes = &message[1..1 + surb_len]; - let reply_surb = ReplySurb::from_bytes(surb_bytes)?; + fn decrypt_raw_message(&self, message: &mut [u8], key: &CipherKey) + where + C: StreamCipher + KeyIvInit, + { + let zero_iv = stream_cipher::zero_iv::(); + stream_cipher::decrypt_in_place::(key, &zero_iv, message) + } - *message = message.drain(1 + surb_len..).collect(); - Ok(Some(reply_surb)) - } - _ => Err(MessageRecoveryError::InvalidSurbPrefixError), - } + /// Given raw fragment data, **WITH KEY DIGEST PREFIX ALREADY REMOVED!!**, uses looked up + /// key to decrypt fragment data + pub fn recover_plaintext_from_reply( + &self, + reply_ciphertext: &mut [u8], + reply_key: SurbEncryptionKey, + ) { + self.decrypt_raw_message::( + reply_ciphertext, + reply_key.inner(), + ) } /// Given raw fragment data, recovers the remote ephemeral key, recomputes shared secret, /// uses it to decrypt fragment data - pub fn recover_plaintext( + pub fn recover_plaintext_from_regular_packet<'a>( &self, local_key: &encryption::PrivateKey, - mut raw_enc_frag: Vec, - ) -> Result, MessageRecoveryError> { + raw_enc_frag: &'a mut [u8], + ) -> Result<&'a mut [u8], MessageRecoveryError> { // 1. recover remote encryption key let remote_key_bytes = &raw_enc_frag[..encryption::PUBLIC_KEY_SIZE]; let remote_ephemeral_key = encryption::PublicKey::from_bytes(remote_key_bytes)?; @@ -105,33 +134,17 @@ impl MessageReceiver { ); // 3. decrypt fragment data - let fragment_bytes = &mut raw_enc_frag[encryption::PUBLIC_KEY_SIZE..]; + let fragment_ciphertext = &mut raw_enc_frag[encryption::PUBLIC_KEY_SIZE..]; - let zero_iv = stream_cipher::zero_iv::(); - Ok(stream_cipher::decrypt::( - &encryption_key, - &zero_iv, - fragment_bytes, - )) + self.decrypt_raw_message::(fragment_ciphertext, &encryption_key); + let fragment_data = fragment_ciphertext; + + Ok(fragment_data) } /// Given fragment data recovers [`Fragment`] itself. pub fn recover_fragment(&self, frag_data: &[u8]) -> Result { - Fragment::try_from_bytes(frag_data) - .map_err(|_| MessageRecoveryError::MalformedFragmentError) - } - - /// Removes the zero padding from the message that was initially included to ensure same length - /// sphinx payloads. - pub fn remove_padding(message: &mut Vec) -> Result<(), MessageRecoveryError> { - // we are looking for first occurrence of 1 in the tail and we get its index - if let Some(i) = message.iter().rposition(|b| *b == 1) { - // and now we only take bytes until that point (but not including it) - *message = message.drain(..i).collect(); - Ok(()) - } else { - Err(MessageRecoveryError::InvalidMessagePaddingError) - } + Ok(Fragment::try_from_bytes(frag_data)?) } /// Inserts given [`Fragment`] into the reconstructor. @@ -144,30 +157,15 @@ impl MessageReceiver { pub fn insert_new_fragment( &mut self, fragment: Fragment, - ) -> Result)>, MessageRecoveryError> { - if let Some((mut message, used_sets)) = self.reconstructor.insert_new_fragment(fragment) { - // Split message into plaintext and reply-SURB - let reply_surb = match self.recover_reply_surb_from_message(&mut message) { - Ok(reply_surb) => reply_surb, - Err(_) => { - return Err(MessageRecoveryError::MalformedReconstructedMessage( - used_sets, - )); - } - }; - - // Finally, remove the zero padding from the message - Self::remove_padding(&mut message).map_err(|_| { - MessageRecoveryError::MalformedReconstructedMessage(used_sets.clone()) - })?; - - Ok(Some(( - ReconstructedMessage { - message, - reply_surb, - }, - used_sets, - ))) + ) -> Result)>, MessageRecoveryError> { + if let Some((message, used_sets)) = self.reconstructor.insert_new_fragment(fragment) { + match PaddedMessage::new_reconstructed(message).remove_padding(self.num_mix_hops) { + Ok(message) => Ok(Some((message, used_sets))), + Err(err) => Err(MessageRecoveryError::MalformedReconstructedMessage { + source: err, + used_sets, + }), + } } else { Ok(None) } @@ -188,16 +186,14 @@ mod message_receiver { use super::*; use crypto::asymmetric::identity; use mixnet_contract_common::Layer; - use nymsphinx_addressing::clients::Recipient; - use rand::rngs::OsRng; use std::collections::HashMap; - use std::time::Duration; use topology::{gateway, mix, NymTopology}; // TODO: is it somehow maybe possible to move it to `topology` and have if conditionally // available to other modules? /// Returns a hardcoded, valid instance of [`NymTopology`] that is to be used in /// tests requiring instance of topology. + #[allow(dead_code)] fn topology_fixture() -> NymTopology { let mut mixes = HashMap::new(); mixes.insert( @@ -282,41 +278,4 @@ mod message_receiver { }], ) } - - #[test] - fn correctly_splits_message_into_plaintext_and_surb() { - let message_receiver: MessageReceiver = Default::default(); - - // the actual 'correctness' of the underlying message doesn't matter for this test - let message = vec![42; 100]; - let dummy_recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@FioFa8nMmPpQnYi7JyojoTuwGLeyNS8BF4ChPr29zUML").unwrap(); - let average_delay = Duration::from_millis(500); - let topology = topology_fixture(); - - let reply_surb = - ReplySurb::construct(&mut OsRng, &dummy_recipient, average_delay, &topology).unwrap(); - - let reply_surb_bytes = reply_surb.to_bytes(); - - // this is not exactly what is 'received' but rather after "some" processing, however, - // this is the expected argument to the function - let mut received_without_surb: Vec<_> = - std::iter::once(0).chain(message.iter().cloned()).collect(); - - let reply_surb = message_receiver - .recover_reply_surb_from_message(&mut received_without_surb) - .unwrap(); - assert_eq!(received_without_surb, message); - assert!(reply_surb.is_none()); - - let mut received_with_surb: Vec<_> = std::iter::once(1) - .chain(reply_surb_bytes.iter().cloned()) - .chain(message.iter().cloned()) - .collect(); - let reply_surb = message_receiver - .recover_reply_surb_from_message(&mut received_with_surb) - .unwrap(); - assert_eq!(received_with_surb, message); - assert_eq!(reply_surb_bytes, reply_surb.unwrap().to_bytes()); - } } diff --git a/common/nymsphinx/types/Cargo.toml b/common/nymsphinx/types/Cargo.toml index 34a916bbf5..353de2b1b2 100644 --- a/common/nymsphinx/types/Cargo.toml +++ b/common/nymsphinx/types/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sphinx = { git = "https://github.com/nymtech/sphinx", rev="c494250f2a78bed33a618d470792418eee932859" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e05a1992522ed0afd3c6fcac160313ffc9bb306a" } #sphinx = { path = "../../../../sphinx"} diff --git a/common/socks5/ordered-buffer/Cargo.toml b/common/socks5/ordered-buffer/Cargo.toml index f5ea5545d8..58dd09b263 100644 --- a/common/socks5/ordered-buffer/Cargo.toml +++ b/common/socks5/ordered-buffer/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] log = "0.4" +thiserror = "1.0.37" \ No newline at end of file diff --git a/common/socks5/ordered-buffer/src/message.rs b/common/socks5/ordered-buffer/src/message.rs index f5f28f53b1..15627d7fe0 100644 --- a/common/socks5/ordered-buffer/src/message.rs +++ b/common/socks5/ordered-buffer/src/message.rs @@ -1,9 +1,16 @@ -use std::cmp::Ordering; +// Copyright 2020-2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 -#[derive(Debug, PartialEq, Eq)] +use std::cmp::Ordering; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq, Eq)] pub enum MessageError { + #[error("the received message was empty")] NoData, - IndexTooShort, + + #[error("could not extract message index. Received {received} bytes, but expected {expected}")] + IndexTooShort { received: usize, expected: usize }, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -32,7 +39,10 @@ impl OrderedMessage { } if data.len() < 8 { - return Err(MessageError::IndexTooShort); + return Err(MessageError::IndexTooShort { + received: data.len(), + expected: 8, + }); } let index = u64::from_be_bytes([ data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], @@ -81,7 +91,13 @@ mod ordered_message_from_bytes { #[test] fn fails_when_data_is_too_short() { let result = OrderedMessage::try_from_bytes(vec![1, 2, 3]); - assert_eq!(Err(MessageError::IndexTooShort), result); + assert_eq!( + Err(MessageError::IndexTooShort { + received: 3, + expected: 8 + }), + result + ); } #[test] diff --git a/common/socks5/proxy-helpers/src/connection_controller.rs b/common/socks5/proxy-helpers/src/connection_controller.rs index 97c4487af6..66c8b94a7a 100644 --- a/common/socks5/proxy-helpers/src/connection_controller.rs +++ b/common/socks5/proxy-helpers/src/connection_controller.rs @@ -11,7 +11,7 @@ use std::{ collections::{HashMap, HashSet}, time::Duration, }; -use task::ShutdownListener; +use task::TaskClient; use tokio::time; /// A generic message produced after reading from a socket/connection. It includes data that was @@ -53,7 +53,7 @@ impl ActiveConnection { let ordered_message = match OrderedMessage::try_from_bytes(payload) { Ok(msg) => msg, Err(err) => { - error!("Malformed ordered message - {:?}", err); + error!("Malformed ordered message - {err}"); return; } }; @@ -98,14 +98,14 @@ pub struct Controller { // un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here pending_messages: HashMap, bool)>>, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl Controller { pub fn new( client_connection_tx: ConnectionCommandSender, broadcast_connections: BroadcastActiveConnections, - shutdown: ShutdownListener, + shutdown: TaskClient, ) -> (Self, ControllerSender) { let (sender, receiver) = mpsc::unbounded(); ( @@ -158,9 +158,9 @@ impl Controller { .unbounded_send(ConnectionCommand::Close(conn_id)) { if self.shutdown.is_shutdown_poll() { - log::debug!("Failed to send: {}", err); + log::debug!("Failed to send: {err}"); } else { - log::error!("Failed to send: {}", err); + log::error!("Failed to send: {err}"); } } } @@ -197,7 +197,7 @@ impl Controller { socket_closed: active_connection.is_closed, }) { - error!("WTF IS THIS: {:?}", err); + error!("WTF IS THIS: {err}"); } // TODO: ABOVE UNWRAP CAUSED A CRASH IN A NORMAL USE!!!! diff --git a/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs b/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs index f2ebc2cd9b..2065f7770b 100644 --- a/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs +++ b/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs @@ -15,7 +15,7 @@ use socks5_requests::ConnectionId; use std::fmt::Debug; use std::time::Duration; use std::{io, sync::Arc}; -use task::ShutdownListener; +use task::TaskClient; use tokio::select; use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep}; @@ -54,7 +54,7 @@ where Some(data) => match data { Ok(data) => (data, false), Err(err) => { - error!(target: &*format!("({}) socks5 inbound", connection_id), "failed to read request from the socket - {}", err); + error!(target: &*format!("({}) socks5 inbound", connection_id), "failed to read request from the socket - {err}"); (Default::default(), true) } }, @@ -77,16 +77,11 @@ where ordered_msg.len() ); - // If we are closing the socket, wait until the data has passed `OutQueueControl` and the lane - // is empty, otherwise just wait until we are reasonably close to finish sending as a way to - // throttle the incoming data. - if let Some(lane_queue_lengths) = lane_queue_lengths { - if is_finished { - wait_until_lane_empty(lane_queue_lengths, connection_id).await; - } else { - // We allow a bit of slack when this is not the last msg - wait_until_lane_almost_empty(lane_queue_lengths, connection_id).await; - } + // Before sending the data downstream, wait for the lane at the `OutQueueControl` is reasonably + // close to finishing. This is a way of pacing the sending application (backpressure). + if let Some(ref lane_queue_lengths) = lane_queue_lengths { + // We allow a bit of slack to try to keep the pipeline >0 + wait_until_lane_almost_empty(lane_queue_lengths, connection_id).await; } mix_sender @@ -95,16 +90,30 @@ where .expect("InputMessageReceiver has stopped receiving!"); if is_finished { - // technically we already informed it when we sent the message to mixnet above - debug!(target: &*format!("({}) socks5 inbound", connection_id), "The local socket is closed - won't receive any more data. Informing remote about that..."); + // After sending, if this is the last message, wait until we've actually transmitted the data + // in the `OutQueueControl` and the lane is empty. + if let Some(ref lane_queue_lengths) = lane_queue_lengths { + // This is basically an ugly workaround to make sure that we don't start waiting until + // the data that we pushed arrived at the OutQueueControl. + // This usually not a problem in the socks5-client, but for the network-requester this + // info is synced at up to every 500ms. + sleep(Duration::from_secs(2)).await; + wait_until_lane_empty(lane_queue_lengths, connection_id).await; + } + + // Technically we already informed it when we sent the message to mixnet above + debug!( + target: &*format!("({}) socks5 inbound", connection_id), + "The local socket is closed - won't receive any more data. Informing remote about that..." + ); } is_finished } -async fn wait_until_lane_empty(lane_queue_lengths: LaneQueueLengths, connection_id: u64) { +async fn wait_until_lane_empty(lane_queue_lengths: &LaneQueueLengths, connection_id: u64) { if tokio::time::timeout( - Duration::from_secs(2 * 60), + Duration::from_secs(4 * 60), wait_for_lane( lane_queue_lengths, connection_id, @@ -119,13 +128,13 @@ async fn wait_until_lane_empty(lane_queue_lengths: LaneQueueLengths, connection_ } } -async fn wait_until_lane_almost_empty(lane_queue_lengths: LaneQueueLengths, connection_id: u64) { +async fn wait_until_lane_almost_empty(lane_queue_lengths: &LaneQueueLengths, connection_id: u64) { if tokio::time::timeout( - Duration::from_secs(2 * 60), + Duration::from_secs(4 * 60), wait_for_lane( lane_queue_lengths, connection_id, - 10, + 30, Duration::from_millis(100), ), ) @@ -137,7 +146,7 @@ async fn wait_until_lane_almost_empty(lane_queue_lengths: LaneQueueLengths, conn } async fn wait_for_lane( - lane_queue_lengths: LaneQueueLengths, + lane_queue_lengths: &LaneQueueLengths, connection_id: u64, queue_length_threshold: usize, sleep_duration: Duration, @@ -161,7 +170,7 @@ pub(super) async fn run_inbound( adapter_fn: F, shutdown_notify: Arc, lane_queue_lengths: Option, - mut shutdown_listener: ShutdownListener, + mut shutdown_listener: TaskClient, ) -> OwnedReadHalf where F: Fn(ConnectionId, Vec, bool) -> S + Send + 'static, diff --git a/common/socks5/proxy-helpers/src/proxy_runner/mod.rs b/common/socks5/proxy-helpers/src/proxy_runner/mod.rs index defcbf4f5f..a87ada287e 100644 --- a/common/socks5/proxy-helpers/src/proxy_runner/mod.rs +++ b/common/socks5/proxy-helpers/src/proxy_runner/mod.rs @@ -6,7 +6,7 @@ use client_connections::LaneQueueLengths; use socks5_requests::ConnectionId; use std::fmt::Debug; use std::{sync::Arc, time::Duration}; -use task::ShutdownListener; +use task::TaskClient; use tokio::{net::TcpStream, sync::Notify}; mod inbound; @@ -50,7 +50,7 @@ pub struct ProxyRunner { lane_queue_lengths: Option, // Listens to shutdown commands from higher up - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, } impl ProxyRunner @@ -66,7 +66,7 @@ where mix_sender: MixProxySender, connection_id: ConnectionId, lane_queue_lengths: Option, - shutdown_listener: ShutdownListener, + shutdown_listener: TaskClient, ) -> Self { ProxyRunner { mix_receiver: Some(mix_receiver), diff --git a/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs b/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs index 78db73afb6..ed7f8d74c6 100644 --- a/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs +++ b/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs @@ -8,7 +8,7 @@ use futures::StreamExt; use log::*; use socks5_requests::ConnectionId; use std::{sync::Arc, time::Duration}; -use task::ShutdownListener; +use task::TaskClient; use tokio::io::AsyncWriteExt; use tokio::select; use tokio::{net::tcp::OwnedWriteHalf, sync::Notify, time::sleep, time::Instant}; @@ -33,7 +33,7 @@ async fn deal_with_message( if let Err(err) = writer.write_all(&connection_message.payload).await { // the other half is probably going to blow up too (if not, this task also needs to notify the other one!!) - error!(target: &*format!("({}) socks5 outbound", connection_id), "failed to write response back to the socket - {}", err); + error!(target: &*format!("({}) socks5 outbound", connection_id), "failed to write response back to the socket - {err}"); return true; } if connection_message.socket_closed { @@ -51,7 +51,7 @@ pub(super) async fn run_outbound( mut mix_receiver: ConnectionReceiver, connection_id: ConnectionId, shutdown_notify: Arc, - mut shutdown_listener: ShutdownListener, + mut shutdown_listener: TaskClient, ) -> (OwnedWriteHalf, ConnectionReceiver) { let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT)); tokio::pin!(shutdown_future); diff --git a/common/socks5/requests/src/msg.rs b/common/socks5/requests/src/msg.rs index 2b688d8214..906c643524 100644 --- a/common/socks5/requests/src/msg.rs +++ b/common/socks5/requests/src/msg.rs @@ -9,13 +9,13 @@ use crate::response::{Response, ResponseError}; #[derive(Debug, Error)] pub enum MessageError { - #[error("{0}")] + #[error(transparent)] Request(RequestError), #[error("{0:?}")] Response(ResponseError), - #[error("{0}")] + #[error(transparent)] NetworkRequesterResponseError(NrError), #[error("no data")] diff --git a/common/socks5/requests/src/network_requester_response.rs b/common/socks5/requests/src/network_requester_response.rs index 64e459dfb0..eba3f88dd9 100644 --- a/common/socks5/requests/src/network_requester_response.rs +++ b/common/socks5/requests/src/network_requester_response.rs @@ -63,7 +63,7 @@ impl NetworkRequesterResponse { self.connection_id .to_be_bytes() .iter() - .cloned() + .copied() .chain(self.network_requester_error.into_bytes().into_iter()) .collect() } diff --git a/common/socks5/requests/src/request.rs b/common/socks5/requests/src/request.rs index f6921e362a..3148f6cd84 100644 --- a/common/socks5/requests/src/request.rs +++ b/common/socks5/requests/src/request.rs @@ -15,6 +15,18 @@ pub enum RequestFlag { Send = 1, } +impl TryFrom for RequestFlag { + type Error = RequestError; + + fn try_from(value: u8) -> Result { + match value { + _ if value == (RequestFlag::Connect as u8) => Ok(Self::Connect), + _ if value == (RequestFlag::Send as u8) => Ok(Self::Send), + _ => Err(RequestError::UnknownRequestFlag), + } + } +} + #[derive(Debug, Error)] pub enum RequestError { #[error("not enough bytes to recover the length of the address")] @@ -45,23 +57,12 @@ impl RequestError { } } -impl TryFrom for RequestFlag { - type Error = RequestError; - - fn try_from(value: u8) -> Result { - match value { - _ if value == (RequestFlag::Connect as u8) => Ok(Self::Connect), - _ if value == (RequestFlag::Send as u8) => Ok(Self::Send), - _ => Err(RequestError::UnknownRequestFlag), - } - } -} - #[derive(Debug)] pub struct ConnectRequest { + // TODO: is connection_id redundant now? pub conn_id: ConnectionId, pub remote_addr: RemoteAddress, - pub return_address: Recipient, + pub return_address: Option, } /// A request from a SOCKS5 client that a Nym Socks5 service provider should @@ -82,7 +83,7 @@ impl Request { pub fn new_connect( conn_id: ConnectionId, remote_addr: RemoteAddress, - return_address: Recipient, + return_address: Option, ) -> Request { Request::Connect(Box::new(ConnectRequest { conn_id, @@ -99,12 +100,13 @@ impl Request { /// Deserialize the request type, connection id, destination address and port, /// and the request body from bytes. /// - /// Serialized bytes looks like this: - /// - /// -------------------------------------------------------------------------------------- - /// request_flag | connection_id | address_length | remote_address_bytes | request_data | - /// 1 | 8 | 2 | address_length | ... | - /// -------------------------------------------------------------------------------------- + // TODO: this was already inaccurate + // /// Serialized bytes looks like this: + // /// + // /// -------------------------------------------------------------------------------------- + // /// request_flag | connection_id | address_length | remote_address_bytes | request_data | + // /// 1 | 8 | 2 | address_length | ... | + // /// -------------------------------------------------------------------------------------- /// /// The request_flag tells us whether this is a new connection request (`new_connect`), /// an already-established connection we should send up (`new_send`), or @@ -144,14 +146,20 @@ impl Request { // just a temporary reference to mid-slice for ease of use let recipient_data_bytes = &connect_request_bytes[address_end..]; - if recipient_data_bytes.len() != Recipient::LEN { - return Err(RequestError::ReturnAddressTooShort); - } + let return_address = if recipient_data_bytes.is_empty() { + None + } else { + if recipient_data_bytes.len() != Recipient::LEN { + return Err(RequestError::ReturnAddressTooShort); + } - let mut return_bytes = [0u8; Recipient::LEN]; - return_bytes.copy_from_slice(&recipient_data_bytes[..Recipient::LEN]); - let return_address = Recipient::try_from_bytes(return_bytes) - .map_err(RequestError::MalformedReturnAddress)?; + let mut return_bytes = [0u8; Recipient::LEN]; + return_bytes.copy_from_slice(&recipient_data_bytes[..Recipient::LEN]); + Some( + Recipient::try_from_bytes(return_bytes) + .map_err(RequestError::MalformedReturnAddress)?, + ) + }; Ok(Request::new_connect( connection_id, @@ -161,7 +169,7 @@ impl Request { } RequestFlag::Send => { let local_closed = b[9] != 0; - let data = b[10..].as_ref().to_vec(); + let data = b[10..].to_vec(); Ok(Request::Send(connection_id, data, local_closed)) } @@ -178,15 +186,19 @@ impl Request { let remote_address_bytes = req.remote_addr.into_bytes(); let remote_address_bytes_len = remote_address_bytes.len() as u16; - std::iter::once(RequestFlag::Connect as u8) - .chain(req.conn_id.to_be_bytes().iter().cloned()) - .chain(remote_address_bytes_len.to_be_bytes().iter().cloned()) - .chain(remote_address_bytes.into_iter()) - .chain(req.return_address.to_bytes().iter().cloned()) - .collect() + let iter = std::iter::once(RequestFlag::Connect as u8) + .chain(req.conn_id.to_be_bytes().into_iter()) + .chain(remote_address_bytes_len.to_be_bytes().into_iter()) + .chain(remote_address_bytes.into_iter()); + + if let Some(return_address) = req.return_address { + iter.chain(return_address.to_bytes().into_iter()).collect() + } else { + iter.collect() + } } Request::Send(conn_id, data, local_closed) => std::iter::once(RequestFlag::Send as u8) - .chain(conn_id.to_be_bytes().iter().cloned()) + .chain(conn_id.to_be_bytes().into_iter()) .chain(std::iter::once(local_closed as u8)) .chain(data.into_iter()) .collect(), @@ -324,7 +336,7 @@ mod request_deserialization_tests { let request_bytes: Vec<_> = request_bytes_prefix .iter() .cloned() - .chain(recipient_bytes.iter().cloned()) + .chain(recipient_bytes.into_iter()) .collect(); assert!(Request::try_from_bytes(&request_bytes) .unwrap_err() @@ -361,7 +373,7 @@ mod request_deserialization_tests { let request_bytes: Vec<_> = request_bytes .into_iter() - .chain(recipient_bytes.iter().cloned()) + .chain(recipient_bytes.into_iter()) .collect(); let request = Request::try_from_bytes(&request_bytes).unwrap(); @@ -370,7 +382,7 @@ mod request_deserialization_tests { assert_eq!("foo.com".to_string(), req.remote_addr); assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), req.conn_id); assert_eq!( - req.return_address.to_bytes().to_vec(), + req.return_address.unwrap().to_bytes().to_vec(), recipient.to_bytes().to_vec() ); } @@ -408,7 +420,7 @@ mod request_deserialization_tests { let request_bytes: Vec<_> = request_bytes .into_iter() - .chain(recipient_bytes.iter().cloned()) + .chain(recipient_bytes.into_iter()) .collect(); let request = Request::try_from_bytes(&request_bytes).unwrap(); @@ -417,7 +429,7 @@ mod request_deserialization_tests { assert_eq!("foo.com".to_string(), req.remote_addr); assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), req.conn_id); assert_eq!( - req.return_address.to_bytes().to_vec(), + req.return_address.unwrap().to_bytes().to_vec(), recipient.to_bytes().to_vec() ); } diff --git a/common/task/Cargo.toml b/common/task/Cargo.toml index 8240207575..ee83423f5f 100644 --- a/common/task/Cargo.toml +++ b/common/task/Cargo.toml @@ -15,5 +15,11 @@ tokio = { version = "1.21.2", features = ["macros", "sync"] } version = "1.21.2" features = ["signal", "time"] +[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures] +version = "0.4" + +[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] +version = "0.2.83" + [dev-dependencies] tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] } diff --git a/common/task/src/lib.rs b/common/task/src/lib.rs index e2b5f623df..7ac5ab8431 100644 --- a/common/task/src/lib.rs +++ b/common/task/src/lib.rs @@ -1,10 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub mod shutdown; +pub mod manager; #[cfg(not(target_arch = "wasm32"))] pub mod signal; +pub mod spawn; -pub use shutdown::{ShutdownListener, ShutdownNotifier}; +pub use manager::{StatusReceiver, StatusSender, TaskClient, TaskManager}; #[cfg(not(target_arch = "wasm32"))] pub use signal::{wait_for_signal, wait_for_signal_and_error}; + +pub use spawn::spawn_with_report_error; diff --git a/common/task/src/shutdown.rs b/common/task/src/manager.rs similarity index 52% rename from common/task/src/shutdown.rs rename to common/task/src/manager.rs index 0f8efa51d7..f40a525527 100644 --- a/common/task/src/shutdown.rs +++ b/common/task/src/manager.rs @@ -3,7 +3,7 @@ use std::{error::Error, time::Duration}; -use futures::FutureExt; +use futures::{future::pending, FutureExt, SinkExt, StreamExt}; use tokio::{ sync::{ mpsc, @@ -14,19 +14,31 @@ use tokio::{ const DEFAULT_SHUTDOWN_TIMER_SECS: u64 = 5; -pub(crate) type SentError = Box; +pub(crate) type SentError = Box; type ErrorSender = mpsc::UnboundedSender; type ErrorReceiver = mpsc::UnboundedReceiver; +pub type SentStatus = Box; +pub type StatusSender = futures::channel::mpsc::Sender; +pub type StatusReceiver = futures::channel::mpsc::Receiver; + #[derive(thiserror::Error, Debug)] enum TaskError { #[error("Task halted unexpectedly")] UnexpectedHalt, } -/// Used to notify other tasks to gracefully shutdown +// TODO: possibly we should create a `Status` trait instead of reusing `Error` +#[derive(thiserror::Error, Debug)] +pub enum TaskStatus { + #[error("Ready")] + Ready, +} + +/// Listens to status and error messages from tasks, as well as notifying them to gracefully +/// shutdown. Keeps track of if task stop unexpectedly, such as in a panic. #[derive(Debug)] -pub struct ShutdownNotifier { +pub struct TaskManager { // These channels have the dual purpose of signalling it's time to shutdown, but also to keep // track of which tasks we are still waiting for. notify_tx: watch::Sender<()>, @@ -43,13 +55,21 @@ pub struct ShutdownNotifier { // didn't manage to reliably get the explicitly sent error (and not the error sent during drop) task_drop_tx: ErrorSender, task_drop_rx: Option, + + // A task might also send non-fatal errors (effectively, warnings) while running that is not + // the result of exiting. + task_status_tx: StatusSender, + task_status_rx: Option, } -impl Default for ShutdownNotifier { +impl Default for TaskManager { fn default() -> Self { let (notify_tx, notify_rx) = watch::channel(()); let (task_halt_tx, task_halt_rx) = mpsc::unbounded_channel(); let (task_drop_tx, task_drop_rx) = mpsc::unbounded_channel(); + // The status channel is bounded (unlike the others), since it's not always the case that + // there is a listener. + let (task_status_tx, task_status_rx) = futures::channel::mpsc::channel(128); Self { notify_tx, notify_rx: Some(notify_rx), @@ -58,11 +78,13 @@ impl Default for ShutdownNotifier { task_return_error_rx: Some(task_halt_rx), task_drop_tx, task_drop_rx: Some(task_drop_rx), + task_status_tx, + task_status_rx: Some(task_status_rx), } } } -impl ShutdownNotifier { +impl TaskManager { pub fn new(shutdown_timer_secs: u64) -> Self { Self { shutdown_timer_secs, @@ -70,14 +92,15 @@ impl ShutdownNotifier { } } - pub fn subscribe(&self) -> ShutdownListener { - ShutdownListener::new( + pub fn subscribe(&self) -> TaskClient { + TaskClient::new( self.notify_rx .as_ref() .expect("Unable to subscribe to shutdown notifier that is already shutdown") .clone(), self.task_return_error_tx.clone(), self.task_drop_tx.clone(), + self.task_status_tx.clone(), ) } @@ -85,6 +108,32 @@ impl ShutdownNotifier { self.notify_tx.send(()) } + pub async fn start_status_listener(&mut self, mut sender: StatusSender) { + // Announce that we are operational. This means that in the application where this is used, + // everything is up and running and ready to go. + if let Err(msg) = sender.send(Box::new(TaskStatus::Ready)).await { + log::error!("Error sending status message: {}", msg); + }; + + if let Some(mut task_status_rx) = self.task_status_rx.take() { + log::info!("Starting status message listener"); + crate::spawn::spawn(async move { + loop { + if let Some(msg) = task_status_rx.next().await { + log::trace!("Got msg: {}", msg); + if let Err(msg) = sender.send(msg).await { + log::error!("Error sending status message: {}", msg); + } + } else { + log::trace!("Stopping since channel closed"); + break; + } + } + log::debug!("Status listener: Exiting"); + }); + } + } + pub async fn wait_for_error(&mut self) -> Option { let mut error_rx = self .task_return_error_rx @@ -109,7 +158,7 @@ impl ShutdownNotifier { } pub async fn wait_for_shutdown(&mut self) { - log::info!("Waiting for shutdown"); + log::debug!("Waiting for shutdown"); if let Some(notify_rx) = self.notify_rx.take() { drop(notify_rx); } @@ -133,9 +182,10 @@ impl ShutdownNotifier { } } -/// Listen for shutdown notifications +/// Listen for shutdown notifications, and can send error and status messages back to the +/// `TaskManager` #[derive(Clone, Debug)] -pub struct ShutdownListener { +pub struct TaskClient { // If a shutdown notification has been registered shutdown: bool, @@ -149,12 +199,14 @@ pub struct ShutdownListener { // Also notify if we dropped without shutdown being registered drop_error: ErrorSender, - // Sometimes it's necessary to clone and drop the shutdown listener during normal operation, - // for those situations we need to explicitly not drop (and trigger shutdown). - set_not_drop: bool, + // Send non-exit messages + status_msg: StatusSender, + + // The current operating mode + mode: ClientOperatingMode, } -impl ShutdownListener { +impl TaskClient { #[cfg(not(target_arch = "wasm32"))] const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); @@ -162,21 +214,51 @@ impl ShutdownListener { notify: watch::Receiver<()>, return_error: ErrorSender, drop_error: ErrorSender, - ) -> ShutdownListener { - ShutdownListener { + status_msg: StatusSender, + ) -> TaskClient { + TaskClient { shutdown: false, notify, return_error, drop_error, - set_not_drop: false, + status_msg, + mode: ClientOperatingMode::Listening, } } + // Create a dummy that will never report that we should shutdown. + pub fn dummy() -> TaskClient { + let (_notify_tx, notify_rx) = watch::channel(()); + let (task_halt_tx, _task_halt_rx) = mpsc::unbounded_channel(); + let (task_drop_tx, _task_drop_rx) = mpsc::unbounded_channel(); + //let (task_status_tx, _task_status_rx) = futures::channel::mpsc::unbounded(); + let (task_status_tx, _task_status_rx) = futures::channel::mpsc::channel(128); + TaskClient { + shutdown: false, + notify: notify_rx, + return_error: task_halt_tx, + drop_error: task_drop_tx, + status_msg: task_status_tx, + mode: ClientOperatingMode::Dummy, + } + } + + pub fn is_dummy(&self) -> bool { + self.mode.is_dummy() + } + pub fn is_shutdown(&self) -> bool { - self.shutdown + if self.mode.is_dummy() { + false + } else { + self.shutdown + } } pub async fn recv(&mut self) { + if self.mode.is_dummy() { + return pending().await; + } if self.shutdown { return; } @@ -184,7 +266,19 @@ impl ShutdownListener { self.shutdown = true; } + pub async fn recv_with_delay(&mut self) { + self.recv() + .then(|msg| async move { + sleep(Duration::from_secs(2)).await; + msg + }) + .await + } + pub async fn recv_timeout(&mut self) { + if self.mode.is_dummy() { + return pending().await; + } #[cfg(not(target_arch = "wasm32"))] tokio::time::timeout(Self::SHUTDOWN_TIMEOUT, self.recv()) .await @@ -192,6 +286,9 @@ impl ShutdownListener { } pub fn is_shutdown_poll(&mut self) -> bool { + if self.mode.is_dummy() { + return false; + } if self.shutdown { return true; } @@ -210,21 +307,39 @@ impl ShutdownListener { } } + // This listener should to *not* notify the ShutdownNotifier to shutdown when dropped. For + // example when we clone the listener for a task handling connections, we often want to drop + // without signal failure. + pub fn mark_as_success(&mut self) { + self.mode.set_should_not_signal_on_drop(); + } + pub fn send_we_stopped(&mut self, err: SentError) { - log::trace!("Notifying we stopped: {:?}", err); + if self.mode.is_dummy() { + return; + } + log::trace!("Notifying we stopped: {err}"); if self.return_error.send(err).is_err() { log::error!("Failed to send back error message"); } } - pub fn mark_as_success(&mut self) { - self.set_not_drop = true; + pub fn send_status_msg(&mut self, msg: SentStatus) { + if self.mode.is_dummy() { + return; + } + // Since it's not always the case that anyone is listening, just try send and ignore any + // failures. + self.status_msg.try_send(msg).ok(); } } -impl Drop for ShutdownListener { +impl Drop for TaskClient { fn drop(&mut self) { - if !self.set_not_drop && !self.is_shutdown_poll() { + if !self.mode.should_signal_on_drop() { + return; + } + if !self.is_shutdown_poll() { log::trace!("Notifying stop on unexpected drop"); // If we can't send, well then there is not much to do self.drop_error @@ -234,13 +349,44 @@ impl Drop for ShutdownListener { } } +#[derive(Clone, Debug, PartialEq, Eq)] +enum ClientOperatingMode { + // Normal operations + Listening, + // Normal operations, but we don't report back if the we stop by getting dropped. + ListeningButDontReportHalt, + // Dummy mode, for when we don't do anything at all. + Dummy, +} + +impl ClientOperatingMode { + fn is_dummy(&self) -> bool { + self == &ClientOperatingMode::Dummy + } + + fn should_signal_on_drop(&self) -> bool { + match self { + ClientOperatingMode::Listening => true, + ClientOperatingMode::ListeningButDontReportHalt | ClientOperatingMode::Dummy => false, + } + } + + fn set_should_not_signal_on_drop(&mut self) { + use ClientOperatingMode::{Dummy, Listening, ListeningButDontReportHalt}; + *self = match &self { + ListeningButDontReportHalt | Listening => ListeningButDontReportHalt, + Dummy => Dummy, + }; + } +} + #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn signal_shutdown() { - let shutdown = ShutdownNotifier::default(); + let shutdown = TaskManager::default(); let mut listener = shutdown.subscribe(); let task = tokio::spawn(async move { diff --git a/common/task/src/signal.rs b/common/task/src/signal.rs index a04a0f5601..483c7c630b 100644 --- a/common/task/src/signal.rs +++ b/common/task/src/signal.rs @@ -1,4 +1,4 @@ -use crate::{shutdown::SentError, ShutdownNotifier}; +use crate::{manager::SentError, TaskManager}; #[cfg(unix)] pub async fn wait_for_signal() { @@ -29,7 +29,7 @@ pub async fn wait_for_signal() { } #[cfg(unix)] -pub async fn wait_for_signal_and_error(shutdown: &mut ShutdownNotifier) -> Result<(), SentError> { +pub async fn wait_for_signal_and_error(shutdown: &mut TaskManager) -> Result<(), SentError> { use tokio::signal::unix::{signal, SignalKind}; let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel"); @@ -56,7 +56,7 @@ pub async fn wait_for_signal_and_error(shutdown: &mut ShutdownNotifier) -> Resul } #[cfg(not(unix))] -pub async fn wait_for_signal_and_error(shutdown: &mut ShutdownNotifier) -> Result<(), SentError> { +pub async fn wait_for_signal_and_error(shutdown: &mut TaskManager) -> Result<(), SentError> { tokio::select! { _ = tokio::signal::ctrl_c() => { log::info!("Received SIGINT"); diff --git a/common/task/src/spawn.rs b/common/task/src/spawn.rs new file mode 100644 index 0000000000..d30c9ac850 --- /dev/null +++ b/common/task/src/spawn.rs @@ -0,0 +1,33 @@ +use crate::TaskClient; +use std::future::Future; + +#[cfg(target_arch = "wasm32")] +pub(crate) fn spawn(future: F) +where + F: Future + 'static, +{ + wasm_bindgen_futures::spawn_local(future); +} + +#[cfg(not(target_arch = "wasm32"))] +pub(crate) fn spawn(future: F) +where + F: Future + Send + 'static, + F::Output: Send + 'static, +{ + tokio::spawn(future); +} + +pub fn spawn_with_report_error(future: F, mut shutdown: TaskClient) +where + F: Future> + Send + 'static, + T: 'static, + E: std::error::Error + Send + Sync + 'static, +{ + let future_that_sends = async move { + if let Err(err) = future.await { + shutdown.send_we_stopped(Box::new(err)); + } + }; + spawn(future_that_sends); +} diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index f2a8a899fd..2996aeb639 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" bs58 = "0.4" log = "0.4" rand = { version = "0.7.3", features = ["wasm-bindgen"] } +thiserror = "1.0.37" ## internal crypto = { path = "../crypto" } diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index 57b34146e6..9df2156b32 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -7,63 +7,25 @@ use mixnet_contract_common::GatewayBond; use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress}; use nymsphinx_types::Node as SphinxNode; use std::convert::{TryFrom, TryInto}; -use std::fmt::{self, Display, Formatter}; +use std::fmt; use std::io; use std::net::SocketAddr; +use thiserror::Error; -#[derive(Debug)] +#[derive(Error, Debug)] pub enum GatewayConversionError { - InvalidIdentityKey(identity::Ed25519RecoveryError), - InvalidSphinxKey(encryption::KeyRecoveryError), - InvalidAddress(String, io::Error), - InvalidStake, - Other(Box), -} + #[error("gateway identity key was malformed - {0}")] + InvalidIdentityKey(#[from] identity::Ed25519RecoveryError), -impl From for GatewayConversionError { - fn from(err: encryption::KeyRecoveryError) -> Self { - GatewayConversionError::InvalidSphinxKey(err) - } -} + #[error("gateway sphinx key was malformed - {0}")] + InvalidSphinxKey(#[from] encryption::KeyRecoveryError), -impl From for GatewayConversionError { - fn from(err: identity::Ed25519RecoveryError) -> Self { - GatewayConversionError::InvalidIdentityKey(err) - } -} - -impl Display for GatewayConversionError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - GatewayConversionError::InvalidIdentityKey(err) => write!( - f, - "failed to convert gateway due to invalid identity key - {}", - err - ), - GatewayConversionError::InvalidSphinxKey(err) => write!( - f, - "failed to convert gateway due to invalid sphinx key - {}", - err - ), - GatewayConversionError::InvalidAddress(address, err) => { - write!( - f, - "failed to convert gateway due to invalid address {} - {}", - address, err - ) - } - GatewayConversionError::InvalidStake => { - write!(f, "failed to convert gateway due to invalid stake") - } - GatewayConversionError::Other(err) => { - write!( - f, - "failed to convert gateway due to another error - {}", - err - ) - } - } - } + #[error("'{value}' is not a valid gateway address - {source}")] + InvalidAddress { + value: String, + #[source] + source: io::Error, + }, } #[derive(Debug, Clone)] @@ -123,14 +85,22 @@ impl<'a> TryFrom<&'a GatewayBond> for Node { type Error = GatewayConversionError; fn try_from(bond: &'a GatewayBond) -> Result { - let host: NetworkAddress = bond.gateway.host.parse().map_err(|err| { - GatewayConversionError::InvalidAddress(bond.gateway.host.clone(), err) - })?; + let host: NetworkAddress = + bond.gateway + .host + .parse() + .map_err(|err| GatewayConversionError::InvalidAddress { + value: bond.gateway.host.clone(), + source: err, + })?; // try to completely resolve the host in the mix situation to avoid doing it every // single time we want to construct a path let mix_host = host.to_socket_addrs(bond.gateway.mix_port).map_err(|err| { - GatewayConversionError::InvalidAddress(bond.gateway.host.clone(), err) + GatewayConversionError::InvalidAddress { + value: bond.gateway.host.clone(), + source: err, + } })?[0]; Ok(Node { diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 6ef41c9aad..527176339d 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::filter::VersionFilterable; @@ -14,19 +14,31 @@ use std::fmt::{self, Display, Formatter}; use std::io; use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; use std::str::FromStr; +use thiserror::Error; pub mod filter; pub mod gateway; pub mod mix; -#[derive(Debug)] +#[derive(Debug, Clone, Error)] pub enum NymTopologyError { - InvalidMixLayerError, - MissingLayerError(Vec), - NonExistentGatewayError, + #[error("The provided network topology is empty - there are no mixnodes and no gateways on it - the network request(s) probably failed")] + EmptyNetworkTopology, - InvalidNumberOfHopsError, - NoMixesOnLayerAvailable(MixLayer), + #[error("The provided network topology has no gateways available")] + NoGatewaysAvailable, + + #[error("The provided network topology has no mixnodes available")] + NoMixnodesAvailable, + + #[error("Gateway with identity key {identity_key} doesn't exist")] + NonExistentGatewayError { identity_key: String }, + + #[error("Wanted to create a mix route with {requested} hops, while only {available} layers are available")] + InvalidNumberOfHopsError { available: usize, requested: usize }, + + #[error("No mixnodes available on layer {layer}")] + EmptyMixLayer { layer: MixLayer }, } #[derive(Debug, Clone)] @@ -84,6 +96,10 @@ impl NymTopology { &self.mixes } + pub fn num_mixnodes(&self) -> usize { + self.mixes.values().flat_map(|m| m.iter()).count() + } + pub fn mixes_as_vec(&self) -> Vec { let mut mixes: Vec = vec![]; @@ -130,7 +146,10 @@ impl NymTopology { use rand::seq::SliceRandom; if self.mixes.len() < num_mix_hops as usize { - return Err(NymTopologyError::InvalidNumberOfHopsError); + return Err(NymTopologyError::InvalidNumberOfHopsError { + available: self.mixes.len(), + requested: num_mix_hops as usize, + }); } let mut route = Vec::with_capacity(num_mix_hops as usize); @@ -140,13 +159,13 @@ impl NymTopology { let layer_mixes = self .mixes .get(&layer) - .ok_or(NymTopologyError::NoMixesOnLayerAvailable(layer))?; + .ok_or(NymTopologyError::EmptyMixLayer { layer })?; // choose a random mix from the above list // this can return a 'None' only if slice is empty let random_mix = layer_mixes .choose(rng) - .ok_or(NymTopologyError::NoMixesOnLayerAvailable(layer))?; + .ok_or(NymTopologyError::EmptyMixLayer { layer })?; route.push(random_mix.into()); } @@ -165,9 +184,11 @@ impl NymTopology { // I don't think there's a need for this RNG to be crypto-secure R: Rng + ?Sized, { - let gateway = self - .get_gateway(gateway_identity) - .ok_or(NymTopologyError::NonExistentGatewayError)?; + let gateway = self.get_gateway(gateway_identity).ok_or( + NymTopologyError::NonExistentGatewayError { + identity_key: gateway_identity.to_base58_string(), + }, + )?; Ok(self .random_mix_route(rng, num_mix_hops)? @@ -182,29 +203,39 @@ impl NymTopology { } /// Checks if a mixnet path can be constructed using the specified number of hops - pub fn can_construct_path_through(&self, num_mix_hops: u8) -> bool { - // if there are no gateways present, we can't do anything - if self.gateways.is_empty() { - return false; + pub fn ensure_can_construct_path_through( + &self, + num_mix_hops: u8, + ) -> Result<(), NymTopologyError> { + let mixnodes = self.mixes(); + // 1. is it completely empty? + if mixnodes.is_empty() && self.gateways().is_empty() { + return Err(NymTopologyError::EmptyNetworkTopology); } - // early termination - if self.mixes.is_empty() { - return false; + // 2. does it have any mixnode at all? + if mixnodes.is_empty() { + return Err(NymTopologyError::NoMixnodesAvailable); } - // make sure there's at least one mix per layer - for i in 1..=num_mix_hops { - match self.mixes.get(&i) { - None => return false, - Some(layer_entry) => { - if layer_entry.is_empty() { - return false; + // 3. does it have any gateways at all? + if self.gateways().is_empty() { + return Err(NymTopologyError::NoGatewaysAvailable); + } + + // 4. does it have a mixnode on each layer? + for layer in 1..=num_mix_hops { + match mixnodes.get(&layer) { + None => return Err(NymTopologyError::EmptyMixLayer { layer }), + Some(layer_nodes) => { + if layer_nodes.is_empty() { + return Err(NymTopologyError::EmptyMixLayer { layer }); } } } } - true + + Ok(()) } #[must_use] @@ -245,7 +276,7 @@ pub fn nym_topology_from_detailed( match bond.try_into() { Ok(mix) => layer_entry.push(mix), Err(err) => { - warn!("Mix {} / {} is malformed - {}", mix_id, mix_identity, err); + warn!("Mix {} / {} is malformed - {err}", mix_id, mix_identity); continue; } } @@ -257,7 +288,7 @@ pub fn nym_topology_from_detailed( match bond.try_into() { Ok(gate) => gateways.push(gate), Err(err) => { - warn!("Gateway {} is malformed - {}", gate_id, err); + warn!("Gateway {} is malformed - {err}", gate_id); continue; } } diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index 81e0308b2f..08d7f5c0b7 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -7,63 +7,24 @@ use mixnet_contract_common::{Layer, MixId, MixNodeBond}; use nymsphinx_addressing::nodes::NymNodeRoutingAddress; use nymsphinx_types::Node as SphinxNode; use std::convert::{TryFrom, TryInto}; -use std::fmt::{self, Display, Formatter}; use std::io; use std::net::SocketAddr; +use thiserror::Error; -#[derive(Debug)] +#[derive(Error, Debug)] pub enum MixnodeConversionError { - InvalidIdentityKey(identity::Ed25519RecoveryError), - InvalidSphinxKey(encryption::KeyRecoveryError), - InvalidAddress(String, io::Error), - InvalidStake, - Other(Box), -} + #[error("mixnode identity key was malformed - {0}")] + InvalidIdentityKey(#[from] identity::Ed25519RecoveryError), -impl From for MixnodeConversionError { - fn from(err: encryption::KeyRecoveryError) -> Self { - MixnodeConversionError::InvalidSphinxKey(err) - } -} + #[error("mixnode sphinx key was malformed - {0}")] + InvalidSphinxKey(#[from] encryption::KeyRecoveryError), -impl From for MixnodeConversionError { - fn from(err: identity::Ed25519RecoveryError) -> Self { - MixnodeConversionError::InvalidIdentityKey(err) - } -} - -impl Display for MixnodeConversionError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - MixnodeConversionError::InvalidIdentityKey(err) => write!( - f, - "failed to convert mixnode due to invalid identity key - {}", - err - ), - MixnodeConversionError::InvalidSphinxKey(err) => write!( - f, - "failed to convert mixnode due to invalid sphinx key - {}", - err - ), - MixnodeConversionError::InvalidAddress(address, err) => { - write!( - f, - "failed to convert mixnode due to invalid address {} - {}", - address, err - ) - } - MixnodeConversionError::InvalidStake => { - write!(f, "failed to convert mixnode due to invalid stake") - } - MixnodeConversionError::Other(err) => { - write!( - f, - "failed to convert mixnode due to another error - {}", - err - ) - } - } - } + #[error("'{value}' is not a valid mixnode address - {source}")] + InvalidAddress { + value: String, + #[source] + source: io::Error, + }, } #[derive(Debug, Clone)] @@ -100,16 +61,22 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node { type Error = MixnodeConversionError; fn try_from(bond: &'a MixNodeBond) -> Result { - let host: NetworkAddress = bond.mix_node.host.parse().map_err(|err| { - MixnodeConversionError::InvalidAddress(bond.mix_node.host.clone(), err) - })?; + let host: NetworkAddress = + bond.mix_node + .host + .parse() + .map_err(|err| MixnodeConversionError::InvalidAddress { + value: bond.mix_node.host.clone(), + source: err, + })?; // try to completely resolve the host in the mix situation to avoid doing it every // single time we want to construct a path let mix_host = host .to_socket_addrs(bond.mix_node.mix_port) - .map_err(|err| { - MixnodeConversionError::InvalidAddress(bond.mix_node.host.clone(), err) + .map_err(|err| MixnodeConversionError::InvalidAddress { + value: bond.mix_node.host.clone(), + source: err, })?[0]; Ok(Node { diff --git a/common/types/Cargo.toml b/common/types/Cargo.toml index 54603c02d2..798303d9a8 100644 --- a/common/types/Cargo.toml +++ b/common/types/Cargo.toml @@ -23,7 +23,7 @@ cosmwasm-std = "1.0.0" cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nymd-client", + "nyxd-client", ] } mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } diff --git a/common/types/src/currency.rs b/common/types/src/currency.rs index 38292d11d3..c9ba2e908b 100644 --- a/common/types/src/currency.rs +++ b/common/types/src/currency.rs @@ -9,7 +9,7 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::fmt::{Display, Formatter}; use strum::{Display, EnumString, EnumVariantNames}; -use validator_client::nymd::Coin; +use validator_client::nyxd::Coin; #[cfg(feature = "generate-ts")] use ts_rs::{Dependency, TS}; diff --git a/common/types/src/error.rs b/common/types/src/error.rs index b0874ba7aa..39248500e2 100644 --- a/common/types/src/error.rs +++ b/common/types/src/error.rs @@ -1,16 +1,16 @@ use serde::{Serialize, Serializer}; use std::io; use thiserror::Error; -use validator_client::validator_api::error::ValidatorAPIError; -use validator_client::{nymd::error::NymdError, ValidatorClientError}; +use validator_client::nym_api::error::NymAPIError; +use validator_client::{nyxd::error::NyxdError, ValidatorClientError}; // TODO: ask @MS why this even exists #[derive(Error, Debug)] pub enum TypesError { #[error("{source}")] - NymdError { + NyxdError { #[from] - source: NymdError, + source: NyxdError, }, #[error("{source}")] CosmwasmStd { @@ -23,9 +23,9 @@ pub enum TypesError { source: eyre::Report, }, #[error("{source}")] - ValidatorApiError { + NymApiError { #[from] - source: ValidatorAPIError, + source: NymAPIError, }, #[error("{source}")] IOError { @@ -52,8 +52,8 @@ pub enum TypesError { #[from] source: cosmwasm_std::DecimalRangeExceeded, }, - #[error("No validator API URL configured")] - NoValidatorApiUrlConfigured, + #[error("No nym API URL configured")] + NoNymApiUrlConfigured, #[error("{0} is not a valid amount string")] InvalidAmount(String), #[error("{0} is not a valid denomination string")] @@ -88,10 +88,10 @@ impl Serialize for TypesError { impl From for TypesError { fn from(e: ValidatorClientError) -> Self { match e { - ValidatorClientError::ValidatorAPIError { source } => source.into(), + ValidatorClientError::NymAPIError { source } => source.into(), ValidatorClientError::MalformedUrlProvided(e) => e.into(), - ValidatorClientError::NymdError(e) => e.into(), - ValidatorClientError::NoAPIUrlAvailable => TypesError::NoValidatorApiUrlConfigured, + ValidatorClientError::NyxdError(e) => e.into(), + ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured, } } } diff --git a/common/types/src/fees.rs b/common/types/src/fees.rs index 747693ce9c..561a1a8e81 100644 --- a/common/types/src/fees.rs +++ b/common/types/src/fees.rs @@ -1,6 +1,6 @@ use crate::currency::DecCoin; use serde::{Deserialize, Serialize}; -use validator_client::nymd::Fee; +use validator_client::nyxd::Fee; #[cfg(feature = "generate-ts")] use ts_rs::{Dependency, TS}; @@ -53,7 +53,7 @@ impl TS for FeeDetails { #[cfg(feature = "generate-ts")] pub mod ts_type_helpers { use serde::{Deserialize, Serialize}; - use validator_client::nymd::GasAdjustment; + use validator_client::nyxd::GasAdjustment; #[derive(Debug, Clone, Serialize, Deserialize, ts_rs::TS)] #[ts(export_to = "ts-packages/types/src/types/rust/Fee.ts")] diff --git a/common/types/src/gas.rs b/common/types/src/gas.rs index 9441008569..23e004293b 100644 --- a/common/types/src/gas.rs +++ b/common/types/src/gas.rs @@ -1,6 +1,6 @@ use cosmrs::tx::Gas as CosmrsGas; use serde::{Deserialize, Serialize}; -use validator_client::nymd::cosmwasm_client::types::GasInfo as ValidatorClientGasInfo; +use validator_client::nyxd::cosmwasm_client::types::GasInfo as ValidatorClientGasInfo; #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( diff --git a/common/types/src/gateway.rs b/common/types/src/gateway.rs index 8895a69761..221097354c 100644 --- a/common/types/src/gateway.rs +++ b/common/types/src/gateway.rs @@ -5,6 +5,7 @@ use mixnet_contract_common::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::fmt; #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( @@ -75,3 +76,37 @@ impl GatewayBond { }) } } + +#[derive(Serialize, Deserialize)] +pub struct GatewayNodeDetailsResponse { + pub identity_key: String, + pub sphinx_key: String, + pub owner_signature: String, + pub announce_address: String, + pub bind_address: String, + pub version: String, + pub mix_port: u16, + pub clients_port: u16, + pub data_store: String, +} + +impl fmt::Display for GatewayNodeDetailsResponse { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Identity Key: {}", self.identity_key)?; + writeln!(f, "Sphinx Key: {}", self.sphinx_key)?; + writeln!(f, "Owner Signature: {}", self.owner_signature)?; + writeln!( + f, + "Host: {} (bind address: {})", + self.announce_address, self.bind_address + )?; + writeln!(f, "Version: {}", self.version)?; + writeln!( + f, + "Mix Port: {}, Clients port: {}", + self.mix_port, self.clients_port + )?; + + writeln!(f, "Data store is at: {}", self.data_store) + } +} diff --git a/common/types/src/mixnode.rs b/common/types/src/mixnode.rs index e1c3bf4940..fe8dd848bb 100644 --- a/common/types/src/mixnode.rs +++ b/common/types/src/mixnode.rs @@ -12,6 +12,7 @@ use mixnet_contract_common::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::fmt; #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( @@ -161,3 +162,42 @@ impl MixNodeCostParams { }) } } + +#[derive(Serialize, Deserialize)] +pub struct MixnodeNodeDetailsResponse { + pub identity_key: String, + pub sphinx_key: String, + pub owner_signature: String, + pub announce_address: String, + pub bind_address: String, + pub version: String, + pub mix_port: u16, + pub http_api_port: u16, + pub verloc_port: u16, + pub wallet_address: Option, +} + +impl fmt::Display for MixnodeNodeDetailsResponse { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let wallet_address = self.wallet_address.clone().unwrap_or_default(); + writeln!(f, "Identity Key: {}", self.identity_key)?; + writeln!(f, "Sphinx Key: {}", self.sphinx_key)?; + writeln!(f, "Owner Signature: {}", self.owner_signature)?; + writeln!( + f, + "Host: {} (bind address: {})", + self.announce_address, self.bind_address + )?; + writeln!(f, "Version: {}", self.version)?; + writeln!( + f, + "Mix Port: {}, Verloc port: {}, Http Port: {}\n", + self.mix_port, self.verloc_port, self.http_api_port + )?; + writeln!( + f, + "You are bonding to wallet address: {}\n\n", + wallet_address + ) + } +} diff --git a/common/types/src/pending_events.rs b/common/types/src/pending_events.rs index afe5a54d9e..e9d578dc12 100644 --- a/common/types/src/pending_events.rs +++ b/common/types/src/pending_events.rs @@ -57,6 +57,10 @@ pub enum PendingEpochEventData { mix_id: MixId, proxy: Option, }, + PledgeMore { + mix_id: MixId, + amount: DecCoin, + }, UnbondMixnode { mix_id: MixId, }, @@ -91,6 +95,12 @@ impl PendingEpochEventData { mix_id, proxy: proxy.map(|p| p.into_string()), }), + MixnetContractPendingEpochEventKind::PledgeMore { mix_id, amount } => { + Ok(PendingEpochEventData::PledgeMore { + mix_id, + amount: reg.attempt_convert_to_display_dec_coin(amount.into())?, + }) + } MixnetContractPendingEpochEventKind::UnbondMixnode { mix_id } => { Ok(PendingEpochEventData::UnbondMixnode { mix_id }) } diff --git a/common/types/src/transaction.rs b/common/types/src/transaction.rs index b5dd50a4ae..e896f95130 100644 --- a/common/types/src/transaction.rs +++ b/common/types/src/transaction.rs @@ -2,8 +2,8 @@ use crate::currency::DecCoin; use crate::error::TypesError; use crate::gas::{Gas, GasInfo}; use serde::{Deserialize, Serialize}; -use validator_client::nymd::cosmwasm_client::types::ExecuteResult; -use validator_client::nymd::TxResponse; +use validator_client::nyxd::cosmwasm_client::types::ExecuteResult; +use validator_client::nyxd::TxResponse; #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( diff --git a/common/wasm-utils/Cargo.toml b/common/wasm-utils/Cargo.toml index 75021ddad9..be39421ce2 100644 --- a/common/wasm-utils/Cargo.toml +++ b/common/wasm-utils/Cargo.toml @@ -16,6 +16,7 @@ wasm-bindgen-futures = "0.4" [dependencies.tungstenite] version = "0.13" default-features = false +optional = true [dependencies.web-sys] version = "0.3" @@ -30,3 +31,9 @@ features = [ "WebSocket", "Window", ] +optional = true + +[features] +default = ["sleep"] +websocket = ["tungstenite", "web-sys"] +sleep = ["web-sys"] \ No newline at end of file diff --git a/common/wasm-utils/src/lib.rs b/common/wasm-utils/src/lib.rs index 94b8faae05..8e0205c6de 100644 --- a/common/wasm-utils/src/lib.rs +++ b/common/wasm-utils/src/lib.rs @@ -5,8 +5,8 @@ use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; -use web_sys::window; +#[cfg(feature = "websocket")] pub mod websocket; // will cause messages to be written as if console.log("...") was called @@ -39,9 +39,10 @@ extern "C" { pub fn error(s: &str); } +#[cfg(feature = "sleep")] pub async fn sleep(ms: i32) -> Result<(), JsValue> { let promise = Promise::new(&mut |yes, _| { - let win = window().expect("no window available!"); + let win = web_sys::window().expect("no window available!"); win.set_timeout_with_callback_and_timeout_and_arguments_0(&yes, ms) .unwrap(); }); diff --git a/contracts/.gitignore b/contracts/.gitignore new file mode 100644 index 0000000000..ffa3bbd20e --- /dev/null +++ b/contracts/.gitignore @@ -0,0 +1 @@ +Cargo.lock \ No newline at end of file diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index f57a633956..d52edc427d 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -1,9 +1,15 @@ ## Unreleased -## Added +## [nym-contracts-v1.1.2](https://github.com/nymtech/nym/tree/nym-contracts-v1.1.2) (2022-12-07) + +### Added - Added migration code to the mixnet contract to allow updating stored vesting contract address to make it easier to deploy any future environments ([#1759],[#1769]) +- Added an option to pledge additional tokens without the need to rebond minxode ([#1679]) +- Added support for node families ([#1670]) +[#1670]: https://github.com/nymtech/nym/pull/1670 +[#1679]: https://github.com/nymtech/nym/pull/1679 [#1759]: https://github.com/nymtech/nym/pull/1759 [#1769]: https://github.com/nymtech/nym/pull/1769 @@ -93,4 +99,4 @@ [#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 \ No newline at end of file +[#1373]: https://github.com/nymtech/nym/pull/1373 diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock deleted file mode 100644 index d2fc273d22..0000000000 --- a/contracts/Cargo.lock +++ /dev/null @@ -1,1783 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "ctr", - "opaque-debug 0.3.0", -] - -[[package]] -name = "anyhow" -version = "1.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "bandwidth-claim-contract" -version = "0.1.0" -dependencies = [ - "schemars", - "serde", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "base64ct" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -dependencies = [ - "byte-tools", - "crypto-mac 0.7.0", - "digest 0.8.1", - "opaque-debug 0.2.3", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "bumpalo" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" -dependencies = [ - "byteorder", - "keystream", -] - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time 0.1.43", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "coconut-bandwidth" -version = "0.1.0" -dependencies = [ - "bandwidth-claim-contract", - "coconut-bandwidth-contract-common", - "cosmwasm-std", - "cosmwasm-storage", - "cw-controllers", - "cw-storage-plus", - "multisig-contract-common", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "coconut-dkg" -version = "0.1.0" -dependencies = [ - "coconut-dkg-common", - "cosmwasm-std", - "cosmwasm-storage", - "cw-controllers", - "cw-storage-plus", - "cw4", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "coconut-dkg-common" -version = "0.1.0" -dependencies = [ - "contracts-common", - "cosmwasm-std", - "cw-utils", - "multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "coconut-test" -version = "0.1.0" -dependencies = [ - "bandwidth-claim-contract", - "coconut-bandwidth", - "coconut-bandwidth-contract-common", - "coconut-dkg", - "coconut-dkg-common", - "cosmwasm-std", - "cosmwasm-storage", - "cw-controllers", - "cw-multi-test", - "cw-storage-plus", - "cw-utils", - "cw3-flex-multisig", - "cw4", - "cw4-group", - "multisig-contract-common", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - -[[package]] -name = "contracts-common" -version = "0.1.0" -dependencies = [ - "bs58", - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cosmwasm-crypto" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" -dependencies = [ - "digest 0.9.0", - "ed25519-zebra", - "k256", - "rand_core 0.6.3", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" -dependencies = [ - "syn", -] - -[[package]] -name = "cosmwasm-schema" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772e80bbad231a47a2068812b723a1ff81dd4a0d56c9391ac748177bea3a61da" -dependencies = [ - "schemars", - "serde_json", -] - -[[package]] -name = "cosmwasm-std" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" -dependencies = [ - "base64", - "cosmwasm-crypto", - "cosmwasm-derive", - "forward_ref", - "schemars", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "cosmwasm-storage" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5" -dependencies = [ - "cosmwasm-std", - "serde", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto" -version = "0.1.0" -dependencies = [ - "bs58", - "ed25519-dalek", - "nymsphinx-types", - "pemstore", - "rand", - "subtle-encoding", - "x25519-dalek", -] - -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array 0.14.5", - "rand_core 0.6.3", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.5", - "subtle 2.4.1", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "cw-controllers" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw-multi-test" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e" -dependencies = [ - "anyhow", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus", - "cw-utils", - "derivative", - "itertools", - "prost", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw-storage-plus" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw2" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "cw3" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "schemars", - "serde", -] - -[[package]] -name = "cw3-fixed-multisig" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "cw2", - "cw3", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw3-flex-multisig" -version = "0.13.1" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-multi-test", - "cw-storage-plus", - "cw-utils", - "cw2", - "cw3", - "cw3-fixed-multisig", - "cw4", - "cw4-group", - "multisig-contract-common", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw4" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "cw4-group" -version = "0.13.4" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw-controllers", - "cw-storage-plus", - "cw-utils", - "cw2", - "cw4", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "dyn-clone" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" - -[[package]] -name = "ecdsa" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand", - "serde", - "sha2", - "zeroize", -] - -[[package]] -name = "ed25519-zebra" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" -dependencies = [ - "curve25519-dalek", - "hex", - "rand_core 0.6.3", - "serde", - "sha2", - "thiserror", - "zeroize", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "elliptic-curve" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff", - "generic-array 0.14.5", - "group", - "rand_core 0.6.3", - "sec1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ff" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" -dependencies = [ - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.10.2+wasi-snapshot-preview1", -] - -[[package]] -name = "getset" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "git2" -version = "0.13.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "url", -] - -[[package]] -name = "group" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" -dependencies = [ - "ff", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" -dependencies = [ - "digest 0.9.0", - "hmac", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2", -] - -[[package]] -name = "keystream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.112" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" - -[[package]] -name = "libgit2-sys" -version = "0.12.26+1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" - -[[package]] -name = "libz-sys" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "lioness" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" -dependencies = [ - "arrayref", - "blake2", - "chacha", - "keystream", -] - -[[package]] -name = "log" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "mixnet-contract" -version = "1.1.0" -dependencies = [ - "bs58", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "crypto", - "cw-storage-plus", - "mixnet-contract-common", - "rand_chacha", - "schemars", - "serde", - "thiserror", - "time 0.3.6", - "vergen", - "vesting-contract-common", -] - -[[package]] -name = "mixnet-contract-common" -version = "0.1.0" -dependencies = [ - "bs58", - "contracts-common", - "cosmwasm-std", - "humantime-serde", - "log", - "schemars", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "time 0.3.6", -] - -[[package]] -name = "multisig-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "cw3", - "cw3-fixed-multisig", - "cw4", - "schemars", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_threads" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" -dependencies = [ - "libc", -] - -[[package]] -name = "nymsphinx-types" -version = "0.1.0" -dependencies = [ - "sphinx", -] - -[[package]] -name = "once_cell" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64", - "once_cell", - "regex", -] - -[[package]] -name = "pemstore" -version = "0.1.0" -dependencies = [ - "pem", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - -[[package]] -name = "pkg-config" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "prost" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.3", -] - -[[package]] -name = "rand_distr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "regex" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "rfc6979" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "schemars" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] -name = "sec1" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" -dependencies = [ - "der", - "generic-array 0.14.5", - "pkcs8", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "semver" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" - -[[package]] -name = "serde" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "signature" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" -dependencies = [ - "digest 0.9.0", - "rand_core 0.6.3", -] - -[[package]] -name = "sphinx" -version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=c494250f2a78bed33a618d470792418eee932859#c494250f2a78bed33a618d470792418eee932859" -dependencies = [ - "aes", - "arrayref", - "blake2", - "bs58", - "byteorder", - "chacha", - "curve25519-dalek", - "digest 0.9.0", - "hkdf", - "hmac", - "lioness", - "log", - "rand", - "rand_distr", - "sha2", - "subtle 2.4.1", -] - -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-encoding" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" -dependencies = [ - "zeroize", -] - -[[package]] -name = "syn" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d54b9298e05179c335de2b9645d061255bcd5155f843b3e328d2cfe0a5b413" -dependencies = [ - "itoa", - "libc", - "num_threads", - "time-macros", -] - -[[package]] -name = "time-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" - -[[package]] -name = "tinyvec" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vergen" -version = "5.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e" -dependencies = [ - "anyhow", - "cfg-if", - "chrono", - "enum-iterator", - "getset", - "git2", - "rustc_version", - "rustversion", - "thiserror", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vesting-contract" -version = "1.1.0" -dependencies = [ - "contracts-common", - "cosmwasm-std", - "cw-storage-plus", - "mixnet-contract-common", - "schemars", - "serde", - "thiserror", - "vergen", - "vesting-contract-common", -] - -[[package]] -name = "vesting-contract-common" -version = "0.1.0" -dependencies = [ - "contracts-common", - "cosmwasm-std", - "mixnet-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasm-bindgen" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] diff --git a/contracts/basic-bandwidth-generation/package-lock.json b/contracts/basic-bandwidth-generation/package-lock.json index c954a7d488..1498ec38eb 100644 --- a/contracts/basic-bandwidth-generation/package-lock.json +++ b/contracts/basic-bandwidth-generation/package-lock.json @@ -15,12 +15,12 @@ }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.5", - "@nomiclabs/hardhat-etherscan": "^3.0.3", + "@nomiclabs/hardhat-etherscan": "^3.1.0", "@nomiclabs/hardhat-waffle": "^2.0.3", "chai": "^4.3.6", "ethereum-waffle": "^3.4.4", "ethers": "^5.6.1", - "hardhat": "^2.9.2", + "hardhat": "^2.11.2", "solidity-coverage": "^0.7.20" } }, @@ -397,48 +397,6 @@ "node": ">=10.0" } }, - "node_modules/@ethereumjs/block": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-3.6.2.tgz", - "integrity": "sha512-mOqYWwMlAZpYUEOEqt7EfMFuVL2eyLqWWIzcf4odn6QgXY8jBI2NhVuJncrMCKeMZrsJAe7/auaRRB6YcdH+Qw==", - "dependencies": { - "@ethereumjs/common": "^2.6.3", - "@ethereumjs/tx": "^3.5.1", - "ethereumjs-util": "^7.1.4", - "merkle-patricia-tree": "^4.2.4" - } - }, - "node_modules/@ethereumjs/blockchain": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/blockchain/-/blockchain-5.5.2.tgz", - "integrity": "sha512-Jz26iJmmsQtngerW6r5BDFaew/f2mObLrRZo3rskLOx1lmtMZ8+TX/vJexmivrnWgmAsTdNWhlKUYY4thPhPig==", - "dependencies": { - "@ethereumjs/block": "^3.6.2", - "@ethereumjs/common": "^2.6.3", - "@ethereumjs/ethash": "^1.1.0", - "debug": "^4.3.3", - "ethereumjs-util": "^7.1.4", - "level-mem": "^5.0.1", - "lru-cache": "^5.1.1", - "semaphore-async-await": "^1.5.1" - } - }, - "node_modules/@ethereumjs/blockchain/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@ethereumjs/common": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.3.tgz", @@ -448,18 +406,6 @@ "ethereumjs-util": "^7.1.4" } }, - "node_modules/@ethereumjs/ethash": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/ethash/-/ethash-1.1.0.tgz", - "integrity": "sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA==", - "dependencies": { - "@ethereumjs/block": "^3.5.0", - "@types/levelup": "^4.3.0", - "buffer-xor": "^2.0.1", - "ethereumjs-util": "^7.1.1", - "miller-rabin": "^4.0.0" - } - }, "node_modules/@ethereumjs/tx": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.1.tgz", @@ -469,41 +415,6 @@ "ethereumjs-util": "^7.1.4" } }, - "node_modules/@ethereumjs/vm": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/vm/-/vm-5.8.0.tgz", - "integrity": "sha512-mn2G2SX79QY4ckVvZUfxlNUpzwT2AEIkvgJI8aHoQaNYEHhH8rmdVDIaVVgz6//PjK52BZsK23afz+WvSR0Qqw==", - "dependencies": { - "@ethereumjs/block": "^3.6.2", - "@ethereumjs/blockchain": "^5.5.2", - "@ethereumjs/common": "^2.6.3", - "@ethereumjs/tx": "^3.5.1", - "async-eventemitter": "^0.2.4", - "core-js-pure": "^3.0.1", - "debug": "^4.3.3", - "ethereumjs-util": "^7.1.4", - "functional-red-black-tree": "^1.0.1", - "mcl-wasm": "^0.7.1", - "merkle-patricia-tree": "^4.2.4", - "rustbn.js": "~0.2.0" - } - }, - "node_modules/@ethereumjs/vm/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@ethersproject/abi": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.0.tgz", @@ -1235,6 +1146,28 @@ "rlp": "^2.2.3" } }, + "node_modules/@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1270,6 +1203,348 @@ "node": ">= 8" } }, + "node_modules/@nomicfoundation/ethereumjs-block": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz", + "integrity": "sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-blockchain": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz", + "integrity": "sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==", + "dependencies": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-ethash": "^2.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "level": "^8.0.0", + "lru-cache": "^5.1.1", + "memory-level": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz", + "integrity": "sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@nomicfoundation/ethereumjs-ethash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz", + "integrity": "sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==", + "dependencies": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "bigint-crypto-utils": "^3.0.23", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-evm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz", + "integrity": "sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz", + "integrity": "sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-statemanager": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz", + "integrity": "sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1" + } + }, + "node_modules/@nomicfoundation/ethereumjs-trie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz", + "integrity": "sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz", + "integrity": "sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz", + "integrity": "sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0-beta.2", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-vm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz", + "integrity": "sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==", + "dependencies": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.0.3.tgz", + "integrity": "sha512-VFMiOQvsw7nx5bFmrmVp2Q9rhIjw2AFST4DYvWVVO9PMHPE23BY2+kyfrQ4J3xCMFC8fcBbGLt7l4q7m1SlTqg==", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.0.3", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.0.3", + "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.0.3", + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.0.3", + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.0.3", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.0.3" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.0.3.tgz", + "integrity": "sha512-W+bIiNiZmiy+MTYFZn3nwjyPUO6wfWJ0lnXx2zZrM8xExKObMrhCh50yy8pQING24mHfpPFCn89wEB/iG7vZDw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.0.3.tgz", + "integrity": "sha512-HuJd1K+2MgmFIYEpx46uzwEFjvzKAI765mmoMxy4K+Aqq1p+q7hHRlsFU2kx3NB8InwotkkIq3A5FLU1sI1WDw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-freebsd-x64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.0.3.tgz", + "integrity": "sha512-2cR8JNy23jZaO/vZrsAnWCsO73asU7ylrHIe0fEsXbZYqBP9sMr+/+xP3CELDHJxUbzBY8zqGvQt1ULpyrG+Kw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.0.3.tgz", + "integrity": "sha512-Eyv50EfYbFthoOb0I1568p+eqHGLwEUhYGOxcRNywtlTE9nj+c+MT1LA53HnxD9GsboH4YtOOmJOulrjG7KtbA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.0.3.tgz", + "integrity": "sha512-V8grDqI+ivNrgwEt2HFdlwqV2/EQbYAdj3hbOvjrA8Qv+nq4h9jhQUxFpegYMDtpU8URJmNNlXgtfucSrAQwtQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.0.3.tgz", + "integrity": "sha512-uRfVDlxtwT1vIy7MAExWAkRD4r9M79zMG7S09mCrWUn58DbLs7UFl+dZXBX0/8FTGYWHhOT/1Etw1ZpAf5DTrg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.0.3.tgz", + "integrity": "sha512-8HPwYdLbhcPpSwsE0yiU/aZkXV43vlXT2ycH+XlOjWOnLfH8C41z0njK8DHRtEFnp4OVN6E7E5lHBBKDZXCliA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.0.3.tgz", + "integrity": "sha512-5WWcT6ZNvfCuxjlpZOY7tdvOqT1kIQYlDF9Q42wMpZ5aTm4PvjdCmFDDmmTvyXEBJ4WTVmY5dWNWaxy8h/E28g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.0.3.tgz", + "integrity": "sha512-P/LWGZwWkyjSwkzq6skvS2wRc3gabzAbk6Akqs1/Iiuggql2CqdLBkcYWL5Xfv3haynhL+2jlNkak+v2BTZI4A==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.0.3.tgz", + "integrity": "sha512-4AcTtLZG1s/S5mYAIr/sdzywdNwJpOcdStGF3QMBzEt+cGn3MchMaS9b1gyhb2KKM2c39SmPF5fUuWq1oBSQZQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nomiclabs/hardhat-ethers": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz", @@ -1281,18 +1556,21 @@ } }, "node_modules/@nomiclabs/hardhat-etherscan": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.0.3.tgz", - "integrity": "sha512-OfNtUKc/ZwzivmZnnpwWREfaYncXteKHskn3yDnz+fPBZ6wfM4GR+d5RwjREzYFWE+o5iR9ruXhWw/8fejWM9g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.0.tgz", + "integrity": "sha512-JroYgfN1AlYFkQTQ3nRwFi4o8NtZF7K/qFR2dxDUgHbCtIagkUseca9L4E/D2ScUm4XT40+8PbCdqZi+XmHyQA==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.1.2", "@ethersproject/address": "^5.0.2", "cbor": "^5.0.2", + "chalk": "^2.4.2", "debug": "^4.1.1", "fs-extra": "^7.0.1", + "lodash": "^4.17.11", "semver": "^6.3.0", - "undici": "^4.14.1" + "table": "^6.8.0", + "undici": "^5.4.0" }, "peerDependencies": { "hardhat": "^2.0.4" @@ -2038,6 +2316,48 @@ "ms": "^2.1.1" } }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@scure/bip32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", + "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@noble/secp256k1": "~1.6.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, "node_modules/@sentry/core": { "version": "5.30.0", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", @@ -2163,6 +2483,7 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz", "integrity": "sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==", + "dev": true, "dependencies": { "antlr4ts": "^0.5.0-alpha.4" } @@ -2563,10 +2884,10 @@ "typechain": "^3.0.0" } }, - "node_modules/@types/abstract-leveldown": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", - "integrity": "sha512-q5veSX6zjUy/DlDhR4Y4cU0k2Ar+DT2LUraP00T19WLmTO6Se1djepCCaqU6nQrwcJ5Hyo/CWqxTzrrFg8eqbQ==" + "node_modules/@types/async-eventemitter": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz", + "integrity": "sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==" }, "node_modules/@types/bignumber.js": { "version": "5.0.0", @@ -2600,21 +2921,6 @@ "@types/node": "*" } }, - "node_modules/@types/level-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/level-errors/-/level-errors-3.0.0.tgz", - "integrity": "sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ==" - }, - "node_modules/@types/levelup": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/levelup/-/levelup-4.3.3.tgz", - "integrity": "sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA==", - "dependencies": { - "@types/abstract-leveldown": "*", - "@types/level-errors": "*", - "@types/node": "*" - } - }, "node_modules/@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2744,28 +3050,53 @@ "node": ">=6.5" } }, - "node_modules/abstract-leveldown": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz", - "integrity": "sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==", + "node_modules/abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", "dependencies": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" }, "engines": { - "node": ">=6" + "node": ">=12" + } + }, + "node_modules/abstract-level/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -2897,7 +3228,8 @@ "node_modules/antlr4ts": { "version": "0.5.0-alpha.4", "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true }, "node_modules/anymatch": { "version": "3.1.2", @@ -2989,10 +3321,19 @@ "node": "*" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dependencies": { "lodash": "^4.17.14" } @@ -3105,6 +3446,25 @@ "node": "*" } }, + "node_modules/bigint-crypto-utils": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.6.tgz", + "integrity": "sha512-k5ljSLHx94jQTW3+18KEfxLJR8/XFBHqhfhEGF48qT8p/jL6EdiG7oNOiiIRGMFh2wEP8kaCXZbVd+5dYkngUg==", + "dependencies": { + "bigint-mod-arith": "^3.1.0" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/bigint-mod-arith": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.1.tgz", + "integrity": "sha512-SzFqdncZKXq5uh3oLFZXmzaZEMDsA7ml9l53xKaVGO6/+y26xNwAaTQEg2R+D+d07YduLbKi0dni3YPsR51UDQ==", + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -3137,23 +3497,26 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/debug": { @@ -3164,53 +3527,11 @@ "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/body-parser/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -3263,6 +3584,17 @@ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, + "node_modules/browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -3393,14 +3725,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" }, - "node_modules/buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "dependencies": { - "safe-buffer": "^5.1.1" - } - }, "node_modules/bufferutil": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", @@ -3414,9 +3738,9 @@ } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -3497,6 +3821,14 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "engines": { + "node": ">=6" + } + }, "node_modules/cbor": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz", @@ -3698,6 +4030,22 @@ "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" }, + "node_modules/classic-level": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz", + "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==", + "hasInstallScript": true, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3822,21 +4170,16 @@ } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/content-hash": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", @@ -3873,16 +4216,6 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" }, - "node_modules/core-js-pure": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", - "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -4062,9 +4395,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -4089,9 +4422,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "engines": { "node": ">=0.10" } @@ -4129,33 +4462,6 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" }, - "node_modules/deferred-leveldown": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", - "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", - "dependencies": { - "abstract-leveldown": "~6.2.1", - "inherits": "^2.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deferred-leveldown/node_modules/abstract-leveldown": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", - "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", - "dependencies": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -4181,11 +4487,11 @@ "integrity": "sha1-m4MZR3wOX4rrPONXrjBfwl6hzSs=" }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/des.js": { @@ -4198,9 +4504,13 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-indent": { "version": "5.0.0", @@ -4370,7 +4680,7 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -4394,25 +4704,11 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } }, - "node_modules/encoding-down": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", - "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", - "dependencies": { - "abstract-leveldown": "^6.2.1", - "inherits": "^2.0.3", - "level-codec": "^9.0.0", - "level-errors": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4448,17 +4744,6 @@ "node": ">=6" } }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4571,7 +4856,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "1.0.5", @@ -4663,7 +4948,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -4976,37 +5261,38 @@ } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -5016,9 +5302,9 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -5036,19 +5322,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/express/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", @@ -5165,16 +5438,16 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -5192,7 +5465,7 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/find-config": { "version": "1.0.0", @@ -5320,7 +5593,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } @@ -5351,6 +5624,19 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5359,7 +5645,7 @@ "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" }, "node_modules/ganache-core": { "version": "2.13.2", @@ -14725,14 +15011,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "engines": { - "node": ">=4.x" - } - }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -14776,19 +15054,24 @@ } }, "node_modules/hardhat": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.9.2.tgz", - "integrity": "sha512-elTcUK1EdFverWinybQ+DoJzsM6sgiHUYs0ZYNNXMfESty6ESHiFSwkfJsC88/q09vmIz6YVaMh73BYnYd+feQ==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.11.2.tgz", + "integrity": "sha512-BdsXC1CFJQDJKmAgCwpmGhFuVU6dcqlgMgT0Kg/xmFAFVugkpYu6NRmh4AaJ3Fah0/BR9DOR4XgQGIbg4eon/Q==", "dependencies": { - "@ethereumjs/block": "^3.6.0", - "@ethereumjs/blockchain": "^5.5.0", - "@ethereumjs/common": "^2.6.0", - "@ethereumjs/tx": "^3.4.0", - "@ethereumjs/vm": "^5.6.0", "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@nomicfoundation/ethereumjs-vm": "^6.0.0", + "@nomicfoundation/solidity-analyzer": "^0.0.3", "@sentry/node": "^5.18.1", - "@solidity-parser/parser": "^0.14.1", "@types/bn.js": "^5.1.0", "@types/lru-cache": "^5.1.0", "abort-controller": "^3.0.0", @@ -14801,31 +15084,28 @@ "debug": "^4.1.1", "enquirer": "^2.3.0", "env-paths": "^2.2.0", - "ethereum-cryptography": "^0.1.2", + "ethereum-cryptography": "^1.0.3", "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^7.1.3", "find-up": "^2.1.0", "fp-ts": "1.19.3", "fs-extra": "^7.0.1", - "glob": "^7.1.3", + "glob": "7.2.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", + "keccak": "^3.0.2", "lodash": "^4.17.11", - "merkle-patricia-tree": "^4.2.2", "mnemonist": "^0.38.0", - "mocha": "^9.2.0", + "mocha": "^10.0.0", "p-map": "^4.0.0", "qs": "^6.7.0", "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", - "slash": "^3.0.0", "solc": "0.7.3", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", - "true-case-path": "^2.2.1", "tsort": "0.0.1", - "undici": "^4.14.1", + "undici": "^5.4.0", "uuid": "^8.3.2", "ws": "^7.4.6" }, @@ -14833,7 +15113,30 @@ "hardhat": "internal/cli/cli.js" }, "engines": { - "node": "^12.0.0 || ^14.0.0 || ^16.0.0" + "node": "^14.0.0 || ^16.0.0 || ^18.0.0" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "dependencies": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" } }, "node_modules/hardhat/node_modules/uuid": { @@ -15008,18 +15311,18 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "node_modules/http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/http-https": { @@ -15103,11 +15406,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", - "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" - }, "node_modules/immutable": { "version": "4.0.0-rc.15", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.15.tgz", @@ -15244,6 +15542,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, "node_modules/is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", @@ -15535,7 +15855,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "node_modules/iso-url": { "version": "0.4.7", @@ -15703,110 +16024,63 @@ "node": ">=0.10.0" } }, - "node_modules/level-codec": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", - "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", + "node_modules/level": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", + "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", "dependencies": { - "buffer": "^5.6.0" + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/level-concat-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", - "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "dependencies": { - "errno": "~0.1.1" + "node": ">=12" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-iterator-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", - "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.4.0", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-mem": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/level-mem/-/level-mem-5.0.1.tgz", - "integrity": "sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg==", - "dependencies": { - "level-packager": "^5.0.3", - "memdown": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-packager": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", - "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", - "dependencies": { - "encoding-down": "^6.3.0", - "levelup": "^4.3.2" - }, - "engines": { - "node": ">=6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/level" } }, "node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/level-transcoder": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", - "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", "dependencies": { - "xtend": "^4.0.2" + "buffer": "^6.0.3", + "module-error": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/level-ws": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-2.0.0.tgz", - "integrity": "sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA==", + "node_modules/level-transcoder/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^3.1.0", - "xtend": "^4.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/levelup": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", - "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", - "dependencies": { - "deferred-leveldown": "~5.3.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~4.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, "node_modules/levn": { @@ -15889,6 +16163,12 @@ "resolved": "https://registry.npmjs.org/lodash.sum/-/lodash.sum-4.0.2.tgz", "integrity": "sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s=" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -16010,11 +16290,6 @@ "yallist": "^3.0.2" } }, - "node_modules/ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" - }, "node_modules/mcl-wasm": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", @@ -16041,42 +16316,19 @@ "node": ">= 0.6" } }, - "node_modules/memdown": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-5.1.0.tgz", - "integrity": "sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw==", + "node_modules/memory-level": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", + "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", "dependencies": { - "abstract-leveldown": "~6.2.1", - "functional-red-black-tree": "~1.0.1", - "immediate": "~3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.2.0" + "abstract-level": "^1.0.0", + "functional-red-black-tree": "^1.0.1", + "module-error": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/memdown/node_modules/abstract-leveldown": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", - "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", - "dependencies": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/memdown/node_modules/immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" - }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -16099,19 +16351,6 @@ "node": ">= 8" } }, - "node_modules/merkle-patricia-tree": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz", - "integrity": "sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w==", - "dependencies": { - "@types/levelup": "^4.3.0", - "ethereumjs-util": "^7.1.4", - "level-mem": "^5.0.1", - "level-ws": "^2.0.0", - "readable-stream": "^3.6.0", - "semaphore-async-await": "^1.5.1" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -16157,19 +16396,19 @@ } }, "node_modules/mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.49.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -16274,41 +16513,39 @@ } }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", - "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14.0.0" }, "funding": { "type": "opencollective", @@ -16320,27 +16557,14 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "balanced-match": "^1.0.0" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -16401,11 +16625,11 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" @@ -16466,25 +16690,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/mocha/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/mock-fs": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" }, + "node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -16540,9 +16758,9 @@ "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" }, "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -16550,10 +16768,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==" + }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } @@ -16765,9 +16988,9 @@ } }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -17228,11 +17451,6 @@ "node": ">= 0.10" } }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -17278,9 +17496,9 @@ } }, "node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, @@ -17318,7 +17536,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -17360,12 +17577,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", - "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.3", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -17519,9 +17736,9 @@ } }, "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "engines": { "node": ">=0.6" } @@ -17638,6 +17855,28 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rustbn.js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", @@ -17773,14 +18012,6 @@ "node": ">=10.0.0" } }, - "node_modules/semaphore-async-await": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz", - "integrity": "sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo=", - "engines": { - "node": ">=4.1" - } - }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -17790,23 +18021,23 @@ } }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" @@ -17823,12 +18054,12 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/sentence-case": { "version": "2.1.1", @@ -17848,14 +18079,14 @@ } }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" @@ -17887,9 +18118,9 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { "version": "2.4.11", @@ -18018,10 +18249,61 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/snake-case": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", @@ -18270,11 +18552,11 @@ } }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/strict-uri-encode": { @@ -18542,6 +18824,65 @@ "ultron": "~1.1.0" } }, + "node_modules/table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar": { "version": "4.4.19", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", @@ -18638,9 +18979,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } @@ -18671,11 +19012,6 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, - "node_modules/true-case-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", - "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==" - }, "node_modules/ts-essentials": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-1.0.4.tgz", @@ -18826,7 +19162,7 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, + "devOptional": true, "peer": true, "bin": { "tsc": "bin/tsc", @@ -18880,9 +19216,9 @@ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "node_modules/undici": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-4.16.0.tgz", - "integrity": "sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", + "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==", "engines": { "node": ">=12.18" } @@ -19562,9 +19898,9 @@ "dev": true }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -20114,42 +20450,6 @@ "postinstall-postinstall": "^2.1.0" } }, - "@ethereumjs/block": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-3.6.2.tgz", - "integrity": "sha512-mOqYWwMlAZpYUEOEqt7EfMFuVL2eyLqWWIzcf4odn6QgXY8jBI2NhVuJncrMCKeMZrsJAe7/auaRRB6YcdH+Qw==", - "requires": { - "@ethereumjs/common": "^2.6.3", - "@ethereumjs/tx": "^3.5.1", - "ethereumjs-util": "^7.1.4", - "merkle-patricia-tree": "^4.2.4" - } - }, - "@ethereumjs/blockchain": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/blockchain/-/blockchain-5.5.2.tgz", - "integrity": "sha512-Jz26iJmmsQtngerW6r5BDFaew/f2mObLrRZo3rskLOx1lmtMZ8+TX/vJexmivrnWgmAsTdNWhlKUYY4thPhPig==", - "requires": { - "@ethereumjs/block": "^3.6.2", - "@ethereumjs/common": "^2.6.3", - "@ethereumjs/ethash": "^1.1.0", - "debug": "^4.3.3", - "ethereumjs-util": "^7.1.4", - "level-mem": "^5.0.1", - "lru-cache": "^5.1.1", - "semaphore-async-await": "^1.5.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - } - } - }, "@ethereumjs/common": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.3.tgz", @@ -20159,18 +20459,6 @@ "ethereumjs-util": "^7.1.4" } }, - "@ethereumjs/ethash": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/ethash/-/ethash-1.1.0.tgz", - "integrity": "sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA==", - "requires": { - "@ethereumjs/block": "^3.5.0", - "@types/levelup": "^4.3.0", - "buffer-xor": "^2.0.1", - "ethereumjs-util": "^7.1.1", - "miller-rabin": "^4.0.0" - } - }, "@ethereumjs/tx": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.1.tgz", @@ -20180,35 +20468,6 @@ "ethereumjs-util": "^7.1.4" } }, - "@ethereumjs/vm": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/vm/-/vm-5.8.0.tgz", - "integrity": "sha512-mn2G2SX79QY4ckVvZUfxlNUpzwT2AEIkvgJI8aHoQaNYEHhH8rmdVDIaVVgz6//PjK52BZsK23afz+WvSR0Qqw==", - "requires": { - "@ethereumjs/block": "^3.6.2", - "@ethereumjs/blockchain": "^5.5.2", - "@ethereumjs/common": "^2.6.3", - "@ethereumjs/tx": "^3.5.1", - "async-eventemitter": "^0.2.4", - "core-js-pure": "^3.0.1", - "debug": "^4.3.3", - "ethereumjs-util": "^7.1.4", - "functional-red-black-tree": "^1.0.1", - "mcl-wasm": "^0.7.1", - "merkle-patricia-tree": "^4.2.4", - "rustbn.js": "~0.2.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - } - } - }, "@ethersproject/abi": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.0.tgz", @@ -20629,6 +20888,16 @@ } } }, + "@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" + }, + "@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -20655,6 +20924,225 @@ "fastq": "^1.6.0" } }, + "@nomicfoundation/ethereumjs-block": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz", + "integrity": "sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==", + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-blockchain": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz", + "integrity": "sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==", + "requires": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-ethash": "^2.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "level": "^8.0.0", + "lru-cache": "^5.1.1", + "memory-level": "^1.0.0" + } + }, + "@nomicfoundation/ethereumjs-common": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz", + "integrity": "sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==", + "requires": { + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "crc-32": "^1.2.0" + } + }, + "@nomicfoundation/ethereumjs-ethash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz", + "integrity": "sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==", + "requires": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "bigint-crypto-utils": "^3.0.23", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-evm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz", + "integrity": "sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==", + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + } + }, + "@nomicfoundation/ethereumjs-rlp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz", + "integrity": "sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==" + }, + "@nomicfoundation/ethereumjs-statemanager": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz", + "integrity": "sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==", + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1" + } + }, + "@nomicfoundation/ethereumjs-trie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz", + "integrity": "sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==", + "requires": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3", + "readable-stream": "^3.6.0" + } + }, + "@nomicfoundation/ethereumjs-tx": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz", + "integrity": "sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==", + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-util": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz", + "integrity": "sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==", + "requires": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0-beta.2", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-vm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz", + "integrity": "sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==", + "requires": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + } + }, + "@nomicfoundation/solidity-analyzer": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.0.3.tgz", + "integrity": "sha512-VFMiOQvsw7nx5bFmrmVp2Q9rhIjw2AFST4DYvWVVO9PMHPE23BY2+kyfrQ4J3xCMFC8fcBbGLt7l4q7m1SlTqg==", + "requires": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.0.3", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.0.3", + "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.0.3", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.0.3", + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.0.3", + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.0.3", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.0.3" + } + }, + "@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.0.3.tgz", + "integrity": "sha512-W+bIiNiZmiy+MTYFZn3nwjyPUO6wfWJ0lnXx2zZrM8xExKObMrhCh50yy8pQING24mHfpPFCn89wEB/iG7vZDw==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.0.3.tgz", + "integrity": "sha512-HuJd1K+2MgmFIYEpx46uzwEFjvzKAI765mmoMxy4K+Aqq1p+q7hHRlsFU2kx3NB8InwotkkIq3A5FLU1sI1WDw==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-freebsd-x64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.0.3.tgz", + "integrity": "sha512-2cR8JNy23jZaO/vZrsAnWCsO73asU7ylrHIe0fEsXbZYqBP9sMr+/+xP3CELDHJxUbzBY8zqGvQt1ULpyrG+Kw==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.0.3.tgz", + "integrity": "sha512-Eyv50EfYbFthoOb0I1568p+eqHGLwEUhYGOxcRNywtlTE9nj+c+MT1LA53HnxD9GsboH4YtOOmJOulrjG7KtbA==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.0.3.tgz", + "integrity": "sha512-V8grDqI+ivNrgwEt2HFdlwqV2/EQbYAdj3hbOvjrA8Qv+nq4h9jhQUxFpegYMDtpU8URJmNNlXgtfucSrAQwtQ==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.0.3.tgz", + "integrity": "sha512-uRfVDlxtwT1vIy7MAExWAkRD4r9M79zMG7S09mCrWUn58DbLs7UFl+dZXBX0/8FTGYWHhOT/1Etw1ZpAf5DTrg==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.0.3.tgz", + "integrity": "sha512-8HPwYdLbhcPpSwsE0yiU/aZkXV43vlXT2ycH+XlOjWOnLfH8C41z0njK8DHRtEFnp4OVN6E7E5lHBBKDZXCliA==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.0.3.tgz", + "integrity": "sha512-5WWcT6ZNvfCuxjlpZOY7tdvOqT1kIQYlDF9Q42wMpZ5aTm4PvjdCmFDDmmTvyXEBJ4WTVmY5dWNWaxy8h/E28g==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.0.3.tgz", + "integrity": "sha512-P/LWGZwWkyjSwkzq6skvS2wRc3gabzAbk6Akqs1/Iiuggql2CqdLBkcYWL5Xfv3haynhL+2jlNkak+v2BTZI4A==", + "optional": true + }, + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.0.3.tgz", + "integrity": "sha512-4AcTtLZG1s/S5mYAIr/sdzywdNwJpOcdStGF3QMBzEt+cGn3MchMaS9b1gyhb2KKM2c39SmPF5fUuWq1oBSQZQ==", + "optional": true + }, "@nomiclabs/hardhat-ethers": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz", @@ -20663,18 +21151,21 @@ "requires": {} }, "@nomiclabs/hardhat-etherscan": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.0.3.tgz", - "integrity": "sha512-OfNtUKc/ZwzivmZnnpwWREfaYncXteKHskn3yDnz+fPBZ6wfM4GR+d5RwjREzYFWE+o5iR9ruXhWw/8fejWM9g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.0.tgz", + "integrity": "sha512-JroYgfN1AlYFkQTQ3nRwFi4o8NtZF7K/qFR2dxDUgHbCtIagkUseca9L4E/D2ScUm4XT40+8PbCdqZi+XmHyQA==", "dev": true, "requires": { "@ethersproject/abi": "^5.1.2", "@ethersproject/address": "^5.0.2", "cbor": "^5.0.2", + "chalk": "^2.4.2", "debug": "^4.1.1", "fs-extra": "^7.0.1", + "lodash": "^4.17.11", "semver": "^6.3.0", - "undici": "^4.14.1" + "table": "^6.8.0", + "undici": "^5.4.0" } }, "@nomiclabs/hardhat-truffle5": { @@ -21312,6 +21803,30 @@ } } }, + "@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" + }, + "@scure/bip32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", + "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", + "requires": { + "@noble/hashes": "~1.1.1", + "@noble/secp256k1": "~1.6.0", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "requires": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, "@sentry/core": { "version": "5.30.0", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", @@ -21413,6 +21928,7 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz", "integrity": "sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==", + "dev": true, "requires": { "antlr4ts": "^0.5.0-alpha.4" } @@ -21801,10 +22317,10 @@ "ethers": "^5.0.2" } }, - "@types/abstract-leveldown": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", - "integrity": "sha512-q5veSX6zjUy/DlDhR4Y4cU0k2Ar+DT2LUraP00T19WLmTO6Se1djepCCaqU6nQrwcJ5Hyo/CWqxTzrrFg8eqbQ==" + "@types/async-eventemitter": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz", + "integrity": "sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==" }, "@types/bignumber.js": { "version": "5.0.0", @@ -21837,21 +22353,6 @@ "@types/node": "*" } }, - "@types/level-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/level-errors/-/level-errors-3.0.0.tgz", - "integrity": "sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ==" - }, - "@types/levelup": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/levelup/-/levelup-4.3.3.tgz", - "integrity": "sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA==", - "requires": { - "@types/abstract-leveldown": "*", - "@types/level-errors": "*", - "@types/node": "*" - } - }, "@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", @@ -21978,25 +22479,38 @@ "event-target-shim": "^5.0.0" } }, - "abstract-leveldown": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz", - "integrity": "sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==", + "abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", "requires": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } } }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "address": { @@ -22091,7 +22605,8 @@ "antlr4ts": { "version": "0.5.0-alpha.4", "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true }, "anymatch": { "version": "3.1.2", @@ -22165,10 +22680,16 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "requires": { "lodash": "^4.17.14" } @@ -22254,6 +22775,19 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, + "bigint-crypto-utils": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.6.tgz", + "integrity": "sha512-k5ljSLHx94jQTW3+18KEfxLJR8/XFBHqhfhEGF48qT8p/jL6EdiG7oNOiiIRGMFh2wEP8kaCXZbVd+5dYkngUg==", + "requires": { + "bigint-mod-arith": "^3.1.0" + } + }, + "bigint-mod-arith": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.1.tgz", + "integrity": "sha512-SzFqdncZKXq5uh3oLFZXmzaZEMDsA7ml9l53xKaVGO6/+y26xNwAaTQEg2R+D+d07YduLbKi0dni3YPsR51UDQ==" + }, "bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -22280,20 +22814,22 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -22304,43 +22840,10 @@ "ms": "2.0.0" } }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } } } }, @@ -22392,6 +22895,17 @@ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, + "browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "requires": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -22514,14 +23028,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" }, - "buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "requires": { - "safe-buffer": "^5.1.1" - } - }, "bufferutil": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", @@ -22531,9 +23037,9 @@ } }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "cacheable-request": { "version": "6.1.0", @@ -22592,6 +23098,11 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==" + }, "cbor": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz", @@ -22756,6 +23267,18 @@ "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" }, + "classic-level": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz", + "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==", + "requires": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + } + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -22861,18 +23384,11 @@ } }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "safe-buffer": "5.2.1" } }, "content-hash": { @@ -22905,11 +23421,6 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" }, - "core-js-pure": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", - "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -23061,9 +23572,9 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -23074,9 +23585,9 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "decompress-response": { "version": "3.3.0", @@ -23105,29 +23616,6 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" }, - "deferred-leveldown": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", - "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", - "requires": { - "abstract-leveldown": "~6.2.1", - "inherits": "^2.0.3" - }, - "dependencies": { - "abstract-leveldown": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", - "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", - "requires": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - } - } - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -23147,9 +23635,9 @@ "integrity": "sha1-m4MZR3wOX4rrPONXrjBfwl6hzSs=" }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "des.js": { "version": "1.0.1", @@ -23161,9 +23649,9 @@ } }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "detect-indent": { "version": "5.0.0", @@ -23297,7 +23785,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "elliptic": { "version": "6.5.4", @@ -23321,18 +23809,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "encoding-down": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", - "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", - "requires": { - "abstract-leveldown": "^6.2.1", - "inherits": "^2.0.3", - "level-codec": "^9.0.0", - "level-errors": "^2.0.0" - } + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "end-of-stream": { "version": "1.4.4", @@ -23360,14 +23837,6 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "requires": { - "prr": "~1.0.1" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -23461,7 +23930,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", @@ -23520,7 +23989,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "eth-ens-namehash": { "version": "2.0.8", @@ -23803,46 +24272,47 @@ "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "debug": { "version": "2.6.9", @@ -23856,16 +24326,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -23971,16 +24431,16 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "dependencies": { @@ -23995,7 +24455,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -24089,7 +24549,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-extra": { "version": "7.0.1", @@ -24114,6 +24574,12 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -24122,7 +24588,7 @@ "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" }, "ganache-core": { "version": "2.13.2", @@ -31319,11 +31785,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" - }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -31352,19 +31813,24 @@ } }, "hardhat": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.9.2.tgz", - "integrity": "sha512-elTcUK1EdFverWinybQ+DoJzsM6sgiHUYs0ZYNNXMfESty6ESHiFSwkfJsC88/q09vmIz6YVaMh73BYnYd+feQ==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.11.2.tgz", + "integrity": "sha512-BdsXC1CFJQDJKmAgCwpmGhFuVU6dcqlgMgT0Kg/xmFAFVugkpYu6NRmh4AaJ3Fah0/BR9DOR4XgQGIbg4eon/Q==", "requires": { - "@ethereumjs/block": "^3.6.0", - "@ethereumjs/blockchain": "^5.5.0", - "@ethereumjs/common": "^2.6.0", - "@ethereumjs/tx": "^3.4.0", - "@ethereumjs/vm": "^5.6.0", "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@nomicfoundation/ethereumjs-vm": "^6.0.0", + "@nomicfoundation/solidity-analyzer": "^0.0.3", "@sentry/node": "^5.18.1", - "@solidity-parser/parser": "^0.14.1", "@types/bn.js": "^5.1.0", "@types/lru-cache": "^5.1.0", "abort-controller": "^3.0.0", @@ -31377,35 +31843,43 @@ "debug": "^4.1.1", "enquirer": "^2.3.0", "env-paths": "^2.2.0", - "ethereum-cryptography": "^0.1.2", + "ethereum-cryptography": "^1.0.3", "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^7.1.3", "find-up": "^2.1.0", "fp-ts": "1.19.3", "fs-extra": "^7.0.1", - "glob": "^7.1.3", + "glob": "7.2.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", + "keccak": "^3.0.2", "lodash": "^4.17.11", - "merkle-patricia-tree": "^4.2.2", "mnemonist": "^0.38.0", - "mocha": "^9.2.0", + "mocha": "^10.0.0", "p-map": "^4.0.0", "qs": "^6.7.0", "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", - "slash": "^3.0.0", "solc": "0.7.3", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", - "true-case-path": "^2.2.1", "tsort": "0.0.1", - "undici": "^4.14.1", + "undici": "^5.4.0", "uuid": "^8.3.2", "ws": "^7.4.6" }, "dependencies": { + "ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "requires": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" + } + }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -31532,15 +32006,15 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" } }, "http-https": { @@ -31594,11 +32068,6 @@ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, - "immediate": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", - "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" - }, "immutable": { "version": "4.0.0-rc.15", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.15.tgz", @@ -31702,6 +32171,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, "is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", @@ -31890,7 +32364,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "iso-url": { "version": "0.4.7", @@ -32033,83 +32508,38 @@ "invert-kv": "^1.0.0" } }, - "level-codec": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", - "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", + "level": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", + "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", "requires": { - "buffer": "^5.6.0" - } - }, - "level-concat-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", - "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==" - }, - "level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "requires": { - "errno": "~0.1.1" - } - }, - "level-iterator-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", - "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.4.0", - "xtend": "^4.0.2" - } - }, - "level-mem": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/level-mem/-/level-mem-5.0.1.tgz", - "integrity": "sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg==", - "requires": { - "level-packager": "^5.0.3", - "memdown": "^5.0.0" - } - }, - "level-packager": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", - "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", - "requires": { - "encoding-down": "^6.3.0", - "levelup": "^4.3.2" + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" } }, "level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==" + }, + "level-transcoder": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", - "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", "requires": { - "xtend": "^4.0.2" - } - }, - "level-ws": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-2.0.0.tgz", - "integrity": "sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA==", - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^3.1.0", - "xtend": "^4.0.1" - } - }, - "levelup": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", - "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", - "requires": { - "deferred-leveldown": "~5.3.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~4.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } } }, "levn": { @@ -32183,6 +32613,12 @@ "resolved": "https://registry.npmjs.org/lodash.sum/-/lodash.sum-4.0.2.tgz", "integrity": "sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s=" }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -32276,11 +32712,6 @@ "yallist": "^3.0.2" } }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" - }, "mcl-wasm": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", @@ -32301,36 +32732,14 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, - "memdown": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-5.1.0.tgz", - "integrity": "sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw==", + "memory-level": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", + "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", "requires": { - "abstract-leveldown": "~6.2.1", - "functional-red-black-tree": "~1.0.1", - "immediate": "~3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "abstract-leveldown": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", - "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", - "requires": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - } - }, - "immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" - } + "abstract-level": "^1.0.0", + "functional-red-black-tree": "^1.0.1", + "module-error": "^1.0.1" } }, "memorystream": { @@ -32349,19 +32758,6 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "merkle-patricia-tree": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz", - "integrity": "sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w==", - "requires": { - "@types/levelup": "^4.3.0", - "ethereumjs-util": "^7.1.4", - "level-mem": "^5.0.1", - "level-ws": "^2.0.0", - "readable-stream": "^3.6.0", - "semaphore-async-await": "^1.5.1" - } - }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -32392,16 +32788,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.49.0" + "mime-db": "1.52.0" } }, "mimic-response": { @@ -32487,31 +32883,29 @@ } }, "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", - "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", + "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -32522,19 +32916,12 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } + "balanced-match": "^1.0.0" } }, "escape-string-regexp": { @@ -32573,11 +32960,11 @@ } }, "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } }, "ms": { @@ -32613,14 +33000,6 @@ "requires": { "has-flag": "^4.0.0" } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } } } }, @@ -32629,6 +33008,11 @@ "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" }, + "module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -32683,14 +33067,19 @@ "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" }, "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==" + }, + "napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==" }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "neo-async": { "version": "2.6.2", @@ -32850,9 +33239,9 @@ } }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -33191,11 +33580,6 @@ "ipaddr.js": "1.9.1" } }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -33234,9 +33618,9 @@ "integrity": "sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ==" }, "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } @@ -33260,8 +33644,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "randombytes": { "version": "2.1.0", @@ -33286,12 +33669,12 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", - "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.3", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -33413,9 +33796,9 @@ } }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" } } }, @@ -33495,6 +33878,14 @@ "queue-microtask": "^1.2.2" } }, + "run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, "rustbn.js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", @@ -33595,34 +33986,29 @@ "node-gyp-build": "^4.2.0" } }, - "semaphore-async-await": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz", - "integrity": "sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo=" - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { "debug": { @@ -33636,14 +34022,14 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -33665,14 +34051,14 @@ } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "servify": { @@ -33698,9 +34084,9 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sha.js": { "version": "2.4.11", @@ -33784,7 +34170,45 @@ "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } }, "snake-case": { "version": "2.1.0", @@ -33998,9 +34422,9 @@ } }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "strict-uri-encode": { "version": "1.1.0", @@ -34216,6 +34640,54 @@ } } }, + "table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "tar": { "version": "4.4.19", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", @@ -34292,9 +34764,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { "version": "2.5.0", @@ -34318,11 +34790,6 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, - "true-case-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", - "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==" - }, "ts-essentials": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-1.0.4.tgz", @@ -34449,7 +34916,7 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, + "devOptional": true, "peer": true }, "typical": { @@ -34487,9 +34954,9 @@ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "undici": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-4.16.0.tgz", - "integrity": "sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw==" + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", + "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==" }, "universalify": { "version": "0.1.2", @@ -35056,9 +35523,9 @@ "dev": true }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" }, "wrap-ansi": { "version": "7.0.0", diff --git a/contracts/basic-bandwidth-generation/package.json b/contracts/basic-bandwidth-generation/package.json index 0fee523d6c..94ba8f7402 100644 --- a/contracts/basic-bandwidth-generation/package.json +++ b/contracts/basic-bandwidth-generation/package.json @@ -1,12 +1,12 @@ { "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.5", - "@nomiclabs/hardhat-etherscan": "^3.0.3", + "@nomiclabs/hardhat-etherscan": "^3.1.0", "@nomiclabs/hardhat-waffle": "^2.0.3", "chai": "^4.3.6", "ethereum-waffle": "^3.4.4", "ethers": "^5.6.1", - "hardhat": "^2.9.2", + "hardhat": "^2.11.2", "solidity-coverage": "^0.7.20" }, "dependencies": { diff --git a/contracts/coconut-bandwidth/src/error.rs b/contracts/coconut-bandwidth/src/error.rs index 7bc8b11d4a..9f4aed6224 100644 --- a/contracts/coconut-bandwidth/src/error.rs +++ b/contracts/coconut-bandwidth/src/error.rs @@ -11,7 +11,7 @@ use thiserror::Error; /// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error(transparent)] Std(#[from] StdError), #[error("Received multiple coin types")] @@ -29,6 +29,6 @@ pub enum ContractError { #[error("Credential already spent or in process of spending")] DuplicateBlindedSerialNumber, - #[error("{0}")] + #[error(transparent)] Admin(#[from] AdminError), } diff --git a/contracts/coconut-bandwidth/src/storage.rs b/contracts/coconut-bandwidth/src/storage.rs index 40e33b9f8d..549e64caaf 100644 --- a/contracts/coconut-bandwidth/src/storage.rs +++ b/contracts/coconut-bandwidth/src/storage.rs @@ -25,7 +25,7 @@ impl<'a> IndexList for SpendCredentialIndex<'a> { } } -// gateways() is the storage access function. +// spent_credentials() is the storage access function. pub(crate) fn spent_credentials<'a>( ) -> IndexedMap<'a, &'a str, SpendCredential, SpendCredentialIndex<'a>> { let indexes = SpendCredentialIndex { diff --git a/contracts/coconut-dkg/Cargo.toml b/contracts/coconut-dkg/Cargo.toml index 479187c370..fe4440cf2f 100644 --- a/contracts/coconut-dkg/Cargo.toml +++ b/contracts/coconut-dkg/Cargo.toml @@ -19,4 +19,8 @@ cw4 = { version = "0.13.4" } schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = "1.0.23" \ No newline at end of file +thiserror = "1.0.23" + +[dev-dependencies] +cw-multi-test = { version = "0.13.4" } +cw4-group = { path = "../multisig/cw4-group" } \ No newline at end of file diff --git a/contracts/coconut-dkg/src/contract.rs b/contracts/coconut-dkg/src/contract.rs new file mode 100644 index 0000000000..650cf312f0 --- /dev/null +++ b/contracts/coconut-dkg/src/contract.rs @@ -0,0 +1,265 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::dealers::queries::{ + query_current_dealers_paged, query_dealer_details, query_past_dealers_paged, +}; +use crate::dealers::transactions::try_add_dealer; +use crate::dealings::queries::query_dealings_paged; +use crate::dealings::transactions::try_commit_dealings; +use crate::epoch_state::queries::{query_current_epoch, query_current_epoch_threshold}; +use crate::epoch_state::storage::CURRENT_EPOCH; +use crate::epoch_state::transactions::advance_epoch_state; +use crate::error::ContractError; +use crate::state::{State, MULTISIG, STATE}; +use crate::verification_key_shares::queries::query_vk_shares_paged; +use crate::verification_key_shares::transactions::try_commit_verification_key_share; +use crate::verification_key_shares::transactions::try_verify_verification_key_share; +use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use coconut_dkg_common::types::{Epoch, EpochState}; +use cosmwasm_std::{ + entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, +}; +use cw4::Cw4Contract; + +/// Instantiate the contract. +/// +/// `deps` contains Storage, API and Querier +/// `env` contains block, message and contract info +/// `msg` is the contract initialization message, sort of like a constructor call. +#[entry_point] +pub fn instantiate( + mut deps: DepsMut<'_>, + env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?; + MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?; + + let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| { + ContractError::InvalidGroup { + addr: msg.group_addr.clone(), + } + })?); + + let state = State { + group_addr, + multisig_addr, + mix_denom: msg.mix_denom, + }; + STATE.save(deps.storage, &state)?; + + CURRENT_EPOCH.save( + deps.storage, + &Epoch::new(EpochState::default(), env.block.time), + )?; + + Ok(Response::default()) +} + +/// Handle an incoming message +#[entry_point] +pub fn execute( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::RegisterDealer { + bte_key_with_proof, + announce_address, + } => try_add_dealer(deps, info, bte_key_with_proof, announce_address), + ExecuteMsg::CommitDealing { dealing_bytes } => { + try_commit_dealings(deps, info, dealing_bytes) + } + ExecuteMsg::CommitVerificationKeyShare { share } => { + try_commit_verification_key_share(deps, env, info, share) + } + ExecuteMsg::VerifyVerificationKeyShare { owner } => { + try_verify_verification_key_share(deps, info, owner) + } + ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env), + } +} + +#[entry_point] +pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result { + let response = match msg { + QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?, + QueryMsg::GetCurrentEpochThreshold {} => { + to_binary(&query_current_epoch_threshold(deps.storage)?)? + } + QueryMsg::GetDealerDetails { dealer_address } => { + to_binary(&query_dealer_details(deps, dealer_address)?)? + } + QueryMsg::GetCurrentDealers { limit, start_after } => { + to_binary(&query_current_dealers_paged(deps, start_after, limit)?)? + } + QueryMsg::GetPastDealers { limit, start_after } => { + to_binary(&query_past_dealers_paged(deps, start_after, limit)?)? + } + QueryMsg::GetDealing { + idx, + limit, + start_after, + } => to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?, + QueryMsg::GetVerificationKeys { limit, start_after } => { + to_binary(&query_vk_shares_paged(deps, start_after, limit)?)? + } + }; + + Ok(response) +} + +#[entry_point] +pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { + Ok(Default::default()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::support::tests::fixtures::TEST_MIX_DENOM; + use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT}; + use coconut_dkg_common::msg::ExecuteMsg::RegisterDealer; + use coconut_dkg_common::types::NodeIndex; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{coins, Addr}; + use cw4::Member; + use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg; + use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor}; + + fn instantiate_with_group(app: &mut App, members: &[Addr]) -> Addr { + let group_code_id = app.store_code(Box::new(ContractWrapper::new( + cw4_group::contract::execute, + cw4_group::contract::instantiate, + cw4_group::contract::query, + ))); + let msg = GroupInstantiateMsg { + admin: Some(ADMIN_ADDRESS.to_string()), + members: members + .iter() + .map(|member| Member { + addr: member.to_string(), + weight: 10, + }) + .collect(), + }; + let group_contract_addr = app + .instantiate_contract( + group_code_id, + Addr::unchecked(ADMIN_ADDRESS), + &msg, + &[], + "group", + None, + ) + .unwrap(); + + let coconut_dkg_code_id = + app.store_code(Box::new(ContractWrapper::new(execute, instantiate, query))); + let msg = InstantiateMsg { + group_addr: group_contract_addr.to_string(), + multisig_addr: MULTISIG_CONTRACT.to_string(), + mix_denom: TEST_MIX_DENOM.to_string(), + }; + app.instantiate_contract( + coconut_dkg_code_id, + Addr::unchecked(ADMIN_ADDRESS), + &msg, + &[], + "coconut dkg", + None, + ) + .unwrap() + } + + fn parse_node_index(res: AppResponse) -> NodeIndex { + res.events + .into_iter() + .find(|e| &e.ty == "wasm") + .unwrap() + .attributes + .into_iter() + .find(|attr| &attr.key == "node_index") + .unwrap() + .value + .parse::() + .unwrap() + } + + #[test] + fn initialize_contract() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let msg = InstantiateMsg { + group_addr: "group_addr".to_string(), + multisig_addr: "multisig_addr".to_string(), + mix_denom: "nym".to_string(), + }; + let info = mock_info("creator", &[]); + + let res = instantiate(deps.as_mut(), env.clone(), info, msg); + assert!(res.is_ok()) + } + + #[test] + fn execute_add_dealer() { + let init_funds = coins(100, TEST_MIX_DENOM); + const MEMBER_SIZE: usize = 100; + let members: [Addr; MEMBER_SIZE] = + std::array::from_fn(|idx| Addr::unchecked(format!("member{}", idx))); + + let mut app = AppBuilder::new().build(|router, _, storage| { + router + .bank + .init_balance(storage, &Addr::unchecked(ADMIN_ADDRESS), init_funds) + .unwrap(); + }); + let coconut_dkg_contract_addr = instantiate_with_group(&mut app, &members); + + for (idx, member) in members.iter().enumerate() { + let res = app + .execute_contract( + member.clone(), + coconut_dkg_contract_addr.clone(), + &RegisterDealer { + bte_key_with_proof: "bte_key_with_proof".to_string(), + announce_address: "127.0.0.1:8000".to_string(), + }, + &vec![], + ) + .unwrap(); + assert_eq!(parse_node_index(res), (idx + 1) as u64); + + let err = app + .execute_contract( + member.clone(), + coconut_dkg_contract_addr.clone(), + &RegisterDealer { + bte_key_with_proof: "bte_key_with_proof".to_string(), + announce_address: "127.0.0.1:8000".to_string(), + }, + &vec![], + ) + .unwrap_err(); + assert_eq!(ContractError::AlreadyADealer, err.downcast().unwrap()); + } + + let unauthorized_member = Addr::unchecked("not_a_member"); + let err = app + .execute_contract( + unauthorized_member, + coconut_dkg_contract_addr.clone(), + &RegisterDealer { + bte_key_with_proof: "bte_key_with_proof".to_string(), + announce_address: "127.0.0.1:8000".to_string(), + }, + &vec![], + ) + .unwrap_err(); + assert_eq!(ContractError::Unauthorized, err.downcast().unwrap()); + } +} diff --git a/contracts/coconut-dkg/src/dealers/queries.rs b/contracts/coconut-dkg/src/dealers/queries.rs index 5221994dc0..b638629851 100644 --- a/contracts/coconut-dkg/src/dealers/queries.rs +++ b/contracts/coconut-dkg/src/dealers/queries.rs @@ -10,7 +10,7 @@ fn query_dealers( deps: Deps<'_>, start_after: Option, limit: Option, - underlying_map: IndexedDealersMap<'_>, + underlying_map: &IndexedDealersMap<'_>, ) -> StdResult { let limit = limit .unwrap_or(storage::DEALERS_PAGE_DEFAULT_LIMIT) @@ -55,7 +55,7 @@ pub fn query_current_dealers_paged( start_after: Option, limit: Option, ) -> StdResult { - query_dealers(deps, start_after, limit, storage::current_dealers()) + query_dealers(deps, start_after, limit, &storage::current_dealers()) } pub fn query_past_dealers_paged( @@ -63,5 +63,159 @@ pub fn query_past_dealers_paged( start_after: Option, limit: Option, ) -> StdResult { - query_dealers(deps, start_after, limit, storage::past_dealers()) + query_dealers(deps, start_after, limit, &storage::past_dealers()) +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT}; + use crate::support::tests::fixtures::dealer_details_fixture; + use crate::support::tests::helpers::init_contract; + use cosmwasm_std::DepsMut; + + fn fill_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) { + for n in 0..size { + let dealer_details = dealer_details_fixture(n as u64); + mapping + .save(deps.storage, &dealer_details.address, &dealer_details) + .unwrap(); + } + } + + fn remove_dealers(deps: DepsMut<'_>, mapping: &IndexedDealersMap<'_>, size: usize) { + for n in 0..size { + let dealer_details = dealer_details_fixture(n as u64); + mapping + .remove(deps.storage, &dealer_details.address) + .unwrap(); + } + } + + #[test] + fn dealers_empty_on_init() { + let deps = init_contract(); + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap(); + assert_eq!(0, page1.dealers.len() as u32); + } + } + + #[test] + fn dealers_paged_retrieval_obeys_limits() { + let mut deps = init_contract(); + let limit = 2; + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 1000); + + let page1 = query_dealers(deps.as_ref(), None, Option::from(limit), &mapping).unwrap(); + assert_eq!(limit, page1.dealers.len() as u32); + + remove_dealers(deps.as_mut(), &mapping, 1000); + } + } + + #[test] + fn dealers_paged_retrieval_has_default_limit() { + let mut deps = init_contract(); + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 1000); + + // query without explicitly setting a limit + let page1 = query_dealers(deps.as_ref(), None, None, &mapping).unwrap(); + + assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32); + + remove_dealers(deps.as_mut(), &mapping, 1000); + } + } + + #[test] + fn dealers_paged_retrieval_has_max_limit() { + let mut deps = init_contract(); + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT; + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 1000); + + let page1 = + query_dealers(deps.as_ref(), None, Option::from(crazy_limit), &mapping).unwrap(); + + // we default to a decent sized upper bound instead + let expected_limit = DEALERS_PAGE_MAX_LIMIT; + assert_eq!(expected_limit, page1.dealers.len() as u32); + + remove_dealers(deps.as_mut(), &mapping, 1000); + } + } + + #[test] + fn dealers_pagination_works() { + let mut deps = init_contract(); + + let per_page = 2; + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 1); + let page1 = + query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap(); + + // page should have 1 result on it + assert_eq!(1, page1.dealers.len()); + remove_dealers(deps.as_mut(), &mapping, 1); + } + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 2); + // page1 should have 2 results on it + let page1 = + query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap(); + assert_eq!(2, page1.dealers.len()); + remove_dealers(deps.as_mut(), &mapping, 2); + } + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 3); + // page1 still has 2 results + let page1 = + query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap(); + assert_eq!(2, page1.dealers.len()); + + // retrieving the next page should start after the last key on this page + let start_after = page1.start_next_after.unwrap(); + let page2 = query_dealers( + deps.as_ref(), + Option::from(start_after.to_string()), + Option::from(per_page), + &mapping, + ) + .unwrap(); + + assert_eq!(1, page2.dealers.len()); + remove_dealers(deps.as_mut(), &mapping, 3); + } + + for mapping in [storage::current_dealers(), storage::past_dealers()] { + fill_dealers(deps.as_mut(), &mapping, 4); + let page1 = + query_dealers(deps.as_ref(), None, Option::from(per_page), &mapping).unwrap(); + let start_after = page1.start_next_after.unwrap(); + let page2 = query_dealers( + deps.as_ref(), + Option::from(start_after.to_string()), + Option::from(per_page), + &mapping, + ) + .unwrap(); + + // now we have 2 pages, with 2 results on the second page + assert_eq!(2, page2.dealers.len()); + remove_dealers(deps.as_mut(), &mapping, 4); + } + } } diff --git a/contracts/coconut-dkg/src/dealers/transactions.rs b/contracts/coconut-dkg/src/dealers/transactions.rs index 74e7d61aee..6566581221 100644 --- a/contracts/coconut-dkg/src/dealers/transactions.rs +++ b/contracts/coconut-dkg/src/dealers/transactions.rs @@ -3,7 +3,8 @@ use crate::dealers::storage as dealers_storage; use crate::epoch_state::utils::check_epoch_state; -use crate::{ContractError, State, STATE}; +use crate::error::ContractError; +use crate::state::{State, STATE}; use coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState}; use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response}; @@ -64,3 +65,40 @@ pub fn try_add_dealer( Ok(Response::new().add_attribute("node_index", node_index.to_string())) } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::epoch_state::transactions::advance_epoch_state; + use crate::support::tests::helpers; + use coconut_dkg_common::types::PUBLIC_KEY_SUBMISSION_TIME_SECS; + use cosmwasm_std::testing::{mock_env, mock_info}; + + #[test] + fn invalid_state() { + let mut deps = helpers::init_contract(); + let owner = Addr::unchecked("owner"); + let mut env = mock_env(); + let info = mock_info(owner.as_str(), &[]); + let bte_key_with_proof = String::from("bte_key_with_proof"); + let announce_address = String::from("localhost:8000"); + + env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env).unwrap(); + + let ret = try_add_dealer( + deps.as_mut(), + info.clone(), + bte_key_with_proof.clone(), + announce_address.clone(), + ) + .unwrap_err(); + assert_eq!( + ret, + ContractError::IncorrectEpochState { + current_state: EpochState::DealingExchange.to_string(), + expected_state: EpochState::default().to_string(), + } + ); + } +} diff --git a/contracts/coconut-dkg/src/dealings/queries.rs b/contracts/coconut-dkg/src/dealings/queries.rs index cf2321837c..0bf0452c14 100644 --- a/contracts/coconut-dkg/src/dealings/queries.rs +++ b/contracts/coconut-dkg/src/dealings/queries.rs @@ -43,3 +43,153 @@ pub fn query_dealings_paged( start_next_after, )) } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT}; + use crate::support::tests::fixtures::dealing_bytes_fixture; + use crate::support::tests::helpers::init_contract; + use cosmwasm_std::{Addr, DepsMut}; + + fn fill_dealings(deps: DepsMut<'_>, size: usize) { + for n in 0..size { + let dealing_share = dealing_bytes_fixture(); + let sender = Addr::unchecked(format!("owner{}", n)); + for idx in 0..TOTAL_DEALINGS { + DEALINGS_BYTES[idx] + .save(deps.storage, &sender, &dealing_share) + .unwrap(); + } + } + } + + #[test] + fn empty_on_bad_idx() { + let mut deps = init_contract(); + fill_dealings(deps.as_mut(), 1000); + + for idx in TOTAL_DEALINGS as u64..100 * TOTAL_DEALINGS as u64 { + let page1 = query_dealings_paged(deps.as_ref(), idx, None, None).unwrap(); + assert_eq!(0, page1.dealings.len() as u32); + } + } + + #[test] + fn dealings_empty_on_init() { + let deps = init_contract(); + for idx in 0..TOTAL_DEALINGS as u64 { + let response = query_dealings_paged(deps.as_ref(), idx, None, Option::from(2)).unwrap(); + assert_eq!(0, response.dealings.len()); + } + } + + #[test] + fn dealings_paged_retrieval_obeys_limits() { + let mut deps = init_contract(); + let limit = 2; + fill_dealings(deps.as_mut(), 1000); + + for idx in 0..TOTAL_DEALINGS as u64 { + let page1 = + query_dealings_paged(deps.as_ref(), idx, None, Option::from(limit)).unwrap(); + assert_eq!(limit, page1.dealings.len() as u32); + } + } + + #[test] + fn dealings_paged_retrieval_has_default_limit() { + let mut deps = init_contract(); + fill_dealings(deps.as_mut(), 1000); + + for idx in 0..TOTAL_DEALINGS as u64 { + // query without explicitly setting a limit + let page1 = query_dealings_paged(deps.as_ref(), idx, None, None).unwrap(); + + assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32); + } + } + + #[test] + fn dealings_paged_retrieval_has_max_limit() { + let mut deps = init_contract(); + fill_dealings(deps.as_mut(), 1000); + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT; + for idx in 0..TOTAL_DEALINGS as u64 { + let page1 = + query_dealings_paged(deps.as_ref(), idx, None, Option::from(crazy_limit)).unwrap(); + + // we default to a decent sized upper bound instead + let expected_limit = DEALINGS_PAGE_MAX_LIMIT; + assert_eq!(expected_limit, page1.dealings.len() as u32); + } + } + + #[test] + fn dealings_pagination_works() { + let mut deps = init_contract(); + + fill_dealings(deps.as_mut(), 1); + + let per_page = 2; + + for idx in 0..TOTAL_DEALINGS as u64 { + let page1 = + query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap(); + + // page should have 1 result on it + assert_eq!(1, page1.dealings.len()); + } + + // save another + fill_dealings(deps.as_mut(), 2); + + for idx in 0..TOTAL_DEALINGS as u64 { + // page1 should have 2 results on it + let page1 = + query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap(); + assert_eq!(2, page1.dealings.len()); + } + + fill_dealings(deps.as_mut(), 3); + + for idx in 0..TOTAL_DEALINGS as u64 { + // page1 still has 2 results + let page1 = + query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap(); + assert_eq!(2, page1.dealings.len()); + + // retrieving the next page should start after the last key on this page + let start_after = page1.start_next_after.unwrap(); + let page2 = query_dealings_paged( + deps.as_ref(), + idx, + Option::from(start_after.to_string()), + Option::from(per_page), + ) + .unwrap(); + + assert_eq!(1, page2.dealings.len()); + } + + fill_dealings(deps.as_mut(), 4); + + for idx in 0..TOTAL_DEALINGS as u64 { + let page1 = + query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap(); + let start_after = page1.start_next_after.unwrap(); + let page2 = query_dealings_paged( + deps.as_ref(), + idx, + Option::from(start_after.to_string()), + Option::from(per_page), + ) + .unwrap(); + + // now we have 2 pages, with 2 results on the second page + assert_eq!(2, page2.dealings.len()); + } + } +} diff --git a/contracts/coconut-dkg/src/dealings/transactions.rs b/contracts/coconut-dkg/src/dealings/transactions.rs index 73e067e0d6..fe2e5544cb 100644 --- a/contracts/coconut-dkg/src/dealings/transactions.rs +++ b/contracts/coconut-dkg/src/dealings/transactions.rs @@ -4,7 +4,7 @@ use crate::dealers::storage as dealers_storage; use crate::dealings::storage::DEALINGS_BYTES; use crate::epoch_state::utils::check_epoch_state; -use crate::ContractError; +use crate::error::ContractError; use coconut_dkg_common::types::{ContractSafeBytes, EpochState}; use cosmwasm_std::{DepsMut, MessageInfo, Response}; @@ -35,3 +35,66 @@ pub fn try_commit_dealings( commitment: String::from("dealing"), }) } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::epoch_state::transactions::advance_epoch_state; + use crate::support::tests::fixtures::dealing_bytes_fixture; + use crate::support::tests::helpers; + use coconut_dkg_common::dealer::DealerDetails; + use coconut_dkg_common::types::PUBLIC_KEY_SUBMISSION_TIME_SECS; + use cosmwasm_std::testing::{mock_env, mock_info}; + use cosmwasm_std::Addr; + + #[test] + fn invalid_commit_dealing() { + let mut deps = helpers::init_contract(); + let owner = Addr::unchecked("owner"); + let mut env = mock_env(); + let info = mock_info(owner.as_str(), &[]); + let dealing_bytes = dealing_bytes_fixture(); + + let ret = + try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err(); + assert_eq!( + ret, + ContractError::IncorrectEpochState { + current_state: EpochState::default().to_string(), + expected_state: EpochState::DealingExchange.to_string() + } + ); + + env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env).unwrap(); + + let ret = + try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err(); + assert_eq!(ret, ContractError::NotADealer); + + let dealer_details = DealerDetails { + address: owner.clone(), + bte_public_key_with_proof: String::new(), + announce_address: String::new(), + assigned_index: 1, + }; + dealers_storage::current_dealers() + .save(deps.as_mut().storage, &owner, &dealer_details) + .unwrap(); + + for dealings in DEALINGS_BYTES { + assert!(!dealings.has(deps.as_mut().storage, &owner)); + let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()); + assert!(ret.is_ok()); + assert!(dealings.has(deps.as_mut().storage, &owner)); + } + let ret = + try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err(); + assert_eq!( + ret, + ContractError::AlreadyCommitted { + commitment: String::from("dealing"), + } + ); + } +} diff --git a/contracts/coconut-dkg/src/epoch_state/mod.rs b/contracts/coconut-dkg/src/epoch_state/mod.rs index 195ff89361..02c5bc4402 100644 --- a/contracts/coconut-dkg/src/epoch_state/mod.rs +++ b/contracts/coconut-dkg/src/epoch_state/mod.rs @@ -3,4 +3,5 @@ pub mod queries; pub mod storage; +pub mod transactions; pub mod utils; diff --git a/contracts/coconut-dkg/src/epoch_state/queries.rs b/contracts/coconut-dkg/src/epoch_state/queries.rs index 142ee4826a..3a68e0117c 100644 --- a/contracts/coconut-dkg/src/epoch_state/queries.rs +++ b/contracts/coconut-dkg/src/epoch_state/queries.rs @@ -1,13 +1,48 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::storage; -use crate::ContractError; -use coconut_dkg_common::types::EpochState; +use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD}; +use crate::error::ContractError; +use coconut_dkg_common::types::Epoch; use cosmwasm_std::Storage; -pub(crate) fn query_current_epoch_state( - storage: &dyn Storage, -) -> Result { - storage::current_epoch_state(storage) +pub(crate) fn query_current_epoch(storage: &dyn Storage) -> Result { + CURRENT_EPOCH + .load(storage) + .map_err(|_| ContractError::EpochNotInitialised) +} + +pub(crate) fn query_current_epoch_threshold( + storage: &dyn Storage, +) -> Result, ContractError> { + Ok(THRESHOLD.may_load(storage)?) +} + +#[cfg(test)] +pub(crate) mod test { + use super::*; + use crate::support::tests::helpers::init_contract; + use coconut_dkg_common::types::{EpochState, PUBLIC_KEY_SUBMISSION_TIME_SECS}; + use cosmwasm_std::testing::mock_env; + + #[test] + fn query_state() { + let mut deps = init_contract(); + let epoch = query_current_epoch(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::PublicKeySubmission); + assert_eq!( + epoch.finish_timestamp, + mock_env() + .block + .time + .plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS) + ); + } + + #[test] + fn query_threshold() { + let mut deps = init_contract(); + let state = query_current_epoch_threshold(deps.as_mut().storage).unwrap(); + assert!(state.is_none()); + } } diff --git a/contracts/coconut-dkg/src/epoch_state/storage.rs b/contracts/coconut-dkg/src/epoch_state/storage.rs index b0abe5fa5b..d5aac5b041 100644 --- a/contracts/coconut-dkg/src/epoch_state/storage.rs +++ b/contracts/coconut-dkg/src/epoch_state/storage.rs @@ -1,29 +1,8 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{ContractError, ADMIN}; -use coconut_dkg_common::types::EpochState; -use cosmwasm_std::{DepsMut, MessageInfo, Response, Storage}; +use coconut_dkg_common::types::Epoch; use cw_storage_plus::Item; -pub(crate) const CURRENT_EPOCH_STATE: Item<'_, EpochState> = Item::new("current_epoch_state"); - -pub(crate) fn current_epoch_state(storage: &dyn Storage) -> Result { - CURRENT_EPOCH_STATE - .load(storage) - .map_err(|_| ContractError::EpochNotInitialised) -} - -pub(crate) fn advance_epoch_state( - deps: DepsMut<'_>, - info: MessageInfo, -) -> Result { - ADMIN.assert_admin(deps.as_ref(), &info.sender)?; - CURRENT_EPOCH_STATE.update::<_, ContractError>(deps.storage, |mut epoch_state| { - // TODO: When defaulting to the first state, some action will probably need to be taken on the - // rest of the contract, as we're starting with a new set of signers - epoch_state = epoch_state.next().unwrap_or_default(); - Ok(epoch_state) - })?; - Ok(Response::default()) -} +pub(crate) const CURRENT_EPOCH: Item<'_, Epoch> = Item::new("current_epoch"); +pub const THRESHOLD: Item = Item::new("threshold"); diff --git a/contracts/coconut-dkg/src/epoch_state/transactions.rs b/contracts/coconut-dkg/src/epoch_state/transactions.rs new file mode 100644 index 0000000000..e2e75a1678 --- /dev/null +++ b/contracts/coconut-dkg/src/epoch_state/transactions.rs @@ -0,0 +1,208 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::dealers::storage::current_dealers; +use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD}; +use crate::error::ContractError; +use coconut_dkg_common::types::{Epoch, EpochState}; +use cosmwasm_std::{DepsMut, Env, Order, Response}; + +pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result { + let epoch = CURRENT_EPOCH.load(deps.storage)?; + if epoch.finish_timestamp > env.block.time { + return Err(ContractError::EarlyEpochStateAdvancement( + epoch + .finish_timestamp + .minus_seconds(env.block.time.seconds()) + .seconds(), + )); + } + + let current_epoch = CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |mut epoch| { + // TODO: When defaulting to the first state, some action will probably need to be taken on the + // rest of the contract, as we're starting with a new set of signers + epoch = Epoch::new(epoch.state.next().unwrap_or_default(), env.block.time); + Ok(epoch) + })?; + if current_epoch.state == EpochState::DealingExchange { + let current_dealer_count = current_dealers() + .keys(deps.storage, None, None, Order::Ascending) + .count(); + // note: ceiling in integer division can be achieved via q = (x + y - 1) / y; + let threshold = (2 * current_dealer_count as u64 + 3 - 1) / 3; + THRESHOLD.save(deps.storage, &threshold)?; + } + Ok(Response::default()) +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::error::ContractError::EarlyEpochStateAdvancement; + use crate::support::tests::helpers::init_contract; + use coconut_dkg_common::types::{ + DealerDetails, EpochState, DEALING_EXCHANGE_TIME_SECS, IN_PROGRESS_TIME_SECS, + PUBLIC_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_FINALIZATION_TIME_SECS, + VERIFICATION_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_VALIDATION_TIME_SECS, + }; + use cosmwasm_std::testing::mock_env; + use cosmwasm_std::Addr; + + #[test] + fn advance_state() { + let mut deps = init_contract(); + let mut env = mock_env(); + + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::PublicKeySubmission); + assert_eq!( + epoch.finish_timestamp, + env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS) + ); + + env.block.time = env + .block + .time + .plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS - 1); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(1) + ); + + env.block.time = env.block.time.plus_seconds(1); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::DealingExchange); + assert_eq!( + epoch.finish_timestamp, + env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS) + ); + + env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS - 2); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(2) + ); + + env.block.time = env.block.time.plus_seconds(3); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::VerificationKeySubmission); + assert_eq!( + epoch.finish_timestamp, + env.block + .time + .plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS) + ); + + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS - 2); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(2) + ); + + env.block.time = env.block.time.plus_seconds(3); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::VerificationKeyValidation); + assert_eq!( + epoch.finish_timestamp, + env.block + .time + .plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS) + ); + + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS - 3); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(3) + ); + + env.block.time = env.block.time.plus_seconds(3); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::VerificationKeyFinalization); + assert_eq!( + epoch.finish_timestamp, + env.block + .time + .plus_seconds(VERIFICATION_KEY_FINALIZATION_TIME_SECS) + ); + + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_FINALIZATION_TIME_SECS - 1); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(1) + ); + + env.block.time = env.block.time.plus_seconds(1); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::InProgress); + assert_eq!( + epoch.finish_timestamp, + env.block.time.plus_seconds(IN_PROGRESS_TIME_SECS) + ); + + env.block.time = env.block.time.plus_seconds(IN_PROGRESS_TIME_SECS - 100); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(100) + ); + + env.block.time = env.block.time.plus_seconds(50); + assert_eq!( + advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(), + EarlyEpochStateAdvancement(50) + ); + + env.block.time = env.block.time.plus_seconds(100); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); + assert_eq!(epoch.state, EpochState::PublicKeySubmission); + assert_eq!( + epoch.finish_timestamp, + env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS) + ); + } + + #[test] + fn verify_threshold() { + let mut deps = init_contract(); + let mut env = mock_env(); + + assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none()); + + for i in 1..101 { + let address = Addr::unchecked(format!("dealer{}", i)); + current_dealers() + .save( + deps.as_mut().storage, + &address, + &DealerDetails { + address: address.clone(), + bte_public_key_with_proof: "bte_public_key_with_proof".to_string(), + announce_address: "127.0.0.1".to_string(), + assigned_index: i, + }, + ) + .unwrap(); + } + + env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env).unwrap(); + assert_eq!( + THRESHOLD.may_load(deps.as_mut().storage).unwrap().unwrap(), + 67 + ); + } +} diff --git a/contracts/coconut-dkg/src/epoch_state/utils.rs b/contracts/coconut-dkg/src/epoch_state/utils.rs index d78fd49ae3..280436ee2e 100644 --- a/contracts/coconut-dkg/src/epoch_state/utils.rs +++ b/contracts/coconut-dkg/src/epoch_state/utils.rs @@ -1,7 +1,8 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{ContractError, CURRENT_EPOCH_STATE}; +use crate::epoch_state::storage::CURRENT_EPOCH; +use crate::error::ContractError; use coconut_dkg_common::types::EpochState; use cosmwasm_std::Storage; @@ -9,7 +10,7 @@ pub(crate) fn check_epoch_state( storage: &dyn Storage, against: EpochState, ) -> Result<(), ContractError> { - let epoch_state = CURRENT_EPOCH_STATE.load(storage)?; + let epoch_state = CURRENT_EPOCH.load(storage)?.state; if epoch_state != against { Err(ContractError::IncorrectEpochState { current_state: epoch_state.to_string(), @@ -19,3 +20,34 @@ pub(crate) fn check_epoch_state( Ok(()) } } + +#[cfg(test)] +pub(crate) mod test { + use super::*; + use crate::support::tests::helpers::init_contract; + use coconut_dkg_common::types::Epoch; + use cosmwasm_std::testing::mock_env; + + #[test] + pub fn check_state() { + let mut deps = init_contract(); + let env = mock_env(); + + for fixed_state in EpochState::default().all_until(EpochState::InProgress) { + CURRENT_EPOCH + .save( + deps.as_mut().storage, + &Epoch::new(fixed_state, env.block.time), + ) + .unwrap(); + for against_state in EpochState::default().all_until(EpochState::InProgress) { + let ret = check_epoch_state(deps.as_mut().storage, against_state); + if fixed_state == against_state { + assert!(ret.is_ok()); + } else { + assert!(ret.is_err()); + } + } + } + } +} diff --git a/contracts/coconut-dkg/src/error.rs b/contracts/coconut-dkg/src/error.rs index ad3a3e74ef..0baeae5edd 100644 --- a/contracts/coconut-dkg/src/error.rs +++ b/contracts/coconut-dkg/src/error.rs @@ -1,46 +1,31 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use cosmwasm_std::{Addr, StdError, VerificationError}; +use cosmwasm_std::StdError; use cw_controllers::AdminError; use thiserror::Error; /// Custom errors for contract failure conditions. #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error(transparent)] Std(#[from] StdError), - #[error("{0}")] + #[error(transparent)] Admin(#[from] AdminError), #[error("Group contract invalid address '{addr}'")] InvalidGroup { addr: String }, - #[error("No coin was sent for the deposit, you must send {denom}")] - NoDepositFound { denom: String }, - - #[error("Received multiple coin types")] - MultipleDenoms, - - #[error("Wrong coin denomination, you must send {denom}")] - WrongDenom { denom: String }, - - #[error("Not enough funds sent for deposit. (received {received}, minimum {minimum})")] - InsufficientDeposit { received: u128, minimum: u128 }, - - #[error("Failed to perform ed25519 signature verification - {0}. This dealer will be temporarily blacklisted now.")] - Ed25519VerificationError(#[from] VerificationError), - - #[error("Provided ed25519 signature did not verify correctly. This dealer will be temporarily blacklisted now.")] - InvalidEd25519Signature, - #[error("This potential dealer is not in the coconut signer group")] Unauthorized, #[error("This sender is already a dealer for the epoch")] AlreadyADealer, + #[error("Too soon to advance epoch state. {0} more seconds until it can be advanced")] + EarlyEpochStateAdvancement(u64), + #[error("Epoch hasn't been correctly initialised!")] EpochNotInitialised, @@ -52,11 +37,6 @@ pub enum ContractError { expected_state: String, }, - // we should never ever see this error (famous last words in programming), therefore, I'd want to - // explicitly declare it so that when we ultimate do see it, it's gonna be more informative over "normal" panic - #[error("Somehow our validated address {address} is not using correct bech32 encoding")] - InvalidValidatedAddress { address: Addr }, - #[error("This sender is not a dealer for the current epoch")] NotADealer, diff --git a/contracts/coconut-dkg/src/lib.rs b/contracts/coconut-dkg/src/lib.rs index 7255b96ae8..de93fa7b92 100644 --- a/contracts/coconut-dkg/src/lib.rs +++ b/contracts/coconut-dkg/src/lib.rs @@ -1,146 +1,12 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::dealers::queries::{ - query_current_dealers_paged, query_dealer_details, query_past_dealers_paged, -}; -use crate::dealings::queries::query_dealings_paged; -use crate::epoch_state::queries::query_current_epoch_state; -use crate::error::ContractError; -use crate::state::{State, ADMIN, MULTISIG, STATE}; -use crate::verification_key_shares::queries::query_vk_shares_paged; -use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; -use coconut_dkg_common::types::EpochState; -use cosmwasm_std::{ - entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, -}; -use cw4::Cw4Contract; -use epoch_state::storage::{advance_epoch_state, CURRENT_EPOCH_STATE}; - mod constants; +pub mod contract; mod dealers; mod dealings; mod epoch_state; -mod error; +pub mod error; mod state; +mod support; mod verification_key_shares; - -/// Instantiate the contract. -/// -/// `deps` contains Storage, API and Querier -/// `env` contains block, message and contract info -/// `msg` is the contract initialization message, sort of like a constructor call. -#[entry_point] -pub fn instantiate( - mut deps: DepsMut<'_>, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - let admin_addr = deps.api.addr_validate(&msg.admin)?; - let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?; - ADMIN.set(deps.branch(), Some(admin_addr))?; - MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?; - - let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| { - ContractError::InvalidGroup { - addr: msg.group_addr.clone(), - } - })?); - - let state = State { - group_addr, - multisig_addr, - mix_denom: msg.mix_denom, - }; - STATE.save(deps.storage, &state)?; - - CURRENT_EPOCH_STATE.save(deps.storage, &EpochState::default())?; - - Ok(Response::default()) -} - -/// Handle an incoming message -#[entry_point] -pub fn execute( - deps: DepsMut<'_>, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::RegisterDealer { - bte_key_with_proof, - announce_address, - } => { - dealers::transactions::try_add_dealer(deps, info, bte_key_with_proof, announce_address) - } - ExecuteMsg::CommitDealing { dealing_bytes } => { - dealings::transactions::try_commit_dealings(deps, info, dealing_bytes) - } - ExecuteMsg::CommitVerificationKeyShare { share } => { - verification_key_shares::transactions::try_commit_verification_key_share( - deps, env, info, share, - ) - } - ExecuteMsg::VerifyVerificationKeyShare { owner } => { - verification_key_shares::transactions::try_verify_verification_key_share( - deps, info, owner, - ) - } - ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, info), - } -} - -#[entry_point] -pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result { - let response = match msg { - QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch_state(deps.storage)?)?, - QueryMsg::GetDealerDetails { dealer_address } => { - to_binary(&query_dealer_details(deps, dealer_address)?)? - } - QueryMsg::GetCurrentDealers { limit, start_after } => { - to_binary(&query_current_dealers_paged(deps, start_after, limit)?)? - } - QueryMsg::GetPastDealers { limit, start_after } => { - to_binary(&query_past_dealers_paged(deps, start_after, limit)?)? - } - QueryMsg::GetDealing { - idx, - limit, - start_after, - } => to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?, - QueryMsg::GetVerificationKeys { limit, start_after } => { - to_binary(&query_vk_shares_paged(deps, start_after, limit)?)? - } - }; - - Ok(response) -} - -#[entry_point] -pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { - Ok(Default::default()) -} - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - - #[test] - fn initialize_contract() { - let mut deps = mock_dependencies(); - let env = mock_env(); - let msg = InstantiateMsg { - group_addr: "group_addr".to_string(), - multisig_addr: "multisig_addr".to_string(), - admin: "admin".to_string(), - mix_denom: "nym".to_string(), - }; - let info = mock_info("creator", &[]); - - let res = instantiate(deps.as_mut(), env.clone(), info, msg); - assert!(res.is_ok()) - } -} diff --git a/contracts/coconut-dkg/src/state/mod.rs b/contracts/coconut-dkg/src/state/mod.rs index 729618ec60..533bd98a0a 100644 --- a/contracts/coconut-dkg/src/state/mod.rs +++ b/contracts/coconut-dkg/src/state/mod.rs @@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize}; // unique items pub const STATE: Item = Item::new("state"); -pub const ADMIN: Admin = Admin::new("admin"); pub const MULTISIG: Admin = Admin::new("multisig"); #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] diff --git a/contracts/coconut-dkg/src/support/mod.rs b/contracts/coconut-dkg/src/support/mod.rs new file mode 100644 index 0000000000..15f23f8519 --- /dev/null +++ b/contracts/coconut-dkg/src/support/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(test)] +pub mod tests; diff --git a/contracts/coconut-dkg/src/support/tests/fixtures.rs b/contracts/coconut-dkg/src/support/tests/fixtures.rs new file mode 100644 index 0000000000..19aad555d1 --- /dev/null +++ b/contracts/coconut-dkg/src/support/tests/fixtures.rs @@ -0,0 +1,32 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use coconut_dkg_common::dealer::DealerDetails; +use coconut_dkg_common::types::ContractSafeBytes; +use coconut_dkg_common::verification_key::ContractVKShare; +use cosmwasm_std::Addr; + +pub const TEST_MIX_DENOM: &str = "unym"; + +pub fn vk_share_fixture(index: u64) -> ContractVKShare { + ContractVKShare { + share: format!("share{}", index), + announce_address: format!("localhost:{}", index), + node_index: index, + owner: Addr::unchecked(format!("owner{}", index)), + verified: index % 2 == 0, + } +} + +pub fn dealing_bytes_fixture() -> ContractSafeBytes { + ContractSafeBytes(vec![]) +} + +pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails { + DealerDetails { + address: Addr::unchecked(format!("owner{}", assigned_index)), + bte_public_key_with_proof: "".to_string(), + announce_address: "".to_string(), + assigned_index, + } +} diff --git a/contracts/coconut-dkg/src/support/tests/helpers.rs b/contracts/coconut-dkg/src/support/tests/helpers.rs new file mode 100644 index 0000000000..b59ee40bcf --- /dev/null +++ b/contracts/coconut-dkg/src/support/tests/helpers.rs @@ -0,0 +1,26 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::contract::instantiate; +use coconut_dkg_common::msg::InstantiateMsg; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; +use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps}; + +use super::fixtures::TEST_MIX_DENOM; + +pub const ADMIN_ADDRESS: &str = "admin address"; +pub const GROUP_CONTRACT: &str = "group contract address"; +pub const MULTISIG_CONTRACT: &str = "multisig contract address"; + +pub fn init_contract() -> OwnedDeps> { + let mut deps = mock_dependencies(); + let msg = InstantiateMsg { + group_addr: String::from(GROUP_CONTRACT), + multisig_addr: String::from(MULTISIG_CONTRACT), + mix_denom: TEST_MIX_DENOM.to_string(), + }; + let env = mock_env(); + let info = mock_info(ADMIN_ADDRESS, &[]); + instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); + deps +} diff --git a/contracts/coconut-dkg/src/support/tests/mod.rs b/contracts/coconut-dkg/src/support/tests/mod.rs new file mode 100644 index 0000000000..1e96465ed5 --- /dev/null +++ b/contracts/coconut-dkg/src/support/tests/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod fixtures; +pub mod helpers; diff --git a/contracts/coconut-dkg/src/verification_key_shares/queries.rs b/contracts/coconut-dkg/src/verification_key_shares/queries.rs index 6739577abb..331c42e58a 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/queries.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/queries.rs @@ -36,3 +36,142 @@ pub fn query_vk_shares_paged( start_next_after, }) } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::support::tests::fixtures::vk_share_fixture; + use crate::support::tests::helpers::init_contract; + use crate::verification_key_shares::storage::{ + VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT, VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT, + }; + use cosmwasm_std::Addr; + + #[test] + fn vk_shares_empty_on_init() { + let deps = init_contract(); + let response = query_vk_shares_paged(deps.as_ref(), None, Option::from(2)).unwrap(); + assert_eq!(0, response.shares.len()); + } + + #[test] + fn vk_shares_paged_retrieval_obeys_limits() { + let mut deps = init_contract(); + let limit = 2; + for n in 0..1000 { + let vk_share = vk_share_fixture(n); + let sender = Addr::unchecked(format!("owner{}", n)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + } + + let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(limit)).unwrap(); + assert_eq!(limit, page1.shares.len() as u32); + } + + #[test] + fn vk_shares_paged_retrieval_has_default_limit() { + let mut deps = init_contract(); + for n in 0..1000 { + let vk_share = vk_share_fixture(n); + let sender = Addr::unchecked(format!("owner{}", n)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + } + + // query without explicitly setting a limit + let page1 = query_vk_shares_paged(deps.as_ref(), None, None).unwrap(); + + assert_eq!( + VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT, + page1.shares.len() as u32 + ); + } + + #[test] + fn vk_shares_paged_retrieval_has_max_limit() { + let mut deps = init_contract(); + for n in 0..1000 { + let vk_share = vk_share_fixture(n); + let sender = Addr::unchecked(format!("owner{}", n)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + } + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000 * VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT; + let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap(); + + // we default to a decent sized upper bound instead + let expected_limit = VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT; + assert_eq!(expected_limit, page1.shares.len() as u32); + } + + #[test] + fn vk_shares_pagination_works() { + let mut deps = init_contract(); + + let vk_share = vk_share_fixture(1); + let sender = Addr::unchecked(format!("owner{}", 1)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + + let per_page = 2; + let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + + // page should have 1 result on it + assert_eq!(1, page1.shares.len()); + + // save another + let vk_share = vk_share_fixture(2); + let sender = Addr::unchecked(format!("owner{}", 2)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + + // page1 should have 2 results on it + let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + assert_eq!(2, page1.shares.len()); + + let vk_share = vk_share_fixture(3); + let sender = Addr::unchecked(format!("owner{}", 3)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + + // page1 still has 2 results + let page1 = query_vk_shares_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + assert_eq!(2, page1.shares.len()); + + // retrieving the next page should start after the last key on this page + let start_after = page1.start_next_after.unwrap(); + let page2 = query_vk_shares_paged( + deps.as_ref(), + Option::from(start_after.to_string()), + Option::from(per_page), + ) + .unwrap(); + + assert_eq!(1, page2.shares.len()); + + let vk_share = vk_share_fixture(4); + let sender = Addr::unchecked(format!("owner{}", 4)); + VK_SHARES + .save(&mut deps.storage, &sender, &vk_share) + .unwrap(); + + let page2 = query_vk_shares_paged( + deps.as_ref(), + Option::from(start_after.to_string()), + Option::from(per_page), + ) + .unwrap(); + + // now we have 2 pages, with 2 results on the second page + assert_eq!(2, page2.shares.len()); + } +} diff --git a/contracts/coconut-dkg/src/verification_key_shares/transactions.rs b/contracts/coconut-dkg/src/verification_key_shares/transactions.rs index d86748e7f4..69df1bad7b 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/transactions.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/transactions.rs @@ -69,3 +69,168 @@ pub fn try_verify_verification_key_share( Ok(Response::default()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::epoch_state::transactions::advance_epoch_state; + use crate::support::tests::helpers; + use crate::support::tests::helpers::MULTISIG_CONTRACT; + use coconut_dkg_common::dealer::DealerDetails; + use coconut_dkg_common::types::{ + EpochState, DEALING_EXCHANGE_TIME_SECS, PUBLIC_KEY_SUBMISSION_TIME_SECS, + VERIFICATION_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_VALIDATION_TIME_SECS, + }; + use cosmwasm_std::testing::{mock_env, mock_info}; + use cw_controllers::AdminError; + + #[test] + fn commit_vk_share() { + let mut deps = helpers::init_contract(); + let mut env = mock_env(); + let info = mock_info("requester", &[]); + let share = "share".to_string(); + + let ret = try_commit_verification_key_share( + deps.as_mut(), + env.clone(), + info.clone(), + share.clone(), + ) + .unwrap_err(); + assert_eq!( + ret, + ContractError::IncorrectEpochState { + current_state: EpochState::default().to_string(), + expected_state: EpochState::VerificationKeySubmission.to_string() + } + ); + env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + let ret = try_commit_verification_key_share( + deps.as_mut(), + env.clone(), + info.clone(), + share.clone(), + ) + .unwrap_err(); + assert_eq!(ret, ContractError::NotADealer); + + let dealer = Addr::unchecked("requester"); + let dealer_details = DealerDetails { + address: dealer.clone(), + bte_public_key_with_proof: String::new(), + announce_address: String::new(), + assigned_index: 1, + }; + dealers_storage::current_dealers() + .save(deps.as_mut().storage, &dealer, &dealer_details) + .unwrap(); + + try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone()) + .unwrap(); + + let ret = try_commit_verification_key_share( + deps.as_mut(), + env.clone(), + info.clone(), + share.clone(), + ) + .unwrap_err(); + assert_eq!( + ret, + ContractError::AlreadyCommitted { + commitment: String::from("verification key share") + } + ); + } + + #[test] + fn invalid_verify_vk_share() { + let mut deps = helpers::init_contract(); + let mut env = mock_env(); + let info = mock_info("requester", &[]); + let owner = Addr::unchecked("owner"); + let multisig_info = mock_info(MULTISIG_CONTRACT, &[]); + + let ret = try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone()) + .unwrap_err(); + assert_eq!( + ret, + ContractError::IncorrectEpochState { + current_state: EpochState::default().to_string(), + expected_state: EpochState::VerificationKeyFinalization.to_string() + } + ); + + env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env).unwrap(); + + let ret = + try_verify_verification_key_share(deps.as_mut(), info, owner.clone()).unwrap_err(); + assert_eq!(ret, ContractError::Admin(AdminError::NotAdmin {})); + + let ret = try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone()) + .unwrap_err(); + assert_eq!( + ret, + ContractError::NoCommitForOwner { + owner: owner.to_string() + } + ); + } + + #[test] + fn verify_vk_share() { + let mut deps = helpers::init_contract(); + let mut env = mock_env(); + let owner = Addr::unchecked("owner"); + let info = mock_info(owner.as_ref(), &[]); + let share = "share".to_string(); + let multisig_info = mock_info(MULTISIG_CONTRACT, &[]); + + env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + + let dealer_details = DealerDetails { + address: owner.clone(), + bte_public_key_with_proof: String::new(), + announce_address: String::new(), + assigned_index: 1, + }; + dealers_storage::current_dealers() + .save(deps.as_mut().storage, &owner, &dealer_details) + .unwrap(); + try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone()) + .unwrap(); + + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); + env.block.time = env + .block + .time + .plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS); + advance_epoch_state(deps.as_mut(), env).unwrap(); + + try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone()).unwrap(); + } +} diff --git a/contracts/coconut-test/Cargo.toml b/contracts/coconut-test/Cargo.toml index 3aff6fc368..f9dd795e4b 100644 --- a/contracts/coconut-test/Cargo.toml +++ b/contracts/coconut-test/Cargo.toml @@ -13,6 +13,7 @@ multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multi cosmwasm-std = "1.0.0" cosmwasm-storage = "1.0.0" +cw3 = "0.13.4" cw4 = "0.13.4" cw-storage-plus = "0.13.4" cw-controllers = "0.13.4" diff --git a/contracts/coconut-test/src/helpers.rs b/contracts/coconut-test/src/helpers.rs index b8e8ad6d03..319e523c91 100644 --- a/contracts/coconut-test/src/helpers.rs +++ b/contracts/coconut-test/src/helpers.rs @@ -42,9 +42,9 @@ pub fn mock_app(init_funds: &[Coin]) -> App { } pub fn contract_dkg() -> Box> { let contract = ContractWrapper::new( - coconut_dkg::execute, - coconut_dkg::instantiate, - coconut_dkg::query, + coconut_dkg::contract::execute, + coconut_dkg::contract::instantiate, + coconut_dkg::contract::query, ); Box::new(contract) } diff --git a/contracts/coconut-test/src/submit_vk_creates_proposal.rs b/contracts/coconut-test/src/submit_vk_creates_proposal.rs index dd8f59b2da..1122115b6d 100644 --- a/contracts/coconut-test/src/submit_vk_creates_proposal.rs +++ b/contracts/coconut-test/src/submit_vk_creates_proposal.rs @@ -11,15 +11,18 @@ use coconut_dkg_common::msg::ExecuteMsg::{ AdvanceEpochState, CommitVerificationKeyShare, RegisterDealer, }; use coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg; +use coconut_dkg_common::msg::QueryMsg::GetVerificationKeys; +use coconut_dkg_common::verification_key::PagedVKSharesResponse; use cosmwasm_std::{coins, Addr, Decimal}; use cw4::Member; use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg; use cw_multi_test::Executor; use cw_utils::{Duration, Threshold}; +use multisig_contract_common::msg::ExecuteMsg::{Execute, Vote}; use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; #[test] -fn dkg_create_proposal() { +fn dkg_proposal() { let init_funds = coins(10000000000, TEST_COIN_DENOM); let mut app = mock_app(&init_funds); let member1 = Member { @@ -47,7 +50,7 @@ fn dkg_create_proposal() { let msg = MultisigInstantiateMsg { group_addr: group_contract_addr.to_string(), threshold: Threshold::AbsolutePercentage { - percentage: Decimal::from_ratio(2u128, 3u128), + percentage: Decimal::from_ratio(1u128, 1u128), }, max_voting_period: Duration::Time(1000), coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), @@ -68,7 +71,6 @@ fn dkg_create_proposal() { let msg = DkgInstantiateMsg { group_addr: group_contract_addr.to_string(), multisig_addr: multisig_contract_addr.to_string(), - admin: Addr::unchecked(OWNER).to_string(), mix_denom: TEST_COIN_DENOM.to_string(), }; let coconut_dkg_contract_addr = app @@ -88,7 +90,7 @@ fn dkg_create_proposal() { }; app.migrate_contract( Addr::unchecked(OWNER), - multisig_contract_addr, + multisig_contract_addr.clone(), &msg, multisig_code_id, ) @@ -106,6 +108,7 @@ fn dkg_create_proposal() { .unwrap(); for _ in 0..2 { + app.update_block(|block| block.time = block.time.plus_seconds(1000)); app.execute_contract( Addr::unchecked(OWNER), coconut_dkg_contract_addr.clone(), @@ -115,6 +118,9 @@ fn dkg_create_proposal() { .unwrap(); } + // Proposal needs to be later then the member became part of the group + app.update_block(|block| block.height += 1); + let msg = CommitVerificationKeyShare { share: "share".to_string(), }; @@ -126,6 +132,7 @@ fn dkg_create_proposal() { &vec![], ) .unwrap(); + let proposal_id = res .events .into_iter() @@ -138,5 +145,63 @@ fn dkg_create_proposal() { .value .parse::() .unwrap(); - assert_eq!(1, proposal_id); + + let mut res: PagedVKSharesResponse = app + .wrap() + .query_wasm_smart( + coconut_dkg_contract_addr.clone(), + &GetVerificationKeys { + limit: None, + start_after: None, + }, + ) + .unwrap(); + let share = res.shares.pop().unwrap(); + assert_eq!(share.share, "share".to_string()); + assert_eq!(share.announce_address, "127.0.0.1:8000".to_string()); + assert_eq!(share.node_index, 1); + assert_eq!(share.owner, Addr::unchecked(MEMBER1)); + assert!(!share.verified); + + app.execute_contract( + Addr::unchecked(MEMBER1), + multisig_contract_addr.clone(), + &Vote { + proposal_id, + vote: cw3::Vote::Yes, + }, + &vec![], + ) + .unwrap(); + + for _ in 0..2 { + app.update_block(|block| block.time = block.time.plus_seconds(1000)); + app.execute_contract( + Addr::unchecked(OWNER), + coconut_dkg_contract_addr.clone(), + &AdvanceEpochState {}, + &vec![], + ) + .unwrap(); + } + + app.execute_contract( + Addr::unchecked(MEMBER1), + multisig_contract_addr.clone(), + &Execute { proposal_id }, + &vec![], + ) + .unwrap(); + + let res: PagedVKSharesResponse = app + .wrap() + .query_wasm_smart( + coconut_dkg_contract_addr, + &GetVerificationKeys { + limit: None, + start_after: None, + }, + ) + .unwrap(); + assert!(res.shares[0].verified); } diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index d4d0ac43a5..6dc7273c18 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mixnet-contract" -version = "1.1.0" +version = "1.1.2" authors = ["Dave Hrycyszyn "] edition = "2021" diff --git a/contracts/mixnet/src/constants.rs b/contracts/mixnet/src/constants.rs index 3d3ed82caf..882b89def4 100644 --- a/contracts/mixnet/src/constants.rs +++ b/contracts/mixnet/src/constants.rs @@ -35,6 +35,9 @@ pub const INTERVAL_EVENTS_MAX_RETRIEVAL_LIMIT: u32 = 250; pub const REWARDED_SET_DEFAULT_RETRIEVAL_LIMIT: u32 = 500; pub const REWARDED_SET_MAX_RETRIEVAL_LIMIT: u32 = 1000; +pub const FAMILIES_DEFAULT_RETRIEVAL_LIMIT: u32 = 10; +pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20; + // storage keys pub(crate) const DELEGATION_PK_NAMESPACE: &str = "dl"; pub(crate) const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo"; @@ -69,3 +72,7 @@ pub(crate) const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi"; pub(crate) const REWARDING_PARAMS_KEY: &str = "rparams"; pub(crate) const PENDING_REWARD_POOL_KEY: &str = "prp"; pub(crate) const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr"; + +pub(crate) const FAMILIES_INDEX_NAMESPACE: &str = "faml2"; +pub(crate) const FAMILIES_MAP_NAMESPACE: &str = "fam2"; +pub(crate) const MEMBERS_MAP_NAMESPACE: &str = "memb2"; diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index e07b7ba32a..ade53d7d89 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -1,6 +1,5 @@ // Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 - use crate::constants::{INITIAL_GATEWAY_PLEDGE_AMOUNT, INITIAL_MIXNODE_PLEDGE_AMOUNT}; use crate::interval::storage as interval_storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage; @@ -82,6 +81,69 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::AssignNodeLayer { mix_id, layer } => { + crate::mixnodes::transactions::assign_mixnode_layer(deps, info, mix_id, layer) + } + // families + ExecuteMsg::CreateFamily { + owner_signature, + label, + } => crate::families::transactions::try_create_family(deps, info, owner_signature, &label), + ExecuteMsg::JoinFamily { + signature, + family_head, + } => crate::families::transactions::try_join_family(deps, info, signature, family_head), + ExecuteMsg::LeaveFamily { + signature, + family_head, + } => crate::families::transactions::try_leave_family(deps, info, signature, family_head), + ExecuteMsg::KickFamilyMember { signature, member } => { + crate::families::transactions::try_head_kick_member(deps, info, signature, &member) + } + ExecuteMsg::CreateFamilyOnBehalf { + owner_address, + owner_signature, + label, + } => crate::families::transactions::try_create_family_on_behalf( + deps, + info, + owner_address, + owner_signature, + &label, + ), + ExecuteMsg::JoinFamilyOnBehalf { + member_address, + signature, + family_head, + } => crate::families::transactions::try_join_family_on_behalf( + deps, + info, + member_address, + signature, + family_head, + ), + ExecuteMsg::LeaveFamilyOnBehalf { + member_address, + signature, + family_head, + } => crate::families::transactions::try_leave_family_on_behalf( + deps, + info, + member_address, + signature, + family_head, + ), + ExecuteMsg::KickFamilyMemberOnBehalf { + head_address, + signature, + member, + } => crate::families::transactions::try_head_kick_member_on_behalf( + deps, + info, + head_address, + signature, + &member, + ), // state/sys-params-related ExecuteMsg::UpdateRewardingValidatorAddress { address } => { crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address( @@ -129,6 +191,7 @@ pub fn execute( ), ExecuteMsg::AdvanceCurrentEpoch { new_rewarded_set, + // families_in_layer, expected_active_set_size, } => crate::interval::transactions::try_advance_epoch( deps, @@ -168,6 +231,12 @@ pub fn execute( owner, owner_signature, ), + ExecuteMsg::PledgeMore {} => { + crate::mixnodes::transactions::try_increase_pledge(deps, env, info) + } + ExecuteMsg::PledgeMoreOnBehalf { owner } => { + crate::mixnodes::transactions::try_increase_pledge_on_behalf(deps, env, info, owner) + } ExecuteMsg::UnbondMixnode {} => { crate::mixnodes::transactions::try_remove_mixnode(deps, env, info) } @@ -279,6 +348,24 @@ pub fn query( msg: QueryMsg, ) -> Result { let query_res = match msg { + QueryMsg::GetAllFamiliesPaged { limit, start_after } => to_binary( + &crate::families::queries::get_all_families_paged(deps.storage, start_after, limit)?, + ), + QueryMsg::GetAllMembersPaged { limit, start_after } => to_binary( + &crate::families::queries::get_all_members_paged(deps.storage, start_after, limit)?, + ), + QueryMsg::GetFamilyByHead { head } => to_binary( + &crate::families::queries::get_family_by_head(&head, deps.storage)?, + ), + QueryMsg::GetFamilyByLabel { label } => to_binary( + &crate::families::queries::get_family_by_label(&label, deps.storage)?, + ), + QueryMsg::GetFamilyMembersByHead { head } => to_binary( + &crate::families::queries::get_family_members_by_head(&head, deps.storage)?, + ), + QueryMsg::GetFamilyMembersByLabel { label } => to_binary( + &crate::families::queries::get_family_members_by_label(&label, deps.storage)?, + ), QueryMsg::GetContractVersion {} => { to_binary(&crate::mixnet_contract_settings::queries::query_contract_version()) } diff --git a/contracts/mixnet/src/families/mod.rs b/contracts/mixnet/src/families/mod.rs new file mode 100644 index 0000000000..f355b587a7 --- /dev/null +++ b/contracts/mixnet/src/families/mod.rs @@ -0,0 +1,3 @@ +pub mod queries; +pub mod storage; +pub mod transactions; diff --git a/contracts/mixnet/src/families/queries.rs b/contracts/mixnet/src/families/queries.rs new file mode 100644 index 0000000000..238c38aaa7 --- /dev/null +++ b/contracts/mixnet/src/families/queries.rs @@ -0,0 +1,107 @@ +use std::collections::HashSet; + +use crate::constants::{FAMILIES_DEFAULT_RETRIEVAL_LIMIT, FAMILIES_MAX_RETRIEVAL_LIMIT}; + +use super::storage::{families, get_family, get_members, MEMBERS}; +use cosmwasm_std::{Order, Storage}; +use cw_storage_plus::Bound; +use mixnet_contract_common::families::{Family, FamilyHead}; +use mixnet_contract_common::{error::MixnetContractError, IdentityKeyRef}; +use mixnet_contract_common::{IdentityKey, PagedFamiliesResponse, PagedMembersResponse}; + +pub fn get_family_by_label( + label: &str, + storage: &dyn Storage, +) -> Result, MixnetContractError> { + Ok(families() + .idx + .label + .item(storage, label.to_string())? + .map(|o| o.1)) +} + +pub fn get_family_by_head( + head: IdentityKeyRef<'_>, + storage: &dyn Storage, +) -> Result { + let family_head = FamilyHead::new(head); + get_family(&family_head, storage) +} + +pub fn get_family_members_by_head( + head: IdentityKeyRef<'_>, + storage: &dyn Storage, +) -> Result, MixnetContractError> { + let family_head = FamilyHead::new(head); + let family = get_family(&family_head, storage)?; + get_members(&family, storage) +} + +pub fn get_family_members_by_label( + label: &str, + storage: &dyn Storage, +) -> Result>, MixnetContractError> { + if let Some(family) = families() + .idx + .label + .item(storage, label.to_string())? + .map(|o| o.1) + { + Ok(Some(get_members(&family, storage)?)) + } else { + Ok(None) + } +} + +pub fn get_all_families_paged( + storage: &dyn Storage, + start_after: Option, + limit: Option, +) -> Result { + let limit = limit + .unwrap_or(FAMILIES_DEFAULT_RETRIEVAL_LIMIT) + .min(FAMILIES_MAX_RETRIEVAL_LIMIT) as usize; + + let start = start_after.map(Bound::exclusive); + + let response = families() + .range(storage, start, None, Order::Ascending) + .take(limit) + .filter_map(|r| r.ok()) + .map(|(_key, family)| family) + .collect::>(); + + let start_next_after = response + .last() + .map(|response| response.head_identity().to_string()); + + Ok(PagedFamiliesResponse { + families: response, + start_next_after, + }) +} + +pub fn get_all_members_paged( + storage: &dyn Storage, + start_after: Option, + limit: Option, +) -> Result { + let limit = limit + .unwrap_or(FAMILIES_DEFAULT_RETRIEVAL_LIMIT) + .min(FAMILIES_MAX_RETRIEVAL_LIMIT) as usize; + + let start = start_after.map(Bound::exclusive); + + let response = MEMBERS + .range(storage, start, None, Order::Ascending) + .take(limit) + .filter_map(|r| r.ok()) + .collect::>(); + + let start_next_after = response.last().map(|r| r.0.clone()); + + Ok(PagedMembersResponse { + members: response, + start_next_after, + }) +} diff --git a/contracts/mixnet/src/families/storage.rs b/contracts/mixnet/src/families/storage.rs new file mode 100644 index 0000000000..2a99f9054e --- /dev/null +++ b/contracts/mixnet/src/families/storage.rs @@ -0,0 +1,99 @@ +use std::collections::HashSet; + +use cosmwasm_std::{Order, StdError, Storage}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex}; +use mixnet_contract_common::families::{Family, FamilyHead}; +use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef}; + +use crate::constants::{FAMILIES_INDEX_NAMESPACE, FAMILIES_MAP_NAMESPACE, MEMBERS_MAP_NAMESPACE}; + +pub struct FamilyIndex<'a> { + pub label: UniqueIndex<'a, String, Family>, +} + +impl<'a> IndexList for FamilyIndex<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.label]; + Box::new(v.into_iter()) + } +} + +// storage access function. +pub fn families<'a>() -> IndexedMap<'a, String, Family, FamilyIndex<'a>> { + let indexes = FamilyIndex { + label: UniqueIndex::new(|d| d.label().to_string(), FAMILIES_INDEX_NAMESPACE), + }; + IndexedMap::new(FAMILIES_MAP_NAMESPACE, indexes) +} + +pub const MEMBERS: Map = Map::new(MEMBERS_MAP_NAMESPACE); + +pub fn get_members( + family: &Family, + store: &dyn Storage, +) -> Result, MixnetContractError> { + Ok(MEMBERS + .range(store, None, None, Order::Ascending) + .filter_map(|res| res.ok()) + .filter(|(_member, head)| head == family.head()) + .map(|(member, _storage_key)| member) + .collect()) +} + +pub fn get_family(head: &FamilyHead, store: &dyn Storage) -> Result { + let key = head.identity(); + if let Some(family) = families().may_load(store, key.to_string())? { + Ok(family) + } else { + Err(MixnetContractError::FamilyDoesNotExist { + head: head.identity().to_string(), + }) + } +} + +pub fn create_family(f: &Family, store: &mut dyn Storage) -> Result<(), MixnetContractError> { + match families().save(store, f.head_identity().to_string(), f) { + Ok(()) => Ok(()), + Err(e) => match &e { + StdError::GenericErr { msg } => { + if msg.starts_with("Violates unique constraint") { + Err(MixnetContractError::FamilyWithLabelExists( + f.label().to_string(), + )) + } else { + Err(e.into()) + } + } + _ => Err(e.into()), + }, + } +} + +pub fn add_family_member( + f: &Family, + store: &mut dyn Storage, + member: IdentityKeyRef<'_>, +) -> Result<(), MixnetContractError> { + Ok(MEMBERS.save(store, member.to_string(), f.head())?) +} + +pub fn remove_family_member(store: &mut dyn Storage, member: IdentityKeyRef<'_>) { + MEMBERS.remove(store, member.to_string()) +} + +#[allow(dead_code)] +pub fn is_family_member( + store: &dyn Storage, + f: &Family, + member: IdentityKeyRef<'_>, +) -> Result { + let m = get_members(f, store)?; + Ok(m.contains(member)) +} + +pub fn is_any_member( + store: &dyn Storage, + member: IdentityKeyRef<'_>, +) -> Result, MixnetContractError> { + Ok(MEMBERS.may_load(store, member.to_string())?) +} diff --git a/contracts/mixnet/src/families/transactions.rs b/contracts/mixnet/src/families/transactions.rs new file mode 100644 index 0000000000..7f9412f1f2 --- /dev/null +++ b/contracts/mixnet/src/families/transactions.rs @@ -0,0 +1,414 @@ +use crate::support::helpers::{ + ensure_bonded, validate_family_signature, validate_node_identity_signature, +}; + +use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response}; +use mixnet_contract_common::families::{Family, FamilyHead}; +use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef}; + +use super::storage::{ + add_family_member, create_family, get_family, is_any_member, is_family_member, + remove_family_member, +}; + +/// Creates a new MixNode family with senders node as head +pub fn try_create_family( + deps: DepsMut, + info: MessageInfo, + owner_signature: String, + label: &str, +) -> Result { + _try_create_family(deps, &info.sender, owner_signature, label, None) +} + +pub fn try_create_family_on_behalf( + deps: DepsMut, + info: MessageInfo, + owner_address: String, + owner_signature: String, + label: &str, +) -> Result { + let owner_address = deps.api.addr_validate(&owner_address)?; + _try_create_family( + deps, + &owner_address, + owner_signature, + label, + Some(info.sender), + ) +} + +fn _try_create_family( + deps: DepsMut, + owner: &Addr, + owner_signature: String, + label: &str, + proxy: Option, +) -> Result { + let existing_bond = crate::mixnodes::storage::mixnode_bonds() + .idx + .owner + .item(deps.storage, owner.clone())? + .ok_or(MixnetContractError::NoAssociatedMixNodeBond { + owner: owner.clone(), + })? + .1; + + ensure_bonded(&existing_bond)?; + + validate_node_identity_signature( + deps.as_ref(), + owner, + &owner_signature, + existing_bond.identity(), + )?; + + let family_head = FamilyHead::new(existing_bond.identity()); + + if let Ok(_family) = get_family(&family_head, deps.storage) { + return Err(MixnetContractError::FamilyCanHaveOnlyOne); + } + + let family = Family::new(family_head, proxy, label); + create_family(&family, deps.storage)?; + Ok(Response::default()) +} + +pub fn try_join_family( + deps: DepsMut, + info: MessageInfo, + signature: String, + family_head: IdentityKey, +) -> Result { + let family_head = FamilyHead::new(&family_head); + _try_join_family(deps, &info.sender, signature, family_head) +} + +pub fn try_join_family_on_behalf( + deps: DepsMut, + _info: MessageInfo, + member_address: String, + signature: String, + family_head: IdentityKey, +) -> Result { + let member_address = deps.api.addr_validate(&member_address)?; + let family_head = FamilyHead::new(&family_head); + _try_join_family(deps, &member_address, signature, family_head) +} + +fn _try_join_family( + deps: DepsMut, + owner: &Addr, + signature: String, + family_head: FamilyHead, +) -> Result { + let existing_bond = crate::mixnodes::storage::mixnode_bonds() + .idx + .owner + .item(deps.storage, owner.clone())? + .ok_or(MixnetContractError::NoAssociatedMixNodeBond { + owner: owner.clone(), + })? + .1; + + ensure_bonded(&existing_bond)?; + + if family_head.identity() == existing_bond.identity() { + return Err(MixnetContractError::CantJoinOwnFamily { + head: family_head.identity().to_string(), + member: existing_bond.identity().to_string(), + }); + } + + if let Some(family) = is_any_member(deps.storage, existing_bond.identity())? { + return Err(MixnetContractError::AlreadyMemberOfFamily( + family.identity().to_string(), + )); + } + + validate_family_signature( + deps.as_ref(), + existing_bond.identity(), + &signature, + family_head.identity(), + )?; + + let family = get_family(&family_head, deps.storage)?; + + add_family_member(&family, deps.storage, existing_bond.identity())?; + + Ok(Response::default()) +} + +pub fn try_leave_family( + deps: DepsMut, + info: MessageInfo, + signature: String, + family_head: IdentityKey, +) -> Result { + let family_head = FamilyHead::new(&family_head); + _try_leave_family(deps, &info.sender, signature, family_head) +} + +pub fn try_leave_family_on_behalf( + deps: DepsMut, + _info: MessageInfo, + member_address: String, + signature: String, + family_head: IdentityKey, +) -> Result { + let family_head = FamilyHead::new(&family_head); + let member_address = deps.api.addr_validate(&member_address)?; + _try_leave_family(deps, &member_address, signature, family_head) +} + +fn _try_leave_family( + deps: DepsMut, + owner: &Addr, + signature: String, + family_head: FamilyHead, +) -> Result { + let existing_bond = crate::mixnodes::storage::mixnode_bonds() + .idx + .owner + .item(deps.storage, owner.clone())? + .ok_or(MixnetContractError::NoAssociatedMixNodeBond { + owner: owner.clone(), + })? + .1; + + ensure_bonded(&existing_bond)?; + + if family_head.identity() == existing_bond.identity() { + return Err(MixnetContractError::CantLeaveOwnFamily { + head: family_head.identity().to_string(), + member: existing_bond.identity().to_string(), + }); + } + + let family = get_family(&family_head, deps.storage)?; + if !is_family_member(deps.storage, &family, existing_bond.identity())? { + return Err(MixnetContractError::NotAMember { + head: family_head.identity().to_string(), + member: existing_bond.identity().to_string(), + }); + } + + validate_family_signature( + deps.as_ref(), + existing_bond.identity(), + &signature, + family_head.identity(), + )?; + + remove_family_member(deps.storage, existing_bond.identity()); + + Ok(Response::default()) +} + +pub fn try_head_kick_member( + deps: DepsMut, + info: MessageInfo, + owner_signature: String, + member: IdentityKeyRef, +) -> Result { + _try_head_kick_member(deps, &info.sender, owner_signature, member) +} + +pub fn try_head_kick_member_on_behalf( + deps: DepsMut, + _info: MessageInfo, + head_address: String, + owner_signature: String, + member: IdentityKeyRef, +) -> Result { + let head_address = deps.api.addr_validate(&head_address)?; + _try_head_kick_member(deps, &head_address, owner_signature, member) +} + +#[allow(unused_variables)] +fn _try_head_kick_member( + deps: DepsMut, + owner: &Addr, + owner_signature: String, + member: IdentityKeyRef<'_>, +) -> Result { + Err(MixnetContractError::NotImplemented) + // let existing_bond = crate::mixnodes::storage::mixnode_bonds() + // .idx + // .owner + // .item(deps.storage, owner.clone())? + // .ok_or(MixnetContractError::NoAssociatedMixNodeBond { + // owner: owner.clone(), + // })? + // .1; + + // ensure_bonded(&existing_bond)?; + + // validate_node_identity_signature( + // deps.as_ref(), + // owner, + // &owner_signature, + // existing_bond.identity(), + // )?; + + // let family_head = FamilyHead::new(existing_bond.identity()); + // let family = get_family(&family_head, deps.storage)?; + // if !is_family_member(deps.storage, &family, member)? { + // return Err(MixnetContractError::NotAMember { + // head: family_head.identity().to_string(), + // member: existing_bond.identity().to_string(), + // }); + // } + + // remove_family_member(deps.storage, member); + // Ok(Response::default()) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::families::queries::{get_family_by_head, get_family_by_label}; + use crate::families::storage::is_family_member; + use crate::mixnet_contract_settings::storage::minimum_mixnode_pledge; + use crate::support::tests::{fixtures, test_helpers}; + use cosmwasm_std::testing::{mock_env, mock_info}; + + #[test] + fn test_family_crud() { + let mut deps = test_helpers::init_contract(); + let env = mock_env(); + let mut rng = test_helpers::test_rng(); + + let head = "alice"; + let malicious_head = "timmy"; + + let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().storage).unwrap(); + + let (head_mixnode, head_sig, head_keypair) = + test_helpers::mixnode_with_signature(&mut rng, head); + + let (malicious_mixnode, malicious_sig, _malicious_keypair) = + test_helpers::mixnode_with_signature(&mut rng, malicious_head); + + let cost_params = fixtures::mix_node_cost_params_fixture(); + + let member = "bob"; + let (member_mixnode, member_sig, _) = + test_helpers::mixnode_with_signature(&mut rng, member); + + // we are informed that we didn't send enough funds + crate::mixnodes::transactions::try_add_mixnode( + deps.as_mut(), + env.clone(), + mock_info(head, &[minimum_pledge.clone()]), + head_mixnode.clone(), + cost_params.clone(), + head_sig.clone(), + ) + .unwrap(); + + crate::mixnodes::transactions::try_add_mixnode( + deps.as_mut(), + env.clone(), + mock_info(malicious_head, &[minimum_pledge.clone()]), + malicious_mixnode.clone(), + cost_params.clone(), + malicious_sig.clone(), + ) + .unwrap(); + + crate::mixnodes::transactions::try_add_mixnode( + deps.as_mut(), + env.clone(), + mock_info(member, &[minimum_pledge.clone()]), + member_mixnode.clone(), + cost_params.clone(), + member_sig.clone(), + ) + .unwrap(); + + try_create_family( + deps.as_mut(), + mock_info(head.clone(), &[]), + head_sig.clone(), + "test", + ) + .unwrap(); + let family_head = FamilyHead::new(&head_mixnode.identity_key); + assert!(get_family(&family_head, &deps.storage).is_ok()); + + let nope = try_create_family( + deps.as_mut(), + mock_info(malicious_head.clone(), &[]), + malicious_sig.clone(), + "test", + ); + + match nope { + Ok(_) => panic!("This should fail, since family with label already exists"), + Err(e) => match e { + MixnetContractError::FamilyWithLabelExists(label) => assert_eq!(label, "test"), + _ => panic!("This should return FamilyWithLabelExists"), + }, + } + + let family = get_family_by_label("test", &deps.storage).unwrap(); + assert!(family.is_some()); + assert_eq!(family.unwrap().head_identity(), family_head.identity()); + + let family = get_family_by_head(family_head.identity(), &deps.storage).unwrap(); + assert_eq!(family.head_identity(), family_head.identity()); + + let join_signature = head_keypair + .private_key() + .sign(member_mixnode.identity_key.as_bytes()) + .to_base58_string(); + + try_join_family( + deps.as_mut(), + mock_info(member, &[]), + join_signature.clone(), + head_mixnode.identity_key.clone(), + ) + .unwrap(); + + let family = get_family(&family_head, &deps.storage).unwrap(); + + assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap()); + + try_leave_family( + deps.as_mut(), + mock_info(member, &[]), + join_signature.clone(), + head_mixnode.identity_key.clone(), + ) + .unwrap(); + + let family = get_family(&family_head, &deps.storage).unwrap(); + assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap()); + + try_join_family( + deps.as_mut(), + mock_info(member, &[]), + join_signature.clone(), + head_mixnode.identity_key.clone(), + ) + .unwrap(); + + let family = get_family(&family_head, &deps.storage).unwrap(); + + assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap()); + + // try_head_kick_member( + // deps.as_mut(), + // mock_info(&head, &[]), + // head_sig.clone(), + // &member_mixnode.identity_key.clone(), + // ) + // .unwrap(); + + // let family = get_family(&family_head, &deps.storage).unwrap(); + // assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap()); + } +} diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index 55973f7589..dc53817987 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -82,7 +82,7 @@ pub(crate) fn _try_add_gateway( validate_node_identity_signature( deps.as_ref(), &owner, - owner_signature, + &owner_signature, &gateway.identity_key, )?; diff --git a/contracts/mixnet/src/interval/pending_events.rs b/contracts/mixnet/src/interval/pending_events.rs index 8cb09566f2..b242944988 100644 --- a/contracts/mixnet/src/interval/pending_events.rs +++ b/contracts/mixnet/src/interval/pending_events.rs @@ -7,13 +7,14 @@ use crate::interval::helpers::change_interval_config; use crate::interval::storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnodes::helpers::{cleanup_post_unbond_mixnode_storage, get_mixnode_details_by_id}; +use crate::mixnodes::storage as mixnodes_storage; use crate::rewards::storage as rewards_storage; use crate::support::helpers::send_to_proxy_or_owner; use cosmwasm_std::{wasm_execute, Addr, Coin, DepsMut, Env, Response}; use mixnet_contract_common::error::MixnetContractError; use mixnet_contract_common::events::{ new_active_set_update_event, new_delegation_event, new_delegation_on_unbonded_node_event, - new_mixnode_cost_params_update_event, new_mixnode_unbonding_event, + new_mixnode_cost_params_update_event, new_mixnode_unbonding_event, new_pledge_increase_event, new_rewarding_params_update_event, new_undelegation_event, }; use mixnet_contract_common::mixnode::MixNodeCostParams; @@ -258,6 +259,42 @@ pub(crate) fn update_active_set_size( Ok(Response::new().add_event(new_active_set_update_event(created_at, active_set_size))) } +pub(crate) fn increase_pledge( + deps: DepsMut<'_>, + created_at: BlockHeight, + mix_id: MixId, + increase: Coin, +) -> Result { + // note: we have already validated the amount to know it has the correct denomination + + // the target node MUST exist - we have checked it at the time of putting this event onto the queue + // we have also verified there were no preceding unbond events + let mix_details = get_mixnode_details_by_id(deps.storage, mix_id)?.ok_or( + MixnetContractError::InconsistentState { + comment: + "mixnode getting processed to increase its pledge doesn't exist in the storage" + .into(), + }, + )?; + + let mut updated_bond = mix_details.bond_information.clone(); + let mut updated_rewarding = mix_details.rewarding_details; + + updated_bond.original_pledge.amount += increase.amount; + updated_rewarding.increase_operator_uint128(increase.amount)?; + + // update both, bond information and rewarding details + mixnodes_storage::mixnode_bonds().replace( + deps.storage, + mix_id, + Some(&updated_bond), + Some(&mix_details.bond_information), + )?; + rewards_storage::MIXNODE_REWARDING.save(deps.storage, mix_id, &updated_rewarding)?; + + Ok(Response::new().add_event(new_pledge_increase_event(created_at, mix_id, &increase))) +} + impl ContractExecutableEvent for PendingEpochEventData { fn execute(self, deps: DepsMut<'_>, env: &Env) -> Result { // note that the basic validation on all those events was already performed before @@ -274,6 +311,9 @@ impl ContractExecutableEvent for PendingEpochEventData { mix_id, proxy, } => undelegate(deps, self.created_at, owner, mix_id, proxy), + PendingEpochEventKind::PledgeMore { mix_id, amount } => { + increase_pledge(deps, self.created_at, mix_id, amount) + } PendingEpochEventKind::UnbondMixnode { mix_id } => { unbond_mixnode(deps, env, self.created_at, mix_id) } @@ -1135,6 +1175,228 @@ mod tests { } } + #[cfg(test)] + mod increasing_pledge { + use super::*; + use cosmwasm_std::Uint128; + use mixnet_contract_common::rewarding::helpers::truncate_reward_amount; + + #[test] + fn returns_hard_error_if_mixnode_doesnt_exist() { + // this should have never happened so hard error MUST be thrown here + let mut test = TestSetup::new(); + + let amount = test.coin(123); + let res = increase_pledge(test.deps_mut(), 123, 1, amount); + assert!(matches!( + res, + Err(MixnetContractError::InconsistentState { .. }) + )); + } + + #[test] + fn updates_stored_bond_information_and_rewarding_details() { + let mut test = TestSetup::new(); + let mix_id = test.add_dummy_mixnode("mix-owner", None); + + let old_details = get_mixnode_details_by_id(test.deps().storage, mix_id) + .unwrap() + .unwrap(); + + let amount = test.coin(12345); + increase_pledge(test.deps_mut(), 123, mix_id, amount.clone()).unwrap(); + + let updated_details = get_mixnode_details_by_id(test.deps().storage, mix_id) + .unwrap() + .unwrap(); + + assert_eq!( + updated_details.bond_information.original_pledge.amount, + old_details.bond_information.original_pledge.amount + amount.amount + ); + + assert_eq!( + updated_details.rewarding_details.operator, + old_details.rewarding_details.operator + + Decimal::from_atomics(amount.amount, 0).unwrap() + ); + } + + #[test] + fn without_any_events_in_between_is_equivalent_to_pledging_the_same_amount_immediately() { + let mut test = TestSetup::new(); + let pledge1 = Uint128::new(150_000_000); + let pledge2 = Uint128::new(50_000_000); + let pledge3 = Uint128::new(200_000_000); + + let mix_id_repledge = test.add_dummy_mixnode("mix-owner1", Some(pledge1)); + let increase = test.coin(pledge2.u128()); + increase_pledge(test.deps_mut(), 123, mix_id_repledge, increase).unwrap(); + + let mix_id_full_pledge = test.add_dummy_mixnode("mix-owner2", Some(pledge3)); + + test.add_immediate_delegation("alice", 123_456_789u128, mix_id_repledge); + test.add_immediate_delegation("bob", 500_000_000u128, mix_id_repledge); + test.add_immediate_delegation("carol", 111_111_111u128, mix_id_repledge); + + test.add_immediate_delegation("alice", 123_456_789u128, mix_id_full_pledge); + test.add_immediate_delegation("bob", 500_000_000u128, mix_id_full_pledge); + test.add_immediate_delegation("carol", 111_111_111u128, mix_id_full_pledge); + + test.skip_to_next_epoch_end(); + test.update_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]); + + let dist1 = + test.reward_with_distribution(mix_id_repledge, test_helpers::performance(100.0)); + let dist2 = + test.reward_with_distribution(mix_id_full_pledge, test_helpers::performance(100.0)); + + assert_eq!(dist1, dist2) + } + + #[test] + fn correctly_increases_future_rewards() { + let mut test = TestSetup::new(); + let pledge1 = Uint128::new(150_000_000_000); + let pledge2 = Uint128::new(50_000_000_000); + + let mix_id_repledge = test.add_dummy_mixnode("mix-owner1", Some(pledge1)); + + test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_repledge); + test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_repledge); + test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge); + + test.skip_to_next_epoch_end(); + test.update_rewarded_set(vec![mix_id_repledge]); + + let dist = + test.reward_with_distribution(mix_id_repledge, test_helpers::performance(100.0)); + + let increase = test.coin(pledge2.u128()); + increase_pledge(test.deps_mut(), 123, mix_id_repledge, increase).unwrap(); + + let pledge3 = Uint128::new(200_000_000_000) + truncate_reward_amount(dist.operator); + let mix_id_full_pledge = test.add_dummy_mixnode("mix-owner2", Some(pledge3)); + + test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_full_pledge); + test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_full_pledge); + test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_full_pledge); + + let lost_operator = dist.operator + - Decimal::from_atomics(truncate_reward_amount(dist.operator), 0).unwrap(); + let lost_delegates = dist.delegates + - Decimal::from_atomics(truncate_reward_amount(dist.delegates), 0).unwrap(); + + // add the tiny bit of lost precision manually + let mut mix_rewarding_full = test.mix_rewarding(mix_id_full_pledge); + mix_rewarding_full.delegates += lost_delegates; + mix_rewarding_full.operator += lost_operator; + rewards_storage::MIXNODE_REWARDING + .save( + test.deps_mut().storage, + mix_id_full_pledge, + &mix_rewarding_full, + ) + .unwrap(); + + test.add_immediate_delegation( + "dave", + truncate_reward_amount(dist.delegates).u128(), + mix_id_full_pledge, + ); + + test.skip_to_next_epoch_end(); + test.update_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]); + + // go through few epochs of rewarding + for _ in 0..500 { + test.skip_to_next_epoch_end(); + let dist1 = test + .reward_with_distribution(mix_id_repledge, test_helpers::performance(100.0)); + let dist2 = test + .reward_with_distribution(mix_id_full_pledge, test_helpers::performance(100.0)); + + assert_eq!(dist1, dist2) + } + } + + #[test] + fn correctly_increases_future_rewards_with_more_passed_epochs() { + let mut test = TestSetup::new(); + let pledge1 = Uint128::new(150_000_000_000); + let pledge2 = Uint128::new(50_000_000_000); + + let mix_id_repledge = test.add_dummy_mixnode("mix-owner1", Some(pledge1)); + + test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_repledge); + test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_repledge); + test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge); + + test.skip_to_next_epoch_end(); + test.update_rewarded_set(vec![mix_id_repledge]); + + let mut cumulative_op_reward = Decimal::zero(); + let mut cumulative_del_reward = Decimal::zero(); + + // go few epochs of rewarding before adding more pledge + for _ in 0..500 { + test.skip_to_next_epoch_end(); + let dist = test + .reward_with_distribution(mix_id_repledge, test_helpers::performance(100.0)); + cumulative_op_reward += dist.operator; + cumulative_del_reward += dist.delegates; + } + + let increase = test.coin(pledge2.u128()); + increase_pledge(test.deps_mut(), 123, mix_id_repledge, increase).unwrap(); + + let pledge3 = + Uint128::new(200_000_000_000) + truncate_reward_amount(cumulative_op_reward); + let mix_id_full_pledge = test.add_dummy_mixnode("mix-owner2", Some(pledge3)); + + test.add_immediate_delegation("alice", 123_456_789_000u128, mix_id_full_pledge); + test.add_immediate_delegation("bob", 500_000_000_000u128, mix_id_full_pledge); + test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_full_pledge); + + let lost_operator = cumulative_op_reward + - Decimal::from_atomics(truncate_reward_amount(cumulative_op_reward), 0).unwrap(); + let lost_delegates = cumulative_del_reward + - Decimal::from_atomics(truncate_reward_amount(cumulative_del_reward), 0).unwrap(); + + // add the tiny bit of lost precision manually + let mut mix_rewarding_full = test.mix_rewarding(mix_id_full_pledge); + mix_rewarding_full.delegates += lost_delegates; + mix_rewarding_full.operator += lost_operator; + rewards_storage::MIXNODE_REWARDING + .save( + test.deps_mut().storage, + mix_id_full_pledge, + &mix_rewarding_full, + ) + .unwrap(); + + test.add_immediate_delegation( + "dave", + truncate_reward_amount(cumulative_del_reward).u128(), + mix_id_full_pledge, + ); + + test.skip_to_next_epoch_end(); + test.update_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]); + + // go through few more epochs of rewarding + for _ in 0..500 { + test.skip_to_next_epoch_end(); + let dist1 = test + .reward_with_distribution(mix_id_repledge, test_helpers::performance(100.0)); + let dist2 = test + .reward_with_distribution(mix_id_full_pledge, test_helpers::performance(100.0)); + + assert_eq!(dist1, dist2) + } + } + } + #[test] fn updating_active_set_updates_rewarding_params() { let mut test = TestSetup::new(); diff --git a/contracts/mixnet/src/interval/transactions.rs b/contracts/mixnet/src/interval/transactions.rs index 705f36804a..a55978e4e4 100644 --- a/contracts/mixnet/src/interval/transactions.rs +++ b/contracts/mixnet/src/interval/transactions.rs @@ -5,6 +5,7 @@ use super::storage; use crate::interval::helpers::change_interval_config; use crate::interval::pending_events::ContractExecutableEvent; use crate::interval::storage::push_new_interval_event; +use crate::mixnodes::transactions::update_mixnode_layer; use crate::rewards; use crate::rewards::storage as rewards_storage; use crate::support::helpers::{ensure_is_authorized, ensure_is_owner}; @@ -16,7 +17,7 @@ use mixnet_contract_common::events::{ new_reconcile_pending_events, }; use mixnet_contract_common::pending_events::PendingIntervalEventKind; -use mixnet_contract_common::MixId; +use mixnet_contract_common::{LayerAssignment, MixId}; use std::collections::BTreeSet; // those two should be called in separate tx (from advancing epoch), @@ -202,7 +203,7 @@ pub fn try_advance_epoch( mut deps: DepsMut<'_>, env: Env, info: MessageInfo, - new_rewarded_set: Vec, + layer_assignments: Vec, expected_active_set_size: u32, ) -> Result { // Only rewarding validator can attempt to advance epoch @@ -247,12 +248,18 @@ pub fn try_advance_epoch( } let updated_interval = current_interval.advance_epoch(); - let num_nodes = new_rewarded_set.len(); + let num_nodes = layer_assignments.len(); + + let new_rewarded_set = layer_assignments.iter().map(|l| l.mix_id()).collect(); // finally save updated interval and the rewarded set storage::save_interval(deps.storage, &updated_interval)?; update_rewarded_set(deps.storage, new_rewarded_set, expected_active_set_size)?; + for a in layer_assignments { + update_mixnode_layer(a.mix_id(), a.layer(), deps.storage)?; + } + Ok(response.add_event(new_advance_epoch_event(updated_interval, num_nodes as u32))) } @@ -1133,30 +1140,40 @@ mod tests { #[cfg(test)] mod advancing_epoch { use super::*; + use crate::mixnodes::queries::query_mixnode_details; use crate::rewards::models::RewardPoolChange; use crate::support::tests::fixtures::TEST_COIN_DENOM; use cosmwasm_std::testing::mock_info; - use cosmwasm_std::{coin, coins, BankMsg, Decimal, Empty, SubMsg}; + use cosmwasm_std::{coin, coins, BankMsg, Decimal, Empty, SubMsg, Uint128}; use mixnet_contract_common::events::{ new_delegation_on_unbonded_node_event, new_rewarding_params_update_event, }; use mixnet_contract_common::reward_params::IntervalRewardingParamsUpdate; - use mixnet_contract_common::RewardedSetNodeStatus; + use mixnet_contract_common::{Layer, RewardedSetNodeStatus}; #[test] fn can_only_be_performed_by_specified_rewarding_validator() { let mut test = TestSetup::new(); + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); let current_active_set = test.rewarding_params().active_set_size; let some_sender = mock_info("foomper", &[]); test.skip_to_current_epoch_end(); + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + let env = test.env(); let res = try_advance_epoch( test.deps_mut(), env, some_sender, - vec![1, 2, 3], + layer_assignments.clone(), current_active_set, ); assert_eq!(res, Err(MixnetContractError::Unauthorized)); @@ -1168,9 +1185,10 @@ mod tests { test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ); + println!("{:?}", res); assert!(res.is_ok()) } @@ -1179,13 +1197,23 @@ mod tests { let mut test = TestSetup::new(); let current_active_set = test.rewarding_params().active_set_size; + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); + + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + let env = test.env(); let sender = test.rewarding_validator(); let res = try_advance_epoch( test.deps_mut(), env, sender.clone(), - vec![1, 2, 3], + layer_assignments.clone(), current_active_set, ); assert!(matches!( @@ -1193,6 +1221,24 @@ mod tests { Err(MixnetContractError::EpochInProgress { .. }) )); + let mixnode_1 = query_mixnode_details(test.deps.as_ref(), 1).unwrap(); + assert_eq!( + mixnode_1.mixnode_details.unwrap().bond_information.layer, + Layer::One + ); + + let mixnode_1 = query_mixnode_details(test.deps.as_ref(), 2).unwrap(); + assert_eq!( + mixnode_1.mixnode_details.unwrap().bond_information.layer, + Layer::Two + ); + + let mixnode_1 = query_mixnode_details(test.deps.as_ref(), 3).unwrap(); + assert_eq!( + mixnode_1.mixnode_details.unwrap().bond_information.layer, + Layer::Three + ); + // sanity check test.skip_to_current_epoch_end(); let env = test.env(); @@ -1200,7 +1246,7 @@ mod tests { test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ); assert!(res.is_ok()) @@ -1211,17 +1257,27 @@ mod tests { let mut test = TestSetup::new(); let current_active_set = test.rewarding_params().active_set_size; + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); + push_n_dummy_epoch_actions(&mut test, 10); push_n_dummy_interval_actions(&mut test, 10); test.skip_to_current_epoch_end(); + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + let env = test.env(); let sender = test.rewarding_validator(); try_advance_epoch( test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ) .unwrap(); @@ -1237,17 +1293,27 @@ mod tests { let mut test = TestSetup::new(); let current_active_set = test.rewarding_params().active_set_size; + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); + push_n_dummy_epoch_actions(&mut test, 10); push_n_dummy_interval_actions(&mut test, 10); test.skip_to_current_interval_end(); + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + let env = test.env(); let sender = test.rewarding_validator(); try_advance_epoch( test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ) .unwrap(); @@ -1264,6 +1330,10 @@ mod tests { let env = test.env(); let current_active_set = test.rewarding_params().active_set_size; + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); + let mut expected_events = Vec::new(); let mut expected_messages: Vec> = Vec::new(); @@ -1310,13 +1380,19 @@ mod tests { test.skip_to_current_interval_end(); + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + let env = test.env(); let sender = test.rewarding_validator(); let res = try_advance_epoch( test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ) .unwrap(); @@ -1343,6 +1419,10 @@ mod tests { let mut test = TestSetup::new(); let current_active_set = test.rewarding_params().active_set_size; + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); + let start_params = test.rewarding_params(); let pool_update = Decimal::from_atomics(100_000_000u32, 0).unwrap(); @@ -1357,6 +1437,12 @@ mod tests { ) .unwrap(); + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + // end of epoch - nothing has happened let sender = test.rewarding_validator(); test.skip_to_current_epoch_end(); @@ -1365,7 +1451,7 @@ mod tests { test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments.clone(), current_active_set, ) .unwrap(); @@ -1384,7 +1470,7 @@ mod tests { test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ) .unwrap(); @@ -1413,10 +1499,20 @@ mod tests { let mut test = TestSetup::new(); let current_active_set = test.rewarding_params().active_set_size; + test.add_dummy_mixnode("1", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("2", Some(Uint128::new(100000000))); + test.add_dummy_mixnode("3", Some(Uint128::new(100000000))); + let interval_pre = test.current_interval(); let rewarded_set_pre = test.rewarded_set(); assert!(rewarded_set_pre.is_empty()); + let layer_assignments = vec![ + LayerAssignment::new(1, Layer::One), + LayerAssignment::new(2, Layer::Two), + LayerAssignment::new(3, Layer::Three), + ]; + let sender = test.rewarding_validator(); test.skip_to_current_interval_end(); let env = test.env(); @@ -1424,7 +1520,7 @@ mod tests { test.deps_mut(), env, sender, - vec![1, 2, 3], + layer_assignments, current_active_set, ) .unwrap(); diff --git a/contracts/mixnet/src/lib.rs b/contracts/mixnet/src/lib.rs index c22e7dc1a1..6f420d3541 100644 --- a/contracts/mixnet/src/lib.rs +++ b/contracts/mixnet/src/lib.rs @@ -7,6 +7,7 @@ mod constants; pub mod contract; mod delegations; +mod families; mod gateways; mod interval; mod mixnet_contract_settings; diff --git a/contracts/mixnet/src/mixnet_contract_settings/storage.rs b/contracts/mixnet/src/mixnet_contract_settings/storage.rs index 990c5e604d..45037a2ed5 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/storage.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/storage.rs @@ -37,7 +37,6 @@ pub(crate) fn minimum_delegation_stake( .map(|state| state.params.minimum_mixnode_delegation)?) } -#[allow(unused)] pub(crate) fn rewarding_denom(storage: &dyn Storage) -> Result { Ok(CONTRACT_STATE .load(storage) diff --git a/contracts/mixnet/src/mixnodes/helpers.rs b/contracts/mixnet/src/mixnodes/helpers.rs index 8244f67b13..383dbc6224 100644 --- a/contracts/mixnet/src/mixnodes/helpers.rs +++ b/contracts/mixnet/src/mixnodes/helpers.rs @@ -147,7 +147,7 @@ pub(crate) fn cleanup_post_unbond_mixnode_storage( } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; use crate::support::tests::fixtures::{ mix_node_cost_params_fixture, mix_node_fixture, TEST_COIN_DENOM, @@ -155,13 +155,13 @@ mod tests { use crate::support::tests::test_helpers::TestSetup; use cosmwasm_std::coin; - const OWNER_EXISTS: &str = "mix-owner-existing"; - const OWNER_UNBONDING: &str = "mix-owner-unbonding"; - const OWNER_UNBONDED: &str = "mix-owner-unbonded"; - const OWNER_UNBONDED_LEFTOVER: &str = "mix-owner-unbonded-leftover"; + pub(crate) const OWNER_EXISTS: &str = "mix-owner-existing"; + pub(crate) const OWNER_UNBONDING: &str = "mix-owner-unbonding"; + pub(crate) const OWNER_UNBONDED: &str = "mix-owner-unbonded"; + pub(crate) const OWNER_UNBONDED_LEFTOVER: &str = "mix-owner-unbonded-leftover"; // create a mixnode that is bonded, unbonded, in the process of unbonding and unbonded with leftover mix rewarding details - fn setup_mix_combinations(test: &mut TestSetup) -> Vec { + pub(crate) fn setup_mix_combinations(test: &mut TestSetup) -> Vec { let mix_id_exists = test.add_dummy_mixnode(OWNER_EXISTS, None); let mix_id_unbonding = test.add_dummy_mixnode(OWNER_UNBONDING, None); let mix_id_unbonded = test.add_dummy_mixnode(OWNER_UNBONDED, None); diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 0a1e816ffd..1d0da0449f 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -5,20 +5,54 @@ use super::storage; use crate::interval::storage as interval_storage; use crate::interval::storage::push_new_interval_event; use crate::mixnet_contract_settings::storage as mixnet_params_storage; -use crate::mixnodes::helpers::{must_get_mixnode_bond_by_owner, save_new_mixnode}; -use crate::support::helpers::{ - ensure_bonded, ensure_no_existing_bond, ensure_proxy_match, validate_node_identity_signature, - validate_pledge, +use crate::mixnet_contract_settings::storage::rewarding_denom; +use crate::mixnodes::helpers::{ + get_mixnode_details_by_owner, must_get_mixnode_bond_by_owner, save_new_mixnode, }; -use cosmwasm_std::{Addr, Coin, DepsMut, Env, MessageInfo, Response}; +use crate::support::helpers::{ + ensure_bonded, ensure_is_authorized, ensure_no_existing_bond, ensure_proxy_match, + validate_node_identity_signature, validate_pledge, +}; +use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage}; use mixnet_contract_common::error::MixnetContractError; use mixnet_contract_common::events::{ new_mixnode_bonding_event, new_mixnode_config_update_event, new_mixnode_pending_cost_params_update_event, new_pending_mixnode_unbonding_event, + new_pending_pledge_increase_event, }; use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use mixnet_contract_common::pending_events::{PendingEpochEventKind, PendingIntervalEventKind}; -use mixnet_contract_common::MixNode; +use mixnet_contract_common::{Layer, MixId, MixNode}; + +pub(crate) fn update_mixnode_layer( + mix_id: MixId, + layer: Layer, + storage: &mut dyn Storage, +) -> Result<(), MixnetContractError> { + let bond = if let Some(bond_information) = storage::mixnode_bonds().may_load(storage, mix_id)? { + bond_information + } else { + return Err(MixnetContractError::MixNodeBondNotFound { mix_id }); + }; + let mut updated_bond = bond.clone(); + updated_bond.layer = layer; + + storage::mixnode_bonds().replace(storage, bond.mix_id, Some(&updated_bond), Some(&bond))?; + Ok(()) +} + +pub fn assign_mixnode_layer( + deps: DepsMut<'_>, + info: MessageInfo, + mix_id: MixId, + layer: Layer, +) -> Result { + ensure_is_authorized(info.sender, deps.storage)?; + + update_mixnode_layer(mix_id, layer, deps.storage)?; + + Ok(Response::default()) +} pub fn try_add_mixnode( deps: DepsMut<'_>, @@ -92,7 +126,7 @@ fn _try_add_mixnode( validate_node_identity_signature( deps.as_ref(), &owner, - owner_signature, + &owner_signature, &mixnode.identity_key, )?; @@ -117,6 +151,54 @@ fn _try_add_mixnode( ))) } +pub fn try_increase_pledge( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, +) -> Result { + _try_increase_pledge(deps, env, info.funds, info.sender, None) +} + +pub fn try_increase_pledge_on_behalf( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + owner: String, +) -> Result { + let proxy = info.sender; + let owner = deps.api.addr_validate(&owner)?; + _try_increase_pledge(deps, env, info.funds, owner, Some(proxy)) +} + +pub fn _try_increase_pledge( + deps: DepsMut<'_>, + env: Env, + increase: Vec, + owner: Addr, + proxy: Option, +) -> Result { + let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())? + .ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?; + let mix_id = mix_details.mix_id(); + + ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?; + ensure_bonded(&mix_details.bond_information)?; + + let rewarding_denom = rewarding_denom(deps.storage)?; + let pledge_increase = validate_pledge(increase, coin(1, rewarding_denom))?; + + let cosmos_event = new_pending_pledge_increase_event(mix_id, &pledge_increase); + + // push the event to execute it at the end of the epoch + let epoch_event = PendingEpochEventKind::PledgeMore { + mix_id, + amount: pledge_increase, + }; + interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?; + + Ok(Response::new().add_event(cosmos_event)) +} + pub fn try_remove_mixnode_on_behalf( deps: DepsMut<'_>, env: Env, @@ -307,7 +389,7 @@ pub mod tests { // if we don't send enough funds let info = mock_info(sender, &[insufficient_pledge.clone()]); - let (mixnode, sig) = test_helpers::mixnode_with_signature(&mut rng, sender); + let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender); let cost_params = fixtures::mix_node_cost_params_fixture(); // we are informed that we didn't send enough funds @@ -375,7 +457,7 @@ pub mod tests { ); let info = mock_info(sender2, &tests::fixtures::good_mixnode_pledge()); - let (mixnode, sig) = test_helpers::mixnode_with_signature(&mut rng, sender2); + let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender2); let result = try_add_mixnode( deps.as_mut(), @@ -680,4 +762,187 @@ pub mod tests { ) .is_err()); } + + #[cfg(test)] + mod increasing_mixnode_pledge { + use super::*; + use crate::mixnodes::helpers::tests::{ + setup_mix_combinations, OWNER_UNBONDED, OWNER_UNBONDED_LEFTOVER, OWNER_UNBONDING, + }; + use crate::support::tests::test_helpers::TestSetup; + + #[test] + fn is_not_allowed_if_account_doesnt_own_mixnode() { + let mut test = TestSetup::new(); + let env = test.env(); + let sender = mock_info("not-mix-owner", &[]); + + let res = try_increase_pledge(test.deps_mut(), env, sender); + assert_eq!( + res, + Err(MixnetContractError::NoAssociatedMixNodeBond { + owner: Addr::unchecked("not-mix-owner") + }) + ) + } + + #[test] + fn is_not_allowed_if_theres_proxy_mismatch() { + let mut test = TestSetup::new(); + let env = test.env(); + + let owner_without_proxy = Addr::unchecked("no-proxy"); + let owner_with_proxy = Addr::unchecked("with-proxy"); + let proxy = Addr::unchecked("proxy"); + let wrong_proxy = Addr::unchecked("unrelated-proxy"); + + test.add_dummy_mixnode(owner_without_proxy.as_str(), None); + test.add_dummy_mixnode_with_proxy(owner_with_proxy.as_str(), None, proxy.clone()); + + let res = _try_increase_pledge( + test.deps_mut(), + env.clone(), + Vec::new(), + owner_without_proxy.clone(), + Some(proxy), + ); + assert_eq!( + res, + Err(MixnetContractError::ProxyMismatch { + existing: "None".to_string(), + incoming: "proxy".to_string() + }) + ); + + let res = _try_increase_pledge( + test.deps_mut(), + env.clone(), + Vec::new(), + owner_with_proxy.clone(), + None, + ); + assert_eq!( + res, + Err(MixnetContractError::ProxyMismatch { + existing: "proxy".to_string(), + incoming: "None".to_string() + }) + ); + + let res = _try_increase_pledge( + test.deps_mut(), + env, + Vec::new(), + owner_with_proxy.clone(), + Some(wrong_proxy), + ); + assert_eq!( + res, + Err(MixnetContractError::ProxyMismatch { + existing: "proxy".to_string(), + incoming: "unrelated-proxy".to_string() + }) + ) + } + + #[test] + fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() { + let mut test = TestSetup::new(); + let env = test.env(); + + // TODO: I dislike this cross-test access, but it provides us with exactly what we need + // perhaps it should be refactored a bit? + let owner_unbonding = Addr::unchecked(OWNER_UNBONDING); + let owner_unbonded = Addr::unchecked(OWNER_UNBONDED); + let owner_unbonded_leftover = Addr::unchecked(OWNER_UNBONDED_LEFTOVER); + + let ids = setup_mix_combinations(&mut test); + let mix_id_unbonding = ids[1]; + + let res = try_increase_pledge( + test.deps_mut(), + env.clone(), + mock_info(owner_unbonding.as_str(), &[]), + ); + assert_eq!( + res, + Err(MixnetContractError::MixnodeIsUnbonding { + mix_id: mix_id_unbonding + }) + ); + + // if the nodes are gone we treat them as tey never existed in the first place + // (regardless of if there's some leftover data) + let res = try_increase_pledge( + test.deps_mut(), + env.clone(), + mock_info(owner_unbonded_leftover.as_str(), &[]), + ); + assert_eq!( + res, + Err(MixnetContractError::NoAssociatedMixNodeBond { + owner: owner_unbonded_leftover + }) + ); + + let res = try_increase_pledge( + test.deps_mut(), + env, + mock_info(owner_unbonded.as_str(), &[]), + ); + assert_eq!( + res, + Err(MixnetContractError::NoAssociatedMixNodeBond { + owner: owner_unbonded + }) + ) + } + + #[test] + fn is_not_allowed_if_no_tokens_were_sent() { + let mut test = TestSetup::new(); + let env = test.env(); + let owner = "mix-owner"; + + test.add_dummy_mixnode(owner, None); + + let sender_empty = mock_info(owner, &[]); + let res = try_increase_pledge(test.deps_mut(), env.clone(), sender_empty); + assert_eq!(res, Err(MixnetContractError::NoBondFound)); + + let sender_zero = mock_info(owner, &[test.coin(0)]); + let res = try_increase_pledge(test.deps_mut(), env, sender_zero); + assert_eq!( + res, + Err(MixnetContractError::InsufficientPledge { + received: test.coin(0), + minimum: test.coin(1) + }) + ) + } + + #[test] + fn with_valid_information_creates_pending_event() { + let mut test = TestSetup::new(); + let env = test.env(); + let owner = "mix-owner"; + let mix_id = test.add_dummy_mixnode(owner, None); + + let events = test.pending_epoch_events(); + assert!(events.is_empty()); + + let sender = mock_info(owner, &[test.coin(1000)]); + try_increase_pledge(test.deps_mut(), env, sender).unwrap(); + + let events = test.pending_epoch_events(); + + assert_eq!( + events[0].kind, + PendingEpochEventKind::PledgeMore { + mix_id, + amount: test.coin(1000) + } + ); + } + } } diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index e25e4c1bd8..d1552bb50b 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -408,7 +408,7 @@ pub mod tests { let env = test.env(); // note: we don't have to test for cases where `is_unbonding` is set to true on a mixnode - // since before performing the validator-api should clear out the event queue + // since before performing the nym-api should clear out the event queue // manually adjust delegation info as to indicate the rewarding information shouldnt get removed let mut rewarding_details = storage::MIXNODE_REWARDING diff --git a/contracts/mixnet/src/support/helpers.rs b/contracts/mixnet/src/support/helpers.rs index 9ec82ac2b8..ee47b48f3a 100644 --- a/contracts/mixnet/src/support/helpers.rs +++ b/contracts/mixnet/src/support/helpers.rs @@ -194,14 +194,30 @@ pub(crate) fn ensure_no_existing_bond( Ok(()) } -pub(crate) fn validate_node_identity_signature( +pub fn validate_node_identity_signature( deps: Deps<'_>, owner: &Addr, - signature: String, + signature: &str, identity: IdentityKeyRef<'_>, ) -> Result<(), MixnetContractError> { - let owner_bytes = owner.as_bytes(); + validate_signature(deps, owner.as_bytes(), signature, identity) +} +pub fn validate_family_signature( + deps: Deps<'_>, + family_member: IdentityKeyRef<'_>, + signature: &str, + family_head: IdentityKeyRef<'_>, +) -> Result<(), MixnetContractError> { + validate_signature(deps, family_member.as_bytes(), signature, family_head) +} + +pub(crate) fn validate_signature( + deps: Deps<'_>, + signed_bytes: &[u8], + signature: &str, + identity: IdentityKeyRef<'_>, +) -> Result<(), MixnetContractError> { let mut identity_bytes = [0u8; 32]; let mut signature_bytes = [0u8; 64]; @@ -226,7 +242,7 @@ pub(crate) fn validate_node_identity_signature( let res = deps .api - .ed25519_verify(owner_bytes, &signature_bytes, &identity_bytes) + .ed25519_verify(signed_bytes, &signature_bytes, &identity_bytes) .map_err(cosmwasm_std::StdError::verification_err)?; if !res { Err(MixnetContractError::InvalidEd25519Signature) @@ -276,12 +292,7 @@ mod tests { Err(MixnetContractError::MalformedEd25519IdentityKey( "buffer provided to decode base58 encoded string into was too small".into() )), - validate_node_identity_signature( - deps.as_ref(), - &address1, - sig_addr1_key1.clone(), - long_bs58, - ) + validate_node_identity_signature(deps.as_ref(), &address1, &sig_addr1_key1, long_bs58,) ); assert_eq!( @@ -300,12 +311,7 @@ mod tests { Err(MixnetContractError::MalformedEd25519IdentityKey( "Too few bytes provided for the public key".into() )), - validate_node_identity_signature( - deps.as_ref(), - &address1, - sig_addr1_key1.clone(), - short_bs58, - ) + validate_node_identity_signature(deps.as_ref(), &address1, &sig_addr1_key1, short_bs58,) ); assert_eq!( @@ -325,7 +331,7 @@ mod tests { validate_node_identity_signature( deps.as_ref(), &address1, - sig_addr1_key1.clone(), + &sig_addr1_key1, &keypair2.public_key().to_base58_string(), ) ); @@ -335,7 +341,7 @@ mod tests { validate_node_identity_signature( deps.as_ref(), &address1, - sig_addr2_key1, + &sig_addr2_key1, &keypair1.public_key().to_base58_string(), ) ); @@ -345,7 +351,7 @@ mod tests { validate_node_identity_signature( deps.as_ref(), &address2, - sig_addr1_key1.clone(), + &sig_addr1_key1, &keypair1.public_key().to_base58_string(), ) ); @@ -355,7 +361,7 @@ mod tests { validate_node_identity_signature( deps.as_ref(), &address1, - sig_addr1_key2, + &sig_addr1_key2, &keypair1.public_key().to_base58_string(), ) ); @@ -363,7 +369,7 @@ mod tests { assert!(validate_node_identity_signature( deps.as_ref(), &address1, - sig_addr1_key1, + &sig_addr1_key1, &keypair1.public_key().to_base58_string(), ) .is_ok()); diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index a7cf4a3875..9f03f9c93c 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -28,6 +28,9 @@ pub mod test_helpers { use crate::mixnodes::transactions::{ try_add_mixnode, try_add_mixnode_on_behalf, try_remove_mixnode, }; + use crate::rewards::queries::{ + query_pending_delegator_reward, query_pending_mixnode_operator_reward, + }; use crate::rewards::storage as rewards_storage; use crate::rewards::transactions::try_reward_mixnode; use crate::support::tests; @@ -37,12 +40,13 @@ pub mod test_helpers { use cosmwasm_std::testing::mock_info; use cosmwasm_std::testing::MockApi; use cosmwasm_std::testing::MockQuerier; - use cosmwasm_std::{Addr, BankMsg, CosmosMsg, Storage}; + use cosmwasm_std::{coin, Addr, BankMsg, CosmosMsg, Storage}; use cosmwasm_std::{Coin, Order}; use cosmwasm_std::{Decimal, Empty, MemoryStorage}; use cosmwasm_std::{Deps, OwnedDeps}; use cosmwasm_std::{DepsMut, MessageInfo}; use cosmwasm_std::{Env, Response, Timestamp, Uint128}; + use crypto::asymmetric::identity::KeyPair; use mixnet_contract_common::events::{ may_find_attribute, MixnetEventType, DELEGATES_REWARD_KEY, OPERATOR_REWARD_KEY, }; @@ -138,6 +142,10 @@ pub mod test_helpers { .vesting_contract_address } + pub fn coin(&self, amount: u128) -> Coin { + coin(amount, rewarding_denom(self.deps().storage).unwrap()) + } + pub fn current_interval(&self) -> Interval { interval_storage::current_interval(self.deps().storage).unwrap() } @@ -308,6 +316,22 @@ pub mod test_helpers { .unwrap(); } + #[allow(unused)] + pub fn pending_operator_reward(&mut self, mix: MixId) -> Decimal { + query_pending_mixnode_operator_reward(self.deps(), mix) + .unwrap() + .amount_earned_detailed + .expect("no reward!") + } + + #[allow(unused)] + pub fn pending_delegator_reward(&mut self, delegator: &str, target: MixId) -> Decimal { + query_pending_delegator_reward(self.deps(), delegator.into(), target, None) + .unwrap() + .amount_earned_detailed + .expect("no reward!") + } + pub fn skip_to_next_epoch_end(&mut self) { self.skip_to_next_epoch(); self.skip_to_current_epoch_end(); @@ -337,8 +361,19 @@ pub mod test_helpers { self.env.block.time = Timestamp::from_seconds(epoch_end as u64 + 1); let advanced = interval.advance_epoch(); - if interval.current_epoch_id() != interval.epochs_in_interval() { + assert_eq!( + interval.current_epoch_absolute_id() + 1, + advanced.current_epoch_absolute_id() + ); + + if interval.current_epoch_id() != interval.epochs_in_interval() - 1 { assert_eq!(interval.current_epoch_id() + 1, advanced.current_epoch_id()) + } else { + assert_eq!(advanced.current_epoch_id(), 0); + assert_eq!( + interval.current_interval_id() + 1, + advanced.current_interval_id() + ) } interval_storage::save_interval(self.deps_mut().storage, &advanced).unwrap() @@ -522,7 +557,7 @@ pub mod test_helpers { pub fn mixnode_with_signature( mut rng: impl RngCore + CryptoRng, sender: &str, - ) -> (MixNode, String) { + ) -> (MixNode, String, KeyPair) { let keypair = crypto::asymmetric::identity::KeyPair::new(&mut rng); let legit_sphinx_key = crypto::asymmetric::encryption::KeyPair::new(&mut rng); let owner_signature = keypair @@ -540,6 +575,7 @@ pub mod test_helpers { ..tests::fixtures::mix_node_fixture() }, owner_signature, + keypair, ) } diff --git a/contracts/mixnet/v2-changes.md b/contracts/mixnet/v2-changes.md index ecb290703c..dc09e6784a 100644 --- a/contracts/mixnet/v2-changes.md +++ b/contracts/mixnet/v2-changes.md @@ -898,6 +898,6 @@ As with everything in this module, storage was also completely revamped. The cha ## Final remarks -As mentioned during multiple chats, I think the migration the rest of our codebase is going to be a huge undertaking mostly because of how many aspects of the system this change is affecting. From the top of my head, we'd need to definitely change our `nymd client` (and as a result `validator-api`, `clients`, etc.) and also the vesting contract. +As mentioned during multiple chats, I think the migration the rest of our codebase is going to be a huge undertaking mostly because of how many aspects of the system this change is affecting. From the top of my head, we'd need to definitely change our `nyxd client` (and as a result `validator-api`, `clients`, etc.) and also the vesting contract. With the latter case (and with the current mixnet contract), it's going to be even trickier given that the current contract is already live. We will need to adjust how the values are stored, i.e. mixnodes are now indexed by `NodeId` as opposed to `IdentityKey`. My recommendation would be to create a migration such that it would "cancel" / "return" (you name it) all existing delegations and bonds so that the users would have to make new ones under the new contract. diff --git a/contracts/multisig/cw3-flex-multisig/src/error.rs b/contracts/multisig/cw3-flex-multisig/src/error.rs index f7addae37b..75a546ca0e 100644 --- a/contracts/multisig/cw3-flex-multisig/src/error.rs +++ b/contracts/multisig/cw3-flex-multisig/src/error.rs @@ -5,10 +5,10 @@ use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error(transparent)] Std(#[from] StdError), - #[error("{0}")] + #[error(transparent)] Threshold(#[from] ThresholdError), #[error("Group contract invalid address '{addr}'")] diff --git a/contracts/multisig/cw4-group/src/error.rs b/contracts/multisig/cw4-group/src/error.rs index 82a84fe833..8c88fb2255 100644 --- a/contracts/multisig/cw4-group/src/error.rs +++ b/contracts/multisig/cw4-group/src/error.rs @@ -5,13 +5,13 @@ use cw_controllers::{AdminError, HookError}; #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error("{0}")] + #[error(transparent)] Std(#[from] StdError), - #[error("{0}")] + #[error(transparent)] Hook(#[from] HookError), - #[error("{0}")] + #[error(transparent)] Admin(#[from] AdminError), #[error("Unauthorized")] diff --git a/contracts/vesting/Cargo.toml b/contracts/vesting/Cargo.toml index e13491a4f6..dd8d2966f0 100644 --- a/contracts/vesting/Cargo.toml +++ b/contracts/vesting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vesting-contract" -version = "1.1.0" +version = "1.1.2" authors = ["Drazen Urch "] edition = "2021" diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index 5efb3d750a..4a528b41d9 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -5,7 +5,7 @@ use crate::storage::{ MIXNET_CONTRACT_ADDRESS, MIX_DENOM, }; use crate::traits::{ - DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount, + DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount, }; use crate::vesting::{populate_vesting_periods, Account}; use contracts_common::ContractBuildInformation; @@ -60,6 +60,21 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::CreateFamily { + owner_signature, + label, + } => try_create_family(info, deps, owner_signature, label), + ExecuteMsg::JoinFamily { + signature, + family_head, + } => try_join_family(info, deps, signature, family_head), + ExecuteMsg::LeaveFamily { + signature, + family_head, + } => try_leave_family(info, deps, signature, family_head), + ExecuteMsg::KickFamilyMember { signature, member } => { + try_kick_family_member(info, deps, signature, member) + } ExecuteMsg::UpdateLockedPledgeCap { address, cap } => { try_update_locked_pledge_cap(address, cap, info, deps) } @@ -121,6 +136,7 @@ pub fn execute( env, deps, ), + ExecuteMsg::PledgeMore { amount } => try_pledge_more(deps, env, info, amount), ExecuteMsg::UnbondMixnode {} => try_unbond_mixnode(info, deps), ExecuteMsg::TrackUnbondMixnode { owner, amount } => { try_track_unbond_mixnode(&owner, amount, info, deps) @@ -143,6 +159,43 @@ pub fn execute( } } +pub fn try_create_family( + info: MessageInfo, + deps: DepsMut, + owner_signature: String, + label: String, +) -> Result { + let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; + account.try_create_family(deps.storage, owner_signature, label) +} +pub fn try_join_family( + info: MessageInfo, + deps: DepsMut, + signature: String, + family_head: String, +) -> Result { + let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; + account.try_join_family(deps.storage, signature, &family_head) +} +pub fn try_leave_family( + info: MessageInfo, + deps: DepsMut, + signature: String, + family_head: String, +) -> Result { + let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; + account.try_leave_family(deps.storage, signature, &family_head) +} +pub fn try_kick_family_member( + info: MessageInfo, + deps: DepsMut, + signature: String, + member: String, +) -> Result { + let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; + account.try_head_kick_member(deps.storage, signature, &member) +} + /// Update locked_pledge_cap, the hard cap for staking/bonding with unvested tokens. /// /// Callable by ADMIN only, see [instantiate]. @@ -331,6 +384,19 @@ pub fn try_bond_mixnode( ) } +pub fn try_pledge_more( + deps: DepsMut<'_>, + env: Env, + info: MessageInfo, + amount: Coin, +) -> Result { + let mix_denom = MIX_DENOM.load(deps.storage)?; + let additional_pledge = validate_funds(&[amount], mix_denom)?; + + let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; + account.try_pledge_additional_tokens(additional_pledge, &env, deps.storage) +} + /// Unbond a mixnode, sends [mixnet_contract_common::ExecuteMsg::UnbondMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]. pub fn try_unbond_mixnode(info: MessageInfo, deps: DepsMut<'_>) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; diff --git a/contracts/vesting/src/traits/bonding_account.rs b/contracts/vesting/src/traits/bonding_account.rs index 36c248e393..491378d630 100644 --- a/contracts/vesting/src/traits/bonding_account.rs +++ b/contracts/vesting/src/traits/bonding_account.rs @@ -18,6 +18,13 @@ pub trait MixnodeBondingAccount { storage: &mut dyn Storage, ) -> Result; + fn try_pledge_additional_tokens( + &self, + additional_pledge: Coin, + env: &Env, + storage: &mut dyn Storage, + ) -> Result; + fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result; fn try_track_unbond_mixnode( diff --git a/contracts/vesting/src/traits/mod.rs b/contracts/vesting/src/traits/mod.rs index 60cff028d4..d1126bb92c 100644 --- a/contracts/vesting/src/traits/mod.rs +++ b/contracts/vesting/src/traits/mod.rs @@ -1,7 +1,9 @@ pub mod bonding_account; pub mod delegating_account; +pub mod node_families; pub mod vesting_account; pub use self::bonding_account::{GatewayBondingAccount, MixnodeBondingAccount}; pub use self::delegating_account::DelegatingAccount; +pub use self::node_families::NodeFamilies; pub use self::vesting_account::VestingAccount; diff --git a/contracts/vesting/src/traits/node_families.rs b/contracts/vesting/src/traits/node_families.rs new file mode 100644 index 0000000000..274ac41f94 --- /dev/null +++ b/contracts/vesting/src/traits/node_families.rs @@ -0,0 +1,33 @@ +use crate::errors::ContractError; +use cosmwasm_std::{Response, Storage}; +use mixnet_contract_common::IdentityKeyRef; + +pub trait NodeFamilies { + fn try_create_family( + &self, + storage: &dyn Storage, + owner_signature: String, + label: String, + ) -> Result; + + fn try_join_family( + &self, + storage: &dyn Storage, + signature: String, + family_head: IdentityKeyRef, + ) -> Result; + + fn try_leave_family( + &self, + storage: &dyn Storage, + signature: String, + family_head: IdentityKeyRef, + ) -> Result; + + fn try_head_kick_member( + &self, + storage: &dyn Storage, + signature: String, + member: IdentityKeyRef<'_>, + ) -> Result; +} diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index e3aec82584..74917a16c7 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -12,7 +12,8 @@ use mixnet_contract_common::mixnode::MixNodeCostParams; use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode}; use vesting_contract_common::events::{ new_vesting_mixnode_bonding_event, new_vesting_mixnode_unbonding_event, - new_vesting_update_mixnode_config_event, new_vesting_update_mixnode_cost_params_event, + new_vesting_pledge_more_event, new_vesting_update_mixnode_config_event, + new_vesting_update_mixnode_cost_params_event, }; use vesting_contract_common::PledgeData; @@ -84,6 +85,64 @@ impl MixnodeBondingAccount for Account { .add_event(new_vesting_mixnode_bonding_event())) } + fn try_pledge_additional_tokens( + &self, + additional_pledge: Coin, + env: &Env, + storage: &mut dyn Storage, + ) -> Result { + let current_balance = self.load_balance(storage)?; + let total_pledged_after = + self.total_pledged_locked(storage, env)? + additional_pledge.amount; + let locked_pledge_cap = self.absolute_pledge_cap()?; + + if locked_pledge_cap < total_pledged_after { + return Err(ContractError::LockedPledgeCapReached { + current: total_pledged_after, + cap: locked_pledge_cap, + }); + } + + if current_balance < additional_pledge.amount { + return Err(ContractError::InsufficientBalance( + self.owner_address().as_str().to_string(), + current_balance.u128(), + )); + } + + let mut pledge_data = if let Some(pledge_data) = self.load_mixnode_pledge(storage)? { + pledge_data + } else { + return Err(ContractError::NoBondFound( + self.owner_address().as_str().to_string(), + )); + }; + + // need a second pair of eyes here to make sure updating existing timestamp on pledge data + // is not going to have some unexpected consequences + pledge_data.amount.amount += additional_pledge.amount; + pledge_data.block_time = env.block.time; + + let msg = MixnetExecuteMsg::PledgeMoreOnBehalf { + owner: self.owner_address().into_string(), + }; + + let new_balance = Uint128::new(current_balance.u128() - additional_pledge.amount.u128()); + + let pledge_more_mag = wasm_execute( + MIXNET_CONTRACT_ADDRESS.load(storage)?, + &msg, + vec![additional_pledge], + )?; + + self.save_balance(new_balance, storage)?; + self.save_mixnode_pledge(pledge_data, storage)?; + + Ok(Response::new() + .add_message(pledge_more_mag) + .add_event(new_vesting_pledge_more_event())) + } + fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result { let msg = MixnetExecuteMsg::UnbondMixnodeOnBehalf { owner: self.owner_address().into_string(), diff --git a/contracts/vesting/src/vesting/account/mod.rs b/contracts/vesting/src/vesting/account/mod.rs index 71663247b7..a368431689 100644 --- a/contracts/vesting/src/vesting/account/mod.rs +++ b/contracts/vesting/src/vesting/account/mod.rs @@ -16,6 +16,7 @@ use vesting_contract_common::{Period, PledgeCap, PledgeData}; mod delegating_account; mod gateway_bonding_account; mod mixnode_bonding_account; +mod node_families; mod vesting_account; fn generate_storage_key(storage: &mut dyn Storage) -> Result { diff --git a/contracts/vesting/src/vesting/account/node_families.rs b/contracts/vesting/src/vesting/account/node_families.rs new file mode 100644 index 0000000000..a83efdabf9 --- /dev/null +++ b/contracts/vesting/src/vesting/account/node_families.rs @@ -0,0 +1,74 @@ +use super::Account; +use crate::{errors::ContractError, storage::MIXNET_CONTRACT_ADDRESS, traits::NodeFamilies}; +use cosmwasm_std::{wasm_execute, Response, Storage}; +use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, IdentityKeyRef}; + +impl NodeFamilies for Account { + fn try_create_family( + &self, + storage: &dyn Storage, + owner_signature: String, + label: String, + ) -> Result { + let msg = MixnetExecuteMsg::CreateFamilyOnBehalf { + owner_address: self.owner_address().to_string(), + owner_signature, + label, + }; + + let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + + Ok(Response::new().add_message(msg)) + } + + fn try_join_family( + &self, + storage: &dyn Storage, + signature: String, + family_head: IdentityKeyRef, + ) -> Result { + let msg = MixnetExecuteMsg::JoinFamilyOnBehalf { + member_address: self.owner_address().to_string(), + signature, + family_head: family_head.to_string(), + }; + + let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + + Ok(Response::new().add_message(msg)) + } + + fn try_leave_family( + &self, + storage: &dyn Storage, + signature: String, + family_head: IdentityKeyRef, + ) -> Result { + let msg = MixnetExecuteMsg::LeaveFamilyOnBehalf { + member_address: self.owner_address().to_string(), + signature, + family_head: family_head.to_string(), + }; + + let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + + Ok(Response::new().add_message(msg)) + } + + fn try_head_kick_member( + &self, + storage: &dyn Storage, + signature: String, + member: IdentityKeyRef<'_>, + ) -> Result { + let msg = MixnetExecuteMsg::KickFamilyMemberOnBehalf { + head_address: self.owner_address().to_string(), + signature, + member: member.to_string(), + }; + + let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?; + + Ok(Response::new().add_message(msg)) + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 6c756e2ebd..7108925d69 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: container_name: genesis_validator volumes: - "genesis_volume:/genesis_volume" - - "genesis_nymd:/root/.nymd" + - "genesis_nyxd:/root/.nyxd" environment: *NETWORK networks: localnet: @@ -34,7 +34,7 @@ services: - "2317:1317" volumes: - "genesis_volume:/genesis_volume" - - "secondary_nymd:/root/.nymd" + - "secondary_nyxd:/root/.nyxd" environment: *NETWORK networks: localnet: @@ -120,8 +120,8 @@ services: volumes: genesis_volume: - genesis_nymd: - secondary_nymd: + genesis_nyxd: + secondary_nyxd: # contract_volume: # mongo_data: diff --git a/docker/typescript_client/upload_contract/package-lock.json b/docker/typescript_client/upload_contract/package-lock.json index a87087b263..5afb3c4577 100644 --- a/docker/typescript_client/upload_contract/package-lock.json +++ b/docker/typescript_client/upload_contract/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@nymproject/nym-validator-client": "0.18.0", "@types/node": "^14.14.22", - "nodemon": "^2.0.7", + "nodemon": "^2.0.20", "save-dev": "0.0.1-security", "tasktimer": "^3.0.0", "ts-node": "^9.1.1" @@ -309,25 +309,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@tsconfig/recommended": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.1.tgz", @@ -349,81 +330,6 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "node_modules/ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dependencies": { - "string-width": "^3.0.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -507,27 +413,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "node_modules/boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -558,84 +443,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chokidar": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", @@ -656,11 +463,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -670,61 +472,10 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/create-hash": { "version": "1.2.0", @@ -756,14 +507,6 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, "node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -772,30 +515,6 @@ "ms": "^2.1.1" } }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -815,22 +534,6 @@ "node": ">=0.3.1" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -845,27 +548,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "engines": { - "node": ">=8" - } - }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -919,17 +601,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -941,20 +612,6 @@ "node": ">= 6" } }, - "node_modules/global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dependencies": { - "ini": "1.3.7" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globalthis": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", @@ -969,32 +626,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1003,14 +634,6 @@ "node": ">=4" } }, - "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "engines": { - "node": ">=8" - } - }, "node_modules/hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -1043,42 +666,16 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1090,17 +687,6 @@ "node": ">=8" } }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1109,14 +695,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -1128,29 +706,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dependencies": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1159,32 +714,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -1198,30 +727,6 @@ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, - "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/libsodium": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", @@ -1240,36 +745,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1285,14 +760,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -1304,9 +771,9 @@ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1314,32 +781,26 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nodemon": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz", - "integrity": "sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA==", - "hasInstallScript": true, + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", "dependencies": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", + "chokidar": "^3.5.2", + "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" + "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" @@ -1374,14 +835,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "engines": { - "node": ">=8" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -1390,44 +843,6 @@ "node": ">= 0.4" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/pako": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz", @@ -1459,14 +874,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "engines": { - "node": ">=4" - } - }, "node_modules/prettier": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", @@ -1514,26 +921,6 @@ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1542,20 +929,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -1585,36 +958,6 @@ "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==" }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -1656,25 +999,6 @@ "semver": "bin/semver" } }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dependencies": { - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -1687,10 +1011,24 @@ "sha.js": "bin.js" } }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } }, "node_modules/source-map": { "version": "0.6.1", @@ -1717,38 +1055,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1776,25 +1082,6 @@ "eventemitter3": "^4.0.0" } }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1842,22 +1129,6 @@ "typescript": ">=2.7" } }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", @@ -1871,106 +1142,15 @@ } }, "node_modules/undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dependencies": { - "debug": "^2.2.0" - } - }, - "node_modules/undefsafe/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/undefsafe/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dependencies": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, "node_modules/ws": { "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", @@ -1991,14 +1171,6 @@ } } }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "engines": { - "node": ">=8" - } - }, "node_modules/xstream": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/xstream/-/xstream-11.14.0.tgz", @@ -2266,19 +1438,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, "@tsconfig/recommended": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.1.tgz", @@ -2300,62 +1459,6 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -2421,21 +1524,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2463,64 +1551,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "chokidar": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", @@ -2536,11 +1566,6 @@ "readdirp": "~3.6.0" } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2550,49 +1575,10 @@ "safe-buffer": "^5.0.1" } }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "create-hash": { "version": "1.2.0", @@ -2624,11 +1610,6 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -2637,24 +1618,6 @@ "ms": "^2.1.1" } }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2668,19 +1631,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -2695,24 +1645,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" - }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -2742,14 +1674,6 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "optional": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -2758,14 +1682,6 @@ "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "requires": { - "ini": "1.3.7" - } - }, "globalthis": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", @@ -2774,39 +1690,11 @@ "define-properties": "^1.1.3" } }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" - }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -2836,36 +1724,16 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2874,24 +1742,11 @@ "binary-extensions": "^2.0.0" } }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -2900,45 +1755,11 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, "isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -2950,27 +1771,6 @@ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "requires": { - "package-json": "^6.3.0" - } - }, "libsodium": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", @@ -2989,26 +1789,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -3024,11 +1804,6 @@ "safe-buffer": "^5.1.2" } }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -3040,38 +1815,33 @@ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "nodemon": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz", - "integrity": "sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", + "chokidar": "^3.5.2", + "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" + "undefsafe": "^2.0.5" } }, "nopt": { @@ -3087,47 +1857,11 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "pako": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz", @@ -3150,11 +1884,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, "prettier": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", @@ -3193,23 +1922,6 @@ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "requires": { - "escape-goat": "^2.0.0" - } - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3218,17 +1930,6 @@ "safe-buffer": "^5.1.0" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -3252,30 +1953,6 @@ "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==" }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "requires": { - "rc": "^1.2.8" - } - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -3300,21 +1977,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -3324,10 +1986,20 @@ "safe-buffer": "^5.0.1" } }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } }, "source-map": { "version": "0.6.1", @@ -3351,29 +2023,6 @@ "safe-buffer": "~5.2.0" } }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -3395,16 +2044,6 @@ "eventemitter3": "^4.0.0" } }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3434,123 +2073,27 @@ "yn": "3.1.1" } }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, "typescript": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==" }, "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, "ws": { "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", "requires": {} }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" - }, "xstream": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/xstream/-/xstream-11.14.0.tgz", diff --git a/docker/typescript_client/upload_contract/package.json b/docker/typescript_client/upload_contract/package.json index b52590cc53..7ae335c578 100644 --- a/docker/typescript_client/upload_contract/package.json +++ b/docker/typescript_client/upload_contract/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@types/node": "^14.14.22", - "nodemon": "^2.0.7", + "nodemon": "^2.0.20", "@nymproject/nym-validator-client" : "0.18.0", "save-dev": "0.0.1-security", "tasktimer": "^3.0.0", diff --git a/docker/validator/init_and_start.sh b/docker/validator/init_and_start.sh index 722e7224d2..a1e8820fbc 100755 --- a/docker/validator/init_and_start.sh +++ b/docker/validator/init_and_start.sh @@ -7,39 +7,39 @@ cd /root if [ "$1" = "genesis" ]; then if [ ! -f "/root/.nymd/config/genesis.json" ]; then - ./nymd init nymnet --chain-id nymnet 2> /dev/null + ./nyxd init nymnet --chain-id nymnet 2> /dev/null # staking/governance token is hardcoded in config, change this - sed -i "s/\"stake\"/\"u${STAKE_DENOM}\"/" /root/.nymd/config/genesis.json - sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025u'"${DENOM}"'"/' /root/.nymd/config/app.toml - sed -i '0,/enable = false/s//enable = true/g' /root/.nymd/config/app.toml - sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/' /root/.nymd/config/config.toml - sed -i 's/create_empty_blocks = true/create_empty_blocks = false/' /root/.nymd/config/config.toml - sed -i 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/' /root/.nymd/config/config.toml + sed -i "s/\"stake\"/\"u${STAKE_DENOM}\"/" /root/.nyxd/config/genesis.json + sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025u'"${DENOM}"'"/' /root/.nyxd/config/app.toml + sed -i '0,/enable = false/s//enable = true/g' /root/.nyxd/config/app.toml + sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/' /root/.nyxd/config/config.toml + sed -i 's/create_empty_blocks = true/create_empty_blocks = false/' /root/.nyxd/config/config.toml + sed -i 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/' /root/.nyxd/config/config.toml # create accounts - yes "${PASSPHRASE}" | ./nymd keys add node_admin 2>&1 >/dev/null | tail -n 1 > /root/.nymd/mnemonic - yes "${PASSPHRASE}" | ./nymd keys add secondary 2>&1 >/dev/null | tail -n 1 > /root/.nymd/secondary_mnemonic - cp /root/.nymd/mnemonic /genesis_volume/genesis_mnemonic - cp /root/.nymd/secondary_mnemonic /genesis_volume/secondary_mnemonic + yes "${PASSPHRASE}" | ./nyxd keys add node_admin 2>&1 >/dev/null | tail -n 1 > /root/.nyxd/mnemonic + yes "${PASSPHRASE}" | ./nyxd keys add secondary 2>&1 >/dev/null | tail -n 1 > /root/.nyxd/secondary_mnemonic + cp /root/.nyxd/mnemonic /genesis_volume/genesis_mnemonic + cp /root/.nyxd/secondary_mnemonic /genesis_volume/secondary_mnemonic # add genesis accounts with some initial tokens - GENESIS_ADDRESS=$(yes "${PASSPHRASE}" | ./nymd keys show node_admin -a) - SECONDARY_ADDRESS=$(yes "${PASSPHRASE}" | ./nymd keys show secondary -a) - yes "${PASSPHRASE}" | ./nymd add-genesis-account "${GENESIS_ADDRESS}" 1000000000000000u"${DENOM}",1000000000000000u"${STAKE_DENOM}" - yes "${PASSPHRASE}" | ./nymd add-genesis-account "${SECONDARY_ADDRESS}" 1000000000000000u"${DENOM}",1000000000000000u"${STAKE_DENOM}" + GENESIS_ADDRESS=$(yes "${PASSPHRASE}" | ./nyxd keys show node_admin -a) + SECONDARY_ADDRESS=$(yes "${PASSPHRASE}" | ./nyxd keys show secondary -a) + yes "${PASSPHRASE}" | ./nyxd add-genesis-account "${GENESIS_ADDRESS}" 1000000000000000u"${DENOM}",1000000000000000u"${STAKE_DENOM}" + yes "${PASSPHRASE}" | ./nyxd add-genesis-account "${SECONDARY_ADDRESS}" 1000000000000000u"${DENOM}",1000000000000000u"${STAKE_DENOM}" - yes "${PASSPHRASE}" | ./nymd gentx node_admin 1000000000u"${STAKE_DENOM}" --chain-id nymnet 2> /dev/null - ./nymd collect-gentxs 2> /dev/null - ./nymd validate-genesis > /dev/null - cp /root/.nymd/config/genesis.json /genesis_volume/genesis.json + yes "${PASSPHRASE}" | ./nyxd gentx node_admin 1000000000u"${STAKE_DENOM}" --chain-id nymnet 2> /dev/null + ./nyxd collect-gentxs 2> /dev/null + ./nyxd validate-genesis > /dev/null + cp /root/.nyxd/config/genesis.json /genesis_volume/genesis.json else echo "Validator already initialized, starting with the existing configuration." echo "If you want to re-init the validator, destroy the existing container" fi - ./nymd start + ./nyxd start elif [ "$1" = "secondary" ]; then if [ ! -f "/root/.nymd/config/genesis.json" ]; then - ./nymd init nymnet --chain-id nym-secondary 2> /dev/null + ./nyxd init nymnet --chain-id nym-secondary 2> /dev/null # Wait until the genesis node writes the genesis.json to the shared volume while ! [ -s /genesis_volume/genesis.json ]; do @@ -49,29 +49,29 @@ elif [ "$1" = "secondary" ]; then # wait for the actual validator to start up sleep 5 - cp /genesis_volume/genesis.json /root/.nymd/config/genesis.json - GENESIS_PEER=$(cat /root/.nymd/config/genesis.json | grep '"memo"' | cut -d'"' -f 4) - GENESIS_IP=$(cat /root/.nymd/config/genesis.json | grep '"memo"' | cut -d'@' -f2 | cut -d: -f1) - sed -i 's/persistent_peers = ""/persistent_peers = "'"${GENESIS_PEER}"'"/' /root/.nymd/config/config.toml - sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025u'"${BECH32_PREFIX}"'"/' /root/.nymd/config/app.toml - sed -i '0,/enable = false/s//enable = true/g' /root/.nymd/config/app.toml - sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/' /root/.nymd/config/config.toml - sed -i 's/create_empty_blocks = true/create_empty_blocks = false/' /root/.nymd/config/config.toml - sed -i 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/' /root/.nymd/config/config.toml + cp /genesis_volume/genesis.json /root/.nyxd/config/genesis.json + GENESIS_PEER=$(cat /root/.nyxd/config/genesis.json | grep '"memo"' | cut -d'"' -f 4) + GENESIS_IP=$(cat /root/.nyxd/config/genesis.json | grep '"memo"' | cut -d'@' -f2 | cut -d: -f1) + sed -i 's/persistent_peers = ""/persistent_peers = "'"${GENESIS_PEER}"'"/' /root/.nyxd/config/config.toml + sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025u'"${BECH32_PREFIX}"'"/' /root/.nyxd/config/app.toml + sed -i '0,/enable = false/s//enable = true/g' /root/.nyxd/config/app.toml + sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/' /root/.nyxd/config/config.toml + sed -i 's/create_empty_blocks = true/create_empty_blocks = false/' /root/.nyxd/config/config.toml + sed -i 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/' /root/.nyxd/config/config.toml # import mnemonic generated by the genesis validator (have a local copy for ease of use) - cp /genesis_volume/secondary_mnemonic /root/.nymd/mnemonic - { cat /root/.nymd/mnemonic; echo "${PASSPHRASE}"; echo "${PASSPHRASE}"; } | ./nymd keys add node_admin --recover #> /dev/null - ./nymd validate-genesis > /dev/null + cp /genesis_volume/secondary_mnemonic /root/.nyxd/mnemonic + { cat /root/.nyxd/mnemonic; echo "${PASSPHRASE}"; echo "${PASSPHRASE}"; } | ./nyxd keys add node_admin --recover #> /dev/null + ./nyxd validate-genesis > /dev/null # create validator # don't even ask about those sleeps... - { echo "${PASSPHRASE}"; sleep 10; yes; sleep 10; } | ./nymd tx staking create-validator --amount=10000000u"${STAKE_DENOM}" --fees 100000u"${DENOM}" --pubkey="$(./nymd tendermint show-validator)" --moniker="secondary" --commission-rate="0.10" --commission-max-rate="0.20" --commission-max-change-rate="0.01" --min-self-delegation="1" --chain-id=nymnet --from=node_admin -b async --node http://"${GENESIS_IP}":26657 + { echo "${PASSPHRASE}"; sleep 10; yes; sleep 10; } | ./nyxd tx staking create-validator --amount=10000000u"${STAKE_DENOM}" --fees 100000u"${DENOM}" --pubkey="$(./nyxd tendermint show-validator)" --moniker="secondary" --commission-rate="0.10" --commission-max-rate="0.20" --commission-max-change-rate="0.01" --min-self-delegation="1" --chain-id=nymnet --from=node_admin -b async --node http://"${GENESIS_IP}":26657 else echo "Validator already initialized, starting with the existing configuration." echo "If you want to re-init the validator, destroy the existing container" fi - ./nymd start + ./nyxd start else echo "Wrong command. Usage: ./$0 [genesis/secondary]" fi diff --git a/docker/validator/setup.sh b/docker/validator/setup.sh index efd40f0e14..4b176fb04e 100755 --- a/docker/validator/setup.sh +++ b/docker/validator/setup.sh @@ -8,7 +8,7 @@ git checkout "${WASMD_VERSION}" WASMD_COMMIT_HASH=$(git rev-parse HEAD) mkdir build go build \ - -o build/nymd -mod=readonly -tags "netgo,ledger" \ + -o build/nyxd -mod=readonly -tags "netgo,ledger" \ -ldflags "-X github.com/cosmos/cosmos-sdk/version.Name=nymd \ -X github.com/cosmos/cosmos-sdk/version.AppName=nymd \ -X github.com/CosmWasm/wasmd/app.NodeDir=.nymd \ diff --git a/envs/mainnet.env b/envs/mainnet.env index d53b7f4b8b..3a8ec4b17c 100644 --- a/envs/mainnet.env +++ b/envs/mainnet.env @@ -9,7 +9,7 @@ MIX_DENOM_DISPLAY=nym STAKE_DENOM=unyx STAKE_DENOM_DISPLAY=nyx DENOMS_EXPONENT=6 -MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g +MIXNET_CONTRACT_ADDRESS=n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 @@ -17,5 +17,5 @@ MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_DKG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090" -NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/" +nyxd_VALIDATOR="https://rpc.nymtech.net"; API_VALIDATOR="https://validator.nymtech.net/api/" diff --git a/envs/qa-qwerty.env b/envs/qa-qwerty.env index 26e5b0fe1f..6bb6895d72 100644 --- a/envs/qa-qwerty.env +++ b/envs/qa-qwerty.env @@ -12,9 +12,10 @@ DENOMS_EXPONENT=6 MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 -COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 -MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n12ckdkm3q7eytefs7rwu4ue3t9hxgvl9v08jddmtwgct2ve0pv50q0t8dlt +MULTISIG_CONTRACT_ADDRESS=n14krxe8ukzagwhvec0rmteexu62w8k9kp9sra9ww6em2hnmzcukqsa0utc8 +COCONUT_DKG_CONTRACT_ADDRESS=n1rl5n6cxuz2hdy3f7d9hsnw8zn0zwwwr0r4dxfz7tktgpgkcnz9zshmvksc REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090" -NYMD_VALIDATOR="https://qwerty-validator.qa.nymte.ch/" +NYXD_VALIDATOR="https://qwerty-validator.qa.nymte.ch/" API_VALIDATOR="https://qwerty-validator-api.qa.nymte.ch/api" diff --git a/envs/qa.env b/envs/qa.env index 64511fa922..999850f798 100644 --- a/envs/qa.env +++ b/envs/qa.env @@ -17,5 +17,5 @@ MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs COCONUT_DKG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" -NYMD_VALIDATOR="https://qa-validator.nymtech.net" +NYXD_VALIDATOR="https://qa-validator.nymtech.net" API_VALIDATOR="https://qa-validator-api.nymtech.net/api" diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index cc665b1f83..129423cf7f 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] chrono = { version = "0.4.19", features = ["serde"] } -clap = { version = "3.2", features = ["cargo", "derive"] } +clap = { version = "4.0", features = ["cargo", "derive"] } humantime-serde = "1.0" isocountry = "0.3.2" itertools = "0.10.3" @@ -25,10 +25,13 @@ thiserror = "1.0.29" tokio = {version = "1.21.2", features = ["full"] } maxminddb = "0.23.0" dotenv = "0.15.0" +rand = "0.8.5" +rand_seeder = "0.2.3" +rand_pcg = "0.3.1" mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" } contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" } network-defaults = { path = "../common/network-defaults" } logging = { path = "../common/logging"} task = { path = "../common/task" } -validator-client = { path = "../common/client-libs/validator-client", features=["nymd-client"] } +validator-client = { path = "../common/client-libs/validator-client", features=["nyxd-client"] } diff --git a/explorer-api/src/client.rs b/explorer-api/src/client.rs index 129a394d31..ca9449f971 100644 --- a/explorer-api/src/client.rs +++ b/explorer-api/src/client.rs @@ -1,10 +1,10 @@ use network_defaults::{ - var_names::{API_VALIDATOR, NYMD_VALIDATOR}, + var_names::{NYM_API, NYXD}, NymNetworkDetails, }; use reqwest::Url; use std::{str::FromStr, sync::Arc}; -use validator_client::nymd::QueryNymdClient; +use validator_client::nyxd::QueryNyxdClient; // since this is just a query client, we don't need any locking mechanism to keep sequence numbers in check // nor we need to access any of its methods taking mutable reference (like updating api URL) @@ -12,7 +12,7 @@ use validator_client::nymd::QueryNymdClient; #[derive(Clone)] pub(crate) struct ThreadsafeValidatorClient( - pub(crate) Arc>, + pub(crate) Arc>, ); impl ThreadsafeValidatorClient { @@ -21,22 +21,22 @@ impl ThreadsafeValidatorClient { } pub(crate) fn api_endpoint(&self) -> &Url { - self.0.validator_api.current_url() + self.0.nym_api.current_url() } } pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient { - let nymd_url = Url::from_str(&std::env::var(NYMD_VALIDATOR).expect("nymd validator not set")) - .expect("nymd validator not in url format"); - let api_url = Url::from_str(&std::env::var(API_VALIDATOR).expect("nymd validator not set")) - .expect("nymd validator not in url format"); + let nyxd_url = Url::from_str(&std::env::var(NYXD).expect("nyxd validator not set")) + .expect("nyxd validator not in url format"); + let api_url = Url::from_str(&std::env::var(NYM_API).expect("nyxd validator not set")) + .expect("nyxd validator not in url format"); let details = NymNetworkDetails::new_from_env(); let client_config = validator_client::Config::try_from_nym_network_details(&details) .expect("failed to construct valid validator client config with the provided network") - .with_urls(nymd_url, api_url); + .with_urls(nyxd_url, api_url); ThreadsafeValidatorClient(Arc::new( - validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"), + validator_client::Client::new_query(client_config).expect("Failed to connect to nyxd!"), )) } diff --git a/explorer-api/src/country_statistics/distribution.rs b/explorer-api/src/country_statistics/distribution.rs index f48e87eebb..93d12067b6 100644 --- a/explorer-api/src/country_statistics/distribution.rs +++ b/explorer-api/src/country_statistics/distribution.rs @@ -1,5 +1,5 @@ use log::info; -use task::ShutdownListener; +use task::TaskClient; use crate::country_statistics::country_nodes_distribution::CountryNodesDistribution; use crate::COUNTRY_DATA_REFRESH_INTERVAL; @@ -8,11 +8,11 @@ use crate::state::ExplorerApiStateContext; pub(crate) struct CountryStatisticsDistributionTask { state: ExplorerApiStateContext, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl CountryStatisticsDistributionTask { - pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self { + pub(crate) fn new(state: ExplorerApiStateContext, shutdown: TaskClient) -> Self { CountryStatisticsDistributionTask { state, shutdown } } diff --git a/explorer-api/src/country_statistics/geolocate.rs b/explorer-api/src/country_statistics/geolocate.rs index c77a61a9a1..0ae79985c7 100644 --- a/explorer-api/src/country_statistics/geolocate.rs +++ b/explorer-api/src/country_statistics/geolocate.rs @@ -4,15 +4,15 @@ use crate::mix_nodes::location::Location; use crate::state::ExplorerApiStateContext; use log::{info, warn}; -use task::ShutdownListener; +use task::TaskClient; pub(crate) struct GeoLocateTask { state: ExplorerApiStateContext, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl GeoLocateTask { - pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self { + pub(crate) fn new(state: ExplorerApiStateContext, shutdown: TaskClient) -> Self { GeoLocateTask { state, shutdown } } diff --git a/explorer-api/src/main.rs b/explorer-api/src/main.rs index 47dddf9f54..ecfde18949 100644 --- a/explorer-api/src/main.rs +++ b/explorer-api/src/main.rs @@ -8,7 +8,7 @@ use dotenv::dotenv; use log::info; use logging::setup_logging; use network_defaults::setup_env; -use task::ShutdownNotifier; +use task::{wait_for_signal, TaskManager}; mod buy_terms; pub(crate) mod cache; @@ -35,7 +35,7 @@ async fn main() { dotenv().ok(); setup_logging(); let args = commands::Cli::parse(); - setup_env(args.config_env_file); + setup_env(args.config_env_file.as_ref()); let mut explorer_api = ExplorerApi::new(); explorer_api.run().await; } @@ -54,10 +54,10 @@ impl ExplorerApi { async fn run(&mut self) { info!("Explorer API starting up..."); - let validator_api_url = self.state.inner.validator_client.api_endpoint(); - info!("Using validator API - {}", validator_api_url); + let nym_api_url = self.state.inner.validator_client.api_endpoint(); + info!("Using validator API - {}", nym_api_url); - let shutdown = ShutdownNotifier::default(); + let shutdown = TaskManager::default(); // spawn concurrent tasks crate::tasks::ExplorerApiTasks::new(self.state.clone(), shutdown.subscribe()).start(); @@ -78,7 +78,7 @@ impl ExplorerApi { self.wait_for_interrupt(shutdown).await } - async fn wait_for_interrupt(&self, mut shutdown: ShutdownNotifier) { + async fn wait_for_interrupt(&self, mut shutdown: TaskManager) { wait_for_signal().await; log::info!("Sending shutdown"); @@ -88,31 +88,3 @@ impl ExplorerApi { log::info!("Stopping explorer API"); } } - -#[cfg(unix)] -async fn wait_for_signal() { - use tokio::signal::unix::{signal, SignalKind}; - let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel"); - let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel"); - - tokio::select! { - _ = tokio::signal::ctrl_c() => { - log::info!("Received SIGINT"); - }, - _ = sigterm.recv() => { - log::info!("Received SIGTERM"); - } - _ = sigquit.recv() => { - log::info!("Received SIGQUIT"); - } - } -} - -#[cfg(not(unix))] -async fn wait_for_signal() { - tokio::select! { - _ = tokio::signal::ctrl_c() => { - log::info!("Received SIGINT"); - }, - } -} diff --git a/explorer-api/src/mix_node/delegations.rs b/explorer-api/src/mix_node/delegations.rs index 3d061c942a..00c55446e8 100644 --- a/explorer-api/src/mix_node/delegations.rs +++ b/explorer-api/src/mix_node/delegations.rs @@ -12,7 +12,7 @@ pub(crate) async fn get_single_mixnode_delegations( ) -> Vec { match client .0 - .get_all_nymd_single_mixnode_delegations(mix_id) + .get_all_nyxd_single_mixnode_delegations(mix_id) .await { Ok(result) => result, diff --git a/explorer-api/src/mix_node/econ_stats.rs b/explorer-api/src/mix_node/econ_stats.rs index cde29d3a86..30d89a29b2 100644 --- a/explorer-api/src/mix_node/econ_stats.rs +++ b/explorer-api/src/mix_node/econ_stats.rs @@ -13,31 +13,26 @@ pub(crate) async fn retrieve_mixnode_econ_stats( ) -> Option { let stake_saturation = client .0 - .validator_api + .nym_api .get_mixnode_stake_saturation(mix_id) .await .ok()?; let inclusion_probability = client .0 - .validator_api + .nym_api .get_mixnode_inclusion_probability(mix_id) .await .ok()?; let reward_estimation = client .0 - .validator_api + .nym_api .get_mixnode_reward_estimation(mix_id) .await .ok()?; - let uptime_response = client - .0 - .validator_api - .get_mixnode_avg_uptime(mix_id) - .await - .ok()?; + let uptime_response = client.0.nym_api.get_mixnode_avg_uptime(mix_id).await.ok()?; Some(EconomicDynamicsStats { stake_saturation: best_effort_small_dec_to_f64(stake_saturation.saturation) as f32, diff --git a/explorer-api/src/mix_node/models.rs b/explorer-api/src/mix_node/models.rs index ef894e4b40..92f4dbe8aa 100644 --- a/explorer-api/src/mix_node/models.rs +++ b/explorer-api/src/mix_node/models.rs @@ -40,6 +40,7 @@ pub(crate) struct PrettyDetailedMixNodeBond { pub estimated_delegators_apy: f64, pub operating_cost: Coin, pub profit_margin_percent: Percent, + pub family_id: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] diff --git a/explorer-api/src/mix_nodes/mod.rs b/explorer-api/src/mix_nodes/mod.rs index 82c2aedd1f..e2aced241a 100644 --- a/explorer-api/src/mix_nodes/mod.rs +++ b/explorer-api/src/mix_nodes/mod.rs @@ -6,6 +6,7 @@ use std::time::Duration; pub(crate) mod http; pub(crate) mod location; pub(crate) mod models; +pub(crate) mod utils; pub(crate) const CACHE_REFRESH_RATE: Duration = Duration::from_secs(30); pub(crate) const CACHE_ENTRY_TTL: Duration = Duration::from_secs(60); diff --git a/explorer-api/src/mix_nodes/models.rs b/explorer-api/src/mix_nodes/models.rs index 8dc9c96ac3..fdbd99e467 100644 --- a/explorer-api/src/mix_nodes/models.rs +++ b/explorer-api/src/mix_nodes/models.rs @@ -13,6 +13,7 @@ use tokio::sync::{RwLock, RwLockReadGuard}; use crate::helpers::best_effort_small_dec_to_f64; use validator_client::models::MixNodeBondAnnotated; +use super::utils::family_numerical_id; use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem}; use crate::mix_nodes::CACHE_ENTRY_TTL; @@ -140,6 +141,8 @@ impl ThreadsafeMixNodesCache { let denom = &node.mixnode_details.original_pledge().denom; let rewarding_info = &node.mixnode_details.rewarding_details; + let family_id = node.family.as_ref().map(family_numerical_id); + PrettyDetailedMixNodeBond { mix_id, location: location.and_then(|l| l.location.clone()), @@ -157,6 +160,7 @@ impl ThreadsafeMixNodesCache { estimated_delegators_apy: best_effort_small_dec_to_f64(node.estimated_delegators_apy), operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(), profit_margin_percent: rewarding_info.cost_params.profit_margin_percent, + family_id, } } diff --git a/explorer-api/src/mix_nodes/utils.rs b/explorer-api/src/mix_nodes/utils.rs index a151ec318a..e1040dda62 100644 --- a/explorer-api/src/mix_nodes/utils.rs +++ b/explorer-api/src/mix_nodes/utils.rs @@ -1,9 +1,15 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use mixnet_contract_common::families::FamilyHead; +use rand::Rng; +use rand_pcg::Pcg64; +use rand_seeder::Seeder; + use crate::mix_nodes::location::GeoLocation; use isocountry::CountryCode; +#[allow(dead_code)] pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String { match CountryCode::for_alpha2(&geo.country_code) { Ok(three_letter_country_code) => three_letter_country_code.alpha3().to_string(), @@ -16,3 +22,10 @@ pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String } } } + +// We don't need numerical IDs anywhere, so to avoid modifying the contract storage again and +// since this is for explorer ergonomics, it will generate a deterministic random u16 based on the family Identity. +pub(crate) fn family_numerical_id(fh: &FamilyHead) -> u16 { + let mut rng: Pcg64 = Seeder::from(fh.identity()).make_rng(); + rng.gen() +} diff --git a/explorer-api/src/tasks.rs b/explorer-api/src/tasks.rs index 08d43fb805..6452405761 100644 --- a/explorer-api/src/tasks.rs +++ b/explorer-api/src/tasks.rs @@ -4,10 +4,10 @@ use std::future::Future; use mixnet_contract_common::GatewayBond; -use task::ShutdownListener; +use task::TaskClient; use validator_client::models::MixNodeBondAnnotated; -use validator_client::nymd::error::NymdError; -use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse}; +use validator_client::nyxd::error::NyxdError; +use validator_client::nyxd::{Paging, QueryNyxdClient, ValidatorResponse}; use validator_client::ValidatorClientError; use crate::mix_nodes::CACHE_REFRESH_RATE; @@ -15,24 +15,24 @@ use crate::state::ExplorerApiStateContext; pub(crate) struct ExplorerApiTasks { state: ExplorerApiStateContext, - shutdown: ShutdownListener, + shutdown: TaskClient, } impl ExplorerApiTasks { - pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self { + pub(crate) fn new(state: ExplorerApiStateContext, shutdown: TaskClient) -> Self { ExplorerApiTasks { state, shutdown } } // a helper to remove duplicate code when grabbing active/rewarded/all mixnodes async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec where - F: FnOnce(&'a validator_client::Client) -> Fut, + F: FnOnce(&'a validator_client::Client) -> Fut, Fut: Future, ValidatorClientError>>, { let bonds = match f(&self.state.inner.validator_client.0).await { Ok(result) => result, - Err(e) => { - error!("Unable to retrieve mixnode bonds: {:?}", e); + Err(err) => { + error!("Unable to retrieve mixnode bonds: {err}"); vec![] } }; @@ -57,14 +57,14 @@ impl ExplorerApiTasks { .await } - async fn retrieve_all_validators(&self) -> Result { + async fn retrieve_all_validators(&self) -> Result { info!("About to retrieve all validators..."); let height = self .state .inner .validator_client .0 - .nymd + .nyxd .get_current_block_height() .await?; let response: ValidatorResponse = self @@ -72,7 +72,7 @@ impl ExplorerApiTasks { .inner .validator_client .0 - .nymd + .nyxd .get_validators(height.value(), Paging::All) .await?; info!("Fetched {} validators", response.validators.len()); @@ -115,8 +115,8 @@ impl ExplorerApiTasks { async fn update_validators_cache(&self) { match self.retrieve_all_validators().await { Ok(response) => self.state.inner.validators.update_cache(response).await, - Err(e) => { - error!("Failed to get validators: {:?}", e) + Err(err) => { + error!("Failed to get validators: {err}") } } } @@ -124,8 +124,8 @@ impl ExplorerApiTasks { async fn update_gateways_cache(&self) { match self.retrieve_all_gateways().await { Ok(response) => self.state.inner.gateways.update_cache(response).await, - Err(e) => { - error!("Failed to get gateways: {:?}", e) + Err(err) => { + error!("Failed to get gateways: {err}") } } } diff --git a/explorer-api/src/validators/models.rs b/explorer-api/src/validators/models.rs index cc68b58f40..a66052fbbb 100644 --- a/explorer-api/src/validators/models.rs +++ b/explorer-api/src/validators/models.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use serde::Serialize; use tokio::sync::RwLock; -use validator_client::nymd::ValidatorResponse; +use validator_client::nyxd::ValidatorResponse; use crate::cache::Cache; diff --git a/explorer/.env.prod b/explorer/.env.prod index e053bd085a..70bd50c7ca 100644 --- a/explorer/.env.prod +++ b/explorer/.env.prod @@ -1,6 +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 +NYM_API_URL=https://validator.nymtech.net +VALIDATOR_URL=https://rpc.nymtech.net BIG_DIPPER_URL=https://blocks.nymtech.net CURRENCY_DENOM=unym CURRENCY_STAKING_DENOM=unyx diff --git a/explorer/.env.qa b/explorer/.env.qa index 60b30e1bd1..dd7052ce4d 100644 --- a/explorer/.env.qa +++ b/explorer/.env.qa @@ -1,5 +1,5 @@ EXPLORER_API_URL=https://qa-explorer.nymtech.net/api/v1 -VALIDATOR_API_URL=https://qa-validator-api.nymtech.net +NYM_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=unym diff --git a/explorer/src/api/constants.ts b/explorer/src/api/constants.ts index fb7216b64f..dc6124c53d 100644 --- a/explorer/src/api/constants.ts +++ b/explorer/src/api/constants.ts @@ -1,6 +1,6 @@ // 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 NYM_API_BASE_URL = process.env.NYM_API_URL; export const VALIDATOR_BASE_URL = process.env.VALIDATOR_URL; export const BIG_DIPPER = process.env.BIG_DIPPER_URL; @@ -9,12 +9,12 @@ export const OVERVIEW_API = `${API_BASE_URL}/overview`; 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 GATEWAYS_API = `${NYM_API_BASE_URL}/api/v1/gateways`; export const VALIDATORS_API = `${VALIDATOR_BASE_URL}/validators`; -export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`; +export const BLOCK_API = `${NYM_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. -export const UPTIME_STORY_API_GATEWAY = `${VALIDATOR_API_BASE_URL}/api/v1/status/gateway`; // add ID then '/history' or '/report' to this. +export const UPTIME_STORY_API = `${NYM_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this. +export const UPTIME_STORY_API_GATEWAY = `${NYM_API_BASE_URL}/api/v1/status/gateway`; // add ID then '/history' or '/report' to this. // errors export const MIXNODE_API_ERROR = "We're having trouble finding that record, please try again or Contact Us."; diff --git a/explorer/src/components/TableToolbar.tsx b/explorer/src/components/TableToolbar.tsx index 7452b4707a..7426433868 100644 --- a/explorer/src/components/TableToolbar.tsx +++ b/explorer/src/components/TableToolbar.tsx @@ -4,6 +4,8 @@ import Select, { SelectChangeEvent } from '@mui/material/Select'; import { Filters } from './Filters/Filters'; import { useIsMobile } from '../hooks/useIsMobile'; +const fieldsHeight = '42.25px'; + type TableToolBarProps = { onChangeSearch: (arg: string) => void; onChangePageSize: (event: SelectChangeEvent) => void; @@ -35,7 +37,7 @@ export const TableToolbar: React.FC = ({ }} > - + {childrenBefore} handleNewService(event.target.value)} + fullWidth + MenuProps={{ + PaperProps: { + sx: { + background: '#383C41', + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + '&& .Mui-selected': { + backgroundColor: '#FFFFFF33', + }, + '&& .Mui-focusVisible': { + backgroundColor: '#FFFFFF33', + }, + }, + }, + }} + > + {serviceProvider && ( + + + {serviceProvider.description} + + {serviceProvider.id} + + + + )} + {serviceProvider && } + {services.map((item) => ( + handleServiceSelected(item)}> + {item.description} + + ))} + + + Advanced + + setPopupVisible(true)}>Choose service provider + Clear settings + + + setPopupVisible(false)} + onServiceProviderChanged={handleAdvancedSpChange} + /> + + ); +}; diff --git a/nym-connect/src/context/main.tsx b/nym-connect/src/context/main.tsx index 5d2b049bf9..17b627c823 100644 --- a/nym-connect/src/context/main.tsx +++ b/nym-connect/src/context/main.tsx @@ -4,11 +4,12 @@ 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 { Error } from 'src/types/error'; +import { TauriEvent } from 'src/types/event'; +import { getVersion } from '@tauri-apps/api/app'; import { ConnectionStatusKind } from '../types'; import { ConnectionStatsItem } from '../components/ConnectionStats'; import { ServiceProvider, Services } from '../types/directory'; -import { Error } from 'src/types/error'; -import { TauriEvent } from 'src/types/event'; const TAURI_EVENT_STATUS_CHANGED = 'app:connection-status-changed'; @@ -16,6 +17,7 @@ type ModeType = 'light' | 'dark'; export type TClientContext = { mode: ModeType; + appVersion?: string; connectionStatus: ConnectionStatusKind; connectionStats?: ConnectionStatsItem[]; connectedSince?: DateTime; @@ -30,7 +32,7 @@ export type TClientContext = { setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void; setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void; setConnectedSince: (connectedSince: DateTime | undefined) => void; - setServiceProvider: (serviceProvider: ServiceProvider) => void; + setServiceProvider: (serviceProvider?: ServiceProvider) => void; startConnecting: () => Promise; startDisconnecting: () => Promise; @@ -47,11 +49,26 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode const [serviceProvider, setRawServiceProvider] = React.useState(); const [showHelp, setShowHelp] = useState(false); const [error, setError] = useState(); + const [appVersion, setAppVersion] = useState(); + + const getAppVersion = async () => { + const version = await getVersion(); + setAppVersion(version); + }; useEffect(() => { invoke('get_services').then((result) => { setServices(result as Services); }); + getAppVersion(); + }, []); + + useEffect(() => { + // when mounting, load the connection state (needed for the Growth window, that checks the connection state) + (async () => { + const currentStatus: ConnectionStatusKind = await invoke('get_connection_status'); + setConnectionStatus(currentStatus); + })(); }, []); useEffect(() => { @@ -103,10 +120,12 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode } 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); + const setServiceProvider = useCallback(async (newServiceProvider?: ServiceProvider) => { + await invoke('set_gateway', { gateway: newServiceProvider?.gateway }); + await invoke('set_service_provider', { serviceProvider: newServiceProvider?.address }); + if (newServiceProvider) { + await setSpInStorage(newServiceProvider); + } setRawServiceProvider(newServiceProvider); }, []); @@ -149,6 +168,7 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode const contextValue = useMemo( () => ({ mode, + appVersion, setMode, error, clearError, @@ -167,7 +187,9 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode handleShowHelp, }), [ + appVersion, mode, + appVersion, error, connectedSince, showHelp, diff --git a/nym-connect/src/context/mocks/main.tsx b/nym-connect/src/context/mocks/main.tsx index e2974413eb..9b09b6dc71 100644 --- a/nym-connect/src/context/mocks/main.tsx +++ b/nym-connect/src/context/mocks/main.tsx @@ -3,6 +3,7 @@ import { ConnectionStatusKind } from 'src/types'; import { ClientContext, TClientContext } from '../main'; const mockValues: TClientContext = { + appVersion: 'v1.x.x', mode: 'dark', connectionStatus: ConnectionStatusKind.disconnected, services: [], @@ -19,6 +20,9 @@ const mockValues: TClientContext = { startDisconnecting: async () => {}, }; -export const MockProvider = ({ children }: { children: React.ReactNode }) => { - return {children}; -}; +export const MockProvider: React.FC<{ + children?: React.ReactNode; + connectionStatus?: ConnectionStatusKind; +}> = ({ connectionStatus = ConnectionStatusKind.disconnected, children }) => ( + {children} +); diff --git a/nym-connect/src/growth.tsx b/nym-connect/src/growth.tsx new file mode 100644 index 0000000000..d90a8bb462 --- /dev/null +++ b/nym-connect/src/growth.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { ErrorBoundary } from 'react-error-boundary'; +import { ClientContextProvider } from './context/main'; +import { ErrorFallback } from './components/Error'; +import { NymShipyardTheme } from './theme'; +import { TestAndEarnPopup } from './components/Growth/TestAndEarnPopup'; +import { TestAndEarnContextProvider } from './components/Growth/context/TestAndEarnContext'; + +const root = document.getElementById('root-growth'); + +ReactDOM.render( + + + + + + + + + , + root, +); diff --git a/nym-connect/src/index.tsx b/nym-connect/src/index.tsx index 66a3e11d17..bd41e06822 100644 --- a/nym-connect/src/index.tsx +++ b/nym-connect/src/index.tsx @@ -6,17 +6,20 @@ import { ErrorFallback } from './components/Error'; import { NymMixnetTheme } from './theme'; import { App } from './App'; import { AppWindowFrame } from './components/AppWindowFrame'; +import { TestAndEarnContextProvider } from './components/Growth/context/TestAndEarnContext'; const root = document.getElementById('root'); ReactDOM.render( - - - - - + + + + + + + , root, diff --git a/nym-connect/src/layouts/ConnectedLayout.tsx b/nym-connect/src/layouts/ConnectedLayout.tsx index 8e34415b39..0bf61fd4c0 100644 --- a/nym-connect/src/layouts/ConnectedLayout.tsx +++ b/nym-connect/src/layouts/ConnectedLayout.tsx @@ -9,6 +9,7 @@ import { ConnectionStatsItem } from '../components/ConnectionStats'; import { ConnectionButton } from '../components/ConnectionButton'; import { IpAddressAndPort } from '../components/IpAddressAndPort'; import { ServiceProvider } from '../types/directory'; +import { TestAndEarnButtonArea } from '../components/Growth/TestAndEarnButtonArea'; export const ConnectedLayout: React.FC<{ status: ConnectionStatusKind; @@ -44,5 +45,6 @@ export const ConnectedLayout: React.FC<{ {/* */} + ); diff --git a/nym-connect/src/layouts/DefaultLayout.tsx b/nym-connect/src/layouts/DefaultLayout.tsx index ed42aad5fe..5c414a5bc1 100644 --- a/nym-connect/src/layouts/DefaultLayout.tsx +++ b/nym-connect/src/layouts/DefaultLayout.tsx @@ -1,15 +1,16 @@ import React from 'react'; -import { Typography } from '@mui/material'; -import { Box } from '@mui/material'; +import { Box, Typography } from '@mui/material'; import { ConnectionStatus } from 'src/components/ConnectionStatus'; import { ConnectionTimer } from 'src/components/ConntectionTimer'; import { InfoModal } from 'src/components/InfoModal'; import { Error } from 'src/types/error'; import { ConnectionButton } from '../components/ConnectionButton'; -import { ServiceProviderSelector } from '../components/ServiceProviderSelector'; +import { ServiceSelector } from '../components/ServiceSelector'; import { useClientContext } from '../context/main'; import { ConnectionStatusKind } from '../types'; -import { ServiceProvider, Services } from '../types/directory'; +import { Services } from '../types/directory'; +import { TestAndEarnButtonArea } from '../components/Growth/TestAndEarnButtonArea'; +import { AppVersion } from '../components/AppVersion'; export const DefaultLayout: React.FC<{ error?: Error; @@ -19,30 +20,32 @@ export const DefaultLayout: React.FC<{ isError?: boolean; clearError: () => void; onConnectClick?: (status: ConnectionStatusKind) => void; - onServiceProviderChange?: (serviceProvider: ServiceProvider) => void; -}> = ({ status, error, services, busy, isError, onConnectClick, onServiceProviderChange, clearError }) => { - const handleServiceProviderChange = (newServiceProvider: ServiceProvider) => { - onServiceProviderChange?.(newServiceProvider); - }; - - const { serviceProvider: currentSp } = useClientContext(); +}> = ({ status, error, services, busy, isError, onConnectClick, clearError }) => { + const context = useClientContext(); return ( {error && } - - Connect to the Nym
mixnet for privacy. -
- + + + Connect to the Nym
mixnet for privacy. +
+ + This is experimental software. Do not rely on it for strong anonymity (yet). + +
+ + +
); }; diff --git a/nym-connect/src/log.tsx b/nym-connect/src/log.tsx new file mode 100644 index 0000000000..cd17271368 --- /dev/null +++ b/nym-connect/src/log.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { ErrorBoundary } from 'react-error-boundary'; +import { LogViewer } from './components/LogViewer'; +import { ErrorFallback } from './components/ErrorFallback'; +import { NymMixnetTheme } from './theme'; + +const Log = () => ( + + + + + +); + +const root = document.getElementById('root-log'); + +ReactDOM.render(, root); diff --git a/nym-connect/src/stories/AppFlow.stories.tsx b/nym-connect/src/stories/AppFlow.stories.tsx index 34064e6202..7055288f63 100644 --- a/nym-connect/src/stories/AppFlow.stories.tsx +++ b/nym-connect/src/stories/AppFlow.stories.tsx @@ -8,12 +8,16 @@ import { ConnectionStatusKind } from '../types'; import { DefaultLayout } from '../layouts/DefaultLayout'; import { ConnectedLayout } from '../layouts/ConnectedLayout'; import { Services } from '../types/directory'; +import { TestAndEarnButtonArea } from '../components/Growth/TestAndEarnButtonArea'; export default { title: 'App/Flow', component: AppWindowFrame, } as ComponentMeta; +const width = 240; +const height = 575; + export const Mock: ComponentStory = () => { const context = useClientContext(); const [busy, setBusy] = React.useState(); @@ -67,45 +71,47 @@ export const Mock: ComponentStory = () => { context.connectionStatus === ConnectionStatusKind.connecting ) { return ( - - {}} - /> - + + + {}} + /> + + ); } return ( - - { - return undefined; - }} - status={context.connectionStatus} - busy={busy} - onConnectClick={handleConnectClick} - ipAddress="127.0.0.1" - port={1080} - connectedSince={context.connectedSince} - serviceProvider={services[0].items[0]} - stats={[ - { - label: 'in:', - totalBytes: 1024, - rateBytesPerSecond: 1024 * 1024 * 1024 + 10, - }, - { - label: 'out:', - totalBytes: 1024 * 1024 * 1024 * 1024 * 20, - rateBytesPerSecond: 1024 * 1024 + 10, - }, - ]} - /> - + + + undefined} + status={context.connectionStatus} + busy={busy} + onConnectClick={handleConnectClick} + ipAddress="127.0.0.1" + port={1080} + connectedSince={context.connectedSince} + serviceProvider={services[0].items[0]} + stats={[ + { + label: 'in:', + totalBytes: 1024, + rateBytesPerSecond: 1024 * 1024 * 1024 + 10, + }, + { + label: 'out:', + totalBytes: 1024 * 1024 * 1024 * 1024 * 20, + rateBytesPerSecond: 1024 * 1024 + 10, + }, + ]} + /> + + ); }; diff --git a/nym-connect/src/theme/index.tsx b/nym-connect/src/theme/index.tsx index 075aa7920a..08f5c62924 100644 --- a/nym-connect/src/theme/index.tsx +++ b/nym-connect/src/theme/index.tsx @@ -1,8 +1,7 @@ -import React, { useContext } from 'react'; +import React from 'react'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { CssBaseline } from '@mui/material'; import { getDesignTokens } from './theme'; -import { ClientContext } from '../context/main'; import '../../../assets/fonts/non-variable/fonts.css'; /** @@ -17,3 +16,13 @@ export const NymMixnetTheme: React.FC<{ mode: 'light' | 'dark' }> = ({ children, ); }; + +export const NymShipyardTheme: React.FC<{ mode?: 'light' | 'dark' }> = ({ children, mode = 'dark' }) => { + const theme = React.useMemo(() => createTheme(getDesignTokens(mode, true)), [mode]); + return ( + + + {children} + + ); +}; diff --git a/nym-connect/src/theme/mui-theme.d.ts b/nym-connect/src/theme/mui-theme.d.ts index ef042f6305..6c4d88fe39 100644 --- a/nym-connect/src/theme/mui-theme.d.ts +++ b/nym-connect/src/theme/mui-theme.d.ts @@ -38,6 +38,7 @@ declare module '@mui/material/styles' { light: string; dark: string; }; + shipyard: string; } interface NymPaletteVariant { @@ -52,6 +53,9 @@ declare module '@mui/material/styles' { topNav: { background: string; }; + shipyard: { + main: string; + }; } /** diff --git a/nym-connect/src/theme/theme.tsx b/nym-connect/src/theme/theme.tsx index 5b70f5118b..a580ae0b4e 100644 --- a/nym-connect/src/theme/theme.tsx +++ b/nym-connect/src/theme/theme.tsx @@ -30,6 +30,7 @@ const nymPalette: NymPalette = { light: '#F2F2F2', dark: '#1D2125', }, + shipyard: '#817FFA', }; const darkMode: NymPaletteVariant = { @@ -44,6 +45,9 @@ const darkMode: NymPaletteVariant = { topNav: { background: '#111826', }, + shipyard: { + main: '#817FFA', + }, }; const lightMode: NymPaletteVariant = { @@ -58,6 +62,9 @@ const lightMode: NymPaletteVariant = { topNav: { background: '#111826', }, + shipyard: { + main: '#817FFA', + }, }; /** @@ -103,6 +110,35 @@ const variantToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({ }, }); +/** + * Map a Nym palette variant onto the MUI palette for Shipyard + */ +const variantShipyardToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({ + text: { + primary: variant.text.main, + }, + primary: { + main: nymPalette.shipyard, + contrastText: '#fff', + }, + secondary: { + main: variant.mode === 'dark' ? nymPalette.background.light : nymPalette.background.dark, + }, + success: { + main: nymPalette.success, + }, + info: { + main: nymPalette.info, + }, + warning: { + main: nymPalette.warning, + }, + background: { + default: variant.background.main, + paper: variant.background.paper, + }, +}); + /** * Returns the Network Explorer palette for light mode. */ @@ -125,6 +161,17 @@ const createDarkModePalette = (): PaletteOptions => ({ ...variantToMUIPalette(darkMode), }); +/** + * Returns the Shipyard palette for dark mode. + */ +const createShipyardDarkModePalette = (): PaletteOptions => ({ + nym: { + ...nymPalette, + ...nymMixnetPalette(darkMode), + }, + ...variantShipyardToMUIPalette(darkMode), +}); + /** * IMPORANT: if you need to get the default MUI theme, use the following * @@ -157,12 +204,19 @@ const createDarkModePalette = (): PaletteOptions => ({ * * @param mode The theme mode: 'light' or 'dark' */ -export const getDesignTokens = (mode: PaletteMode): ThemeOptions => { - // first, create the palette from user's choice of light or dark mode +export const getDesignTokens = (mode: PaletteMode, isShipyard: boolean = false): ThemeOptions => { + let overrides; + if (isShipyard) { + overrides = createShipyardDarkModePalette(); + } else { + overrides = mode === 'light' ? createLightModePalette() : createDarkModePalette(); + } + + // create the palette from user's choice of light or dark mode const { palette } = createTheme({ palette: { mode, - ...(mode === 'light' ? createLightModePalette() : createDarkModePalette()), + ...overrides, }, }); diff --git a/nym-connect/src/typings/webp.d.ts b/nym-connect/src/typings/webp.d.ts new file mode 100644 index 0000000000..d594a17f53 --- /dev/null +++ b/nym-connect/src/typings/webp.d.ts @@ -0,0 +1,4 @@ +declare module '*.webp' { + const value: any; + export default value; +} diff --git a/nym-connect/src/typings/yaml.d.ts b/nym-connect/src/typings/yaml.d.ts new file mode 100644 index 0000000000..4c36d21ec0 --- /dev/null +++ b/nym-connect/src/typings/yaml.d.ts @@ -0,0 +1,9 @@ +declare module '*.yml' { + const content: { [key: string]: any }; + export default content; +} + +declare module '*.yaml' { + const content: { [key: string]: any }; + export default content; +} diff --git a/nym-connect/src/utils/index.ts b/nym-connect/src/utils/index.ts index 70b786d12e..9dc9fd09e3 100644 --- a/nym-connect/src/utils/index.ts +++ b/nym-connect/src/utils/index.ts @@ -1 +1,19 @@ -// TODO +import { useEffect, useRef } from 'react'; +import { EventName, listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event'; + +export const useTauriEvents = (event: EventName, handler: EventCallback) => { + const unlisten = useRef(); + + // list for events to clear local storage + useEffect(() => { + listen(event, handler).then((fn) => { + unlisten.current = fn; + }); + + return () => { + if (unlisten.current) { + unlisten.current(); + } + }; + }, []); +}; diff --git a/nym-connect/webpack.common.js b/nym-connect/webpack.common.js index 0e6b071aaa..38423b5e6b 100644 --- a/nym-connect/webpack.common.js +++ b/nym-connect/webpack.common.js @@ -2,6 +2,12 @@ const path = require('path'); const { mergeWithRules } = require('webpack-merge'); const { webpackCommon } = require('@nymproject/webpack'); +const entry = { + app: path.resolve(__dirname, 'src/index.tsx'), + growth: path.resolve(__dirname, 'src/growth.tsx'), + log: path.resolve(__dirname, 'src/log.tsx'), +}; + module.exports = mergeWithRules({ module: { rules: { @@ -9,10 +15,45 @@ module.exports = mergeWithRules({ use: 'replace', }, }, -})(webpackCommon(__dirname, 'public/index.html'), { - entry: path.resolve(__dirname, 'src/index.tsx'), - output: { - path: path.resolve(__dirname, 'dist'), - publicPath: '/', +})( + webpackCommon(__dirname, [ + { filename: 'index.html', chunks: ['app'], template: path.resolve(__dirname, 'public/index.html') }, + { filename: 'log.html', chunks: ['log'], template: path.resolve(__dirname, 'public/log.html') }, + { filename: 'growth.html', chunks: ['growth'], template: path.resolve(__dirname, 'public/growth.html') }, + ]), + { + module: { + rules: [ + { + test: /\.mdx?$/, + use: [ + { + loader: '@mdx-js/loader', + /** @type {import('@mdx-js/loader').Options} */ + options: {}, + }, + ], + }, + { + test: /\.ya?ml$/, + type: 'asset/resource', + use: [ + { + loader: 'yaml-loader', + options: { + asJSON: true, + }, + }, + ], + }, + ], + }, + entry, + output: { + clean: true, + path: path.resolve(__dirname, 'dist'), + filename: '[name].bundle.js', + publicPath: '/', + }, }, -}); +); diff --git a/nym-connect/webpack.prod.js b/nym-connect/webpack.prod.js index 40e7bc95d7..0e9061e25a 100644 --- a/nym-connect/webpack.prod.js +++ b/nym-connect/webpack.prod.js @@ -2,10 +2,16 @@ const path = require('path'); const { default: merge } = require('webpack-merge'); const common = require('./webpack.common'); +const entry = { + app: path.resolve(__dirname, 'src/index.tsx'), + growth: path.resolve(__dirname, 'src/growth.tsx'), + log: path.resolve(__dirname, 'src/log.tsx'), +}; + module.exports = merge(common, { mode: 'production', node: { __dirname: false, }, - entry: path.resolve(__dirname, './src/index'), + entry, }); diff --git a/nym-outfox/Cargo.toml b/nym-outfox/Cargo.toml new file mode 100644 index 0000000000..48a05c842c --- /dev/null +++ b/nym-outfox/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "nym-outfox" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rayon = "1.5.1" +blake3 = "1.3" +zeroize = "1.5" +chacha20 = "0.9.0" +curve25519-dalek = "3.2" +chacha20poly1305 = "0.10.1" +# Need this star over here to pull in js into getrandom +getrandom = { version = "*", features = ["js"] } +thiserror = "1" + +[dev-dependencies] +criterion = "0.4" +fastrand = "1.8" diff --git a/nym-outfox/src/error.rs b/nym-outfox/src/error.rs new file mode 100644 index 0000000000..afbba2fc4b --- /dev/null +++ b/nym-outfox/src/error.rs @@ -0,0 +1,20 @@ +use crate::lion::MIN_MESSAGE_LEN; +use chacha20::cipher::InvalidLength; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum OutFoxError { + #[error("Lengths mismatch, expected: {expected}, got: {got}")] + LenMismatch { expected: usize, got: usize }, + #[error("{source}")] + ChaCha20InvalidLength { + #[from] + source: InvalidLength, + }, + #[error("ChaCha20Poly1305 - Opaque error")] + ChaCha20Poly1305Error, + #[error("Key length must be 32 bytes")] + InvalidKeyLength, + #[error("Message length must be greater then {MIN_MESSAGE_LEN} bytes")] + InvalidMessageLength, +} diff --git a/nym-outfox/src/format.rs b/nym-outfox/src/format.rs new file mode 100644 index 0000000000..2ddf895e1f --- /dev/null +++ b/nym-outfox/src/format.rs @@ -0,0 +1,267 @@ +//! # The `outfox` format +//! +//! We define a simple mix packet format geared towards simplicity and performance with the features that are +//! specifically required by a stratified mix topology. +//! +//! `Outfox` assumes that all +//! paths are the same length (no need to hide path lengths), mixes are arranged in layers and therefore +//! know their position in a message path (no need to hide this position). These assumptions allow us +//! to do away with some of the padding traditionally used; further we prioritize efficient computation +//! over very low-bandwidth, as it seems the rate of public key operations is a bottleneck for mixes +//! rather than the availablility of bandwidth. +//! +//! ## Overview and Parameters +//! +//! In a mix network with a stratified topology packets are mixed by nodes at each of the layers. Each layer +//! 'strips' the packet from one layer of encryption, recovers the address of the mix at the next layer, and +//! passes the decoded packet to them. An identifier per processed message is stored and checked to prevent +//! replays of processed messages at each layer. Additional measures, such as adding delays, adding dummy +//! traffic or dropping messages can be empued at each mix to frustrate traffic analysis. +//! +//! +//! A layer of mix processing is defined by three parameters, includes in the structure [MixStageParameters]: +//! * The `routing_information_length_bytes` (`R`) states the number of bytes representing +//! routing information at this layer. +//! * The `remaining_header_length_bytes` (`H`) represents the remaining bytes of the packet header. +//! * The `payload_length_bytes` (`P`). +//! +//! In addition we define two system-wide constants, namely `GROUPELEMENTBYTES` (`GE`=32) and +//! `TAGBYTES` (`T`=24). +//! +//! ## Packet format, decoding +//! +//! A mix at this layer takes in messages of length `GE+T+R+H+P`, and outputs messages of length `H+P`. +//! +//! An input message is processed as follows: +//! +//! * The input packet is parsed as a `[Pk, Tag, Header, Payload]` of length `[GE, T, R+H, P]` respectivelly. +//! * A master key is derived by performing scalar multiplication with the mix secret 's', ie `K = s * Pk`. +//! The master key is stored and checked for duplicates (if it is found processing ends.) +//! * The master key is used to perform AEAD decryption of the `Header` with an IV of zeros and the `tag`. If +//! decryption fails processing ends. Otherwise the Header is parsed as `[Routing, Next_Header]` of length +//! `[R, H]` respectivelly. The routing data `Routing` can be used by the mix to dertermine the next mix. +//! * Finally, the master key is used to perform lion decoding of the `Payload` into `Next_Payload`. +//! * The output packet for the next mix is `[Next_Header, Next_Payload]`. +//! +//! As an AEAD we use `chacha20poly1305_ietf` and for public key operations we use `curve25519`. +//! +//! ## Packet encoding +//! +//! Encoding is +//! performed layer by layer starting with the last hop on the route, and ending with the first. At each stage +//! of encoding a new Secret key `Sk` and corresponding `Pk` is chosen. The layer master key for the layer is +//! derived using the mix public key. And the master key is used to AEAD encrypt the concatenation of the +//! routing data for the layer, and the remaining Header; separately the master key is used to lion encrypt +//! the payload. The process is repeated for each layer (from last to first) to construct the full message. + +use chacha20poly1305::AeadInPlace; +use chacha20poly1305::ChaCha20Poly1305; +use chacha20poly1305::KeyInit; + +use chacha20poly1305::Tag; +use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; +use curve25519_dalek::montgomery::MontgomeryPoint; +use curve25519_dalek::scalar::Scalar; + +use std::convert::TryInto; + +const GROUPELEMENTBYTES: usize = 32; +const TAGBYTES: usize = 16; + +use std::ops::Range; + +use crate::error::OutFoxError; +use crate::lion::*; + +/// A structure that holds mix packet construction parameters. These incluse the length +/// of the routing information at each hop, the number of hops, and the payload length. +pub struct MixCreationParameters { + /// The routing length is inner first, so \[0\] is the innermost routing length, etc (in bytes) + pub routing_information_length_by_stage: Vec, + /// The payload length (in bytes) + pub payload_length_bytes: usize, +} + +impl MixCreationParameters { + /// Create a set of parameters for a mix packet format. + pub fn new(payload_length_bytes: usize) -> MixCreationParameters { + MixCreationParameters { + routing_information_length_by_stage: Vec::new(), + payload_length_bytes, + } + } + + /// Add another outer layer containing some byte length of routing data. + pub fn add_outer_layer(&mut self, routing_information_length_bytes: usize) { + self.routing_information_length_by_stage + .push(routing_information_length_bytes); + } + + /// The length of the buffer needed to build a packet. + pub fn total_packet_length(&self) -> usize { + let mut len = self.payload_length_bytes; + for stage_len in &self.routing_information_length_by_stage { + len += stage_len + GROUPELEMENTBYTES + TAGBYTES + } + len + } + + /// Get the mix packet parameters for a single stage of mixing. + pub fn get_stage_params(&self, layer_number: usize) -> (Range, MixStageParameters) { + assert!(layer_number < self.routing_information_length_by_stage.len()); + + let mut remaining_header_length_bytes = 0; + for (i, stage_len) in self.routing_information_length_by_stage.iter().enumerate() { + if i == layer_number { + let params = MixStageParameters { + routing_information_length_bytes: *stage_len, + remaining_header_length_bytes, + payload_length_bytes: self.payload_length_bytes, + }; + + let total_size = self.total_packet_length(); + let inner_size = params.incoming_packet_length(); + + return (total_size - inner_size..total_size, params); + } else { + remaining_header_length_bytes += stage_len + GROUPELEMENTBYTES + TAGBYTES; + } + } + + unreachable!(); + } +} + +/// A structure representing the parameters of a single stage of mixing. +pub struct MixStageParameters { + /// The routing information length for this stage of mixing + pub routing_information_length_bytes: usize, + /// The reamining header length for this stage of mixing + pub remaining_header_length_bytes: usize, + /// The payload length + pub payload_length_bytes: usize, +} + +impl MixStageParameters { + pub fn incoming_packet_length(&self) -> usize { + GROUPELEMENTBYTES + TAGBYTES + self.outgoing_packet_length() + } + + pub fn outgoing_packet_length(&self) -> usize { + self.routing_information_length_bytes + + self.remaining_header_length_bytes + + self.payload_length_bytes + } + + pub fn pub_element_range(&self) -> Range { + 0..GROUPELEMENTBYTES + } + + pub fn tag_range(&self) -> Range { + GROUPELEMENTBYTES..GROUPELEMENTBYTES + TAGBYTES + } + + pub fn routing_data_range(&self) -> Range { + GROUPELEMENTBYTES + TAGBYTES + ..GROUPELEMENTBYTES + TAGBYTES + self.routing_information_length_bytes + } + + pub fn header_range(&self) -> Range { + GROUPELEMENTBYTES + TAGBYTES + ..GROUPELEMENTBYTES + + TAGBYTES + + self.routing_information_length_bytes + + self.remaining_header_length_bytes + } + + pub fn payload_range(&self) -> Range { + self.incoming_packet_length() - self.payload_length_bytes..self.incoming_packet_length() + } + + pub fn encode_mix_layer( + &self, + buffer: &mut [u8], + user_secret_key: &Scalar, + mix_public_key: &MontgomeryPoint, + routing_data: &[u8], + ) -> Result { + if buffer.len() != self.incoming_packet_length() { + return Err(OutFoxError::LenMismatch { + expected: buffer.len(), + got: self.incoming_packet_length(), + }); + } + + if routing_data.len() != self.routing_information_length_bytes { + return Err(OutFoxError::LenMismatch { + expected: routing_data.len(), + got: self.routing_information_length_bytes, + }); + } + + let user_public_key = (&ED25519_BASEPOINT_TABLE * user_secret_key).to_montgomery(); + let shared_key = user_secret_key * mix_public_key; + + // Copy rounting data into buffer + buffer[self.routing_data_range()].copy_from_slice(routing_data); + + // Perform the AEAD + let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..])?; + // TODO: Should this be all 0s? + let nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let tag = header_aead_key + .encrypt_in_place_detached(&nonce.into(), &[], &mut buffer[self.header_range()]) + .map_err(|_| OutFoxError::ChaCha20Poly1305Error)?; + + // Copy Tag into buffer + buffer[self.tag_range()].copy_from_slice(&tag[..]); + + // Copy own public key into buffer + buffer[self.pub_element_range()].copy_from_slice(&user_public_key.0[..]); + + // Do a round of LION on the payload + lion_transform_encrypt(&mut buffer[self.payload_range()], &shared_key.0)?; + + Ok(shared_key) + } + + pub fn decode_mix_layer( + &self, + buffer: &mut [u8], + mix_secret_key: &Scalar, + ) -> Result { + // Check the length of the incoming buffer is correct. + if buffer.len() != self.incoming_packet_length() { + return Err(OutFoxError::LenMismatch { + expected: buffer.len(), + got: self.incoming_packet_length(), + }); + } + + // Derive the shared key for this packet + let user_public_key = MontgomeryPoint(buffer[self.pub_element_range()].try_into().unwrap()); + let shared_key = mix_secret_key * user_public_key; + + // Compute the AEAD and check the Tag, if wrong return Err + let header_aead_key = ChaCha20Poly1305::new_from_slice(&shared_key.0[..]).unwrap(); + let nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let tag_bytes = buffer[self.tag_range()].to_vec(); + let tag = Tag::from_slice(&tag_bytes); + + header_aead_key + .decrypt_in_place_detached( + &nonce.into(), + &[], + &mut buffer[self.header_range()], + tag.as_slice().try_into().unwrap(), + ) + .map_err(|_| OutFoxError::ChaCha20Poly1305Error)?; + + // Do a round of LION on the payload + lion_transform_decrypt(&mut buffer[self.payload_range()], &shared_key.0)?; + + Ok(shared_key) + } +} diff --git a/nym-outfox/src/lib.rs b/nym-outfox/src/lib.rs new file mode 100644 index 0000000000..fbfcc32078 --- /dev/null +++ b/nym-outfox/src/lib.rs @@ -0,0 +1,3 @@ +pub mod error; +pub mod format; +pub mod lion; diff --git a/nym-outfox/src/lion.rs b/nym-outfox/src/lion.rs new file mode 100644 index 0000000000..e4ea5e2c55 --- /dev/null +++ b/nym-outfox/src/lion.rs @@ -0,0 +1,114 @@ +//! # The lion all-or-nothing transform +//! +//! The lion transform implements a keyed permutation (block cipher) with a +//! variable length block size. It takes a key of 32 bytes, and a message of +//! length >= 48 bytes. +//! +//! The cryptographic primitives used to implement the transform are a +//! stream cipher `PRF1(IV, KEY)` (using `crypto_stream_xsalsa20`), a message authentication +//! code `PRF2(MSG, KEY)` (using `HMAC-SHA-512-256`) and a key derivation function `KDF(KEY, ID)` +//! (using `Blake2b`). +//! +//! The message to encode is split into two parts `M = [L0, R0]`, where L is 24 bytes, and +//! R is the remaining of the message. +//! +//! Encoding then proceeds in 3 steps: +//! * `R1 = PRF1(L0, KDF(key, subkey_0)) XOR R0;` +//! * `L1 = PRF2(R1, KDF(key, subkey_1)) XOR L0;` +//! * `R2 = PRF1(L1, KDF(key, subkey_2)) XOR R1;` +//! +//! The output of the transform is the concatenated byte string `M' = [L1, R2]` which has the same +//! length as the original message. +//! +//! ## Manual key schedule. +//! +//! If you just want to encode / decode using lion as a wide-block block cipher simply use the +//! [lion_transform_encrypt] and [lion_transform_decrypt] functions. +//! +//! If you know what you are doing you can determine your own key schedule for the transform. The +//! key schedule for encypt and decrypt are [1, 2, 3] and [3, 2, 1] respectivelly. You may define +//! a key schedule that is symmetric (such as [1, 2, 1]) to build a transform T(k,m) that has the +//! property m = T(k, T(k, m)). + +use chacha20::cipher::{KeyIvInit, StreamCipher}; +use chacha20::Key; +use chacha20::XChaCha20; +use chacha20::XNonce; +use zeroize::Zeroize; + +use crate::error::OutFoxError; + +pub const MIN_MESSAGE_LEN: usize = 24 * 2; +const CONTEXT: &str = "LIONKEYS"; +const TAG_LEN: usize = 24; + +/// The lion transform encryption function. +/// +/// The `key` must be 32 bytes, and the `message` >= 48. The message is +/// mutated to the encrypted message. +pub fn lion_transform_encrypt(message: &mut [u8], key: &[u8]) -> Result<(), OutFoxError> { + lion_transform(message, key, [1, 2, 3]) +} + +/// The lion transform decryption function. +/// +/// The `key` must be 32 bytes, and the `message` >= 48. The message +/// is mutated to the decrypted message. +pub fn lion_transform_decrypt(message: &mut [u8], key: &[u8]) -> Result<(), OutFoxError> { + lion_transform(message, key, [3, 2, 1]) +} + +/// The core of the lion transform function, that takes a message and a key, +/// and applies the all-or-nothing transform. The key schedule represents the +/// values of the 3 subkeys used by the 3 phases of the transform. +/// +/// The `key` must be 32 bytes, and the `message` >= 48. +/// +/// Unless you know what you are doing use [lion_transform_encrypt] and +/// [lion_transform_decrypt] instead. +pub fn lion_transform( + message: &mut [u8], + key: &[u8], + key_schedule: [u64; 3], +) -> Result<(), OutFoxError> { + if key.len() != 32 { + return Err(OutFoxError::InvalidKeyLength); + } + + if message.len() < MIN_MESSAGE_LEN { + return Err(OutFoxError::InvalidMessageLength); + } + + // Stage 1: Use stream cipher with Nonce from left size, to xor to the right side + let mut derived_key = blake3::derive_key(&format!("{}{}", CONTEXT, key_schedule[0]), key); + let lion_stage_1_key = Key::from_slice(&derived_key); + let left_short_message = XNonce::from_slice(&message[..TAG_LEN]); + + let mut cipher = XChaCha20::new(lion_stage_1_key, left_short_message); + cipher.apply_keystream(&mut message[TAG_LEN..]); + + // Stage 2: Use HMAC of right size, and xor to the left side + derived_key = blake3::derive_key(&format!("{}{}", CONTEXT, key_schedule[1]), key); + + let mac = blake3::keyed_hash(&derived_key, &message[TAG_LEN..]); + let tag_to_xor = mac.as_bytes(); + + // Xor resulting HMAC into the left (short) message + for i in 0..TAG_LEN { + message[i] ^= tag_to_xor[i]; + } + + // Stage 3: (same as 1) + derived_key = blake3::derive_key(&format!("{}{}", CONTEXT, key_schedule[2]), key); + let lion_stage_3_key = Key::from_slice(&derived_key); + let left_short_message_final = XNonce::from_slice(&message[..TAG_LEN]); + + let mut cipher = XChaCha20::new(lion_stage_3_key, left_short_message_final); + + cipher.apply_keystream(&mut message[TAG_LEN..]); + + // clean up temp key + derived_key.zeroize(); + + Ok(()) +} diff --git a/nym-outfox/tests/unittests.rs b/nym-outfox/tests/unittests.rs new file mode 100644 index 0000000000..ea5b46571d --- /dev/null +++ b/nym-outfox/tests/unittests.rs @@ -0,0 +1,142 @@ +extern crate nym_outfox; + +#[cfg(test)] +mod tests { + + use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; + use curve25519_dalek::scalar::Scalar; + use std::convert::TryInto; + + use nym_outfox::format::*; + use nym_outfox::lion::*; + + use std::iter::repeat_with; + + pub fn randombytes(n: usize) -> Vec { + repeat_with(|| fastrand::u8(..)).take(n).collect() + } + + #[test] + fn test_encode_decode() { + let mix_params = MixStageParameters { + routing_information_length_bytes: 32, + remaining_header_length_bytes: (32 + 16 + 32) * 4, + payload_length_bytes: 1024, // 1kb + }; + + let user_secret_bytes = randombytes(32); + let mix_secret_bytes = randombytes(32); + + let user_secret = Scalar::from_bytes_mod_order(user_secret_bytes.try_into().unwrap()); + let mix_secret = Scalar::from_bytes_mod_order(mix_secret_bytes.try_into().unwrap()); + let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret).to_montgomery(); + + let routing = [0; 32]; + + let buffer = randombytes(mix_params.incoming_packet_length()); + + let mut new_buffer = buffer.clone(); + + let _ = mix_params + .encode_mix_layer( + &mut new_buffer[..], + &user_secret, + &mix_public_key, + &routing[..], + ) + .unwrap(); + + assert!(&new_buffer[mix_params.payload_range()] != &buffer[mix_params.payload_range()]); + assert!(&new_buffer[mix_params.routing_data_range()] != &routing[..]); + + let _ = mix_params + .decode_mix_layer(&mut new_buffer[..], &mix_secret) + .unwrap(); + + assert!(&new_buffer[mix_params.payload_range()] == &buffer[mix_params.payload_range()]); + assert!(&new_buffer[mix_params.routing_data_range()] == &routing[..]); + } + + #[test] + fn test_lion() { + let key = randombytes(32); + let message = randombytes(1024); + + let mut message_clone = message.clone(); + lion_transform(&mut message_clone[..], &key, [1, 2, 3]).unwrap(); + assert!(&message_clone[..] != &message[..]); + + let mut message_clone_2 = message.clone(); + lion_transform_encrypt(&mut message_clone_2, &key).unwrap(); + assert_eq!(message_clone_2, message_clone); + + lion_transform(&mut message_clone[..], &key[..], [3, 2, 1]).unwrap(); + assert!(&message_clone[..] == &message[..]); + } + + #[test] + fn test_packet_params() { + // Dummy keys -- we will use the same key for each layer + let user_secret_bytes = randombytes(32); + let mix_secret_bytes = randombytes(32); + + let user_secret = Scalar::from_bytes_mod_order(user_secret_bytes.try_into().unwrap()); + let mix_secret = Scalar::from_bytes_mod_order(mix_secret_bytes.try_into().unwrap()); + let mix_public_key = (&ED25519_BASEPOINT_TABLE * &mix_secret).to_montgomery(); + + let routing = [0; 32]; + + let mut params = MixCreationParameters::new(1025); + params.add_outer_layer(32); + params.add_outer_layer(32); + params.add_outer_layer(32); + + let mut buf = vec![0; params.total_packet_length()]; + + let (range0, layer_params0) = params.get_stage_params(0); + let _ = layer_params0 + .encode_mix_layer( + &mut buf[range0.clone()], + &user_secret, + &mix_public_key, + &routing[..], + ) + .unwrap(); + + let (range1, layer_params1) = params.get_stage_params(1); + let _ = layer_params1 + .encode_mix_layer( + &mut buf[range1.clone()], + &user_secret, + &mix_public_key, + &routing[..], + ) + .unwrap(); + + let (range2, layer_params2) = params.get_stage_params(2); + let _ = layer_params2 + .encode_mix_layer( + &mut buf[range2.clone()], + &user_secret, + &mix_public_key, + &routing[..], + ) + .unwrap(); + + assert!( + &buf[params.total_packet_length() - 1025..params.total_packet_length()] != [0; 1025] + ); + + let _ = layer_params2 + .decode_mix_layer(&mut buf[range2], &mix_secret) + .unwrap(); + + let _ = layer_params1 + .decode_mix_layer(&mut buf[range1], &mix_secret) + .unwrap(); + + let _ = layer_params0 + .decode_mix_layer(&mut buf[range0], &mix_secret) + .unwrap(); + } +} diff --git a/nym-wallet/CHANGELOG.md b/nym-wallet/CHANGELOG.md index 1f19602deb..2e4f234db1 100644 --- a/nym-wallet/CHANGELOG.md +++ b/nym-wallet/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## UNRELEASED + +## [nym-wallet-v1.1.5](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.5) (2022-12-22) + +This release contains the APY calculator feature. + +## [nym-wallet-v1.1.4](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.4) (2022-12-20) + +This release contains a bugfix for hiding and displaying the mnemonic, and displays the Bity wallet address in the buy page, as well as better error handling. + +- wallet: present some cases of the more technical errors (abci, ..) in a more human readable form. +- Bug fix/hide display mnemonic by @fmtabbara in https://github.com/nymtech/nym/pull/1874 +- adding bity icon and wallet address by @gala1234 in https://github.com/nymtech/nym/pull/2689 +- remove warning error message by @gala1234 in https://github.com/nymtech/nym/pull/2694 +- ui and copy changes by @gala1234 in https://github.com/nymtech/nym/pull/2691 + +## [nym-wallet-v1.1.3](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.3) (2022-12-13) + +- wallet: improved unbond screen. + +## [nym-wallet-v1.1.2](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.2) (2022-11-09) + +- wallet: Bity buy functionality + +## [nym-wallet-v1.1.1](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.1) (2022-11-09) + +- wallet: compatibility with nym-binaries-1.1.1 + + ## [nym-wallet-v1.1.0](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.0) (2022-11-09) - wallet: Add window for showing logs for when the terminal is not available. @@ -19,7 +48,7 @@ ## [nym-wallet-v1.0.9](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-09-08) -- wallet: change default `nymd` URL to https://rpc.nymtech.net +- wallet: change default `nyxd` URL to https://rpc.nymtech.net ## [nym-wallet-v1.0.8](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-08-11) @@ -116,7 +145,7 @@ - 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: connection test nyxd 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)) @@ -189,7 +218,7 @@ - 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)) +- Made contract addresses for query NyxdClient 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)) diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 999378447f..0a9ba7103f 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -14,12 +14,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "aead" version = "0.4.3" @@ -153,20 +147,18 @@ dependencies = [ [[package]] name = "attohttpc" -version = "0.19.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262c3f7f5d61249d8c00e5546e2685cd15ebeeb1bc0f3cc5449350a1cb07319e" +checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" dependencies = [ "flate2", "http", "log", "native-tls", - "openssl", "serde", "serde_json", "serde_urlencoded", "url", - "wildmatch", ] [[package]] @@ -175,7 +167,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -438,12 +430,11 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.11.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e270ef0cd868745878982f7ce470aa898d0d4bb248af67f0cf66f54617913ef" +checksum = "aa0e3586af56b3bfa51fca452bd56e8dbbbd5d8d81cbf0b7e4e35b695b537eb8" dependencies = [ "serde", - "serde_derive", "toml", ] @@ -520,26 +511,24 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.20" +version = "4.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" +checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", - "indexmap", + "is-terminal", "once_cell", "strsim", "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -550,9 +539,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -1076,16 +1065,6 @@ dependencies = [ "syn", ] -[[package]] -name = "deflate" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "der" version = "0.5.1" @@ -1226,6 +1205,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + [[package]] name = "dyn-clone" version = "1.0.4" @@ -1304,19 +1289,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "embed-resource" -version = "1.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936c1354206a875581696369aef920e12396e93bbd251c43a7a3f3fa85023a7d" -dependencies = [ - "cc", - "rustc_version 0.4.0", - "toml", - "vswhom", - "winreg 0.10.1", -] - [[package]] name = "embed_plist" version = "1.2.2" @@ -1325,9 +1297,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding_rs" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if", ] @@ -1365,6 +1337,27 @@ dependencies = [ "termcolor", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "execute" version = "0.1.0" @@ -1569,21 +1562,6 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.21" @@ -2057,6 +2035,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -2234,12 +2221,12 @@ dependencies = [ [[package]] name = "ico" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4b3331534254a9b64095ae60d3dc2a8225a7a70229cd5888be127cdc1f6804" +checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd" dependencies = [ "byteorder", - "png 0.11.0", + "png", ] [[package]] @@ -2316,15 +2303,6 @@ dependencies = [ "cfb", ] -[[package]] -name = "inflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f9f47468e9a76a6452271efadc88fe865a82be91fe75e6c0c57b87ccea59d4" -dependencies = [ - "adler32", -] - [[package]] name = "instant" version = "0.1.12" @@ -2334,12 +2312,34 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + [[package]] name = "ipnet" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "itertools" version = "0.10.3" @@ -2386,23 +2386,9 @@ dependencies = [ [[package]] name = "jni" -version = "0.18.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24967112a1e4301ca5342ea339763613a37592b8a6ce6cf2e4494537c7a42faf" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" dependencies = [ "cesu8", "combine", @@ -2524,6 +2510,12 @@ dependencies = [ "safemem", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.6" @@ -2677,7 +2669,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -2803,17 +2795,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - [[package]] name = "num-rational" version = "0.4.1" @@ -2840,7 +2821,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -2874,6 +2855,20 @@ dependencies = [ "libc", ] +[[package]] +name = "nym-api-requests" +version = "0.1.0" +dependencies = [ + "bs58", + "coconut-interface", + "cosmrs", + "cosmwasm-std", + "getset", + "mixnet-contract-common", + "schemars", + "serde", +] + [[package]] name = "nym-types" version = "1.0.0" @@ -2937,7 +2932,7 @@ dependencies = [ [[package]] name = "nym_wallet" -version = "1.1.1" +version = "1.1.5" dependencies = [ "aes-gcm", "argon2 0.3.4", @@ -3047,9 +3042,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "opaque-debug" @@ -3070,7 +3065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23a407004a1033f53e93f9b45580d14de23928faad187384f891507c9b0c045" dependencies = [ "pathdiff", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -3146,12 +3141,6 @@ dependencies = [ "system-deps 6.0.2", ] -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - [[package]] name = "parking_lot" version = "0.11.2" @@ -3197,7 +3186,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec 1.8.0", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -3290,9 +3279,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" @@ -3498,18 +3487,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "png" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b0cabbbd20c2d7f06dbf015e06aad59b6ca3d9ed14848783e98af9aaf19925" -dependencies = [ - "bitflags", - "deflate", - "inflate", - "num-iter", -] - [[package]] name = "png" version = "0.17.6" @@ -3598,11 +3575,11 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -3854,9 +3831,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" dependencies = [ "cty", ] @@ -3891,9 +3868,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -3957,7 +3934,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.7.0", + "winreg", ] [[package]] @@ -3973,13 +3950,12 @@ dependencies = [ [[package]] name = "rfd" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f121348fd3b9035ed11be1f028e8944263c30641f8c5deacf57a4320782fb402" +checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" dependencies = [ "block", "dispatch", - "embed-resource", "glib-sys", "gobject-sys", "gtk-sys", @@ -3993,7 +3969,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows", + "windows 0.37.0", ] [[package]] @@ -4040,6 +4016,20 @@ dependencies = [ "semver 1.0.6", ] +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -4234,9 +4224,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" dependencies = [ "serde_derive", ] @@ -4261,9 +4251,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" dependencies = [ "proc-macro2", "quote", @@ -4630,13 +4620,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -4679,9 +4669,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.12.2" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6fd7725dc1e593e9ecabd9fe49c112a204c8c8694db4182e78b2a5af490b1ae" +checksum = "a1fa15735311b4816d030ff54da58560b047daca0970e1031aed5502e84231a8" dependencies = [ "bitflags", "cairo-rs", @@ -4701,7 +4691,7 @@ dependencies = [ "gtk", "image", "instant", - "jni 0.19.0", + "jni", "lazy_static", "libc", "log", @@ -4712,13 +4702,13 @@ dependencies = [ "once_cell", "parking_lot 0.12.1", "paste", - "png 0.17.6", + "png", "raw-window-handle", "scopeguard", "serde", "unicode-segmentation", - "uuid 1.1.2", - "windows", + "uuid 1.2.2", + "windows 0.39.0", "windows-implement", "x11-dl", ] @@ -4742,9 +4732,9 @@ dependencies = [ [[package]] name = "tauri" -version = "1.0.5" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1a56a8b125069c2682bd31610109b4436c050c74447bee1078217a0325c1add" +checksum = "d8ea1d785ab2164373703817bff144c4610a69ad3f659becaca0e1ea004b98d8" dependencies = [ "anyhow", "attohttpc", @@ -4752,9 +4742,9 @@ dependencies = [ "cocoa", "dirs-next", "embed_plist", + "encoding_rs", "flate2", - "futures", - "futures-lite", + "futures-util", "glib", "glob", "gtk", @@ -4786,18 +4776,18 @@ dependencies = [ "time 0.3.7", "tokio", "url", - "uuid 1.1.2", + "uuid 1.2.2", "webkit2gtk", "webview2-com", - "windows", + "windows 0.39.0", "zip", ] [[package]] name = "tauri-build" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acafb1c515c5d14234a294461bd43c723639a84891a45f6a250fd3441ad2e8ed" +checksum = "8807c85d656b2b93927c19fe5a5f1f1f348f96c2de8b90763b3c2d561511f9b4" dependencies = [ "anyhow", "cargo_toml", @@ -4811,16 +4801,16 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d62a3c8790d6cba686cea6e3f7f569d12c662c3274c2d165a4fd33e3871b72" +checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" dependencies = [ "base64", "brotli", "ico", "json-patch", "plist", - "png 0.17.6", + "png", "proc-macro2", "quote", "regex", @@ -4831,15 +4821,15 @@ dependencies = [ "tauri-utils", "thiserror", "time 0.3.7", - "uuid 1.1.2", + "uuid 1.2.2", "walkdir", ] [[package]] name = "tauri-macros" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7296fa17996629f43081e1c66d554703900187ed900c5bf46f97f0bcfb069278" +checksum = "069319e5ecbe653a799b94b0690d9f9bf5d00f7b1d3989aa331c524d4e354075" dependencies = [ "heck 0.4.0", "proc-macro2", @@ -4851,29 +4841,29 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4cff3b4d9469727fa2107c4b3d2eda110df1ba45103fb420178e536362fae4" +checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b" dependencies = [ "gtk", "http", "http-range", - "infer", + "rand 0.8.5", "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror", - "uuid 1.1.2", + "uuid 1.2.2", "webview2-com", - "windows", + "windows 0.39.0", ] [[package]] name = "tauri-runtime-wry" -version = "0.10.2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa8c4edaf01d8b556e7172c844b1b4dd3399adcd1a606bd520fc3e65f698546" +checksum = "36b1c5764a41a13176a4599b5b7bd0881bea7d94dfe45e1e755f789b98317e30" dependencies = [ "cocoa", "gtk", @@ -4882,24 +4872,25 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.1.2", + "uuid 1.2.2", "webkit2gtk", "webview2-com", - "windows", + "windows 0.39.0", "wry", ] [[package]] name = "tauri-utils" -version = "1.0.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ff4b68d9faeb57c9c727bf58c9c9768d2b67d8e84e62ce6146e7859a2e9c6b" +checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1" dependencies = [ "brotli", "ctor", "glob", "heck 0.4.0", "html5ever", + "infer", "json-patch", "kuchiki", "memchr", @@ -4913,7 +4904,7 @@ dependencies = [ "thiserror", "url", "walkdir", - "windows", + "windows 0.39.0", ] [[package]] @@ -5046,12 +5037,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thin-slice" version = "0.1.1" @@ -5326,6 +5311,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + [[package]] name = "unicode-normalization" version = "0.1.9" @@ -5337,9 +5328,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-xid" @@ -5365,13 +5356,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", "serde", ] @@ -5390,27 +5380,13 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "getrandom 0.2.5", ] -[[package]] -name = "validator-api-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "coconut-interface", - "cosmrs", - "cosmwasm-std", - "getset", - "mixnet-contract-common", - "schemars", - "serde", -] - [[package]] name = "validator-client" version = "0.1.0" @@ -5435,6 +5411,7 @@ dependencies = [ "mixnet-contract-common", "multisig-contract-common", "network-defaults", + "nym-api-requests", "prost", "reqwest", "serde", @@ -5443,7 +5420,6 @@ dependencies = [ "thiserror", "tokio", "url", - "validator-api-requests", "vesting-contract", "vesting-contract-common", ] @@ -5497,7 +5473,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vesting-contract" -version = "1.1.0" +version = "1.1.2" dependencies = [ "contracts-common", "cosmwasm-std", @@ -5521,32 +5497,6 @@ dependencies = [ "serde", ] -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22025f6d8eb903ebf920ea6933b70b1e495be37e2cb4099e62c80454aaf57c39" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "walkdir" version = "2.3.2" @@ -5664,9 +5614,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29952969fb5e10fe834a52eb29ad0814ccdfd8387159b0933edf1344a1c9cdcc" +checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" dependencies = [ "bitflags", "cairo-rs", @@ -5730,13 +5680,13 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.16.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a489a9420acabb3c2ed0434b6f71f6b56b9485ec32665a28dec1ee186d716e0f" +checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.39.0", "windows-implement", ] @@ -5753,24 +5703,19 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.16.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0258c53ee9adc0a4f8ba1c8c317588f7a58c7048a55b621d469ba75ab3709ca1" +checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" dependencies = [ "regex", "serde", "serde_json", "thiserror", - "windows", + "windows 0.39.0", "windows-bindgen", + "windows-metadata", ] -[[package]] -name = "wildmatch" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c48bd20df7e4ced539c12f570f937c6b4884928a87fee70a479d72f031d4e0" - [[package]] name = "winapi" version = "0.3.9" @@ -5808,7 +5753,6 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" dependencies = [ - "windows-implement", "windows_aarch64_msvc 0.37.0", "windows_i686_gnu 0.37.0", "windows_i686_msvc 0.37.0", @@ -5817,10 +5761,24 @@ dependencies = [ ] [[package]] -name = "windows-bindgen" -version = "0.37.0" +name = "windows" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bed7be31ade0af08fec9b5343e9edcc005d22b1f11859b8a59b24797f5858e8" +checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +dependencies = [ + "windows-implement", + "windows_aarch64_msvc 0.39.0", + "windows_i686_gnu 0.39.0", + "windows_i686_msvc 0.39.0", + "windows_x86_64_gnu 0.39.0", + "windows_x86_64_msvc 0.39.0", +] + +[[package]] +name = "windows-bindgen" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" dependencies = [ "windows-metadata", "windows-tokens", @@ -5828,9 +5786,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14" +checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" dependencies = [ "syn", "windows-tokens", @@ -5838,9 +5796,9 @@ dependencies = [ [[package]] name = "windows-metadata" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f33f2b90a6664e369c41ab5ff262d06f048fc9685d9bf8a0e99a47750bb0463" +checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" [[package]] name = "windows-sys" @@ -5856,10 +5814,31 @@ dependencies = [ ] [[package]] -name = "windows-tokens" -version = "0.37.0" +name = "windows-sys" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows-tokens" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] name = "windows_aarch64_msvc" @@ -5873,6 +5852,18 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" +[[package]] +name = "windows_aarch64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -5885,6 +5876,18 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" +[[package]] +name = "windows_i686_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -5897,6 +5900,18 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" +[[package]] +name = "windows_i686_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -5909,6 +5924,24 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -5921,6 +5954,18 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "winreg" version = "0.7.0" @@ -5930,15 +5975,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - [[package]] name = "winres" version = "0.1.12" @@ -5950,19 +5986,23 @@ dependencies = [ [[package]] name = "wry" -version = "0.19.0" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce19dddbd3ce01dc8f14eb6d4c8f914123bf8379aaa838f6da4f981ff7104a3f" +checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" dependencies = [ + "base64", "block", "cocoa", "core-graphics", + "crossbeam-channel", + "dunce", "gdk", "gio", "glib", "gtk", + "html5ever", "http", - "jni 0.18.0", + "kuchiki", "libc", "log", "objc", @@ -5970,13 +6010,15 @@ dependencies = [ "once_cell", "serde", "serde_json", + "sha2 0.10.2", + "soup2", "tao", "thiserror", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.39.0", "windows-implement", ] @@ -6001,9 +6043,9 @@ dependencies = [ [[package]] name = "x11-dl" -version = "2.19.1" +version = "2.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +checksum = "b1536d6965a5d4e573c7ef73a2c15ebcd0b2de3347bdf526c34c297c00ac40f0" dependencies = [ "lazy_static", "libc", diff --git a/nym-wallet/nym-wallet-recovery-cli/Cargo.toml b/nym-wallet/nym-wallet-recovery-cli/Cargo.toml index 909467a6e0..3f2569adfd 100644 --- a/nym-wallet/nym-wallet-recovery-cli/Cargo.toml +++ b/nym-wallet/nym-wallet-recovery-cli/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0" argon2 = "0.4" base64 = "0.13" bip39 = "1.0" -clap = { version = "3.2", features = ["derive"] } +clap = { version = "4.0", features = ["derive"] } log = "0.4" pretty_env_logger = "0.4" serde_json = "1.0.0" diff --git a/nym-wallet/nym-wallet-recovery-cli/src/main.rs b/nym-wallet/nym-wallet-recovery-cli/src/main.rs index 7cb2c3e0d7..9798308fac 100644 --- a/nym-wallet/nym-wallet-recovery-cli/src/main.rs +++ b/nym-wallet/nym-wallet-recovery-cli/src/main.rs @@ -82,7 +82,7 @@ fn decrypt_file(file: File, passwords: &[String], parse: &ParseMode) -> Result<( match decrypt_login(login, passwords, parse) { Ok(is_success) if is_success => successes += 1, Ok(_) => println!("None of the provided passwords succeeded"), - Err(err) => println!("Failed: {}", err), + Err(err) => println!("Failed: {err}"), } } @@ -133,9 +133,9 @@ fn decrypt_login(login: &Value, passwords: &[String], parse: &ParseMode) -> Resu } return Ok(true); } - Err(err) => println!(" failed to parse\n{}", err), + Err(err) => println!(" failed to parse\n{err}"), }, - Err(err) => println!(" failed\n{}", err), + Err(err) => println!(" failed\n{err}"), } } @@ -160,10 +160,10 @@ fn get_login_entry(login: &Value) -> Result<(&str, &str, &str)> { fn base64_decode(ciphertext: &str, iv: &str, salt: &str) -> Result<(Vec, Vec, Vec)> { let ciphertext = base64::decode(ciphertext) - .map_err(|err| anyhow!("Unable to base64 decode ciphertext: {}", err))?; - let iv = base64::decode(iv).map_err(|err| anyhow!("Unable to base64 decode iv: {}", err))?; + .map_err(|err| anyhow!("Unable to base64 decode ciphertext: {err}"))?; + let iv = base64::decode(iv).map_err(|err| anyhow!("Unable to base64 decode iv: {err}"))?; let salt = - base64::decode(salt).map_err(|err| anyhow!("Unable to base64 decode salt: {}", err))?; + base64::decode(salt).map_err(|err| anyhow!("Unable to base64 decode salt: {err}"))?; Ok((ciphertext, iv, salt)) } @@ -186,7 +186,7 @@ fn decrypt_password( let mut key = Key::default(); argon2 .hash_password_into(password.as_bytes(), salt, &mut key) - .map_err(|err| anyhow!("Unable to hash password: {}", err))?; + .map_err(|err| anyhow!("Unable to hash password: {err}"))?; // Create the Cipher let cipher = Aes256Gcm::new(&key); diff --git a/nym-wallet/nym-wallet-types/Cargo.toml b/nym-wallet/nym-wallet-types/Cargo.toml index 322aca5c06..d2f0bde28a 100644 --- a/nym-wallet/nym-wallet-types/Cargo.toml +++ b/nym-wallet/nym-wallet-types/Cargo.toml @@ -18,7 +18,7 @@ config = { path = "../../common/config" } network-defaults = { path = "../../common/network-defaults" } mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nymd-client", + "nyxd-client", ] } vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } # Used for Type conversion, can be extracted but its a lot of work diff --git a/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts index 2ac6693e0b..c78eefd6ae 100644 --- a/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts +++ b/nym-wallet/nym-wallet-types/nym-wallet/src/types/rust/ValidatorUrls.ts @@ -1,2 +1,2 @@ -export interface Validator { nymd_url: string, nymd_name: string | null, api_url: string | null, } \ No newline at end of file +export interface Validator { nyxd_url: string, nyxd_name: string | null, api_url: string | null, } \ No newline at end of file diff --git a/nym-wallet/nym-wallet-types/src/network_config.rs b/nym-wallet/nym-wallet-types/src/network_config.rs index b1f987d5b0..c3ec62b265 100644 --- a/nym-wallet/nym-wallet-types/src/network_config.rs +++ b/nym-wallet/nym-wallet-types/src/network_config.rs @@ -32,19 +32,19 @@ pub struct ValidatorUrl { )] #[derive(Debug, Serialize, Deserialize)] pub struct Validator { - pub nymd_url: String, - pub nymd_name: Option, + pub nyxd_url: String, + pub nyxd_name: Option, pub api_url: Option, } impl fmt::Display for Validator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let nymd_url = format!("nymd_url: {}", self.nymd_url); + let nyxd_url = format!("nyxd_url: {}", self.nyxd_url); let api_url = self .api_url .as_ref() .map(|api_url| format!(", api_url: {}", api_url)) .unwrap_or_default(); - write!(f, "{nymd_url}{api_url}") + write!(f, "{nyxd_url}{api_url}") } } diff --git a/nym-wallet/package.json b/nym-wallet/package.json index a9a55d2d0b..21f885b496 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@nymproject/nym-wallet-app", - "version": "1.1.1", + "version": "1.1.5", "main": "index.js", "license": "MIT", "scripts": { @@ -32,7 +32,7 @@ "@nymproject/react": "^1.0.0", "@nymproject/types": "^1.0.0", "@storybook/react": "^6.5.8", - "@tauri-apps/api": "^1.0.2", + "@tauri-apps/api": "^1.2.0", "@tauri-apps/tauri-forage": "^1.0.0-beta.2", "big.js": "^6.2.1", "bs58": "^4.0.1", diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 835383188e..d554b65f83 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym_wallet" -version = "1.1.1" +version = "1.1.5" description = "Nym Native Wallet" authors = ["Nym Technologies SA"] license = "" @@ -13,10 +13,10 @@ rust-version = "1.58" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = { version = "=1.0.4", features = [] } +tauri-build = { version = "=1.2.1", features = [] } -tauri-codegen = "=1.0.4" -tauri-macros = "=1.0.4" +tauri-codegen = "=1.2.1" +tauri-macros = "=1.2.1" [dependencies] bip39 = "1.0" @@ -38,7 +38,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_repr = "0.1" strum = { version = "0.23", features = ["derive"] } -tauri = { version = "=1.0.5", features = ["clipboard-all", "shell-open", "updater", "window-maximize"] } +tauri = { version = "=1.2.2", features = ["clipboard-all", "shell-open", "updater", "window-maximize"] } tendermint-rpc = "0.23.0" thiserror = "1.0" tokio = { version = "1.10", features = ["full"] } @@ -55,7 +55,7 @@ cosmwasm-std = "1.0.0" cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nymd-client", + "nyxd-client", ] } mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } diff --git a/nym-wallet/src-tauri/src/coin.rs b/nym-wallet/src-tauri/src/coin.rs index 79b19171d9..4195a1736d 100644 --- a/nym-wallet/src-tauri/src/coin.rs +++ b/nym-wallet/src-tauri/src/coin.rs @@ -6,9 +6,9 @@ use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use std::str::FromStr; use strum::IntoEnumIterator; -use validator_client::nymd::CosmosCoin; -use validator_client::nymd::Denom as CosmosDenom; -use validator_client::nymd::{Coin as BackendCoin, CosmWasmCoin}; +use validator_client::nyxd::CosmosCoin; +use validator_client::nyxd::Denom as CosmosDenom; +use validator_client::nyxd::{Coin as BackendCoin, CosmWasmCoin}; const MINOR_IN_MAJOR: f64 = 1_000_000.; diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 9da3a1b73e..d30d6236bf 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,7 +11,7 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; use url::Url; -use validator_client::nymd::AccountId as CosmosAccountId; +use validator_client::nyxd::AccountId as CosmosAccountId; use config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; use nym_wallet_types::network::Network as WalletNetwork; @@ -20,7 +20,7 @@ use nym_wallet_types::network_config; use crate::error::BackendError; use crate::platform_constants::{CONFIG_DIR_NAME, CONFIG_FILENAME}; -pub const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str = +pub const REMOTE_SOURCE_OF_NYXD_URLS: &str = "https://nymtech.net/.wellknown/wallet/validators.json"; const CURRENT_GLOBAL_CONFIG_VERSION: u32 = 1; @@ -56,12 +56,12 @@ pub struct NetworkConfig { version: Option, // User selected urls - selected_nymd_url: Option, + selected_nyxd_url: Option, selected_api_url: Option, // Additional user provided validators. - // It is an option for the purpuse of file serialization. - validator_urls: Option>, + // It is an option for the purpose of file serialization. + nyxd_urls: Option>, } impl Default for Base { @@ -85,16 +85,16 @@ impl Default for NetworkConfig { fn default() -> Self { Self { version: Some(CURRENT_NETWORK_CONFIG_VERSION), - selected_nymd_url: None, + selected_nyxd_url: None, selected_api_url: None, - validator_urls: None, + nyxd_urls: None, } } } impl NetworkConfig { fn validators(&self) -> impl Iterator { - self.validator_urls.iter().flat_map(|v| v.iter()) + self.nyxd_urls.iter().flat_map(|v| v.iter()) } } @@ -167,7 +167,7 @@ impl Config { Some(global) } Err(err) => { - log::trace!("Not loading {:#?}: {}", file, err); + log::trace!("Not loading {:#?}: {err}", file); None } } @@ -182,7 +182,7 @@ impl Config { log::trace!("Loaded from file {:#?}", file); networks.insert(network.as_key(), config); } - Err(err) => log::trace!("Not loading {:#?}: {}", file, err), + Err(err) => log::trace!("Not loading {:#?}: {err}", file), }; } @@ -241,41 +241,41 @@ impl Config { .expect("Wrong format for bandwidth claim contract address") } - pub fn select_validator_nymd_url(&mut self, nymd_url: Url, network: WalletNetwork) { + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { - net.selected_nymd_url = Some(nymd_url); + net.selected_nyxd_url = Some(nyxd_url); } else { self.networks.insert( network.as_key(), NetworkConfig { - selected_nymd_url: Some(nymd_url), + selected_nyxd_url: Some(nyxd_url), ..NetworkConfig::default() }, ); } } - pub fn select_validator_api_url(&mut self, api_url: Url, network: WalletNetwork) { + pub fn select_nym_api_url(&mut self, api_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_api_url = Some(api_url); } else { self.networks.insert( network.as_key(), NetworkConfig { - selected_nymd_url: Some(api_url), + selected_nyxd_url: Some(api_url), ..NetworkConfig::default() }, ); } } - pub fn get_selected_validator_nymd_url(&self, network: WalletNetwork) -> Option { + pub fn get_selected_validator_nyxd_url(&self, network: WalletNetwork) -> Option { self.networks .get(&network.as_key()) - .and_then(|config| config.selected_nymd_url.clone()) + .and_then(|config| config.selected_nyxd_url.clone()) } - pub fn get_selected_validator_api_url(&self, network: &WalletNetwork) -> Option { + pub fn get_selected_nym_api_url(&self, network: &WalletNetwork) -> Option { self.networks .get(&network.as_key()) .and_then(|config| config.selected_api_url.clone()) @@ -283,16 +283,16 @@ impl Config { pub fn add_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) { if let Some(network_config) = self.networks.get_mut(&network.as_key()) { - if let Some(ref mut urls) = network_config.validator_urls { + if let Some(ref mut urls) = network_config.nyxd_urls { urls.push(url); } else { - network_config.validator_urls = Some(vec![url]); + network_config.nyxd_urls = Some(vec![url]); } } else { self.networks.insert( network.as_key(), NetworkConfig { - validator_urls: Some(vec![url]), + nyxd_urls: Some(vec![url]), ..NetworkConfig::default() }, ); @@ -301,7 +301,7 @@ impl Config { pub fn remove_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) { if let Some(network_config) = self.networks.get_mut(&network.as_key()) { - if let Some(ref mut urls) = network_config.validator_urls { + if let Some(ref mut urls) = network_config.nyxd_urls { // Removes duplicates too if there are any urls.retain(|existing_url| existing_url != &url); } @@ -321,8 +321,8 @@ where #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct ValidatorConfigEntry { - pub nymd_url: Url, - pub nymd_name: Option, + pub nyxd_url: Url, + pub nyxd_name: Option, pub api_url: Option, } @@ -331,8 +331,8 @@ impl TryFrom for ValidatorConfigEntry { fn try_from(validator: ValidatorDetails) -> Result { Ok(ValidatorConfigEntry { - nymd_url: validator.nymd_url.parse()?, - nymd_name: None, + nyxd_url: validator.nyxd_url.parse()?, + nyxd_name: None, api_url: match &validator.api_url { Some(url) => Some(url.parse()?), None => None, @@ -346,8 +346,8 @@ impl TryFrom for ValidatorConfigEntry { fn try_from(validator: network_config::Validator) -> Result { Ok(ValidatorConfigEntry { - nymd_url: validator.nymd_url.parse()?, - nymd_name: validator.nymd_name, + nyxd_url: validator.nyxd_url.parse()?, + nyxd_name: validator.nyxd_name, api_url: match &validator.api_url { Some(url) => Some(url.parse()?), None => None, @@ -358,8 +358,8 @@ impl TryFrom for ValidatorConfigEntry { impl fmt::Display for ValidatorConfigEntry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s1 = format!("nymd_url: {}", self.nymd_url); - let name = self.nymd_name.as_ref().map(|name| format!(" ({})", name)); + let s1 = format!("nyxd_url: {}", self.nyxd_url); + let name = self.nyxd_name.as_ref().map(|name| format!(" ({})", name)); let s2 = self .api_url .as_ref() @@ -510,23 +510,23 @@ mod tests { fn test_config() -> Config { let netconfig = NetworkConfig { - selected_nymd_url: None, + selected_nyxd_url: None, selected_api_url: Some("https://my_api_url.com".parse().unwrap()), - validator_urls: Some(vec![ + nyxd_urls: Some(vec![ ValidatorConfigEntry { - nymd_url: "https://foo".parse().unwrap(), - nymd_name: Some("FooName".to_string()), + nyxd_url: "https://foo".parse().unwrap(), + nyxd_name: Some("FooName".to_string()), api_url: None, }, ValidatorConfigEntry { - nymd_url: "https://bar".parse().unwrap(), - nymd_name: None, + nyxd_url: "https://bar".parse().unwrap(), + nyxd_name: None, api_url: Some("https://bar/api".parse().unwrap()), }, ValidatorConfigEntry { - nymd_url: "https://baz".parse().unwrap(), - nymd_name: None, + nyxd_url: "https://baz".parse().unwrap(), + nyxd_name: None, api_url: Some("https://baz/api".parse().unwrap()), }, ]), @@ -551,16 +551,16 @@ mod tests { r#"version = 1 selected_api_url = 'https://my_api_url.com/' -[[validator_urls]] -nymd_url = 'https://foo/' -nymd_name = 'FooName' +[[nyxd_urls]] +nyxd_url = 'https://foo/' +nyxd_name = 'FooName' -[[validator_urls]] -nymd_url = 'https://bar/' +[[nyxd_urls]] +nyxd_url = 'https://bar/' api_url = 'https://bar/api' -[[validator_urls]] -nymd_url = 'https://baz/' +[[nyxd_urls]] +nyxd_url = 'https://baz/' api_url = 'https://baz/api' "# ); @@ -575,22 +575,22 @@ api_url = 'https://baz/api' serde_json::to_string_pretty(netconfig).unwrap(), r#"{ "version": 1, - "selected_nymd_url": null, + "selected_nyxd_url": null, "selected_api_url": "https://my_api_url.com/", - "validator_urls": [ + "nyxd_urls": [ { - "nymd_url": "https://foo/", - "nymd_name": "FooName", + "nyxd_url": "https://foo/", + "nyxd_name": "FooName", "api_url": null }, { - "nymd_url": "https://bar/", - "nymd_name": null, + "nyxd_url": "https://bar/", + "nyxd_name": null, "api_url": "https://bar/api" }, { - "nymd_url": "https://baz/", - "nymd_name": null, + "nyxd_url": "https://baz/", + "nyxd_name": null, "api_url": "https://baz/api" } ] @@ -611,12 +611,12 @@ api_url = 'https://baz/api' fn get_urls_parsed_from_config() { let config = test_config(); - let nymd_url = config + let nyxd_url = config .get_configured_validators(WalletNetwork::MAINNET) .next() - .map(|v| v.nymd_url) + .map(|v| v.nyxd_url) .unwrap(); - assert_eq!(nymd_url.as_ref(), "https://foo/"); + assert_eq!(nyxd_url.as_ref(), "https://foo/"); // The first entry is missing an API URL let api_url = config @@ -630,12 +630,12 @@ api_url = 'https://baz/api' fn get_urls_from_defaults() { let config = Config::default(); - let nymd_url = config + let nyxd_url = config .get_base_validators(WalletNetwork::MAINNET) .next() - .map(|v| v.nymd_url) + .map(|v| v.nyxd_url) .unwrap(); - assert_eq!(nymd_url.as_ref(), "https://rpc.nymtech.net/"); + assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); let api_url = config .get_base_validators(WalletNetwork::MAINNET) diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 46d8840947..e47a0ab272 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -4,8 +4,8 @@ use serde::{Serialize, Serializer}; use std::io; use std::num::ParseIntError; use thiserror::Error; -use validator_client::validator_api::error::ValidatorAPIError; -use validator_client::{nymd::error::NymdError, ValidatorClientError}; +use validator_client::nym_api::error::NymAPIError; +use validator_client::{nyxd::error::NyxdError, ValidatorClientError}; #[derive(Error, Debug)] pub enum BackendError { @@ -24,10 +24,11 @@ pub enum BackendError { #[from] source: tendermint_rpc::Error, }, - #[error("{source}")] - NymdError { - #[from] - source: NymdError, + #[error("{pretty_error}")] + NyxdError { + pretty_error: String, + #[source] + source: NyxdError, }, #[error("{source}")] CosmwasmStd { @@ -40,9 +41,9 @@ pub enum BackendError { source: eyre::Report, }, #[error("{source}")] - ValidatorApiError { + NymApiError { #[from] - source: ValidatorAPIError, + source: NymAPIError, }, #[error("{source}")] KeyDerivationError { @@ -110,7 +111,7 @@ pub enum BackendError { WalletUnexpectedMnemonicAccount, #[error("Failed to derive address from mnemonic")] FailedToDeriveAddress, - #[error("{0}")] + #[error(transparent)] ValueParseError(#[from] ParseIntError), #[error("The provided coin has an unknown denomination - {0}")] UnknownCoinDenom(String), @@ -131,15 +132,41 @@ impl Serialize for BackendError { } } -impl From for BackendError { - fn from(e: ValidatorClientError) -> Self { - match e { - ValidatorClientError::ValidatorAPIError { source } => source.into(), - ValidatorClientError::MalformedUrlProvided(e) => e.into(), - ValidatorClientError::NymdError(e) => e.into(), - ValidatorClientError::NoAPIUrlAvailable => { - TypesError::NoValidatorApiUrlConfigured.into() +impl From for BackendError { + fn from(source: NyxdError) -> Self { + match source { + NyxdError::AbciError { + code: _, + log: _, + ref pretty_log, + } => { + if let Some(pretty_log) = pretty_log { + Self::NyxdError { + pretty_error: pretty_log.to_string(), + source, + } + } else { + Self::NyxdError { + pretty_error: source.to_string(), + source, + } + } } + nyxd_error => Self::NyxdError { + pretty_error: nyxd_error.to_string(), + source: nyxd_error, + }, + } + } +} + +impl From for BackendError { + fn from(e: ValidatorClientError) -> Self { + match e { + ValidatorClientError::NymAPIError { source } => source.into(), + ValidatorClientError::MalformedUrlProvided(e) => e.into(), + ValidatorClientError::NyxdError(e) => e.into(), + ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured.into(), } } } diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 9bd44028c2..cc82f9f7df 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -10,9 +10,9 @@ use mixnet_contract_common::{Gateway, MixNode}; use crate::menu::AddDefaultSubmenus; use crate::operations::help; use crate::operations::mixnet; +use crate::operations::nym_api; use crate::operations::signatures; use crate::operations::simulate; -use crate::operations::validator_api; use crate::operations::vesting; use crate::state::WalletState; @@ -55,6 +55,7 @@ fn main() { mixnet::admin::update_contract_settings, mixnet::bond::bond_gateway, mixnet::bond::bond_mixnode, + mixnet::bond::pledge_more, mixnet::bond::gateway_bond_details, mixnet::bond::get_pending_operator_rewards, mixnet::bond::mixnode_bond_details, @@ -72,20 +73,26 @@ fn main() { mixnet::delegate::get_all_mix_delegations, mixnet::delegate::undelegate_from_mixnode, mixnet::delegate::undelegate_all_from_mixnode, + mixnet::families::create_family, + mixnet::families::join_family, + mixnet::families::leave_family, + mixnet::families::kick_family_member, mixnet::interval::get_current_interval, mixnet::interval::get_pending_epoch_events, mixnet::interval::get_pending_interval_events, mixnet::rewards::claim_delegator_reward, mixnet::rewards::claim_operator_reward, mixnet::rewards::claim_locked_and_unlocked_delegator_reward, + mixnet::rewards::get_current_rewarding_parameters, mixnet::send::send, + mixnet::bond::get_mixnode_uptime, network_config::add_validator, - network_config::get_validator_api_urls, - network_config::get_validator_nymd_urls, + network_config::get_nym_api_urls, + network_config::get_nyxd_urls, network_config::remove_validator, - network_config::select_validator_api_url, - network_config::select_validator_nymd_url, - network_config::update_validator_urls, + network_config::select_nym_api_url, + network_config::select_nyxd_url, + network_config::update_nyxd_urls, state::load_config_from_files, state::save_config_to_files, utils::owns_gateway, @@ -94,17 +101,19 @@ fn main() { utils::get_old_and_incorrect_hardcoded_fee, utils::try_convert_pubkey_to_mix_id, utils::default_mixnode_cost_params, - validator_api::status::gateway_core_node_status, - validator_api::status::mixnode_core_node_status, - validator_api::status::mixnode_inclusion_probability, - validator_api::status::mixnode_reward_estimation, - validator_api::status::mixnode_stake_saturation, - validator_api::status::mixnode_status, - validator_api::status::gateway_report, + nym_api::status::compute_mixnode_reward_estimation, + nym_api::status::gateway_core_node_status, + nym_api::status::mixnode_core_node_status, + nym_api::status::mixnode_inclusion_probability, + nym_api::status::mixnode_reward_estimation, + nym_api::status::mixnode_stake_saturation, + nym_api::status::mixnode_status, + nym_api::status::gateway_report, vesting::rewards::vesting_claim_delegator_reward, vesting::rewards::vesting_claim_operator_reward, vesting::bond::vesting_bond_gateway, vesting::bond::vesting_bond_mixnode, + vesting::bond::vesting_pledge_more, vesting::bond::vesting_unbond_gateway, vesting::bond::vesting_unbond_mixnode, vesting::bond::vesting_update_mixnode_cost_params, @@ -130,6 +139,7 @@ fn main() { simulate::mixnet::simulate_bond_gateway, simulate::mixnet::simulate_unbond_gateway, simulate::mixnet::simulate_bond_mixnode, + simulate::mixnet::simulate_pledge_more, simulate::mixnet::simulate_unbond_mixnode, simulate::mixnet::simulate_update_mixnode_config, simulate::mixnet::simulate_update_mixnode_cost_params, @@ -140,6 +150,7 @@ fn main() { simulate::vesting::simulate_vesting_bond_gateway, simulate::vesting::simulate_vesting_unbond_gateway, simulate::vesting::simulate_vesting_bond_mixnode, + simulate::vesting::simulate_vesting_pledge_more, simulate::vesting::simulate_vesting_unbond_mixnode, simulate::vesting::simulate_vesting_update_mixnode_config, simulate::vesting::simulate_vesting_update_mixnode_cost_params, diff --git a/nym-wallet/src-tauri/src/network_config.rs b/nym-wallet/src-tauri/src/network_config.rs index 43e872b717..bcac19bb62 100644 --- a/nym-wallet/src-tauri/src/network_config.rs +++ b/nym-wallet/src-tauri/src/network_config.rs @@ -7,17 +7,17 @@ use nym_wallet_types::network::Network as WalletNetwork; use nym_wallet_types::network_config::{Validator, ValidatorUrl, ValidatorUrls}; #[tauri::command] -pub async fn get_validator_nymd_urls( +pub async fn get_nyxd_urls( network: WalletNetwork, state: tauri::State<'_, WalletState>, ) -> Result { let state = state.read().await; - let urls: Vec = state.get_nymd_urls(network).collect(); + let urls: Vec = state.get_nyxd_urls(network).collect(); Ok(ValidatorUrls { urls }) } #[tauri::command] -pub async fn get_validator_api_urls( +pub async fn get_nym_api_urls( network: WalletNetwork, state: tauri::State<'_, WalletState>, ) -> Result { @@ -27,27 +27,24 @@ pub async fn get_validator_api_urls( } #[tauri::command] -pub async fn select_validator_nymd_url( +pub async fn select_nyxd_url( url: &str, network: WalletNetwork, state: tauri::State<'_, WalletState>, ) -> Result<(), BackendError> { - log::debug!("Selecting new validator nymd_url for {network}: {url}"); - state - .write() - .await - .select_validator_nymd_url(url, network)?; + log::debug!("Selecting new nyxd url for {network}: {url}"); + state.write().await.select_nyxd_url(url, network)?; Ok(()) } #[tauri::command] -pub async fn select_validator_api_url( +pub async fn select_nym_api_url( url: &str, network: WalletNetwork, state: tauri::State<'_, WalletState>, ) -> Result<(), BackendError> { - log::debug!("Selecting new validator api_url for {network}: {url}"); - state.write().await.select_validator_api_url(url, network)?; + log::debug!("Selecting new nym api url for {network}: {url}"); + state.write().await.select_nym_api_url(url, network)?; Ok(()) } @@ -77,10 +74,8 @@ pub async fn remove_validator( // Update the list of validators by fecthing additional ones remotely. If it fails, just ignore. #[tauri::command] -pub async fn update_validator_urls( - state: tauri::State<'_, WalletState>, -) -> Result<(), BackendError> { +pub async fn update_nyxd_urls(state: tauri::State<'_, WalletState>) -> Result<(), BackendError> { let mut w_state = state.write().await; - let _r = w_state.fetch_updated_validator_urls().await; + let _r = w_state.fetch_updated_nyxd_urls().await; Ok(()) } diff --git a/nym-wallet/src-tauri/src/operations/help/log.rs b/nym-wallet/src-tauri/src/operations/help/log.rs index c8ff873f6a..c4c2357304 100644 --- a/nym-wallet/src-tauri/src/operations/help/log.rs +++ b/nym-wallet/src-tauri/src/operations/help/log.rs @@ -5,8 +5,8 @@ use tauri::Manager; pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> { if let Some(current_log_window) = app_handle.windows().get("log") { log::info!("Closing log window..."); - if let Err(e) = current_log_window.close() { - log::error!("Unable to close log window: {:?}", e); + if let Err(err) = current_log_window.close() { + log::error!("Unable to close log window: {err}"); } return Ok(()); } @@ -17,13 +17,13 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen .build() { Ok(window) => { - if let Err(e) = window.set_focus() { - log::error!("Unable to focus log window: {:?}", e); + if let Err(err) = window.set_focus() { + log::error!("Unable to focus log window: {err}"); } Ok(()) } - Err(e) => { - log::error!("Unable to create log window: {:?}", e); + Err(err) => { + log::error!("Unable to create log window: {err}"); Err(BackendError::NewWindowError) } } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 5811c007d1..d9f3f122d7 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -14,8 +14,8 @@ use std::collections::HashMap; use std::str::FromStr; use strum::IntoEnumIterator; use url::Url; -use validator_client::nymd::wallet::{AccountData, DirectSecp256k1HdWallet}; -use validator_client::{nymd::SigningNymdClient, Client}; +use validator_client::nyxd::wallet::{AccountData, DirectSecp256k1HdWallet}; +use validator_client::{nyxd::SigningNyxdClient, Client}; #[tauri::command] pub async fn connect_with_mnemonic( @@ -30,12 +30,12 @@ pub async fn connect_with_mnemonic( pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result { let guard = state.read().await; let client = guard.current_client()?; - let address = client.nymd.address(); + let address = client.nyxd.address(); let network = guard.current_network(); let base_mix_denom = network.base_mix_denom(); match client - .nymd + .nyxd .get_balance(address, base_mix_denom.to_string()) .await? { @@ -67,7 +67,7 @@ pub async fn switch_network( let client = r_state.client(network)?; let denom = network.mix_denom(); - Account::new(client.nymd.address().to_string(), denom) + Account::new(client.nyxd.address().to_string(), denom) }; let mut w_state = state.write().await; @@ -96,7 +96,7 @@ async fn _connect_with_mnemonic( w_state.load_config_files(); } - network_config::update_validator_urls(state.clone()).await?; + network_config::update_nyxd_urls(state.clone()).await?; let config = { let state = state.read().await; @@ -115,11 +115,11 @@ async fn _connect_with_mnemonic( }; // Get all the urls needed for the connection test - let (untested_nymd_urls, untested_api_urls) = { + let (untested_nyxd_urls, untested_api_urls) = { let state = state.read().await; - (state.get_all_nymd_urls(), state.get_all_api_urls()) + (state.get_all_nyxd_urls(), state.get_all_api_urls()) }; - let default_nymd_urls: HashMap = untested_nymd_urls + let default_nyxd_urls: HashMap = untested_nyxd_urls .iter() .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) .collect(); @@ -128,15 +128,15 @@ async fn _connect_with_mnemonic( .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) .collect(); - // Run connection tests on all nymd and validator-api endpoints - let (nymd_urls, api_urls) = - run_connection_test(untested_nymd_urls, untested_api_urls, &config).await; + // Run connection tests on all nyxd and nym-api endpoints + let (nyxd_urls, api_urls) = + run_connection_test(untested_nyxd_urls, untested_api_urls, &config).await; // Create clients for all networks let clients = create_clients( - &nymd_urls, + &nyxd_urls, &api_urls, - &default_nymd_urls, + &default_nyxd_urls, &default_api_urls, &config, &mnemonic, @@ -149,7 +149,7 @@ async fn _connect_with_mnemonic( .find(|(network, _)| *network == default_network); let account_for_default_network = match client_for_default_network { Some((_, client)) => Ok(Account::new( - client.nymd.address().to_string(), + client.nyxd.address().to_string(), default_network.mix_denom(), )), None => Err(BackendError::NetworkNotSupported), @@ -170,7 +170,7 @@ async fn _connect_with_mnemonic( } async fn run_connection_test( - untested_nymd_urls: HashMap>, + untested_nyxd_urls: HashMap>, untested_api_urls: HashMap>, config: &Config, ) -> ( @@ -181,7 +181,7 @@ async fn run_connection_test( .map(|network| (network.into(), config.get_mixnet_contract_address(network))) .collect::>(); - let untested_nymd_urls = untested_nymd_urls + let untested_nyxd_urls = untested_nyxd_urls .into_iter() .flat_map(|(net, urls)| urls.into_iter().map(move |url| (net.into(), url))); @@ -190,7 +190,7 @@ async fn run_connection_test( .flat_map(|(net, urls)| urls.into_iter().map(move |url| (net.into(), url))); validator_client::connection_tester::run_validator_connection_test( - untested_nymd_urls, + untested_nyxd_urls, untested_api_urls, mixnet_contract_address, ) @@ -198,31 +198,31 @@ async fn run_connection_test( } fn create_clients( - nymd_urls: &HashMap>, + nyxd_urls: &HashMap>, api_urls: &HashMap>, - default_nymd_urls: &HashMap, + default_nyxd_urls: &HashMap, default_api_urls: &HashMap, config: &Config, mnemonic: &Mnemonic, -) -> Result)>, BackendError> { +) -> Result)>, BackendError> { let mut clients = Vec::new(); for network in WalletNetwork::iter() { - let nymd_url = if let Some(url) = config.get_selected_validator_nymd_url(network) { - log::debug!("Using selected nymd_url for {network}: {url}"); + let nyxd_url = if let Some(url) = config.get_selected_validator_nyxd_url(network) { + log::debug!("Using selected nyxd_url for {network}: {url}"); url.clone() } else { - let default_nymd_url = default_nymd_urls + let default_nyxd_url = default_nyxd_urls .get(&network) - .expect("Expected at least one nymd_url"); - select_random_responding_url(nymd_urls, network).unwrap_or_else(|| { + .expect("Expected at least one nyxd_url"); + select_random_responding_url(nyxd_urls, network).unwrap_or_else(|| { log::debug!( - "No successful nymd_urls for {network}: using default: {default_nymd_url}" + "No successful nyxd_urls for {network}: using default: {default_nyxd_url}" ); - default_nymd_url.clone() + default_nyxd_url.clone() }) }; - let api_url = if let Some(url) = config.get_selected_validator_api_url(&network) { + let api_url = if let Some(url) = config.get_selected_nym_api_url(&network) { log::debug!("Using selected api_url for {network}: {url}"); url.clone() } else { @@ -235,7 +235,7 @@ fn create_clients( }) }; - log::info!("Connecting to: nymd_url: {nymd_url} for {network}"); + log::info!("Connecting to: nyxd_url: {nyxd_url} for {network}"); log::info!("Connecting to: api_url: {api_url} for {network}"); let network_details = NymNetworkDetails::from(network) @@ -249,10 +249,10 @@ fn create_clients( )); let config = validator_client::Config::try_from_nym_network_details(&network_details)? - .with_urls(nymd_url, api_url); + .with_urls(nyxd_url, api_url); let mut client = validator_client::Client::new_signing(config, mnemonic.clone())?; - client.set_nymd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); + client.set_nyxd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); clients.push((network, client)); } Ok(clients) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs index 5074c5500e..3633c3ac5d 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs @@ -6,8 +6,8 @@ use crate::state::WalletState; use mixnet_contract_common::ContractStateParams; use nym_types::transaction::TransactionExecuteResult; use nym_wallet_types::admin::TauriContractStateParams; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; -use validator_client::nymd::Fee; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::Fee; #[tauri::command] pub async fn get_contract_settings( @@ -17,7 +17,7 @@ pub async fn get_contract_settings( let guard = state.read().await; let reg = guard.registered_coins()?; - let client = &guard.current_client()?.nymd; + let client = &guard.current_client()?.nyxd; let res = client.get_mixnet_contract_settings().await?; let converted = TauriContractStateParams::from_mixnet_contract_contract_state_params(res, reg)?; @@ -33,7 +33,7 @@ pub async fn update_contract_settings( ) -> Result { let guard = state.read().await; let reg = guard.registered_coins()?; - let client = &guard.current_client()?.nymd; + let client = &guard.current_client()?.nyxd; let fee_amount = guard.convert_tx_fee(fee.as_ref()); let mixnet_contract_settings_params: ContractStateParams = diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index 7098577545..c33c4c5190 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -3,7 +3,7 @@ use crate::error::BackendError; use crate::state::WalletState; -use crate::{nymd_client, Gateway, MixNode}; +use crate::{nyxd_client, Gateway, MixNode}; use mixnet_contract_common::{MixId, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::gateway::GatewayBond; @@ -11,8 +11,8 @@ use nym_types::mixnode::{MixNodeCostParams, MixNodeDetails}; use nym_types::transaction::TransactionExecuteResult; use serde::{Deserialize, Serialize}; use std::time::Duration; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; -use validator_client::nymd::Fee; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::Fee; #[derive(Debug, Serialize, Deserialize)] pub struct NodeDescription { @@ -43,7 +43,7 @@ pub async fn bond_gateway( ); let res = guard .current_client()? - .nymd + .nyxd .bond_gateway(gateway, owner_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -61,7 +61,7 @@ pub async fn unbond_gateway( let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!(">>> Unbond gateway, fee = {:?}", fee); - let res = guard.current_client()?.nymd.unbond_gateway(fee).await?; + let res = guard.current_client()?.nyxd.unbond_gateway(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); log::trace!("<<< {:?}", res); Ok(TransactionExecuteResult::from_execute_result( @@ -92,7 +92,7 @@ pub async fn bond_mixnode( ); let res = guard .current_client()? - .nymd + .nyxd .bond_mixnode(mixnode, cost_params, owner_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -102,6 +102,33 @@ pub async fn bond_mixnode( )?) } +#[tauri::command] +pub async fn pledge_more( + fee: Option, + additional_pledge: DecCoin, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let additional_pledge_base = guard.attempt_convert_to_base_coin(additional_pledge.clone())?; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + log::info!( + ">>> Pledge more, additional_pledge_display = {}, additional_pledge_base = {}, fee = {:?}", + additional_pledge, + additional_pledge_base, + fee, + ); + let res = guard + .current_client()? + .nyxd + .pledge_more(additional_pledge_base, fee) + .await?; + log::info!("<<< tx hash = {}", res.transaction_hash); + log::trace!("<<< {:?}", res); + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} + #[tauri::command] pub async fn unbond_mixnode( fee: Option, @@ -110,7 +137,7 @@ pub async fn unbond_mixnode( let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!(">>> Unbond mixnode, fee = {:?}", fee); - let res = guard.current_client()?.nymd.unbond_mixnode(fee).await?; + let res = guard.current_client()?.nyxd.unbond_mixnode(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); log::trace!("<<< {:?}", res); Ok(TransactionExecuteResult::from_execute_result( @@ -135,7 +162,7 @@ pub async fn update_mixnode_cost_params( ); let res = guard .current_client()? - .nymd + .nyxd .update_mixnode_cost_params(cost_params, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -160,7 +187,7 @@ pub async fn update_mixnode_config( ); let res = guard .current_client()? - .nymd + .nyxd .update_mixnode_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -177,14 +204,14 @@ pub async fn get_mixnode_avg_uptime( log::info!(">>> Get mixnode bond details"); let guard = state.read().await; let client = guard.current_client()?; - let res = client.nymd.get_owned_mixnode(client.nymd.address()).await?; + let res = client.nyxd.get_owned_mixnode(client.nyxd.address()).await?; match res.mixnode_details { Some(details) => { let id = details.mix_id(); log::trace!(" >>> Get average uptime percentage: mix_id = {}", id); let avg_uptime_percent = client - .validator_api + .nym_api .get_mixnode_avg_uptime(id) .await .ok() @@ -203,7 +230,7 @@ pub async fn mixnode_bond_details( log::info!(">>> Get mixnode bond details"); let guard = state.read().await; let client = guard.current_client()?; - let res = client.nymd.get_owned_mixnode(client.nymd.address()).await?; + let res = client.nyxd.get_owned_mixnode(client.nyxd.address()).await?; let details = res .mixnode_details .map(|details| { @@ -231,7 +258,7 @@ pub async fn gateway_bond_details( log::info!(">>> Get gateway bond details"); let guard = state.read().await; let client = guard.current_client()?; - let bond = client.nymd.get_owned_gateway(client.nymd.address()).await?; + let bond = client.nyxd.get_owned_gateway(client.nyxd.address()).await?; let res = bond .gateway .map(|bond| { @@ -259,7 +286,7 @@ pub async fn get_pending_operator_rewards( let guard = state.read().await; let res = guard .current_client()? - .nymd + .nyxd .get_pending_operator_reward(&address.parse()?) .await?; @@ -292,7 +319,7 @@ pub async fn get_number_of_mixnode_delegators( mix_id: MixId, state: tauri::State<'_, WalletState>, ) -> Result { - Ok(nymd_client!(state) + Ok(nyxd_client!(state) .get_mixnode_details(mix_id) .await? .mixnode_details @@ -314,3 +341,18 @@ pub async fn get_mix_node_description( .json() .await?) } + +#[tauri::command] +pub async fn get_mixnode_uptime( + mix_id: MixId, + state: tauri::State<'_, WalletState>, +) -> Result { + log::info!(">>> Get mixnode uptime"); + + let guard = state.read().await; + let client = guard.current_client()?; + let uptime = client.nym_api.get_mixnode_avg_uptime(mix_id).await?; + + log::info!(">>> Uptime response: {}", uptime.avg_uptime); + Ok(uptime.avg_uptime) +} diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index dfbf46389b..cf26c1f527 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -13,8 +13,8 @@ use nym_types::deprecated::{ use nym_types::mixnode::MixNodeCostParams; use nym_types::pending_events::PendingEpochEvent; use nym_types::transaction::TransactionExecuteResult; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; -use validator_client::nymd::Fee; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::Fee; #[tauri::command] pub async fn get_pending_delegation_events( @@ -25,7 +25,7 @@ pub async fn get_pending_delegation_events( let reg = guard.registered_coins()?; let client = guard.current_client()?; - let events = client.get_all_nymd_pending_epoch_events().await?; + let events = client.get_all_nyxd_pending_epoch_events().await?; let converted = events .into_iter() .map(|e| PendingEpochEvent::try_from_mixnet_contract(e, reg)) @@ -36,9 +36,9 @@ pub async fn get_pending_delegation_events( // we only care about events concerning THIS client let mut client_specific_events = Vec::new(); for delegation_event in delegation_events { - if delegation_event.address_matches(client.nymd.address().as_ref()) { + if delegation_event.address_matches(client.nyxd.address().as_ref()) { let node_identity = client - .nymd + .nyxd .get_mixnode_details(delegation_event.mix_id) .await? .mixnode_details @@ -82,7 +82,7 @@ pub async fn delegate_to_mixnode( fee, ); let res = client - .nymd + .nyxd .delegate_to_mixnode(mix_id, delegation_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -108,7 +108,7 @@ pub async fn undelegate_from_mixnode( ); let res = guard .current_client()? - .nymd + .nyxd .undelegate_from_mixnode(mix_id, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -153,10 +153,10 @@ pub async fn get_all_mix_delegations( let client = guard.current_client()?; let reg = guard.registered_coins()?; - let address = client.nymd.address(); + let address = client.nyxd.address(); let network = guard.current_network(); let base_mix_denom = network.base_mix_denom().to_string(); - let vesting_contract = client.nymd.vesting_contract_address(); + let vesting_contract = client.nyxd.vesting_contract_address(); log::info!(" >>> Get delegations"); let delegations = client.get_all_delegator_delegations(address).await?; @@ -213,7 +213,7 @@ pub async fn get_all_mix_delegations( log::trace!(" >>> Get accumulated rewards: address = {}", address); let pending_reward = client - .nymd + .nyxd .get_pending_delegator_reward(address, d.mix_id, d.proxy.clone()) .await?; @@ -230,7 +230,7 @@ pub async fn get_all_mix_delegations( }; log::trace!(" >>> Get stake saturation: mix_id = {}", d.mix_id); - let stake_saturation = client.nymd.get_mixnode_stake_saturation(d.mix_id).await?; + let stake_saturation = client.nyxd.get_mixnode_stake_saturation(d.mix_id).await?; log::trace!(" <<< {:?}", stake_saturation); log::trace!( @@ -238,7 +238,7 @@ pub async fn get_all_mix_delegations( d.mix_id ); let avg_uptime_percent = client - .validator_api + .nym_api .get_mixnode_avg_uptime(d.mix_id) .await .ok() @@ -250,7 +250,7 @@ pub async fn get_all_mix_delegations( d.height ); let timestamp = client - .nymd + .nyxd .get_block_timestamp(Some(d.height as u32)) .await?; let delegated_on_iso_datetime = timestamp.to_rfc3339(); @@ -325,7 +325,7 @@ pub async fn get_pending_delegator_rewards( let guard = state.read().await; let res = guard .current_client()? - .nymd + .nyxd .get_pending_delegator_reward(&address.parse()?, mix_id, proxy) .await?; diff --git a/nym-wallet/src-tauri/src/operations/mixnet/families.rs b/nym-wallet/src-tauri/src/operations/mixnet/families.rs new file mode 100644 index 0000000000..aba66c9a58 --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/mixnet/families.rs @@ -0,0 +1,81 @@ +use crate::error::BackendError; +use crate::state::WalletState; +use nym_types::transaction::TransactionExecuteResult; +use validator_client::nyxd::traits::MixnetSigningClient; +use validator_client::nyxd::Fee; + +#[tauri::command] +pub async fn create_family( + signature: String, + label: String, + fee: Option, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + let res = guard + .current_client()? + .nyxd + .create_family(signature, label, fee) + .await?; + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} + +#[tauri::command] +pub async fn join_family( + signature: String, + family_head: String, + fee: Option, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + let res = guard + .current_client()? + .nyxd + .join_family(signature, family_head, fee) + .await?; + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} + +#[tauri::command] +pub async fn leave_family( + signature: String, + family_head: String, + fee: Option, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + let res = guard + .current_client()? + .nyxd + .leave_family(signature, family_head, fee) + .await?; + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} + +#[tauri::command] +pub async fn kick_family_member( + signature: String, + member: String, + fee: Option, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + let res = guard + .current_client()? + .nyxd + .kick_family_member(signature, member, fee) + .await?; + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} diff --git a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs index ce88d840b4..996f9bbf69 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs @@ -2,18 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BackendError; -use crate::nymd_client; +use crate::nyxd_client; use crate::state::WalletState; use nym_types::pending_events::{PendingEpochEvent, PendingIntervalEvent}; use nym_wallet_types::interval::Interval; -use validator_client::nymd::traits::MixnetQueryClient; +use validator_client::nyxd::traits::MixnetQueryClient; #[tauri::command] pub async fn get_current_interval( state: tauri::State<'_, WalletState>, ) -> Result { log::info!(">>> Get current interval"); - let res = nymd_client!(state).get_current_interval_details().await?; + let res = nyxd_client!(state).get_current_interval_details().await?; log::info!("<<< current interval = {:?}", res); Ok(res.interval.into()) } @@ -26,7 +26,7 @@ pub async fn get_pending_epoch_events( let guard = state.read().await; let reg = guard.registered_coins()?; let client = guard.current_client()?; - let res = client.get_all_nymd_pending_epoch_events().await?; + let res = client.get_all_nyxd_pending_epoch_events().await?; log::info!("<<< got = {:?} events", res.len()); @@ -46,7 +46,7 @@ pub async fn get_pending_interval_events( let guard = state.read().await; let reg = guard.registered_coins()?; let client = guard.current_client()?; - let res = client.get_all_nymd_pending_interval_events().await?; + let res = client.get_all_nyxd_pending_interval_events().await?; log::info!("<<< got = {:?} events", res.len()); diff --git a/nym-wallet/src-tauri/src/operations/mixnet/mod.rs b/nym-wallet/src-tauri/src/operations/mixnet/mod.rs index cb94278ee2..8b19a26dd0 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/mod.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/mod.rs @@ -2,6 +2,7 @@ pub mod account; pub mod admin; pub mod bond; pub mod delegate; +pub mod families; pub mod interval; pub mod rewards; pub mod send; diff --git a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs index 6fb8893ee9..0a6fd3ae7f 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs @@ -1,10 +1,10 @@ use crate::error::BackendError; use crate::state::WalletState; use crate::vesting::rewards::vesting_claim_delegator_reward; -use mixnet_contract_common::MixId; +use mixnet_contract_common::{MixId, RewardingParams}; use nym_types::transaction::TransactionExecuteResult; -use validator_client::nymd::traits::{MixnetQueryClient, MixnetSigningClient}; -use validator_client::nymd::Fee; +use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use validator_client::nyxd::Fee; #[tauri::command] pub async fn claim_operator_reward( @@ -17,7 +17,7 @@ pub async fn claim_operator_reward( let fee_amount = guard.convert_tx_fee(fee.as_ref()); let res = guard .current_client()? - .nymd + .nyxd .withdraw_operator_reward(fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -38,7 +38,7 @@ pub async fn claim_delegator_reward( let fee_amount = guard.convert_tx_fee(fee.as_ref()); let res = guard .current_client()? - .nymd + .nyxd .withdraw_delegator_reward(mix_id, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -63,11 +63,11 @@ pub async fn claim_locked_and_unlocked_delegator_reward( let client = guard.current_client()?; log::trace!(">>> Get delegations: mix_id = {}", mix_id); - let address = client.nymd.address(); + let address = client.nyxd.address(); let delegations = client.get_all_delegator_delegations(address).await?; log::trace!("<<< {} delegations", delegations.len()); - let vesting_contract = client.nymd.vesting_contract_address().to_string(); + let vesting_contract = client.nyxd.vesting_contract_address().to_string(); let liquid_delegation = client.get_delegation_details(mix_id, address, None).await?; let vesting_delegation = client .get_delegation_details(mix_id, address, Some(vesting_contract)) @@ -95,3 +95,17 @@ pub async fn claim_locked_and_unlocked_delegator_reward( log::trace!("<<< {:?}", res); Ok(res) } + +#[tauri::command] +pub async fn get_current_rewarding_parameters( + state: tauri::State<'_, WalletState>, +) -> Result { + log::info!(">>> Get current rewarding params",); + + let guard = state.read().await; + let client = guard.current_client()?; + + let reward_params = client.nyxd.get_rewarding_parameters().await?; + + Ok(reward_params) +} diff --git a/nym-wallet/src-tauri/src/operations/mixnet/send.rs b/nym-wallet/src-tauri/src/operations/mixnet/send.rs index 0e4cbdd6a3..b57cf0a267 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/send.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/send.rs @@ -3,7 +3,7 @@ use crate::state::WalletState; use nym_types::currency::DecCoin; use nym_types::transaction::{SendTxResult, TransactionDetails}; use std::str::FromStr; -use validator_client::nymd::{AccountId, Fee}; +use validator_client::nyxd::{AccountId, Fee}; #[tauri::command] pub async fn send( @@ -17,7 +17,7 @@ pub async fn send( let amount_base = guard.attempt_convert_to_base_coin(amount.clone())?; let to_address = AccountId::from_str(address)?; - let from_address = guard.current_client()?.nymd.address().to_string(); + let from_address = guard.current_client()?.nyxd.address().to_string(); let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( ">>> Send: display_amount = {}, base_amount = {}, from = {}, to = {}, fee = {:?}", @@ -29,7 +29,7 @@ pub async fn send( ); let raw_res = guard .current_client()? - .nymd + .nyxd .send(&to_address, vec![amount_base], memo, fee) .await?; log::info!("<<< tx hash = {}", raw_res.hash.to_string()); diff --git a/nym-wallet/src-tauri/src/operations/mod.rs b/nym-wallet/src-tauri/src/operations/mod.rs index 457971d8c8..c6c175ee12 100644 --- a/nym-wallet/src-tauri/src/operations/mod.rs +++ b/nym-wallet/src-tauri/src/operations/mod.rs @@ -1,6 +1,6 @@ pub mod help; pub mod mixnet; +pub mod nym_api; pub mod signatures; pub mod simulate; -pub mod validator_api; pub mod vesting; diff --git a/nym-wallet/src-tauri/src/operations/validator_api/mod.rs b/nym-wallet/src-tauri/src/operations/nym_api/mod.rs similarity index 100% rename from nym-wallet/src-tauri/src/operations/validator_api/mod.rs rename to nym-wallet/src-tauri/src/operations/nym_api/mod.rs diff --git a/nym-wallet/src-tauri/src/operations/validator_api/status.rs b/nym-wallet/src-tauri/src/operations/nym_api/status.rs similarity index 67% rename from nym-wallet/src-tauri/src/operations/validator_api/status.rs rename to nym-wallet/src-tauri/src/operations/nym_api/status.rs index 16c83cfb67..873a1fa0a4 100644 --- a/nym-wallet/src-tauri/src/operations/validator_api/status.rs +++ b/nym-wallet/src-tauri/src/operations/nym_api/status.rs @@ -4,11 +4,11 @@ use crate::api_client; use crate::error::BackendError; use crate::state::WalletState; -use mixnet_contract_common::{IdentityKeyRef, MixId}; +use mixnet_contract_common::{reward_params::Performance, Coin, IdentityKeyRef, MixId, Percent}; use validator_client::models::{ - GatewayCoreStatusResponse, GatewayStatusReportResponse, InclusionProbabilityResponse, - MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, - StakeSaturationResponse, + ComputeRewardEstParam, GatewayCoreStatusResponse, GatewayStatusReportResponse, + InclusionProbabilityResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, + RewardEstimationResponse, StakeSaturationResponse, }; #[tauri::command] @@ -59,6 +59,29 @@ pub async fn mixnode_reward_estimation( .await?) } +#[tauri::command] +pub async fn compute_mixnode_reward_estimation( + mix_id: u32, + performance: Option, + pledge_amount: Option, + total_delegation: Option, + interval_operating_cost: Option, + profit_margin_percent: Option, + state: tauri::State<'_, WalletState>, +) -> Result { + let request_body = ComputeRewardEstParam { + performance, + active_in_rewarded_set: Some(true), + pledge_amount, + total_delegation, + interval_operating_cost, + profit_margin_percent, + }; + Ok(api_client!(state) + .compute_mixnode_reward_estimation(mix_id, &request_body) + .await?) +} + #[tauri::command] pub async fn mixnode_stake_saturation( mix_id: MixId, diff --git a/nym-wallet/src-tauri/src/operations/signatures/sign.rs b/nym-wallet/src-tauri/src/operations/signatures/sign.rs index 4ebca7aa56..6a0b05a1e4 100644 --- a/nym-wallet/src-tauri/src/operations/signatures/sign.rs +++ b/nym-wallet/src-tauri/src/operations/signatures/sign.rs @@ -13,7 +13,7 @@ use serde_json::json; pub struct SignatureOutputJson { pub account_id: String, pub public_key: PublicKey, - pub signature: String, + pub signature_as_hex: String, } #[tauri::command] @@ -23,7 +23,7 @@ pub async fn sign( ) -> Result { let guard = state.read().await; let client = guard.current_client()?; - let wallet = client.nymd.signer(); + let wallet = client.nyxd.signer(); let derived_accounts = wallet.try_derive_accounts()?; let account = derived_accounts.first().ok_or_else(|| { log::error!(">>> Unable to derive account"); @@ -32,14 +32,14 @@ pub async fn sign( log::info!("<<< Signing message"); let signature = wallet.sign_raw_with_account(account, message.as_bytes())?; - let signature_as_hex_string = signature.to_string(); let output = SignatureOutputJson { account_id: account.address().to_string(), public_key: account.public_key(), - signature: signature_as_hex_string.to_string(), + signature_as_hex: signature.to_string(), }; - log::info!(">>> Signing data {}", json!(output),); - Ok(signature_as_hex_string) + let output_json = json!(output).to_string(); + log::info!(">>> Signing data {}", output_json); + Ok(output_json) } async fn get_pubkey_from_account_address( @@ -50,7 +50,7 @@ async fn get_pubkey_from_account_address( let guard = state.read().await; let client = guard.current_client()?; let account = client - .nymd + .nyxd .get_account_details(address) .await? .ok_or_else(|| { @@ -116,7 +116,7 @@ pub async fn verify( // get public key from current account address let guard = state.read().await; let client = guard.current_client()?; - let address = client.nymd.address(); + let address = client.nyxd.address(); get_pubkey_from_account_address(address, &state).await? } }; diff --git a/nym-wallet/src-tauri/src/operations/simulate/admin.rs b/nym-wallet/src-tauri/src/operations/simulate/admin.rs index 29636bebf2..5ca826bb90 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/admin.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/admin.rs @@ -18,9 +18,9 @@ pub async fn simulate_update_contract_settings( params.try_convert_to_mixnet_contract_params(reg)?; let client = guard.current_client()?; - let mixnet_contract = client.nymd.mixnet_contract_address(); + let mixnet_contract = client.nyxd.mixnet_contract_address(); - let msg = client.nymd.wrap_contract_execute_message( + let msg = client.nyxd.wrap_contract_execute_message( mixnet_contract, &ExecuteMsg::UpdateContractStateParams { updated_parameters: mixnet_contract_settings_params, @@ -28,6 +28,6 @@ pub async fn simulate_update_contract_settings( vec![], )?; - let result = client.nymd.simulate(vec![msg]).await?; + let result = client.nyxd.simulate(vec![msg]).await?; guard.create_detailed_fee(result) } diff --git a/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs b/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs index 2796e02097..3679a1d863 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/cosmos.rs @@ -6,7 +6,7 @@ use crate::operations::simulate::FeeDetails; use crate::state::WalletState; use nym_types::currency::DecCoin; use std::str::FromStr; -use validator_client::nymd::{AccountId, MsgSend}; +use validator_client::nyxd::{AccountId, MsgSend}; #[tauri::command] pub async fn simulate_send( @@ -21,7 +21,7 @@ pub async fn simulate_send( let amount = vec![amount_base.into()]; let client = guard.current_client()?; - let from_address = client.nymd.address().clone(); + let from_address = client.nyxd.address().clone(); // TODO: I'm still not 100% convinced whether this should be exposed here or handled somewhere else in the client code let msg = MsgSend { @@ -30,6 +30,6 @@ pub async fn simulate_send( amount, }; - let result = client.nymd.simulate(vec![msg]).await?; + let result = client.nyxd.simulate(vec![msg]).await?; guard.create_detailed_fee(result) } diff --git a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs index 51912e1332..7adc677c74 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs @@ -25,13 +25,13 @@ async fn simulate_mixnet_operation( }; let client = guard.current_client()?; - let mixnet_contract = client.nymd.mixnet_contract_address(); + let mixnet_contract = client.nyxd.mixnet_contract_address(); let msg = client - .nymd + .nyxd .wrap_contract_execute_message(mixnet_contract, &msg, funds)?; - let result = client.nymd.simulate(vec![msg]).await?; + let result = client.nyxd.simulate(vec![msg]).await?; guard.create_detailed_fee(result) } @@ -84,6 +84,14 @@ pub async fn simulate_bond_mixnode( .await } +#[tauri::command] +pub async fn simulate_pledge_more( + additional_pledge: DecCoin, + state: tauri::State<'_, WalletState>, +) -> Result { + simulate_mixnet_operation(ExecuteMsg::PledgeMore {}, Some(additional_pledge), &state).await +} + #[tauri::command] pub async fn simulate_unbond_mixnode( state: tauri::State<'_, WalletState>, diff --git a/nym-wallet/src-tauri/src/operations/simulate/mod.rs b/nym-wallet/src-tauri/src/operations/simulate/mod.rs index 256f6f9a84..384b8b9085 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mod.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mod.rs @@ -4,8 +4,8 @@ use cosmrs::tx; use cosmrs::tx::Gas; use nym_types::fees::FeeDetails; -use validator_client::nymd::cosmwasm_client::types::GasInfo; -use validator_client::nymd::{CosmosCoin, Fee, GasAdjustable, GasAdjustment, GasPrice}; +use validator_client::nyxd::cosmwasm_client::types::GasInfo; +use validator_client::nyxd::{CosmosCoin, Fee, GasAdjustable, GasAdjustment, GasPrice}; pub mod admin; pub mod cosmos; diff --git a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs index c7cebf2ce5..aff0d9b4c5 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs @@ -26,13 +26,13 @@ async fn simulate_vesting_operation( }; let client = guard.current_client()?; - let vesting_contract = client.nymd.vesting_contract_address(); + let vesting_contract = client.nyxd.vesting_contract_address(); let msg = client - .nymd + .nyxd .wrap_contract_execute_message(vesting_contract, &msg, funds)?; - let result = client.nymd.simulate(vec![msg]).await?; + let result = client.nyxd.simulate(vec![msg]).await?; guard.create_detailed_fee(result) } @@ -91,6 +91,19 @@ pub async fn simulate_vesting_bond_mixnode( .await } +#[tauri::command] +pub async fn simulate_vesting_pledge_more( + additional_pledge: DecCoin, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let amount = guard + .attempt_convert_to_base_coin(additional_pledge)? + .into(); + + simulate_vesting_operation(ExecuteMsg::PledgeMore { amount }, None, &state).await +} + #[tauri::command] pub async fn simulate_vesting_unbond_mixnode( state: tauri::State<'_, WalletState>, diff --git a/nym-wallet/src-tauri/src/operations/vesting/bond.rs b/nym-wallet/src-tauri/src/operations/vesting/bond.rs index 99f0086d9c..cb36653b47 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/bond.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/bond.rs @@ -1,5 +1,5 @@ use crate::error::BackendError; -use crate::nymd_client; +use crate::nyxd_client; use crate::state::WalletState; use crate::{Gateway, MixNode}; use mixnet_contract_common::MixNodeConfigUpdate; @@ -7,7 +7,7 @@ use mixnet_contract_common::MixNodeConfigUpdate; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_types::transaction::TransactionExecuteResult; -use validator_client::nymd::{Fee, VestingSigningClient}; +use validator_client::nyxd::{Fee, VestingSigningClient}; #[tauri::command] pub async fn vesting_bond_gateway( @@ -30,7 +30,7 @@ pub async fn vesting_bond_gateway( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_bond_gateway(gateway, &owner_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -51,7 +51,7 @@ pub async fn vesting_unbond_gateway( ">>> Unbond gateway bonded with locked tokens, fee = {:?}", fee ); - let res = nymd_client!(state).vesting_unbond_gateway(fee).await?; + let res = nyxd_client!(state).vesting_unbond_gateway(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); log::trace!("<<< {:?}", res); Ok(TransactionExecuteResult::from_execute_result( @@ -83,7 +83,7 @@ pub async fn vesting_bond_mixnode( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_bond_mixnode(mixnode, cost_params, &owner_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -93,6 +93,33 @@ pub async fn vesting_bond_mixnode( )?) } +#[tauri::command] +pub async fn vesting_pledge_more( + fee: Option, + additional_pledge: DecCoin, + state: tauri::State<'_, WalletState>, +) -> Result { + let guard = state.read().await; + let additional_pledge_base = guard.attempt_convert_to_base_coin(additional_pledge.clone())?; + let fee_amount = guard.convert_tx_fee(fee.as_ref()); + log::info!( + ">>> Pledge more with locked tokens, additional_pledge_display = {}, additional_pledge_base = {}, fee = {:?}", + additional_pledge, + additional_pledge_base, + fee, + ); + let res = guard + .current_client()? + .nyxd + .vesting_pledge_more(additional_pledge_base, fee) + .await?; + log::info!("<<< tx hash = {}", res.transaction_hash); + log::trace!("<<< {:?}", res); + Ok(TransactionExecuteResult::from_execute_result( + res, fee_amount, + )?) +} + #[tauri::command] pub async fn vesting_unbond_mixnode( fee: Option, @@ -106,7 +133,7 @@ pub async fn vesting_unbond_mixnode( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_unbond_mixnode(fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -134,7 +161,7 @@ pub async fn withdraw_vested_coins( ); let res = guard .current_client()? - .nymd + .nyxd .withdraw_vested_coins(amount_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -162,7 +189,7 @@ pub async fn vesting_update_mixnode_cost_params( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_update_mixnode_cost_params(cost_params, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -187,7 +214,7 @@ pub async fn vesting_update_mixnode_config( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_update_mixnode_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); diff --git a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs index 59183878e5..0b3620b129 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs @@ -6,7 +6,7 @@ use crate::state::WalletState; use mixnet_contract_common::MixId; use nym_types::currency::DecCoin; use nym_types::transaction::TransactionExecuteResult; -use validator_client::nymd::{Fee, VestingSigningClient}; +use validator_client::nyxd::{Fee, VestingSigningClient}; #[tauri::command] pub async fn vesting_delegate_to_mixnode( @@ -28,7 +28,7 @@ pub async fn vesting_delegate_to_mixnode( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_delegate_to_mixnode(mix_id, delegation, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -53,7 +53,7 @@ pub async fn vesting_undelegate_from_mixnode( ); let res = guard .current_client()? - .nymd + .nyxd .vesting_undelegate_from_mixnode(mix_id, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); diff --git a/nym-wallet/src-tauri/src/operations/vesting/queries.rs b/nym-wallet/src-tauri/src/operations/vesting/queries.rs index d05608fb25..5fe2d2dba7 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/queries.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/queries.rs @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BackendError; -use crate::nymd_client; +use crate::nyxd_client; use crate::state::WalletState; use cosmwasm_std::Timestamp; use nym_types::currency::DecCoin; use nym_types::vesting::VestingAccountInfo; use nym_types::vesting::{OriginalVestingResponse, PledgeData}; -use validator_client::nymd::VestingQueryClient; +use validator_client::nyxd::VestingQueryClient; use vesting_contract_common::Period; #[tauri::command] @@ -21,9 +21,9 @@ pub async fn locked_coins( let client = guard.current_client()?; let res = client - .nymd + .nyxd .locked_coins( - client.nymd.address().as_ref(), + client.nyxd.address().as_ref(), block_time.map(Timestamp::from_seconds), ) .await?; @@ -42,9 +42,9 @@ pub async fn spendable_coins( let client = guard.current_client()?; let res = client - .nymd + .nyxd .spendable_coins( - client.nymd.address().as_ref(), + client.nyxd.address().as_ref(), block_time.map(Timestamp::from_seconds), ) .await?; @@ -65,7 +65,7 @@ pub async fn vested_coins( let res = guard .current_client()? - .nymd + .nyxd .vested_coins( vesting_account_address, block_time.map(Timestamp::from_seconds), @@ -88,7 +88,7 @@ pub async fn vesting_coins( let res = guard .current_client()? - .nymd + .nyxd .vesting_coins( vesting_account_address, block_time.map(Timestamp::from_seconds), @@ -106,7 +106,7 @@ pub async fn vesting_start_time( state: tauri::State<'_, WalletState>, ) -> Result { log::info!(">>> Query vesting start time"); - let res = nymd_client!(state) + let res = nyxd_client!(state) .vesting_start_time(vesting_account_address) .await? .seconds(); @@ -120,7 +120,7 @@ pub async fn vesting_end_time( state: tauri::State<'_, WalletState>, ) -> Result { log::info!(">>> Query vesting end time"); - let res = nymd_client!(state) + let res = nyxd_client!(state) .vesting_end_time(vesting_account_address) .await? .seconds(); @@ -139,7 +139,7 @@ pub async fn original_vesting( let res = guard .current_client()? - .nymd + .nyxd .original_vesting(vesting_account_address) .await?; @@ -159,7 +159,7 @@ pub async fn delegated_free( let res = guard .current_client()? - .nymd + .nyxd .delegated_free( vesting_account_address, block_time.map(Timestamp::from_seconds), @@ -183,7 +183,7 @@ pub async fn delegated_vesting( let res = guard .current_client()? - .nymd + .nyxd .delegated_vesting( vesting_account_address, block_time.map(Timestamp::from_seconds), @@ -206,7 +206,7 @@ pub async fn vesting_get_mixnode_pledge( let res = guard .current_client()? - .nymd + .nyxd .get_mixnode_pledge(address) .await? .map(|pledge| PledgeData::from_vesting_contract(pledge, reg)) @@ -227,7 +227,7 @@ pub async fn vesting_get_gateway_pledge( let res = guard .current_client()? - .nymd + .nyxd .get_gateway_pledge(address) .await? .map(|pledge| PledgeData::from_vesting_contract(pledge, reg)) @@ -243,7 +243,7 @@ pub async fn get_current_vesting_period( state: tauri::State<'_, WalletState>, ) -> Result { log::info!(">>> Query current vesting period"); - let res = nymd_client!(state) + let res = nyxd_client!(state) .get_current_vesting_period(address) .await?; log::info!("<<< {:?}", res); @@ -259,7 +259,7 @@ pub async fn get_account_info( let guard = state.read().await; let res = guard.registered_coins()?; - let vesting_account = guard.current_client()?.nymd.get_account(address).await?; + let vesting_account = guard.current_client()?.nyxd.get_account(address).await?; let res = VestingAccountInfo::from_vesting_contract(vesting_account, res)?; log::info!("<<< {:?}", res); diff --git a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs index ad3760a364..32fc67257a 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs @@ -5,7 +5,7 @@ use crate::error::BackendError; use crate::state::WalletState; use mixnet_contract_common::MixId; use nym_types::transaction::TransactionExecuteResult; -use validator_client::nymd::Fee; +use validator_client::nyxd::Fee; #[tauri::command] pub async fn vesting_claim_operator_reward( @@ -17,7 +17,7 @@ pub async fn vesting_claim_operator_reward( let fee_amount = guard.convert_tx_fee(fee.as_ref()); let res = guard .current_client()? - .nymd + .nyxd .execute_vesting_withdraw_operator_reward(None) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); @@ -41,7 +41,7 @@ pub async fn vesting_claim_delegator_reward( let fee_amount = guard.convert_tx_fee(fee.as_ref()); let res = guard .current_client()? - .nymd + .nyxd .execute_vesting_withdraw_delegator_reward(mix_id, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index e36dfe1b5c..f81a58e6e0 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -16,8 +16,8 @@ use std::time::Duration; use strum::IntoEnumIterator; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use url::Url; -use validator_client::nymd::cosmwasm_client::types::SimulateResponse; -use validator_client::nymd::{AccountId as CosmosAccountId, Coin, Fee, SigningNymdClient}; +use validator_client::nyxd::cosmwasm_client::types::SimulateResponse; +use validator_client::nyxd::{AccountId as CosmosAccountId, Coin, Fee, SigningNyxdClient}; use validator_client::Client; // Some hardcoded metadata overrides @@ -65,7 +65,7 @@ impl WalletState { #[derive(Default)] pub struct WalletStateInner { config: config::Config, - signing_clients: HashMap>, + signing_clients: HashMap>, current_network: Network, // All the accounts the we get from decrypting the wallet. We hold on to these for being able to @@ -156,8 +156,8 @@ impl WalletStateInner { ) -> Result { // this MUST succeed as we just used it before let client = self.current_client()?; - let gas_price = client.nymd.gas_price().clone(); - let gas_adjustment = client.nymd.gas_adjustment(); + let gas_price = client.nyxd.gas_price().clone(); + let gas_adjustment = client.nyxd.gas_adjustment(); let res = SimulateResult::new(simulate_response.gas_info, gas_price, gas_adjustment); @@ -169,7 +169,7 @@ impl WalletStateInner { Ok(FeeDetails::new(amount, res.to_fee())) } - pub fn client(&self, network: Network) -> Result<&Client, BackendError> { + pub fn client(&self, network: Network) -> Result<&Client, BackendError> { self.signing_clients .get(&network) .ok_or(BackendError::ClientNotInitialized) @@ -178,20 +178,20 @@ impl WalletStateInner { pub fn client_mut( &mut self, network: Network, - ) -> Result<&mut Client, BackendError> { + ) -> Result<&mut Client, BackendError> { self.signing_clients .get_mut(&network) .ok_or(BackendError::ClientNotInitialized) } - pub fn current_client(&self) -> Result<&Client, BackendError> { + pub fn current_client(&self) -> Result<&Client, BackendError> { self.signing_clients .get(&self.current_network) .ok_or(BackendError::ClientNotInitialized) } #[allow(unused)] - pub fn current_client_mut(&mut self) -> Result<&mut Client, BackendError> { + pub fn current_client_mut(&mut self) -> Result<&mut Client, BackendError> { self.signing_clients .get_mut(&self.current_network) .ok_or(BackendError::ClientNotInitialized) @@ -211,7 +211,7 @@ impl WalletStateInner { Ok(self.config.save_to_files()?) } - pub fn add_client(&mut self, network: Network, client: Client) { + pub fn add_client(&mut self, network: Network, client: Client) { self.signing_clients.insert(network, client); } @@ -258,26 +258,26 @@ impl WalletStateInner { let validators = validators_in_config .chain(fetched_validators) .chain(default_validators) - .unique_by(|v| (v.nymd_url.clone(), v.api_url.clone())); + .unique_by(|v| (v.nyxd_url.clone(), v.api_url.clone())); // Annotate with dynamic metadata validators.map(|v| { - let metadata = self.validator_metadata.get(&v.nymd_url); + let metadata = self.validator_metadata.get(&v.nyxd_url); let name = v - .nymd_name + .nyxd_name .or_else(|| metadata.and_then(|m| m.name.clone())); config::ValidatorConfigEntry { - nymd_url: v.nymd_url, - nymd_name: name, + nyxd_url: v.nyxd_url, + nyxd_name: name, api_url: v.api_url, } }) } - pub fn get_nymd_urls_only(&self, network: Network) -> impl Iterator + '_ { + pub fn get_nyxd_urls_only(&self, network: Network) -> impl Iterator + '_ { self.get_config_validator_entries(network) .into_iter() - .map(|v| v.nymd_url) + .map(|v| v.nyxd_url) } pub fn get_api_urls_only(&self, network: Network) -> impl Iterator + '_ { @@ -286,21 +286,21 @@ impl WalletStateInner { .filter_map(|v| v.api_url) } - /// Get the list of validator nymd urls in the network config format, suitable for passing on to + /// Get the list of validator nyxd urls in the network config format, suitable for passing on to /// the UI - pub fn get_nymd_urls( + pub fn get_nyxd_urls( &self, network: Network, ) -> impl Iterator + '_ { self.get_config_validator_entries(network) .into_iter() .map(|v| network_config::ValidatorUrl { - url: v.nymd_url.to_string(), - name: v.nymd_name, + url: v.nyxd_url.to_string(), + name: v.nyxd_name, }) } - /// Get the list of validator-api urls in the network config format, suitable for passing on to + /// Get the list of nym-api urls in the network config format, suitable for passing on to /// the UI pub fn get_api_urls( &self, @@ -316,10 +316,10 @@ impl WalletStateInner { }) } - pub fn get_all_nymd_urls(&self) -> HashMap> { + pub fn get_all_nyxd_urls(&self) -> HashMap> { Network::iter() .flat_map(|network| { - self.get_nymd_urls_only(network) + self.get_nyxd_urls_only(network) .map(move |url| (network, url)) }) .into_group_map() @@ -334,18 +334,18 @@ impl WalletStateInner { .into_group_map() } - /// Fetch validator urls remotely. These are used to in addition to the base ones, and the user + /// Fetch nyxd urls remotely. These are used to in addition to the base ones, and the user /// configured ones. - pub async fn fetch_updated_validator_urls(&mut self) -> Result<(), BackendError> { + pub async fn fetch_updated_nyxd_urls(&mut self) -> Result<(), BackendError> { let client = reqwest::Client::builder() .timeout(Duration::from_secs(3)) .build()?; log::debug!( "Fetching validator urls from: {}", - crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS + crate::config::REMOTE_SOURCE_OF_NYXD_URLS ); let response = client - .get(crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string()) + .get(crate::config::REMOTE_SOURCE_OF_NYXD_URLS.to_string()) .send() .await?; @@ -361,13 +361,13 @@ impl WalletStateInner { log::debug!("Refreshing validator status"); // All urls for all networks - let nymd_urls = self - .get_all_nymd_urls() + let nyxd_urls = self + .get_all_nyxd_urls() .into_iter() .flat_map(|(_, urls)| urls.into_iter()); // Fetch status for all urls - let responses = fetch_status_for_urls(nymd_urls).await?; + let responses = fetch_status_for_urls(nyxd_urls).await?; // Update the stored metadata self.apply_responses(responses)?; @@ -413,26 +413,18 @@ impl WalletStateInner { } } - pub fn select_validator_nymd_url( - &mut self, - url: &str, - network: Network, - ) -> Result<(), BackendError> { - self.config.select_validator_nymd_url(url.parse()?, network); + pub fn select_nyxd_url(&mut self, url: &str, network: Network) -> Result<(), BackendError> { + self.config.select_nyxd_url(url.parse()?, network); if let Ok(client) = self.client_mut(network) { - client.change_nymd(url.parse()?)?; + client.change_nyxd(url.parse()?)?; } Ok(()) } - pub fn select_validator_api_url( - &mut self, - url: &str, - network: Network, - ) -> Result<(), BackendError> { - self.config.select_validator_api_url(url.parse()?, network); + pub fn select_nym_api_url(&mut self, url: &str, network: Network) -> Result<(), BackendError> { + self.config.select_nym_api_url(url.parse()?, network); if let Ok(client) = self.client_mut(network) { - client.change_validator_api(url.parse()?); + client.change_nym_api(url.parse()?); } Ok(()) } @@ -447,13 +439,13 @@ impl WalletStateInner { } async fn fetch_status_for_urls( - nymd_urls: impl Iterator, + nyxd_urls: impl Iterator, ) -> Result>, BackendError> { let client = reqwest::Client::builder() .timeout(Duration::from_secs(3)) .build()?; - let responses = futures::future::join_all(nymd_urls.into_iter().map(|url| { + let responses = futures::future::join_all(nyxd_urls.into_iter().map(|url| { let client = &client; let status_url = url.join("status").unwrap_or_else(|_| url.clone()); async move { @@ -480,16 +472,16 @@ macro_rules! client { } #[macro_export] -macro_rules! nymd_client { +macro_rules! nyxd_client { ($state:ident) => { - $state.read().await.current_client()?.nymd + $state.read().await.current_client()?.nyxd }; } #[macro_export] macro_rules! api_client { ($state:ident) => { - $state.read().await.current_client()?.validator_api + $state.read().await.current_client()?.nym_api }; } @@ -504,17 +496,17 @@ mod tests { state.add_validator_url( config::ValidatorConfigEntry { - nymd_url: "http://nymd_url.com".parse().unwrap(), - nymd_name: Some("NymdUrl".to_string()), - api_url: Some("http://nymd_url.com/api".parse().unwrap()), + nyxd_url: "http://nyxd_url.com".parse().unwrap(), + nyxd_name: Some("NyxdUrl".to_string()), + api_url: Some("http://nyxd_url.com/api".parse().unwrap()), }, Network::MAINNET, ); state.add_validator_url( config::ValidatorConfigEntry { - nymd_url: "http://foo.com".parse().unwrap(), - nymd_name: None, + nyxd_url: "http://foo.com".parse().unwrap(), + nyxd_name: None, api_url: None, }, Network::MAINNET, @@ -522,8 +514,8 @@ mod tests { state.add_validator_url( config::ValidatorConfigEntry { - nymd_url: "http://bar.com".parse().unwrap(), - nymd_name: None, + nyxd_url: "http://bar.com".parse().unwrap(), + nyxd_name: None, api_url: None, }, Network::MAINNET, @@ -531,10 +523,10 @@ mod tests { assert_eq!( state - .get_nymd_urls_only(Network::MAINNET) + .get_nyxd_urls_only(Network::MAINNET) .collect::>(), vec![ - "http://nymd_url.com/".parse().unwrap(), + "http://nyxd_url.com/".parse().unwrap(), "http://foo.com".parse().unwrap(), "http://bar.com".parse().unwrap(), "https://rpc.nymtech.net".parse().unwrap(), @@ -545,18 +537,18 @@ mod tests { .get_api_urls_only(Network::MAINNET) .collect::>(), vec![ - "http://nymd_url.com/api".parse().unwrap(), + "http://nyxd_url.com/api".parse().unwrap(), "https://validator.nymtech.net/api/".parse().unwrap(), ], ); assert_eq!( state - .get_all_nymd_urls() + .get_all_nyxd_urls() .get(&Network::MAINNET) .unwrap() .clone(), vec![ - "http://nymd_url.com/".parse().unwrap(), + "http://nyxd_url.com/".parse().unwrap(), "http://foo.com".parse().unwrap(), "http://bar.com".parse().unwrap(), "https://rpc.nymtech.net".parse().unwrap(), diff --git a/nym-wallet/src-tauri/src/utils.rs b/nym-wallet/src-tauri/src/utils.rs index f04d47722f..355a6cb6cd 100644 --- a/nym-wallet/src-tauri/src/utils.rs +++ b/nym-wallet/src-tauri/src/utils.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BackendError; -use crate::nymd_client; +use crate::nyxd_client; use crate::state::WalletState; use cosmwasm_std::Decimal; use mixnet_contract_common::{IdentityKey, MixId, Percent}; @@ -10,8 +10,8 @@ use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_wallet_types::app::AppEnv; use serde::{Deserialize, Serialize}; -use validator_client::nymd::traits::MixnetQueryClient; -use validator_client::nymd::{tx, Coin, CosmosCoin, Gas, GasPrice}; +use validator_client::nyxd::traits::MixnetQueryClient; +use validator_client::nyxd::{tx, Coin, CosmosCoin, Gas, GasPrice}; fn get_env_as_option(key: &str) -> Option { match ::std::env::var(key) { @@ -31,8 +31,8 @@ pub fn get_env() -> AppEnv { #[tauri::command] pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result { - Ok(nymd_client!(state) - .get_owned_mixnode(nymd_client!(state).address()) + Ok(nyxd_client!(state) + .get_owned_mixnode(nyxd_client!(state).address()) .await? .mixnode_details .is_some()) @@ -40,8 +40,8 @@ pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result) -> Result { - Ok(nymd_client!(state) - .get_owned_gateway(nymd_client!(state).address()) + Ok(nyxd_client!(state) + .get_owned_gateway(nyxd_client!(state).address()) .await? .gateway .is_some()) @@ -52,7 +52,7 @@ pub async fn try_convert_pubkey_to_mix_id( state: tauri::State<'_, WalletState>, mix_identity: IdentityKey, ) -> Result, BackendError> { - let res = nymd_client!(state) + let res = nyxd_client!(state) .get_mixnode_details_by_identity(mix_identity) .await?; Ok(res.map(|mixnode_details| mixnode_details.mix_id())) @@ -185,7 +185,7 @@ pub async fn get_old_and_incorrect_hardcoded_fee( operation: Operation, ) -> Result { let guard = state.read().await; - let mut approximate_fee = operation.default_fee(guard.current_client()?.nymd.gas_price()); + let mut approximate_fee = operation.default_fee(guard.current_client()?.nyxd.gas_price()); // on all our chains it should only ever contain a single type of currency assert_eq!(approximate_fee.amount.len(), 1); let coin: Coin = approximate_fee.amount.pop().unwrap().into(); diff --git a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs index d0027592d9..f5c7296442 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs @@ -15,7 +15,7 @@ // instead treating as muliple accounts with one entry. use serde::{Deserialize, Serialize}; -use validator_client::nymd::bip32::DerivationPath; +use validator_client::nyxd::bip32::DerivationPath; use zeroize::Zeroize; use crate::error::BackendError; @@ -356,7 +356,7 @@ impl Drop for MnemonicAccount { mod display_hd_path { use serde::{Deserialize, Deserializer, Serializer}; - use validator_client::nymd::bip32::DerivationPath; + use validator_client::nyxd::bip32::DerivationPath; pub fn serialize( hd_path: &DerivationPath, diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index bf9e3c3c6c..7bca66d01b 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -18,7 +18,7 @@ use chrono::Local; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; -use validator_client::nymd::bip32::DerivationPath; +use validator_client::nyxd::bip32::DerivationPath; #[cfg(test)] use self::account_data::MnemonicAccount; diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index b7b616b2e8..004dd66912 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "package": { "productName": "nym-wallet", - "version": "1.1.1" + "version": "1.1.5" }, "build": { "distDir": "../dist", diff --git a/nym-wallet/src/components/AppBar.tsx b/nym-wallet/src/components/AppBar.tsx index 9aedf14d1a..3022646798 100644 --- a/nym-wallet/src/components/AppBar.tsx +++ b/nym-wallet/src/components/AppBar.tsx @@ -15,7 +15,7 @@ export const AppBar = () => { const navigate = useNavigate(); return ( - + diff --git a/nym-wallet/src/components/Bonding/BondedMixnode.tsx b/nym-wallet/src/components/Bonding/BondedMixnode.tsx index 5d63555ddb..6ff0151b29 100644 --- a/nym-wallet/src/components/Bonding/BondedMixnode.tsx +++ b/nym-wallet/src/components/Bonding/BondedMixnode.tsx @@ -114,7 +114,6 @@ export const BondedMixnode = ({ ), id: 'actions-cell', @@ -136,7 +135,7 @@ export const BondedMixnode = ({
{name && ( - + {name} diff --git a/nym-wallet/src/components/Bonding/BondedMixnodeActions.tsx b/nym-wallet/src/components/Bonding/BondedMixnodeActions.tsx index 37b52452c7..2a7d6e6bf4 100644 --- a/nym-wallet/src/components/Bonding/BondedMixnodeActions.tsx +++ b/nym-wallet/src/components/Bonding/BondedMixnodeActions.tsx @@ -35,7 +35,7 @@ export const BondedMixnodeActions = ({ )} R} + Icon={R} onClick={() => handleActionClick('redeem')} disabled={disabledRedeemAndCompound} /> diff --git a/nym-wallet/src/components/Bonding/forms/BondMixnodeForm.tsx b/nym-wallet/src/components/Bonding/forms/BondMixnodeForm.tsx index 721d114961..6f229a403d 100644 --- a/nym-wallet/src/components/Bonding/forms/BondMixnodeForm.tsx +++ b/nym-wallet/src/components/Bonding/forms/BondMixnodeForm.tsx @@ -1,14 +1,16 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { Box, Checkbox, FormControlLabel, FormHelperText, Stack, TextField } from '@mui/material'; +import { Box, Checkbox, FormControlLabel, FormHelperText, Stack, TextField, Typography } from '@mui/material'; import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; import { CurrencyDenom, TNodeType } from '@nymproject/types'; import { checkHasEnoughFunds, checkHasEnoughLockedTokens } from 'src/utils'; import { NodeTypeSelector, TokenPoolSelector } from 'src/components'; import { MixnodeAmount, MixnodeData } from 'src/pages/bonding/types'; +import { ModalListItem } from 'src/components/Modals/ModalListItem'; import { amountSchema, mixnodeValidationSchema } from './mixnodeValidationSchema'; +import { AppContext } from 'src/context'; const NodeFormData = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; onNext: (data: any) => void }) => { const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); @@ -138,6 +140,8 @@ const AmountFormData = ({ setError, } = useForm({ resolver: yupResolver(amountSchema), defaultValues: amountData }); + const { userBalance } = useContext(AppContext); + const handleRequestValidation = async (event: { detail: { step: number } }) => { let hasSufficientTokens = true; const values = getValues(); @@ -210,6 +214,17 @@ const AmountFormData = ({ and delegators.
+ + {!hasVestingTokens && ( + + )} + Est. fee for this transaction will be calculated in the next page + ); }; diff --git a/nym-wallet/src/components/Bonding/modals/BondMoreModal.tsx b/nym-wallet/src/components/Bonding/modals/BondMoreModal.tsx index 88518abc28..e2145e431e 100644 --- a/nym-wallet/src/components/Bonding/modals/BondMoreModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/BondMoreModal.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Box, FormHelperText, Stack, TextField } from '@mui/material'; +import { Box, Stack } from '@mui/material'; import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; import { ModalListItem } from 'src/components/Modals/ModalListItem'; import { SimpleModal } from 'src/components/Modals/SimpleModal'; @@ -7,66 +7,74 @@ import { DecCoin } from '@nymproject/types'; import { TokenPoolSelector, TPoolOption } from 'src/components/TokenPoolSelector'; import { ConfirmTx } from 'src/components/ConfirmTX'; import { useGetFee } from 'src/hooks/useGetFee'; -import { validateAmount, validateKey } from 'src/utils'; +import { validateAmount } from 'src/utils'; +import { simulateBondMore, simulateVestingBondMore } from 'src/requests'; +import { TBondMoreArgs } from 'src/types'; +import { TBondedMixnode } from 'src/context'; export const BondMoreModal = ({ - currentBond, + node, userBalance, - hasVestingTokens, - onConfirm, + onBondMore, onClose, + onError, }: { - currentBond: DecCoin; + node: TBondedMixnode; userBalance?: string; - hasVestingTokens: boolean; - onConfirm: (args: { additionalBond: DecCoin; signature: string; tokenPool: TPoolOption }) => Promise; + onBondMore: (data: TBondMoreArgs, tokenPool: TPoolOption) => Promise; onClose: () => void; + onError: (e: string) => void; }) => { - const { fee, resetFeeState } = useGetFee(); + const { bond: currentBond, proxy } = node; + const { fee, getFee, resetFeeState, feeError } = useGetFee(); const [additionalBond, setAdditionalBond] = useState({ amount: '0', denom: currentBond.denom }); - const [signature, setSignature] = useState(''); - const [tokenPool, setTokenPool] = useState('balance'); const [errorAmount, setErrorAmount] = useState(false); - const [errorSignature, setErrorSignature] = useState(false); - const handleOnOk = async () => { - const errors = { - amount: false, - signature: false, - }; - - if (!validateKey(signature || '', 64)) { - errors.signature = true; + useEffect(() => { + if (feeError) { + onError(feeError); } + }, [feeError]); - if (!additionalBond?.amount) { - errors.amount = true; - } + const handleConfirm = async () => { + const data = { additionalPledge: additionalBond }; + const tokenPool = proxy ? 'locked' : 'balance'; + await onBondMore(data, tokenPool); + }; - if (additionalBond && !(await validateAmount(additionalBond.amount, '1'))) { - errors.amount = true; - } + const handleAmountChanged = async (value: DecCoin) => { + setAdditionalBond(value); + const { amount } = value; - if (!errors.amount && !errors.signature) { - onConfirm({ additionalBond, signature, tokenPool }); + if (!amount) { + setErrorAmount(true); } else { - setErrorAmount(errors.amount); - setErrorSignature(errors.signature); + const validAmount = await validateAmount(amount, '1'); + if (!validAmount) { + setErrorAmount(true); + return; + } + setErrorAmount(false); } }; - useEffect(() => { - setErrorAmount(false); - }, [additionalBond]); + const handleOnOk = async () => { + if (!proxy) { + await getFee(simulateBondMore, { additionalPledge: additionalBond }); + } else { + await getFee(simulateVestingBondMore, { additionalPledge: additionalBond }); + } + }; if (fee) return ( onConfirm({ additionalBond, signature, tokenPool })} + onClose={onClose} onPrev={resetFeeState} + onConfirm={handleConfirm} > @@ -80,36 +88,23 @@ export const BondMoreModal = ({ subHeader="Bond more tokens on your node and receive more rewards" okLabel="Next" onOk={handleOnOk} - okDisabled={errorAmount || errorSignature} + okDisabled={errorAmount} onClose={onClose} > - {hasVestingTokens && setTokenPool(pool)} />} { - setAdditionalBond(value); - setErrorSignature(false); + handleAmountChanged(value); }} fullWidth validationError={errorAmount ? 'Please enter a valid amount' : undefined} /> - - setSignature(e.target.value)} - InputLabelProps={{ shrink: true }} - /> - {errorSignature && Invalid signature} - - diff --git a/nym-wallet/src/components/Bonding/modals/BondOversaturatedModal.tsx b/nym-wallet/src/components/Bonding/modals/BondOversaturatedModal.tsx new file mode 100644 index 0000000000..be3ea33869 --- /dev/null +++ b/nym-wallet/src/components/Bonding/modals/BondOversaturatedModal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Stack, Typography } from '@mui/material'; +import { SimpleModal } from 'src/components/Modals/SimpleModal'; + +export const BondOversaturatedModal: React.FC<{ + open: boolean; + saturationPercentage: string; + onClose?: () => void; + onContinue?: () => void; +}> = ({ open, saturationPercentage, onClose, onContinue }) => ( + onContinue?.()} + header="Bond More" + okLabel="Bond More" + buttonFullWidth + > + + t.palette.nym.red }} + >{`Your node saturation: ${saturationPercentage}%`} + Your node is over saturated, are you sure you want to bond more? + + +); diff --git a/nym-wallet/src/components/Buy/SignMessageModal.stories.tsx b/nym-wallet/src/components/Buy/SignMessageModal.stories.tsx new file mode 100644 index 0000000000..201192ac16 --- /dev/null +++ b/nym-wallet/src/components/Buy/SignMessageModal.stories.tsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import { Button } from '@mui/material'; +import { MockBuyContextProvider } from 'src/context/mocks/buy'; + +import { SignMessageModal } from './SignMessageModal'; + +export default { + title: 'Buy/SignMessage', + component: SignMessageModal, +}; + +export const SignMessage = () => { + const [open, setOpen] = useState(false); + + return ( + + + {open && ( + { + setOpen(false); + }} + /> + )} + + ); +}; diff --git a/nym-wallet/src/components/Buy/SignMessageModal.tsx b/nym-wallet/src/components/Buy/SignMessageModal.tsx new file mode 100644 index 0000000000..d076971cd7 --- /dev/null +++ b/nym-wallet/src/components/Buy/SignMessageModal.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { useState } from 'react'; +import { Stack, TextField, Typography } from '@mui/material'; +import { useBuyContext } from 'src/context'; +import { SimpleModal } from '../Modals/SimpleModal'; +import { ErrorModal } from '../Modals/ErrorModal'; +import { CopyToClipboard } from '../CopyToClipboard'; + +export const SignMessageModal = ({ onClose }: { onClose: () => void }) => { + const [message, setMessage] = useState(); + const [signature, setSignature] = useState(); + + const { signMessage, loading, refresh, error } = useBuyContext(); + + const handleSign = async () => { + if (!message) { + return; + } + signMessage(message).then((sig) => { + setSignature(sig); + }); + }; + + const handleChange = (event: React.ChangeEvent) => { + setMessage(event.target.value); + }; + + if (error) { + return ( + { + refresh(); + }} + /> + ); + } + + return ( + + + + + + + + Copy signature + + + + + + ); +}; diff --git a/nym-wallet/src/components/Buy/Tutorial.stories.tsx b/nym-wallet/src/components/Buy/Tutorial.stories.tsx new file mode 100644 index 0000000000..ec73a796eb --- /dev/null +++ b/nym-wallet/src/components/Buy/Tutorial.stories.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { Tutorial } from './Tutorial'; +import { MockBuyContextProvider } from '../../context/mocks/buy'; + +export default { + title: 'Buy/Tutorial', + component: Tutorial, +}; + +export const TutorialPage = () => ( + + + +); diff --git a/nym-wallet/src/components/Buy/Tutorial.tsx b/nym-wallet/src/components/Buy/Tutorial.tsx new file mode 100644 index 0000000000..8d403cb2e6 --- /dev/null +++ b/nym-wallet/src/components/Buy/Tutorial.tsx @@ -0,0 +1,140 @@ +import React, { useContext, useState } from 'react'; +import { Button, Stack, Typography, Grid, useMediaQuery, useTheme } from '@mui/material'; +import { Tune as TuneIcon, BorderColor as BorderColorIcon } from '@mui/icons-material'; +import { CoinMark } from '@nymproject/react/coins/CoinMark'; +import { NymCard, ClientAddress } from '../../components'; +import { SignMessageModal } from './SignMessageModal'; +import { PoweredByBity } from 'src/svg-icons'; +import { AppContext } from 'src/context'; + +// TODO retrieve this value from env +const EXCHANGE_URL = 'https://buy.nymtech.net'; + +const borderColor = 'rgba(141, 147, 153, 0.2)'; + +const TutorialStep = ({ + step, + title, + text, + icon, + borderRight, + borderBottom, +}: { + step: number; + title: string; + text: React.ReactNode; + icon: React.ReactNode; + borderRight?: boolean; + borderBottom?: boolean; +}) => ( + + + + {icon} + + {`STEP ${step}`} + + + + {title} + + {text} + + +); + +export const Tutorial = () => { + const { clientDetails } = useContext(AppContext); + const [showSignModal, setShowSignModal] = useState(false); + const theme = useTheme(); + const showBorder = useMediaQuery(theme.breakpoints.up('md')); + + return ( + }> + + Follow below 3 steps to quickly and easily buy NYM tokens. You can purchase up to 1000 Swiss Francs per day. + + {showSignModal && setShowSignModal(false)} />} + + } + text={ + t.palette.nym.text.muted }}> + Click on{' '} + + Buy NYM + {' '} + button and follow the steps in the browser window that opens. You will be asked for purchase details i.e. + amount, wallet address, etc. + + } + borderRight={showBorder} + borderBottom={!showBorder} + /> + } + text={ + t.palette.nym.text.muted }}> + When asked for signature, copy the message and sign it using{' '} + + Sign message + {' '} + button below. Then copy and paste your signature back in the browser window. + + } + borderRight={showBorder} + borderBottom={!showBorder} + /> + } + text={ + t.palette.nym.text.muted }}> + {`Send the defined BTC amount to Bity's address that's given to you. As soon as your BTC tx has 4 confirmations, Bity will send the purchased NYM tokens to your wallet.`} + + } + /> + + + + + + + + + + ); +}; diff --git a/nym-wallet/src/components/CopyToClipboard.tsx b/nym-wallet/src/components/CopyToClipboard.tsx index 8df15668a8..d6de5cab9b 100644 --- a/nym-wallet/src/components/CopyToClipboard.tsx +++ b/nym-wallet/src/components/CopyToClipboard.tsx @@ -35,6 +35,7 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon sx={{ color: 'text.primary', }} + disabled={!text} > {!copied ? : } @@ -51,6 +52,7 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon }} onClick={() => handleCopy(text)} endIcon={copied && theme.palette.success.light }} />} + disabled={!text} > {!copied ? 'Copy' : 'Copied'} diff --git a/nym-wallet/src/components/Delegation/DelegationActions.tsx b/nym-wallet/src/components/Delegation/DelegationActions.tsx index 653e2a80e2..0784114d9d 100644 --- a/nym-wallet/src/components/Delegation/DelegationActions.tsx +++ b/nym-wallet/src/components/Delegation/DelegationActions.tsx @@ -92,7 +92,7 @@ export const DelegationsActionsMenu: React.FC<{ R} + Icon={R} onClick={() => handleActionSelect('redeem')} disabled={disableRedeemingRewards} /> diff --git a/nym-wallet/src/components/Modals/SimpleModal.stories.tsx b/nym-wallet/src/components/Modals/SimpleModal.stories.tsx index 35c4df516e..994cf7eb48 100644 --- a/nym-wallet/src/components/Modals/SimpleModal.stories.tsx +++ b/nym-wallet/src/components/Modals/SimpleModal.stories.tsx @@ -225,3 +225,36 @@ export const withBackButton = () => { ); }; + +export const withBackButtonAndCustomLabel = () => { + const [open, setOpen] = React.useState(false); + const handleClick = () => setOpen(true); + + const theme = useTheme(); + + return ( + + setOpen(false)} + onOk={async () => setOpen(false)} + header="This is a modal" + okLabel="Primary action" + onBack={() => setOpen(false)} + backLabel="Cancel" + backButtonFullWidth + {...storybookStyles(theme)} + > + + Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna + elit ut mollit. + + + + Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. + + + + ); +}; diff --git a/nym-wallet/src/components/Modals/SimpleModal.tsx b/nym-wallet/src/components/Modals/SimpleModal.tsx index 85c6480619..591354ffe5 100644 --- a/nym-wallet/src/components/Modals/SimpleModal.tsx +++ b/nym-wallet/src/components/Modals/SimpleModal.tsx @@ -20,6 +20,8 @@ export const SimpleModal: React.FC<{ header: string | React.ReactNode; subHeader?: string; okLabel: string; + backLabel?: string; + backButtonFullWidth?: boolean; okDisabled?: boolean; sx?: SxProps; backdropProps?: object; @@ -38,6 +40,8 @@ export const SimpleModal: React.FC<{ header, subHeader, okLabel, + backLabel, + backButtonFullWidth, sx, children, backdropProps, @@ -71,7 +75,7 @@ export const SimpleModal: React.FC<{ {(onOk || onBack) && ( - {onBack && } + {onBack && } {onOk && (