Compare commits

..

6 Commits

Author SHA1 Message Date
Jon Häggblad cc5e12e2b5 validator-api: add comment for JsonSchema impl for ErrorResponse 2022-05-03 10:23:57 +02:00
Jon Häggblad b31587e051 mixnet-contract: interval JsonSchema impl fix 2022-05-03 10:21:02 +02:00
Jon Häggblad 9b1574ff08 validator-api: fix OpenApiResponderInner for ErrorResponse 2022-05-03 09:40:15 +02:00
Jon Häggblad d3d5e327c7 validator-api: tidy swagger stuff 2022-05-03 09:40:15 +02:00
Jon Häggblad 433604dec7 validator-api: add swagger openapi 2022-05-03 09:40:14 +02:00
Jon Häggblad c429e67d68 validator-api: sort in Cargo.toml 2022-05-03 09:36:25 +02:00
276 changed files with 1521 additions and 8226 deletions
-9
View File
@@ -1,9 +0,0 @@
# Description
Closes: #XXXX
<!-- If appropriate, insert relevant description here -->
# Checklist:
- [ ] added a changelog entry to `CHANGELOG.md`
-72
View File
@@ -1,72 +0,0 @@
name: Continuous integration on dispatch
on: workflow_dispatch
jobs:
build:
runs-on: [ self-hosted, custom-linux-exoscale ]
# Enable sccache via environment variable
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace -- -D warnings
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --features=coconut
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features=coconut
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
with:
command: clippy
args: --features=coconut -- -D warnings
-14
View File
@@ -31,8 +31,6 @@ jobs:
# continue-on-error: true # continue-on-error: true
- run: yarn && yarn build - run: yarn && yarn build
continue-on-error: true continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
@@ -44,17 +42,6 @@ jobs:
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/" EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Keybase - Node Install - name: Keybase - Node Install
run: npm install run: npm install
working-directory: .github/workflows/support-files working-directory: .github/workflows/support-files
@@ -64,7 +51,6 @@ jobs:
NYM_PROJECT_NAME: "Network Explorer" NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}" NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}" NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}" GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}" KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
-27
View File
@@ -100,33 +100,6 @@ jobs:
with: with:
command: clippy command: clippy
args: --workspace --all-targets --features=coconut -- -D warnings args: --workspace --all-targets --features=coconut -- -D warnings
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification: notification:
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -44,4 +44,3 @@ jobs:
target/release/nym-mixnode target/release/nym-mixnode
target/release/nym-socks5-client target/release/nym-socks5-client
target/release/nym-validator-api target/release/nym-validator-api
target/release/nym-network-requester
@@ -1,6 +1,5 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/ > :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }}
> ✅ **SUCCESS** > ✅ **SUCCESS**
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
-57
View File
@@ -1,57 +0,0 @@
name: Nym Wallet (rust)
on:
push:
paths-ignore:
- 'explorer/**'
pull_request:
paths-ignore:
- 'explorer/**'
jobs:
build:
runs-on: [ self-hosted, custom-linux-exoscale ]
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features -- -D warnings
-333
View File
@@ -1,337 +1,5 @@
# Changelog # Changelog
## [Unreleased]
### Added
- wallet: require password to switch accounts
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
- wallet: added support for multiple accounts ([#1265])
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
- validator-api: add Swagger to document the REST API ([#1249]).
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
- network-requester: send traffic statistics from all network requesters and receive it in a special network-requester that aggregates the data and exposes it via a rest API ([#1267]).
### Fixed
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
[#1258]: https://github.com/nymtech/nym/pull/1258
[#1249]: https://github.com/nymtech/nym/pull/1249
[#1256]: https://github.com/nymtech/nym/pull/1256
[#1257]: https://github.com/nymtech/nym/pull/1257
[#1260]: https://github.com/nymtech/nym/pull/1260
[#1265]: https://github.com/nymtech/nym/pull/1265
[#1267]: https://github.com/nymtech/nym/pull/1267
[#1275]: https://github.com/nymtech/nym/pull/1275
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
### Changed
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
### Added
- validator-api: introduced endpoint for getting average mixnode uptime ([#1238])
### Changed
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
### Fixed
- nym-network-requester: is included in the Github Actions for building release binaries
[#1238]: https://github.com/nymtech/nym/pull/1238
[#1246]: https://github.com/nymtech/nym/pull/1246
## [v1.0.0](https://github.com/nymtech/nym/tree/v1.0.0) (2022-05-03)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...v1.0.0)
**Merged pull requests:**
- Feature/show pending delegations [\#1229](https://github.com/nymtech/nym/pull/1229) ([fmtabbara](https://github.com/fmtabbara))
- Bucket inclusion probabilities [\#1224](https://github.com/nymtech/nym/pull/1224) ([durch](https://github.com/durch))
- Create a new bundled delegation when compounding rewards [\#1221](https://github.com/nymtech/nym/pull/1221) ([durch](https://github.com/durch))
## [nym-binaries-1.0.0](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0) (2022-04-27)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.3...nym-binaries-1.0.0)
## [nym-wallet-v1.0.3](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.3) (2022-04-25)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.2...nym-wallet-v1.0.3)
**Fixed bugs:**
- \[Issue\] Wallet 1.0.2 cannot send NYM tokens from a DelayedVestingAccount [\#1215](https://github.com/nymtech/nym/issues/1215)
- Main README not showing properly with GitHub dark mode [\#1211](https://github.com/nymtech/nym/issues/1211)
**Merged pull requests:**
- Bugfix - wallet undelegation for vesting accounts [\#1220](https://github.com/nymtech/nym/pull/1220) ([mmsinclair](https://github.com/mmsinclair))
- Bugfix/delegation reconcile [\#1219](https://github.com/nymtech/nym/pull/1219) ([jstuczyn](https://github.com/jstuczyn))
- Bugfix/query proxied pending delegations [\#1218](https://github.com/nymtech/nym/pull/1218) ([jstuczyn](https://github.com/jstuczyn))
- Using custom gas multiplier in the wallet [\#1217](https://github.com/nymtech/nym/pull/1217) ([jstuczyn](https://github.com/jstuczyn))
- Feature/vesting accounts support [\#1216](https://github.com/nymtech/nym/pull/1216) ([jstuczyn](https://github.com/jstuczyn))
- Release/1.0.0 rc.2 [\#1214](https://github.com/nymtech/nym/pull/1214) ([jstuczyn](https://github.com/jstuczyn))
- chore: fix dark mode rendering [\#1212](https://github.com/nymtech/nym/pull/1212) ([pwnfoo](https://github.com/pwnfoo))
- Feature/spend coconut [\#1210](https://github.com/nymtech/nym/pull/1210) ([neacsu](https://github.com/neacsu))
- Bugfix/unique sphinx key [\#1207](https://github.com/nymtech/nym/pull/1207) ([jstuczyn](https://github.com/jstuczyn))
- Add cache read and write timeouts [\#1206](https://github.com/nymtech/nym/pull/1206) ([durch](https://github.com/durch))
- Additional, more informative routes [\#1204](https://github.com/nymtech/nym/pull/1204) ([durch](https://github.com/durch))
- Feature/aggregated econ dynamics explorer endpoint [\#1203](https://github.com/nymtech/nym/pull/1203) ([jstuczyn](https://github.com/jstuczyn))
- Debugging validator [\#1198](https://github.com/nymtech/nym/pull/1198) ([durch](https://github.com/durch))
- wallet: expose additional validator configuration functionality to the frontend [\#1195](https://github.com/nymtech/nym/pull/1195) ([octol](https://github.com/octol))
- Update rewarding validator address [\#1193](https://github.com/nymtech/nym/pull/1193) ([durch](https://github.com/durch))
- Crypto part of the Groth's NIDKG [\#1182](https://github.com/nymtech/nym/pull/1182) ([jstuczyn](https://github.com/jstuczyn))
- fix unbond page [\#1180](https://github.com/nymtech/nym/pull/1180) ([tommyv1987](https://github.com/tommyv1987))
- Type safe bounds [\#1179](https://github.com/nymtech/nym/pull/1179) ([durch](https://github.com/durch))
- Fix delegation paging [\#1174](https://github.com/nymtech/nym/pull/1174) ([durch](https://github.com/durch))
- Update binaries to rc version [\#1172](https://github.com/nymtech/nym/pull/1172) ([tommyv1987](https://github.com/tommyv1987))
- Bump ansi-regex from 4.1.0 to 4.1.1 in /docker/typescript\_client/upload\_contract [\#1171](https://github.com/nymtech/nym/pull/1171) ([dependabot[bot]](https://github.com/apps/dependabot))
## [nym-binaries-1.0.0-rc.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.2) (2022-04-15)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.2...nym-binaries-1.0.0-rc.2)
## [nym-wallet-v1.0.2](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.2) (2022-04-05)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.1...nym-wallet-v1.0.2)
**Merged pull requests:**
- Wallet 1.0.2 visual tweaks [\#1197](https://github.com/nymtech/nym/pull/1197) ([mmsinclair](https://github.com/mmsinclair))
- Password for wallet with routes [\#1196](https://github.com/nymtech/nym/pull/1196) ([fmtabbara](https://github.com/fmtabbara))
- Add auto-updater to Nym Wallet [\#1194](https://github.com/nymtech/nym/pull/1194) ([mmsinclair](https://github.com/mmsinclair))
- Fix clippy warnings for beta toolchain [\#1191](https://github.com/nymtech/nym/pull/1191) ([octol](https://github.com/octol))
- wallet: expose validator urls to the frontend [\#1190](https://github.com/nymtech/nym/pull/1190) ([octol](https://github.com/octol))
- wallet: add test for decrypting stored wallet file [\#1189](https://github.com/nymtech/nym/pull/1189) ([octol](https://github.com/octol))
- Fix clippy warnings [\#1188](https://github.com/nymtech/nym/pull/1188) ([octol](https://github.com/octol))
- Password for wallet with routes [\#1187](https://github.com/nymtech/nym/pull/1187) ([mmsinclair](https://github.com/mmsinclair))
- wallet: add validate\_mnemonic [\#1186](https://github.com/nymtech/nym/pull/1186) ([octol](https://github.com/octol))
- wallet: support removing accounts from the wallet file [\#1185](https://github.com/nymtech/nym/pull/1185) ([octol](https://github.com/octol))
- Feature/adding discord [\#1184](https://github.com/nymtech/nym/pull/1184) ([gala1234](https://github.com/gala1234))
- wallet: config backend for validator selection [\#1183](https://github.com/nymtech/nym/pull/1183) ([octol](https://github.com/octol))
- Add storybook to wallet [\#1178](https://github.com/nymtech/nym/pull/1178) ([mmsinclair](https://github.com/mmsinclair))
- wallet: connection test nymd and api urls independently [\#1170](https://github.com/nymtech/nym/pull/1170) ([octol](https://github.com/octol))
- wallet: wire up account storage [\#1153](https://github.com/nymtech/nym/pull/1153) ([octol](https://github.com/octol))
- Feature/signature on deposit [\#1151](https://github.com/nymtech/nym/pull/1151) ([neacsu](https://github.com/neacsu))
## [nym-wallet-v1.0.1](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.1) (2022-04-05)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.1...nym-wallet-v1.0.1)
**Closed issues:**
- Check enabling bbbc simultaneously with open access. Estimate what it would take to make this the default compilation target. [\#1175](https://github.com/nymtech/nym/issues/1175)
- Get coconut credential for deposited tokens [\#1138](https://github.com/nymtech/nym/issues/1138)
- Make payments lazy [\#1135](https://github.com/nymtech/nym/issues/1135)
- Uptime on node selection for sets [\#1049](https://github.com/nymtech/nym/issues/1049)
## [nym-binaries-1.0.0-rc.1](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.1) (2022-03-28)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.0...nym-binaries-1.0.0-rc.1)
**Fixed bugs:**
- \[Issue\]cargo build --release issue [\#1101](https://github.com/nymtech/nym/issues/1101)
- appimage fail to load in Fedora [\#1098](https://github.com/nymtech/nym/issues/1098)
- \[Issue\] React Example project does not compile when using @nymproject/nym-client-wasm v0.9.0-1 [\#878](https://github.com/nymtech/nym/issues/878)
**Closed issues:**
- Make mainnet coin transfers work [\#1096](https://github.com/nymtech/nym/issues/1096)
- Make Nym wallet validators configurable at runtime [\#1026](https://github.com/nymtech/nym/issues/1026)
- Project Platypus e2e / integration testing [\#942](https://github.com/nymtech/nym/issues/942)
- \[Coconut\]: Replace ElGamal with Pedersen commitments [\#901](https://github.com/nymtech/nym/issues/901)
**Merged pull requests:**
- Different values for mixes and gateways [\#1169](https://github.com/nymtech/nym/pull/1169) ([durch](https://github.com/durch))
- Add global blacklist to validator-cache [\#1168](https://github.com/nymtech/nym/pull/1168) ([durch](https://github.com/durch))
- Feature/upgrade rewarding sandbox [\#1167](https://github.com/nymtech/nym/pull/1167) ([durch](https://github.com/durch))
- Bump node-forge from 1.2.1 to 1.3.0 [\#1165](https://github.com/nymtech/nym/pull/1165) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump minimist from 1.2.5 to 1.2.6 in /nym-wallet/webdriver [\#1164](https://github.com/nymtech/nym/pull/1164) ([dependabot[bot]](https://github.com/apps/dependabot))
- 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))
- 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))
- 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))
- Pending endpoints [\#1150](https://github.com/nymtech/nym/pull/1150) ([durch](https://github.com/durch))
- wallet: add logging [\#1149](https://github.com/nymtech/nym/pull/1149) ([octol](https://github.com/octol))
- 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))
- 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))
- Feature/vesting token pool selector [\#1140](https://github.com/nymtech/nym/pull/1140) ([fmtabbara](https://github.com/fmtabbara))
- Add `ts-packages` for shared Typescript packages [\#1139](https://github.com/nymtech/nym/pull/1139) ([mmsinclair](https://github.com/mmsinclair))
- allow main-net prefix and denom to work [\#1137](https://github.com/nymtech/nym/pull/1137) ([tommyv1987](https://github.com/tommyv1987))
- Upgrade blake3 to v1.3.1 and tauri to 1.0.0-rc.3 [\#1136](https://github.com/nymtech/nym/pull/1136) ([mmsinclair](https://github.com/mmsinclair))
- Bump url-parse from 1.5.7 to 1.5.10 in /clients/native/examples/js-examples/websocket [\#1134](https://github.com/nymtech/nym/pull/1134) ([dependabot[bot]](https://github.com/apps/dependabot))
- Use network explorer map data with disputed areas [\#1133](https://github.com/nymtech/nym/pull/1133) ([Baro1905](https://github.com/Baro1905))
- Feature/vesting UI [\#1132](https://github.com/nymtech/nym/pull/1132) ([fmtabbara](https://github.com/fmtabbara))
- Refactor to a lazy rewarding system [\#1127](https://github.com/nymtech/nym/pull/1127) ([durch](https://github.com/durch))
- Bump ws from 6.2.1 to 6.2.2 in /clients/webassembly/js-example [\#1126](https://github.com/nymtech/nym/pull/1126) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.4.7 to 1.5.7 in /clients/webassembly/react-example [\#1125](https://github.com/nymtech/nym/pull/1125) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.5.4 to 1.5.7 in /clients/native/examples/js-examples/websocket [\#1124](https://github.com/nymtech/nym/pull/1124) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.5.1 to 1.5.7 in /clients/webassembly/js-example [\#1122](https://github.com/nymtech/nym/pull/1122) ([dependabot[bot]](https://github.com/apps/dependabot))
- update contract address [\#1121](https://github.com/nymtech/nym/pull/1121) ([tommyv1987](https://github.com/tommyv1987))
- Refactor GitHub Actions notifications [\#1119](https://github.com/nymtech/nym/pull/1119) ([mmsinclair](https://github.com/mmsinclair))
- Change `pledge` to `bond` in gateway list [\#1118](https://github.com/nymtech/nym/pull/1118) ([mmsinclair](https://github.com/mmsinclair))
- Bump follow-redirects from 1.14.7 to 1.14.8 in /contracts/basic-bandwidth-generation [\#1117](https://github.com/nymtech/nym/pull/1117) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.3 to 1.14.8 in /explorer [\#1116](https://github.com/nymtech/nym/pull/1116) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.5 to 1.14.8 in /nym-wallet [\#1115](https://github.com/nymtech/nym/pull/1115) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.7 to 1.14.8 in /clients/native/examples/js-examples/websocket [\#1114](https://github.com/nymtech/nym/pull/1114) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.7 to 1.14.8 in /testnet-faucet [\#1113](https://github.com/nymtech/nym/pull/1113) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.1 to 1.14.8 in /clients/webassembly/js-example [\#1112](https://github.com/nymtech/nym/pull/1112) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/vesting get current period [\#1111](https://github.com/nymtech/nym/pull/1111) ([durch](https://github.com/durch))
- Bump simple-get from 2.8.1 to 2.8.2 in /contracts/basic-bandwidth-generation [\#1110](https://github.com/nymtech/nym/pull/1110) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump simple-get from 3.1.0 to 3.1.1 in /explorer [\#1109](https://github.com/nymtech/nym/pull/1109) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump simple-get from 3.1.0 to 3.1.1 in /clients/tauri-client [\#1108](https://github.com/nymtech/nym/pull/1108) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump simple-get from 3.1.0 to 3.1.1 in /nym-wallet [\#1107](https://github.com/nymtech/nym/pull/1107) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump node-sass from 4.14.1 to 7.0.0 in /clients/webassembly/react-example [\#1105](https://github.com/nymtech/nym/pull/1105) ([dependabot[bot]](https://github.com/apps/dependabot))
- Fix hardcoded period logic [\#1104](https://github.com/nymtech/nym/pull/1104) ([durch](https://github.com/durch))
- Fixed underflow in rewarding all delegators [\#1099](https://github.com/nymtech/nym/pull/1099) ([jstuczyn](https://github.com/jstuczyn))
- Emit original bond as part of rewarding event [\#1094](https://github.com/nymtech/nym/pull/1094) ([jstuczyn](https://github.com/jstuczyn))
- Add UpdateMixnodeConfigOnBehalf to vestng contract [\#1091](https://github.com/nymtech/nym/pull/1091) ([durch](https://github.com/durch))
- Fixes infinite loops in requests involving pagination [\#1085](https://github.com/nymtech/nym/pull/1085) ([jstuczyn](https://github.com/jstuczyn))
- Removes migration code [\#1071](https://github.com/nymtech/nym/pull/1071) ([jstuczyn](https://github.com/jstuczyn))
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
## [nym-wallet-v1.0.0](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.0) (2022-02-03)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...nym-wallet-v1.0.0)
**Implemented enhancements:**
- \[Feature Request\] Please enable registration without need for Telegram account [\#1016](https://github.com/nymtech/nym/issues/1016)
- Fast mixnode launch with a pre-built ISO + VM software [\#1001](https://github.com/nymtech/nym/issues/1001)
**Fixed bugs:**
- \[Issue\] [\#1000](https://github.com/nymtech/nym/issues/1000)
- \[Issue\] `nym-client` requires multiple attempts to run a server [\#869](https://github.com/nymtech/nym/issues/869)
- De-'float'-ing `Interval` \(`Display` impl + `serde`\) [\#1065](https://github.com/nymtech/nym/pull/1065) ([jstuczyn](https://github.com/jstuczyn))
- display client address on wallet creation [\#1058](https://github.com/nymtech/nym/pull/1058) ([fmtabbara](https://github.com/fmtabbara))
**Closed issues:**
- Rewarded set inclusion probability API endpoint [\#1037](https://github.com/nymtech/nym/issues/1037)
- Update cw-storage-plus to 0.11 [\#1032](https://github.com/nymtech/nym/issues/1032)
- Change `u128` fields in `RewardEstimationResponse` to `u64` [\#1029](https://github.com/nymtech/nym/issues/1029)
- Test out the mainnet Gravity Bridge [\#1006](https://github.com/nymtech/nym/issues/1006)
- Add vesting contract interface to nym-wallet [\#959](https://github.com/nymtech/nym/issues/959)
- Mixnode crash [\#486](https://github.com/nymtech/nym/issues/486)
**Merged pull requests:**
- create custom urls for mainnet [\#1095](https://github.com/nymtech/nym/pull/1095) ([fmtabbara](https://github.com/fmtabbara))
- Wallet signing on MacOS [\#1093](https://github.com/nymtech/nym/pull/1093) ([mmsinclair](https://github.com/mmsinclair))
- Fix rust 2018 idioms warnings [\#1092](https://github.com/nymtech/nym/pull/1092) ([octol](https://github.com/octol))
- Prevent contract overwriting [\#1090](https://github.com/nymtech/nym/pull/1090) ([durch](https://github.com/durch))
- Logout operation [\#1087](https://github.com/nymtech/nym/pull/1087) ([jstuczyn](https://github.com/jstuczyn))
- Update to rust edition 2021 everywhere [\#1086](https://github.com/nymtech/nym/pull/1086) ([octol](https://github.com/octol))
- Tag contract errors, and print out lines for easier QA [\#1084](https://github.com/nymtech/nym/pull/1084) ([durch](https://github.com/durch))
- Feature/flexible vesting + utility queries [\#1083](https://github.com/nymtech/nym/pull/1083) ([durch](https://github.com/durch))
- Bump @openzeppelin/contracts from 4.3.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1082](https://github.com/nymtech/nym/pull/1082) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump nth-check from 2.0.0 to 2.0.1 in /clients/native/examples/js-examples/websocket [\#1081](https://github.com/nymtech/nym/pull/1081) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.5.1 to 1.5.4 in /clients/native/examples/js-examples/websocket [\#1080](https://github.com/nymtech/nym/pull/1080) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.1 to 1.14.7 in /clients/native/examples/js-examples/websocket [\#1079](https://github.com/nymtech/nym/pull/1079) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump nanoid from 3.1.23 to 3.2.0 in /clients/native/examples/js-examples/websocket [\#1078](https://github.com/nymtech/nym/pull/1078) ([dependabot[bot]](https://github.com/apps/dependabot))
- Setup basic test for mixnode stats reporting [\#1077](https://github.com/nymtech/nym/pull/1077) ([octol](https://github.com/octol))
- Make wallet\_address mandatory for mixnode init [\#1076](https://github.com/nymtech/nym/pull/1076) ([octol](https://github.com/octol))
- Tidy nym-mixnode module visibility [\#1075](https://github.com/nymtech/nym/pull/1075) ([octol](https://github.com/octol))
- Feature/wallet login with password [\#1074](https://github.com/nymtech/nym/pull/1074) ([fmtabbara](https://github.com/fmtabbara))
- Add trait to mock client dependency in DelayForwarder [\#1073](https://github.com/nymtech/nym/pull/1073) ([octol](https://github.com/octol))
- Bump rust-version to latest stable for nym-mixnode [\#1072](https://github.com/nymtech/nym/pull/1072) ([octol](https://github.com/octol))
- Fixes CI for our wasm build [\#1069](https://github.com/nymtech/nym/pull/1069) ([jstuczyn](https://github.com/jstuczyn))
- Add @octol as codeowner [\#1068](https://github.com/nymtech/nym/pull/1068) ([octol](https://github.com/octol))
- set-up inclusion probability [\#1067](https://github.com/nymtech/nym/pull/1067) ([fmtabbara](https://github.com/fmtabbara))
- Feature/wasm client [\#1066](https://github.com/nymtech/nym/pull/1066) ([neacsu](https://github.com/neacsu))
- Changed bech32\_prefix from punk to nymt [\#1064](https://github.com/nymtech/nym/pull/1064) ([jstuczyn](https://github.com/jstuczyn))
- Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet [\#1063](https://github.com/nymtech/nym/pull/1063) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet [\#1062](https://github.com/nymtech/nym/pull/1062) ([dependabot[bot]](https://github.com/apps/dependabot))
- Rework vesting contract storage [\#1061](https://github.com/nymtech/nym/pull/1061) ([durch](https://github.com/durch))
- Mixnet Contract constants extraction [\#1060](https://github.com/nymtech/nym/pull/1060) ([jstuczyn](https://github.com/jstuczyn))
- fix: make explorer footer year dynamic [\#1059](https://github.com/nymtech/nym/pull/1059) ([martinyung](https://github.com/martinyung))
- Add mnemonic just on creation, to display it [\#1057](https://github.com/nymtech/nym/pull/1057) ([neacsu](https://github.com/neacsu))
- Network Explorer: updates to API and UI to show the active set [\#1056](https://github.com/nymtech/nym/pull/1056) ([mmsinclair](https://github.com/mmsinclair))
- Made contract addresses for query NymdClient construction optional [\#1055](https://github.com/nymtech/nym/pull/1055) ([jstuczyn](https://github.com/jstuczyn))
- Introduced RPC query for total token supply [\#1053](https://github.com/nymtech/nym/pull/1053) ([jstuczyn](https://github.com/jstuczyn))
- Feature/tokio console [\#1052](https://github.com/nymtech/nym/pull/1052) ([durch](https://github.com/durch))
- Implemented beta clippy lint recommendations [\#1051](https://github.com/nymtech/nym/pull/1051) ([jstuczyn](https://github.com/jstuczyn))
- add new function to update profit percentage [\#1050](https://github.com/nymtech/nym/pull/1050) ([fmtabbara](https://github.com/fmtabbara))
- Upgrade Clap and use declarative argument parsing for nym-mixnode [\#1047](https://github.com/nymtech/nym/pull/1047) ([octol](https://github.com/octol))
- Feature/additional bond validation [\#1046](https://github.com/nymtech/nym/pull/1046) ([fmtabbara](https://github.com/fmtabbara))
- Fix clippy on relevant lints [\#1044](https://github.com/nymtech/nym/pull/1044) ([neacsu](https://github.com/neacsu))
- Bump shelljs from 0.8.4 to 0.8.5 in /contracts/basic-bandwidth-generation [\#1043](https://github.com/nymtech/nym/pull/1043) ([dependabot[bot]](https://github.com/apps/dependabot))
- Endpoint for rewarded set inclusion probabilities [\#1042](https://github.com/nymtech/nym/pull/1042) ([durch](https://github.com/durch))
- Bump follow-redirects from 1.14.4 to 1.14.7 in /contracts/basic-bandwidth-generation [\#1041](https://github.com/nymtech/nym/pull/1041) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet [\#1040](https://github.com/nymtech/nym/pull/1040) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/node settings update [\#1036](https://github.com/nymtech/nym/pull/1036) ([fmtabbara](https://github.com/fmtabbara))
- Migrate to cw-storage-plus 0.11.1 [\#1035](https://github.com/nymtech/nym/pull/1035) ([durch](https://github.com/durch))
- Bump @openzeppelin/contracts from 4.4.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1034](https://github.com/nymtech/nym/pull/1034) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/configurable wallet [\#1033](https://github.com/nymtech/nym/pull/1033) ([neacsu](https://github.com/neacsu))
- Feature/downcast reward estimation [\#1031](https://github.com/nymtech/nym/pull/1031) ([durch](https://github.com/durch))
- Wallet UI updates [\#1028](https://github.com/nymtech/nym/pull/1028) ([fmtabbara](https://github.com/fmtabbara))
- Remove migration code [\#1027](https://github.com/nymtech/nym/pull/1027) ([neacsu](https://github.com/neacsu))
- Chore/stricter dependency requirements [\#1025](https://github.com/nymtech/nym/pull/1025) ([jstuczyn](https://github.com/jstuczyn))
- Feature/validator api client endpoints [\#1024](https://github.com/nymtech/nym/pull/1024) ([jstuczyn](https://github.com/jstuczyn))
- Updated cosmrs to 0.4.1 [\#1023](https://github.com/nymtech/nym/pull/1023) ([jstuczyn](https://github.com/jstuczyn))
- Feature/testnet deploy scripts [\#1022](https://github.com/nymtech/nym/pull/1022) ([mfahampshire](https://github.com/mfahampshire))
- Changed wallet's client to a full validator client [\#1021](https://github.com/nymtech/nym/pull/1021) ([jstuczyn](https://github.com/jstuczyn))
- Fix 404 link [\#1020](https://github.com/nymtech/nym/pull/1020) ([RiccardoMasutti](https://github.com/RiccardoMasutti))
- Feature/additional mixnode endpoints [\#1019](https://github.com/nymtech/nym/pull/1019) ([jstuczyn](https://github.com/jstuczyn))
- Introduced denom check when trying to withdraw vested coins [\#1018](https://github.com/nymtech/nym/pull/1018) ([jstuczyn](https://github.com/jstuczyn))
- Add network defaults for qa [\#1017](https://github.com/nymtech/nym/pull/1017) ([neacsu](https://github.com/neacsu))
- Feature/expanded events [\#1015](https://github.com/nymtech/nym/pull/1015) ([jstuczyn](https://github.com/jstuczyn))
- update frontend to use new profit update api [\#1014](https://github.com/nymtech/nym/pull/1014) ([fmtabbara](https://github.com/fmtabbara))
- Feature/node state endpoint [\#1013](https://github.com/nymtech/nym/pull/1013) ([jstuczyn](https://github.com/jstuczyn))
- Feature/hourly set updates [\#1012](https://github.com/nymtech/nym/pull/1012) ([durch](https://github.com/durch))
- Feature/remove unused profit margin [\#1011](https://github.com/nymtech/nym/pull/1011) ([neacsu](https://github.com/neacsu))
- Feature/explorer node status [\#1010](https://github.com/nymtech/nym/pull/1010) ([jstuczyn](https://github.com/jstuczyn))
- Use serial integer instead of random [\#1009](https://github.com/nymtech/nym/pull/1009) ([durch](https://github.com/durch))
- Feature/configure profit [\#1008](https://github.com/nymtech/nym/pull/1008) ([neacsu](https://github.com/neacsu))
- Feature/fix gateway sign [\#1004](https://github.com/nymtech/nym/pull/1004) ([neacsu](https://github.com/neacsu))
- Fix clippy [\#1003](https://github.com/nymtech/nym/pull/1003) ([neacsu](https://github.com/neacsu))
- Update wallet version [\#998](https://github.com/nymtech/nym/pull/998) ([tommyv1987](https://github.com/tommyv1987))
- Fix wallet build instructions [\#997](https://github.com/nymtech/nym/pull/997) ([tommyv1987](https://github.com/tommyv1987))
- Make the separation between testnet-mode and erc20 bandwidth mode clearer [\#994](https://github.com/nymtech/nym/pull/994) ([neacsu](https://github.com/neacsu))
- Bump @openzeppelin/contracts from 3.4.0 to 4.4.1 in /contracts/basic-bandwidth-generation [\#983](https://github.com/nymtech/nym/pull/983) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/implicit runtime [\#973](https://github.com/nymtech/nym/pull/973) ([jstuczyn](https://github.com/jstuczyn))
- Differentiate staking and ownership [\#961](https://github.com/nymtech/nym/pull/961) ([durch](https://github.com/durch))
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
[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)
**Fixed bugs:**
- \[Issue\] NYM wallet doesn't work after login [\#995](https://github.com/nymtech/nym/issues/995)
- \[Issue\] [\#993](https://github.com/nymtech/nym/issues/993)
- NYM wallet setup trouble\[Issue\] [\#958](https://github.com/nymtech/nym/issues/958)
## [v0.12.0](https://github.com/nymtech/nym/tree/v0.12.0) (2021-12-21) ## [v0.12.0](https://github.com/nymtech/nym/tree/v0.12.0) (2021-12-21)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.11.0...v0.12.0) [Full Changelog](https://github.com/nymtech/nym/compare/v0.11.0...v0.12.0)
@@ -390,7 +58,6 @@
**Merged pull requests:** **Merged pull requests:**
- Update wallet to align with versioning on nodes and gateways [\#991](https://github.com/nymtech/nym/pull/991) ([tommyv1987](https://github.com/tommyv1987))
- Fix success view messages. [\#990](https://github.com/nymtech/nym/pull/990) ([tommyv1987](https://github.com/tommyv1987)) - Fix success view messages. [\#990](https://github.com/nymtech/nym/pull/990) ([tommyv1987](https://github.com/tommyv1987))
- Feature/enable signature check [\#989](https://github.com/nymtech/nym/pull/989) ([neacsu](https://github.com/neacsu)) - Feature/enable signature check [\#989](https://github.com/nymtech/nym/pull/989) ([neacsu](https://github.com/neacsu))
- Update mixnet contract address [\#988](https://github.com/nymtech/nym/pull/988) ([neacsu](https://github.com/neacsu)) - Update mixnet contract address [\#988](https://github.com/nymtech/nym/pull/988) ([neacsu](https://github.com/neacsu))
Generated
+8 -16
View File
@@ -581,7 +581,7 @@ dependencies = [
[[package]] [[package]]
name = "client-core" name = "client-core"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"config", "config",
"crypto", "crypto",
@@ -648,7 +648,6 @@ dependencies = [
name = "config" name = "config"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cfg-if 1.0.0",
"handlebars", "handlebars",
"humantime-serde", "humantime-serde",
"log", "log",
@@ -1595,7 +1594,7 @@ dependencies = [
[[package]] [[package]]
name = "explorer-api" name = "explorer-api"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"chrono", "chrono",
"humantime-serde", "humantime-serde",
@@ -3044,7 +3043,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-client" name = "nym-client"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"clap 2.34.0", "clap 2.34.0",
"client-core", "client-core",
@@ -3079,7 +3078,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-gateway" name = "nym-gateway"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3125,7 +3124,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-mixnode" name = "nym-mixnode"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bs58", "bs58",
@@ -3163,26 +3162,20 @@ dependencies = [
[[package]] [[package]]
name = "nym-network-requester" name = "nym-network-requester"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"bincode",
"clap 2.34.0", "clap 2.34.0",
"dirs", "dirs",
"futures", "futures",
"ipnetwork", "ipnetwork",
"log", "log",
"network-defaults",
"nymsphinx", "nymsphinx",
"ordered-buffer", "ordered-buffer",
"pretty_env_logger", "pretty_env_logger",
"proxy-helpers", "proxy-helpers",
"publicsuffix", "publicsuffix",
"rand 0.7.3", "rand 0.7.3",
"rocket",
"serde",
"socks5-requests", "socks5-requests",
"sqlx",
"thiserror",
"tokio", "tokio",
"tokio-tungstenite", "tokio-tungstenite",
"websocket-requests", "websocket-requests",
@@ -3190,7 +3183,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"clap 2.34.0", "clap 2.34.0",
"client-core", "client-core",
@@ -3226,7 +3219,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-validator-api" name = "nym-validator-api"
version = "1.0.1" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -5225,7 +5218,6 @@ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"either", "either",
-4
View File
@@ -9,10 +9,6 @@ overflow-checks = true
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"
[profile.test]
# equivalent of running in `--release` (but since we're in test profile we're keeping overflow checks and all of those by default)
opt-level = 3
[workspace] [workspace]
resolver = "2" resolver = "2"
+1 -1
View File
@@ -26,7 +26,7 @@ clippy-all-wallet:
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
test-main: test-main:
cargo test --all-features --workspace cargo test --all-features --workspace --release
test-contracts: test-contracts:
cargo test --manifest-path contracts/Cargo.toml --all-features cargo test --manifest-path contracts/Cargo.toml --all-features
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "client-core" name = "client-core"
version = "1.0.1" version = "1.0.0-rc.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
+7 -7
View File
@@ -110,8 +110,8 @@ impl<T: NymConfig> Config<T> {
self.client.id = id; self.client.id = id;
} }
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) { pub fn with_testnet_mode(&mut self, testnet_mode: bool) {
self.client.disabled_credentials_mode = disabled_credentials_mode; self.client.testnet_mode = testnet_mode;
} }
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) { pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
@@ -154,8 +154,8 @@ impl<T: NymConfig> Config<T> {
self.client.id.clone() self.client.id.clone()
} }
pub fn get_disabled_credentials_mode(&self) -> bool { pub fn get_testnet_mode(&self) -> bool {
self.client.disabled_credentials_mode self.client.testnet_mode
} }
pub fn get_nym_root_directory(&self) -> PathBuf { pub fn get_nym_root_directory(&self) -> PathBuf {
@@ -294,10 +294,10 @@ pub struct Client<T> {
/// ID specifies the human readable ID of this particular client. /// ID specifies the human readable ID of this particular client.
id: String, id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting /// Indicates whether this client is running in a testnet mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials. /// to claim bandwidth without presenting bandwidth credentials.
#[serde(default)] #[serde(default)]
disabled_credentials_mode: bool, testnet_mode: bool,
/// Addresses to APIs running on validator from which the client gets the view of the network. /// Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls: Vec<Url>, validator_api_urls: Vec<Url>,
@@ -354,7 +354,7 @@ impl<T: NymConfig> Default for Client<T> {
Client { Client {
version: env!("CARGO_PKG_VERSION").to_string(), version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(), id: "".to_string(),
disabled_credentials_mode: true, testnet_mode: false,
validator_api_urls: default_api_endpoints(), validator_api_urls: default_api_endpoints(),
private_identity_key_file: Default::default(), private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(), public_identity_key_file: Default::default(),
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.0.1" version = "1.0.0-rc.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.56"
@@ -592,9 +592,9 @@
} }
}, },
"node_modules/async": { "node_modules/async": {
"version": "2.6.4", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
@@ -4825,9 +4825,9 @@
"dev": true "dev": true
}, },
"async": { "async": {
"version": "2.6.4", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
+2 -2
View File
@@ -19,9 +19,9 @@ version = '{{ client.version }}'
# Human readable ID of this particular client. # Human readable ID of this particular client.
id = '{{ client.id }}' id = '{{ client.id }}'
# Indicates whether this client is running in a disabled credentials mode, thus attempting # Indicates whether this client is running in a testnet mode, thus attempting
# to claim bandwidth without presenting bandwidth credentials. # to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }} testnet_mode = {{ client.testnet_mode }}
# Addresses to APIs running on validator from which the client gets the view of the network. # Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [ validator_api_urls = [
+2 -2
View File
@@ -199,8 +199,8 @@ impl NymClient {
Some(bandwidth_controller), Some(bandwidth_controller),
); );
if self.config.get_base().get_disabled_credentials_mode() { if self.config.get_base().get_testnet_mode() {
gateway_client.set_disabled_credentials_mode(true) gateway_client.set_testnet_mode(true)
} }
gateway_client gateway_client
.authenticate_and_start() .authenticate_and_start()
+7 -7
View File
@@ -24,8 +24,8 @@ use crate::commands::override_config;
#[cfg(feature = "eth")] #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
use crate::commands::{ use crate::commands::{
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ENABLED_CREDENTIALS_MODE_ARG_NAME, DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME,
}; };
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
@@ -66,22 +66,22 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
let app = app let app = app
.arg( .arg(
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME) Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME) .long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a disabled credentials mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.") .help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME]) .conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
) )
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME) .arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
.long(ETH_ENDPOINT_ARG_NAME) .long(ETH_ENDPOINT_ARG_NAME)
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead") .help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true) .takes_value(true)
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT) .default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
.required(true)) .required(true))
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME) .arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
.long(ETH_PRIVATE_KEY_ARG_NAME) .long(ETH_PRIVATE_KEY_ARG_NAME)
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead") .help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true) .takes_value(true)
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY) .default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
.required(true) .required(true)
); );
+3 -3
View File
@@ -5,7 +5,7 @@ use crate::client::config::{Config, SocketType};
use clap::ArgMatches; use clap::ArgMatches;
use url::Url; use url::Url;
pub(crate) const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-mode"; pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint"; pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
@@ -72,8 +72,8 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY); .with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
} }
if matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) { if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
config.get_base_mut().with_disabled_credentials(false) config.get_base_mut().with_testnet_mode(true)
} }
config config
+4 -6
View File
@@ -6,9 +6,7 @@ use crate::client::NymClient;
use crate::commands::override_config; use crate::commands::override_config;
#[cfg(feature = "eth")] #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
use crate::commands::{ use crate::commands::{ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME};
ENABLED_CREDENTIALS_MODE_ARG_NAME, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
};
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use config::NymConfig; use config::NymConfig;
use log::*; use log::*;
@@ -48,9 +46,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
let app = app let app = app
.arg( .arg(
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME) Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME) .long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.") .help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME]) .conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
) )
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME) .arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
-4
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, App, ArgMatches}; use clap::{crate_version, App, ArgMatches};
use network_defaults::DEFAULT_NETWORK;
pub mod client; pub mod client;
pub mod commands; pub mod commands;
@@ -68,7 +67,6 @@ fn long_version() -> String {
{:<20}{} {:<20}{}
{:<20}{} {:<20}{}
{:<20}{} {:<20}{}
{:<20}{}
"#, "#,
"Build Timestamp:", "Build Timestamp:",
env!("VERGEN_BUILD_TIMESTAMP"), env!("VERGEN_BUILD_TIMESTAMP"),
@@ -86,8 +84,6 @@ fn long_version() -> String {
env!("VERGEN_RUSTC_CHANNEL"), env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:", "cargo Profile:",
env!("VERGEN_CARGO_PROFILE"), env!("VERGEN_CARGO_PROFILE"),
"Network:",
DEFAULT_NETWORK
) )
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.0.1" version = "1.0.0-rc.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.56"
+2 -2
View File
@@ -19,9 +19,9 @@ version = '{{ client.version }}'
# Human readable ID of this particular client. # Human readable ID of this particular client.
id = '{{ client.id }}' id = '{{ client.id }}'
# Indicates whether this client is running in a disabled credentials mode, thus attempting # Indicates whether this client is running in a testnet mode, thus attempting
# to claim bandwidth without presenting bandwidth credentials. # to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }} testnet_mode = {{ client.testnet_mode }}
# Addresses to APIs running on validator from which the client gets the view of the network. # Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [ validator_api_urls = [
+2 -2
View File
@@ -187,8 +187,8 @@ impl NymClient {
Some(bandwidth_controller), Some(bandwidth_controller),
); );
if self.config.get_base().get_disabled_credentials_mode() { if self.config.get_base().get_testnet_mode() {
gateway_client.set_disabled_credentials_mode(true) gateway_client.set_testnet_mode(true)
} }
gateway_client gateway_client
.authenticate_and_start() .authenticate_and_start()
+7 -7
View File
@@ -22,8 +22,8 @@ use crate::commands::override_config;
#[cfg(feature = "eth")] #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
use crate::commands::{ use crate::commands::{
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ENABLED_CREDENTIALS_MODE_ARG_NAME, DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME,
}; };
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
@@ -66,22 +66,22 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
let app = app let app = app
.arg( .arg(
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME) Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME) .long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.") .help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME]) .conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
) )
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME) .arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
.long(ETH_ENDPOINT_ARG_NAME) .long(ETH_ENDPOINT_ARG_NAME)
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead") .help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true) .takes_value(true)
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT) .default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
.required(true)) .required(true))
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME) .arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
.long(ETH_PRIVATE_KEY_ARG_NAME) .long(ETH_PRIVATE_KEY_ARG_NAME)
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead") .help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true) .takes_value(true)
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY) .default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
.required(true) .required(true)
); );
+3 -3
View File
@@ -9,7 +9,7 @@ pub(crate) mod init;
pub(crate) mod run; pub(crate) mod run;
pub(crate) mod upgrade; pub(crate) mod upgrade;
pub(crate) const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-mode"; pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint"; pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
@@ -68,8 +68,8 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY); .with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
} }
if matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) { if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
config.get_base_mut().with_disabled_credentials(false) config.get_base_mut().with_testnet_mode(true)
} }
config config
+4 -6
View File
@@ -6,9 +6,7 @@ use crate::client::NymClient;
use crate::commands::override_config; use crate::commands::override_config;
#[cfg(feature = "eth")] #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
use crate::commands::{ use crate::commands::{ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME};
ENABLED_CREDENTIALS_MODE_ARG_NAME, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
};
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use config::NymConfig; use config::NymConfig;
use log::*; use log::*;
@@ -54,9 +52,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
let app = app let app = app
.arg( .arg(
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME) Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME) .long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a disabled credentials mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.") .help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME]) .conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
) )
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME) .arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
-4
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, App, ArgMatches}; use clap::{crate_version, App, ArgMatches};
use network_defaults::DEFAULT_NETWORK;
pub mod client; pub mod client;
mod commands; mod commands;
@@ -68,7 +67,6 @@ fn long_version() -> String {
{:<20}{} {:<20}{}
{:<20}{} {:<20}{}
{:<20}{} {:<20}{}
{:<20}{}
"#, "#,
"Build Timestamp:", "Build Timestamp:",
env!("VERGEN_BUILD_TIMESTAMP"), env!("VERGEN_BUILD_TIMESTAMP"),
@@ -86,8 +84,6 @@ fn long_version() -> String {
env!("VERGEN_RUSTC_CHANNEL"), env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:", "cargo Profile:",
env!("VERGEN_CARGO_PROFILE"), env!("VERGEN_CARGO_PROFILE"),
"Network:",
DEFAULT_NETWORK
) )
} }
+3 -5
View File
@@ -16,7 +16,7 @@ use proxy_helpers::connection_controller::{
}; };
use proxy_helpers::proxy_runner::ProxyRunner; use proxy_helpers::proxy_runner::ProxyRunner;
use rand::RngCore; use rand::RngCore;
use socks5_requests::{ConnectionId, Message, RemoteAddress, Request}; use socks5_requests::{ConnectionId, RemoteAddress, Request};
use std::io; use std::io;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
@@ -224,9 +224,8 @@ impl SocksClient {
async fn send_connect_to_mixnet(&mut self, remote_address: RemoteAddress) { async fn send_connect_to_mixnet(&mut self, remote_address: RemoteAddress) {
let req = Request::new_connect(self.connection_id, remote_address, self.self_address); let req = Request::new_connect(self.connection_id, remote_address, self.self_address);
let msg = Message::Request(req);
let input_message = InputMessage::new_fresh(self.service_provider, msg.into_bytes(), false); let input_message = InputMessage::new_fresh(self.service_provider, req.into_bytes(), false);
self.input_sender.unbounded_send(input_message).unwrap(); self.input_sender.unbounded_send(input_message).unwrap();
} }
@@ -253,8 +252,7 @@ impl SocksClient {
) )
.run(move |conn_id, read_data, socket_closed| { .run(move |conn_id, read_data, socket_closed| {
let provider_request = Request::new_send(conn_id, read_data, socket_closed); let provider_request = Request::new_send(conn_id, read_data, socket_closed);
let provider_message = Message::Request(provider_request); InputMessage::new_fresh(recipient, provider_request.into_bytes(), false)
InputMessage::new_fresh(recipient, provider_message.into_bytes(), false)
}) })
.await .await
.into_inner(); .into_inner();
+1 -1
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "nym-client-wasm" name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.0.1" version = "1.0.0-rc.2"
edition = "2021" edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0" license = "Apache-2.0"
+8 -7
View File
@@ -24,7 +24,8 @@
}, },
"../pkg": { "../pkg": {
"name": "@nymproject/nym-client-wasm", "name": "@nymproject/nym-client-wasm",
"version": "0.0.1" "version": "0.12.0",
"license": "Apache-2.0"
}, },
"node_modules/@discoveryjs/json-ext": { "node_modules/@discoveryjs/json-ext": {
"version": "0.5.7", "version": "0.5.7",
@@ -585,9 +586,9 @@
} }
}, },
"node_modules/async": { "node_modules/async": {
"version": "2.6.4", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
@@ -4304,9 +4305,9 @@
"dev": true "dev": true
}, },
"async": { "async": {
"version": "2.6.4", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
+8 -11
View File
@@ -26,7 +26,7 @@ const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1_500);
#[wasm_bindgen] #[wasm_bindgen]
pub struct NymClient { pub struct NymClient {
validator_server: Url, validator_server: Url,
disabled_credentials_mode: bool, testnet_mode: bool,
// TODO: technically this doesn't need to be an Arc since wasm is run on a single thread // TODO: technically this doesn't need to be an Arc since wasm is run on a single thread
// however, once we eventually combine this code with the native-client's, it will make things // however, once we eventually combine this code with the native-client's, it will make things
@@ -72,7 +72,7 @@ impl NymClient {
on_message: None, on_message: None,
on_gateway_connect: None, on_gateway_connect: None,
disabled_credentials_mode: true, testnet_mode: true,
} }
} }
@@ -85,12 +85,9 @@ impl NymClient {
self.on_gateway_connect = Some(on_connect) self.on_gateway_connect = Some(on_connect)
} }
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) { pub fn set_testnet_mode(&mut self, testnet_mode: bool) {
console_log!( console_log!("Setting testnet mode to {}", testnet_mode);
"Setting disabled credentials mode to {}", self.testnet_mode = testnet_mode;
disabled_credentials_mode
);
self.disabled_credentials_mode = disabled_credentials_mode;
} }
fn self_recipient(&self) -> Recipient { fn self_recipient(&self) -> Recipient {
@@ -110,7 +107,7 @@ impl NymClient {
// Right now it's impossible to have async exported functions to take `&self` rather than self // Right now it's impossible to have async exported functions to take `&self` rather than self
pub async fn initial_setup(self) -> Self { pub async fn initial_setup(self) -> Self {
let disabled_credentials_mode = self.disabled_credentials_mode; let testnet_mode = self.testnet_mode;
let bandwidth_controller = None; let bandwidth_controller = None;
@@ -132,8 +129,8 @@ impl NymClient {
bandwidth_controller, bandwidth_controller,
); );
if disabled_credentials_mode { if testnet_mode {
gateway_client.set_disabled_credentials_mode(true) gateway_client.set_testnet_mode(true)
} }
gateway_client gateway_client
@@ -45,7 +45,7 @@ const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayClient { pub struct GatewayClient {
authenticated: bool, authenticated: bool,
disabled_credentials_mode: bool, testnet_mode: bool,
bandwidth_remaining: i64, bandwidth_remaining: i64,
gateway_address: String, gateway_address: String,
gateway_identity: identity::PublicKey, gateway_identity: identity::PublicKey,
@@ -83,7 +83,7 @@ impl GatewayClient {
) -> Self { ) -> Self {
GatewayClient { GatewayClient {
authenticated: false, authenticated: false,
disabled_credentials_mode: true, testnet_mode: false,
bandwidth_remaining: 0, bandwidth_remaining: 0,
gateway_address, gateway_address,
gateway_identity, gateway_identity,
@@ -100,8 +100,8 @@ impl GatewayClient {
} }
} }
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) { pub fn set_testnet_mode(&mut self, testnet_mode: bool) {
self.disabled_credentials_mode = disabled_credentials_mode self.testnet_mode = testnet_mode
} }
// TODO: later convert into proper builder methods // TODO: later convert into proper builder methods
@@ -134,7 +134,7 @@ impl GatewayClient {
GatewayClient { GatewayClient {
authenticated: false, authenticated: false,
disabled_credentials_mode: true, testnet_mode: false,
bandwidth_remaining: 0, bandwidth_remaining: 0,
gateway_address, gateway_address,
gateway_identity, gateway_identity,
@@ -548,13 +548,13 @@ impl GatewayClient {
if self.shared_key.is_none() { if self.shared_key.is_none() {
return Err(GatewayClientError::NoSharedKeyAvailable); return Err(GatewayClientError::NoSharedKeyAvailable);
} }
if self.bandwidth_controller.is_none() && !self.disabled_credentials_mode { if self.bandwidth_controller.is_none() && !self.testnet_mode {
return Err(GatewayClientError::NoBandwidthControllerAvailable); return Err(GatewayClientError::NoBandwidthControllerAvailable);
} }
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while"); warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
if self.disabled_credentials_mode { if self.testnet_mode {
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential"); info!("The client is running in testnet mode - attempting to claim bandwidth without a credential");
return self.try_claim_testnet_bandwidth().await; return self.try_claim_testnet_bandwidth().await;
} }
+12 -48
View File
@@ -133,14 +133,20 @@ impl Client {
if current_attempt == 0 { if current_attempt == 0 {
None None
} else { } else {
let exp = 2_u32.checked_pow(current_attempt); // according to https://github.com/tokio-rs/tokio/issues/1953 there's an undocumented
let backoff = exp // limit of tokio delay of about 2 years.
.and_then(|exp| self.config.initial_reconnection_backoff.checked_mul(exp)) // let's ensure our delay is always on a sane side of being maximum 1 hour.
.unwrap_or(self.config.maximum_reconnection_backoff); let maximum_sane_delay = Duration::from_secs(60 * 60);
Some(std::cmp::min( Some(std::cmp::min(
backoff, maximum_sane_delay,
self.config.maximum_reconnection_backoff, std::cmp::min(
self.config
.initial_reconnection_backoff
.checked_mul(2_u32.pow(current_attempt))
.unwrap_or(self.config.maximum_reconnection_backoff),
self.config.maximum_reconnection_backoff,
),
)) ))
} }
} }
@@ -248,45 +254,3 @@ impl SendWithoutResponse for Client {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
fn dummy_client() -> Client {
Client::new(Config {
initial_reconnection_backoff: Duration::from_millis(10_000),
maximum_reconnection_backoff: Duration::from_millis(300_000),
initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128,
})
}
#[test]
fn determining_backoff_works_regardless_of_attempt() {
let client = dummy_client();
assert!(client.determine_backoff(0).is_none());
assert!(client.determine_backoff(1).is_some());
assert!(client.determine_backoff(2).is_some());
assert_eq!(
client.determine_backoff(16).unwrap(),
client.config.maximum_reconnection_backoff
);
assert_eq!(
client.determine_backoff(32).unwrap(),
client.config.maximum_reconnection_backoff
);
assert_eq!(
client.determine_backoff(1024).unwrap(),
client.config.maximum_reconnection_backoff
);
assert_eq!(
client.determine_backoff(65536).unwrap(),
client.config.maximum_reconnection_backoff
);
assert_eq!(
client.determine_backoff(u32::MAX).unwrap(),
client.config.maximum_reconnection_backoff
);
}
}
@@ -5,9 +5,6 @@ use crate::{validator_api, ValidatorClientError};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use url::Url; use url::Url;
#[cfg(feature = "nymd-client")]
use validator_api_requests::models::UptimeResponse;
use validator_api_requests::models::{ use validator_api_requests::models::{
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
StakeSaturationResponse, StakeSaturationResponse,
@@ -585,12 +582,6 @@ impl<C> Client<C> {
Ok(delegations) Ok(delegations)
} }
pub async fn get_mixnode_avg_uptimes(
&self,
) -> Result<Vec<UptimeResponse>, ValidatorClientError> {
Ok(self.validator_api.get_mixnode_avg_uptimes().await?)
}
pub async fn blind_sign( pub async fn blind_sign(
&self, &self,
request_body: &BlindSignRequestBody, request_body: &BlindSignRequestBody,
@@ -30,26 +30,12 @@ use cosmwasm_std::Coin as CosmWasmCoin;
use prost::Message; use prost::Message;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::time::Duration;
#[async_trait] #[async_trait]
impl CosmWasmClient for HttpClient { impl CosmWasmClient for HttpClient {}
fn broadcast_polling_rate(&self) -> Duration {
Duration::from_secs(4)
}
fn broadcast_timeout(&self) -> Duration {
Duration::from_secs(60)
}
}
#[async_trait] #[async_trait]
pub trait CosmWasmClient: rpc::Client { pub trait CosmWasmClient: rpc::Client {
// this should probably get redesigned, but I'm leaving those like that temporarily to fix
// the underlying issue more quickly
fn broadcast_polling_rate(&self) -> Duration;
fn broadcast_timeout(&self) -> Duration;
// helper method to remove duplicate code involved in making abci requests with protobuf messages // helper method to remove duplicate code involved in making abci requests with protobuf messages
// TODO: perhaps it should have an additional argument to determine whether the response should // TODO: perhaps it should have an additional argument to determine whether the response should
// require proof? // require proof?
@@ -267,42 +253,6 @@ pub trait CosmWasmClient: rpc::Client {
Ok(rpc::Client::broadcast_tx_commit(self, tx).await?) Ok(rpc::Client::broadcast_tx_commit(self, tx).await?)
} }
async fn broadcast_tx(&self, tx: Transaction) -> Result<TxResponse, NymdError> {
let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?;
if broadcasted.code.is_err() {
let code_val = broadcasted.code.value();
return Err(NymdError::BroadcastTxErrorDeliverTx {
hash: broadcasted.hash,
height: None,
code: code_val,
raw_log: broadcasted.log.to_string(),
});
}
let tx_hash = broadcasted.hash;
let start = tokio::time::Instant::now();
loop {
log::debug!(
"Polling for result of including {} in a block...",
broadcasted.hash
);
if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() {
return Err(NymdError::BroadcastTimeout {
hash: tx_hash,
timeout: self.broadcast_timeout(),
});
}
if let Ok(poll_res) = self.get_tx(tx_hash).await {
return Ok(poll_res);
}
tokio::time::sleep(self.broadcast_polling_rate()).await;
}
}
async fn get_codes(&self) -> Result<Vec<Code>, NymdError> { async fn get_codes(&self) -> Result<Vec<Code>, NymdError> {
let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap()); let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap());
@@ -19,7 +19,7 @@ impl CheckResponse for broadcast::tx_commit::Response {
if self.check_tx.code.is_err() { if self.check_tx.code.is_err() {
return Err(NymdError::BroadcastTxErrorCheckTx { return Err(NymdError::BroadcastTxErrorCheckTx {
hash: self.hash, hash: self.hash,
height: Some(self.height), height: self.height,
code: self.check_tx.code.value(), code: self.check_tx.code.value(),
raw_log: self.check_tx.log.value().to_owned(), raw_log: self.check_tx.log.value().to_owned(),
}); });
@@ -28,7 +28,7 @@ impl CheckResponse for broadcast::tx_commit::Response {
if self.deliver_tx.code.is_err() { if self.deliver_tx.code.is_err() {
return Err(NymdError::BroadcastTxErrorDeliverTx { return Err(NymdError::BroadcastTxErrorDeliverTx {
hash: self.hash, hash: self.hash,
height: Some(self.height), height: self.height,
code: self.deliver_tx.code.value(), code: self.deliver_tx.code.value(),
raw_log: self.deliver_tx.log.value().to_owned(), raw_log: self.deliver_tx.log.value().to_owned(),
}); });
@@ -38,21 +38,6 @@ impl CheckResponse for broadcast::tx_commit::Response {
} }
} }
impl CheckResponse for crate::nymd::TxResponse {
fn check_response(self) -> Result<Self, NymdError> {
if self.tx_result.code.is_err() {
return Err(NymdError::BroadcastTxErrorDeliverTx {
hash: self.hash,
height: Some(self.height),
code: self.tx_result.code.value(),
raw_log: self.tx_result.log.value().to_owned(),
});
}
Ok(self)
}
}
pub(crate) fn compress_wasm_code(code: &[u8]) -> Result<Vec<u8>, NymdError> { pub(crate) fn compress_wasm_code(code: &[u8]) -> Result<Vec<u8>, NymdError> {
// using compression level 9, same as cosmjs, that optimises for size // using compression level 9, same as cosmjs, that optimises for size
let mut encoder = GzEncoder::new(Vec::new(), Compression::best()); let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto; use std::convert::TryInto;
use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
use cosmrs::bank::MsgSend; use cosmrs::bank::MsgSend;
@@ -25,7 +24,7 @@ use crate::nymd::cosmwasm_client::types::*;
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
use crate::nymd::wallet::DirectSecp256k1HdWallet; use crate::nymd::wallet::DirectSecp256k1HdWallet;
use crate::nymd::{CosmosCoin, GasPrice, TxResponse}; use crate::nymd::{CosmosCoin, GasPrice};
// we need to have **a** valid secp256k1 signature for simulation purposes. // we need to have **a** valid secp256k1 signature for simulation purposes.
// it doesn't matter what it is as long as it parses correctly // it doesn't matter what it is as long as it parses correctly
@@ -36,9 +35,6 @@ const DUMMY_SECP256K1_SIGNATURE: &[u8] = &[
91, 91,
]; ];
const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
#[async_trait] #[async_trait]
pub trait SigningCosmWasmClient: CosmWasmClient { pub trait SigningCosmWasmClient: CosmWasmClient {
fn signer(&self) -> &DirectSecp256k1HdWallet; fn signer(&self) -> &DirectSecp256k1HdWallet;
@@ -115,12 +111,12 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, vec![upload_msg], fee, memo) .sign_and_broadcast_commit(sender_address, vec![upload_msg], fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let logs = parse_raw_logs(tx_res.tx_result.log)?; let logs = parse_raw_logs(tx_res.deliver_tx.log)?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
// TODO: should those strings be extracted into some constants? // TODO: should those strings be extracted into some constants?
// the reason I think unwrap here is fine is that if the transaction succeeded and those // the reason I think unwrap here is fine is that if the transaction succeeded and those
@@ -176,12 +172,12 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, vec![init_msg], fee, memo) .sign_and_broadcast_commit(sender_address, vec![init_msg], fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let logs = parse_raw_logs(tx_res.tx_result.log)?; let logs = parse_raw_logs(tx_res.deliver_tx.log)?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
// TODO: should those strings be extracted into some constants? // TODO: should those strings be extracted into some constants?
// the reason I think unwrap here is fine is that if the transaction succeeded and those // the reason I think unwrap here is fine is that if the transaction succeeded and those
@@ -218,14 +214,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, vec![change_admin_msg], fee, memo) .sign_and_broadcast_commit(sender_address, vec![change_admin_msg], fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
Ok(ChangeAdminResult { Ok(ChangeAdminResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -246,14 +242,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, vec![change_admin_msg], fee, memo) .sign_and_broadcast_commit(sender_address, vec![change_admin_msg], fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
Ok(ChangeAdminResult { Ok(ChangeAdminResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -281,14 +277,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, vec![migrate_msg], fee, memo) .sign_and_broadcast_commit(sender_address, vec![migrate_msg], fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
Ok(MigrateResult { Ok(MigrateResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -316,14 +312,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, vec![execute_msg], fee, memo) .sign_and_broadcast_commit(sender_address, vec![execute_msg], fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
Ok(ExecuteResult { Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -356,14 +352,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
let tx_res = self let tx_res = self
.sign_and_broadcast(sender_address, messages, fee, memo) .sign_and_broadcast_commit(sender_address, messages, fee, memo)
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
Ok(ExecuteResult { Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -376,7 +372,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
amount: Vec<Coin>, amount: Vec<Coin>,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> { ) -> Result<broadcast::tx_commit::Response, NymdError> {
let send_msg = MsgSend { let send_msg = MsgSend {
from_address: sender_address.clone(), from_address: sender_address.clone(),
to_address: recipient_address.clone(), to_address: recipient_address.clone(),
@@ -385,7 +381,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
self.sign_and_broadcast(sender_address, vec![send_msg], fee, memo) self.sign_and_broadcast_commit(sender_address, vec![send_msg], fee, memo)
.await? .await?
.check_response() .check_response()
} }
@@ -396,7 +392,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
msgs: I, msgs: I,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> ) -> Result<broadcast::tx_commit::Response, NymdError>
where where
I: IntoIterator<Item = (AccountId, Vec<Coin>)> + Send, I: IntoIterator<Item = (AccountId, Vec<Coin>)> + Send,
{ {
@@ -413,7 +409,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
}) })
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
self.sign_and_broadcast(sender_address, messages, fee, memo) self.sign_and_broadcast_commit(sender_address, messages, fee, memo)
.await? .await?
.check_response() .check_response()
} }
@@ -425,7 +421,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
amount: Coin, amount: Coin,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> { ) -> Result<broadcast::tx_commit::Response, NymdError> {
let delegate_msg = MsgDelegate { let delegate_msg = MsgDelegate {
delegator_address: delegator_address.to_owned(), delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
@@ -434,7 +430,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
self.sign_and_broadcast(delegator_address, vec![delegate_msg], fee, memo) self.sign_and_broadcast_commit(delegator_address, vec![delegate_msg], fee, memo)
.await? .await?
.check_response() .check_response()
} }
@@ -446,7 +442,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
amount: Coin, amount: Coin,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> { ) -> Result<broadcast::tx_commit::Response, NymdError> {
let undelegate_msg = MsgUndelegate { let undelegate_msg = MsgUndelegate {
delegator_address: delegator_address.to_owned(), delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
@@ -455,7 +451,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
self.sign_and_broadcast(delegator_address, vec![undelegate_msg], fee, memo) self.sign_and_broadcast_commit(delegator_address, vec![undelegate_msg], fee, memo)
.await? .await?
.check_response() .check_response()
} }
@@ -466,7 +462,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
validator_address: &AccountId, validator_address: &AccountId,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> { ) -> Result<broadcast::tx_commit::Response, NymdError> {
let withdraw_msg = MsgWithdrawDelegatorReward { let withdraw_msg = MsgWithdrawDelegatorReward {
delegator_address: delegator_address.to_owned(), delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
@@ -474,7 +470,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?;
self.sign_and_broadcast(delegator_address, vec![withdraw_msg], fee, memo) self.sign_and_broadcast_commit(delegator_address, vec![withdraw_msg], fee, memo)
.await? .await?
.check_response() .check_response()
} }
@@ -577,27 +573,6 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await
} }
/// Broadcast a transaction to the network and monitors its inclusion in a block.
async fn sign_and_broadcast(
&self,
signer_address: &AccountId,
messages: Vec<Any>,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> {
let memo = memo.into();
let fee = self
.determine_transaction_fee(signer_address, &messages, fee, &memo)
.await?;
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()))?;
self.broadcast_tx(tx_bytes.into()).await
}
fn sign_direct( fn sign_direct(
&self, &self,
signer_address: &AccountId, signer_address: &AccountId,
@@ -663,9 +638,6 @@ pub struct Client {
rpc_client: HttpClient, rpc_client: HttpClient,
signer: DirectSecp256k1HdWallet, signer: DirectSecp256k1HdWallet,
gas_price: GasPrice, gas_price: GasPrice,
broadcast_polling_rate: Duration,
broadcast_timeout: Duration,
} }
impl Client { impl Client {
@@ -682,18 +654,8 @@ impl Client {
rpc_client, rpc_client,
signer, signer,
gas_price, gas_price,
broadcast_polling_rate: DEFAULT_BROADCAST_POLLING_RATE,
broadcast_timeout: DEFAULT_BROADCAST_TIMEOUT,
}) })
} }
pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) {
self.broadcast_polling_rate = broadcast_polling_rate
}
pub fn set_broadcast_timeout(&mut self, broadcast_timeout: Duration) {
self.broadcast_timeout = broadcast_timeout
}
} }
#[async_trait] #[async_trait]
@@ -707,15 +669,7 @@ impl rpc::Client for Client {
} }
#[async_trait] #[async_trait]
impl CosmWasmClient for Client { impl CosmWasmClient for Client {}
fn broadcast_polling_rate(&self) -> Duration {
self.broadcast_polling_rate
}
fn broadcast_timeout(&self) -> Duration {
self.broadcast_timeout
}
}
#[async_trait] #[async_trait]
impl SigningCosmWasmClient for Client { impl SigningCosmWasmClient for Client {
@@ -5,7 +5,6 @@ use crate::nymd::cosmwasm_client::types::ContractCodeId;
use cosmrs::tendermint::{abci, block}; use cosmrs::tendermint::{abci, block};
use cosmrs::{bip32, tx, AccountId}; use cosmrs::{bip32, tx, AccountId};
use std::io; use std::io;
use std::time::Duration;
use thiserror::Error; use thiserror::Error;
pub use cosmrs::rpc::error::{ pub use cosmrs::rpc::error::{
@@ -82,21 +81,21 @@ pub enum NymdError {
MalformedLogString, MalformedLogString,
#[error( #[error(
"Error when broadcasting tx {hash} at height {height:?}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}" "Error when broadcasting tx {hash} at height {height}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}"
)] )]
BroadcastTxErrorCheckTx { BroadcastTxErrorCheckTx {
hash: tx::Hash, hash: tx::Hash,
height: Option<block::Height>, height: block::Height,
code: u32, code: u32,
raw_log: String, raw_log: String,
}, },
#[error( #[error(
"Error when broadcasting tx {hash} at height {height:?}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}" "Error when broadcasting tx {hash} at height {height}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}"
)] )]
BroadcastTxErrorDeliverTx { BroadcastTxErrorDeliverTx {
hash: tx::Hash, hash: tx::Hash,
height: Option<block::Height>, height: block::Height,
code: u32, code: u32,
raw_log: String, raw_log: String,
}, },
@@ -118,9 +117,6 @@ pub enum NymdError {
#[error("This account does not have BaseAccount information available to it")] #[error("This account does not have BaseAccount information available to it")]
NoBaseAccountInformationAvailable, NoBaseAccountInformationAvailable,
#[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())]
BroadcastTimeout { hash: tx::Hash, timeout: Duration },
} }
impl NymdError { impl NymdError {
@@ -8,6 +8,7 @@ use crate::nymd::cosmwasm_client::types::{
}; };
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use crate::nymd::wallet::DirectSecp256k1HdWallet; use crate::nymd::wallet::DirectSecp256k1HdWallet;
use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::Error as TendermintRpcError; use cosmrs::rpc::Error as TendermintRpcError;
use cosmrs::rpc::HttpClientUrl; use cosmrs::rpc::HttpClientUrl;
use cosmwasm_std::{Coin, Uint128}; use cosmwasm_std::{Coin, Uint128};
@@ -22,14 +23,12 @@ use mixnet_contract_common::{
PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails, PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails,
}; };
use serde::Serialize; use serde::Serialize;
use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
pub use crate::nymd::fee::Fee; pub use crate::nymd::fee::Fee;
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
pub use cosmrs::bank::MsgSend;
pub use cosmrs::rpc::endpoint::tx::Response as TxResponse; pub use cosmrs::rpc::endpoint::tx::Response as TxResponse;
pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse; pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse;
pub use cosmrs::rpc::HttpClient as QueryNymdClient; pub use cosmrs::rpc::HttpClient as QueryNymdClient;
@@ -44,6 +43,7 @@ pub use cosmrs::tx::{self, Gas};
pub use cosmrs::Coin as CosmosCoin; pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::{AccountId, Decimal, Denom}; pub use cosmrs::{AccountId, Decimal, Denom};
pub use signing_client::Client as SigningNymdClient; pub use signing_client::Client as SigningNymdClient;
use std::collections::HashMap;
pub use traits::{VestingQueryClient, VestingSigningClient}; pub use traits::{VestingQueryClient, VestingSigningClient};
pub mod cosmwasm_client; pub mod cosmwasm_client;
@@ -640,7 +640,7 @@ impl<C> NymdClient<C> {
recipient: &AccountId, recipient: &AccountId,
amount: Vec<CosmosCoin>, amount: Vec<CosmosCoin>,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> ) -> Result<broadcast::tx_commit::Response, NymdError>
where where
C: SigningCosmWasmClient + Sync, C: SigningCosmWasmClient + Sync,
{ {
@@ -655,7 +655,7 @@ impl<C> NymdClient<C> {
&self, &self,
msgs: Vec<(AccountId, Vec<CosmosCoin>)>, msgs: Vec<(AccountId, Vec<CosmosCoin>)>,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<TxResponse, NymdError> ) -> Result<broadcast::tx_commit::Response, NymdError>
where where
C: SigningCosmWasmClient + Sync, C: SigningCosmWasmClient + Sync,
{ {
@@ -197,45 +197,38 @@ impl DirectSecp256k1HdWalletBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use network_defaults::all::Network::*; use network_defaults::DEFAULT_NETWORK;
#[test] #[test]
fn generating_account_addresses() { fn generating_account_addresses() {
let (addr1, addr2, addr3) = match DEFAULT_NETWORK.bech32_prefix() {
"punk" => (
"punk1jw6mp7d5xqc7w6xm79lha27glmd0vdt32a3fj2",
"punk1h5hgn94nsq4kh99rjj794hr5h5q6yfm22mcqqn",
"punk17n9flp6jflljg6fp05dsy07wcprf2uuujse962",
),
"nymt" => (
"nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94",
"nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv",
"nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4",
),
_ => panic!("Test needs to be updated with new bech32 prefix"),
};
// test vectors produced from our js wallet // test vectors produced from our js wallet
let mnemonics = vec![ let mnemonic_address = vec![
"crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", ("crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", addr1),
"acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", ("acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", addr2),
"step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball" ("step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball", addr3)
];
let prefixes = vec![
MAINNET.bech32_prefix(),
SANDBOX.bech32_prefix(),
QA.bech32_prefix(),
]; ];
for prefix in prefixes { for (mnemonic, address) in mnemonic_address.into_iter() {
let addrs = match prefix { let prefix = DEFAULT_NETWORK.bech32_prefix();
"nymt" => vec![ let wallet =
"nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94", DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap()).unwrap();
"nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv", assert_eq!(
"nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4", wallet.try_derive_accounts().unwrap()[0].address,
], address.parse().unwrap()
"n" => vec![ )
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
"n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es",
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
],
_ => panic!("Test needs to be updated with new bech32 prefix"),
};
for (idx, mnemonic) in mnemonics.iter().enumerate() {
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap())
.unwrap();
assert_eq!(
wallet.try_derive_accounts().unwrap()[0].address,
addrs[idx].parse().unwrap()
)
}
} }
} }
} }
@@ -10,7 +10,7 @@ use std::collections::HashMap;
use url::Url; use url::Url;
use validator_api_requests::models::{ use validator_api_requests::models::{
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse, CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse, RewardEstimationResponse, StakeSaturationResponse,
}; };
pub mod error; pub mod error;
@@ -253,36 +253,6 @@ impl Client {
.await .await
} }
pub async fn get_mixnode_avg_uptime(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<UptimeResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::AVG_UPTIME,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_avg_uptimes(&self) -> Result<Vec<UptimeResponse>, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODES,
routes::AVG_UPTIME,
],
NO_PARAMS,
)
.await
}
pub async fn blind_sign( pub async fn blind_sign(
&self, &self,
request_body: &BlindSignRequestBody, request_body: &BlindSignRequestBody,
@@ -26,6 +26,5 @@ pub const SINCE_ARG: &str = "since";
pub const STATUS: &str = "status"; pub const STATUS: &str = "status";
pub const REWARD_ESTIMATION: &str = "reward-estimation"; pub const REWARD_ESTIMATION: &str = "reward-estimation";
pub const AVG_UPTIME: &str = "avg_uptime";
pub const STAKE_SATURATION: &str = "stake-saturation"; pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability"; pub const INCLUSION_CHANCE: &str = "inclusion-probability";
-1
View File
@@ -7,7 +7,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
cfg-if = "1.0.0"
handlebars = "3.0.1" handlebars = "3.0.1"
humantime-serde = "1.0" humantime-serde = "1.0"
log = "0.4" log = "0.4"
+8 -10
View File
@@ -69,16 +69,14 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
let location = custom_location let location = custom_location
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name())); .unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
cfg_if::cfg_if! { fs::write(location.clone(), templated_config)?;
if #[cfg(unix)] {
fs::write(location.clone(), templated_config)?; #[cfg(unix)]
let mut perms = fs::metadata(location.clone())?.permissions(); let mut perms = fs::metadata(location.clone())?.permissions();
perms.set_mode(0o600); #[cfg(unix)]
fs::set_permissions(location, perms)?; perms.set_mode(0o600);
} else { #[cfg(unix)]
fs::write(location, templated_config)?; fs::set_permissions(location, perms)?;
}
}
Ok(()) Ok(())
} }
@@ -13,11 +13,4 @@ pub enum MixnetContractError {
}, },
#[error("Error casting from U128")] #[error("Error casting from U128")]
CastError, CastError,
#[error("{source}")]
StdErr {
#[from]
source: cosmwasm_std::StdError,
},
#[error("Division by zero at {}", line!())]
DivisionByZero,
} }
@@ -222,17 +222,11 @@ impl DelegatorRewardParams {
} }
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 { pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
if self.sigma == 0 {
return 0;
}
// change all values into their fixed representations // change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128()); let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(self.reward_params.circulating_supply()); let circulating_supply = U128::from_num(self.reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply; let scaled_delegation_amount = delegation_amount / circulating_supply;
// Div by zero checked above
let delegator_reward = let delegator_reward =
(ONE - self.profit_margin) * (scaled_delegation_amount / self.sigma) * self.node_profit; (ONE - self.profit_margin) * (scaled_delegation_amount / self.sigma) * self.node_profit;
@@ -256,14 +250,8 @@ impl DelegatorRewardParams {
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)] #[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct StoredNodeRewardResult { pub struct StoredNodeRewardResult {
reward: Uint128, reward: Uint128,
lambda: Uint128,
#[schemars(with = "String")] sigma: Uint128,
#[serde(with = "fixed_U128_as_string")]
lambda: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
sigma: U128,
} }
impl StoredNodeRewardResult { impl StoredNodeRewardResult {
@@ -271,11 +259,11 @@ impl StoredNodeRewardResult {
self.reward self.reward
} }
pub fn lambda(&self) -> U128 { pub fn lambda(&self) -> Uint128 {
self.lambda self.lambda
} }
pub fn sigma(&self) -> U128 { pub fn sigma(&self) -> Uint128 {
self.sigma self.sigma
} }
} }
@@ -291,8 +279,18 @@ impl TryFrom<NodeRewardResult> for StoredNodeRewardResult {
.checked_cast() .checked_cast()
.ok_or(MixnetContractError::CastError)?, .ok_or(MixnetContractError::CastError)?,
), ),
lambda: node_reward_result.lambda(), lambda: Uint128::new(
sigma: node_reward_result.sigma(), node_reward_result
.lambda()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
sigma: Uint128::new(
node_reward_result
.sigma()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
}) })
} }
} }
@@ -471,16 +469,12 @@ impl MixNodeBond {
pub fn operator_reward(&self, params: &RewardParams) -> u128 { pub fn operator_reward(&self, params: &RewardParams) -> u128 {
let reward = self.reward(params); let reward = self.reward(params);
if reward.sigma == 0 {
return 0;
}
let profit = if reward.reward < params.node.operator_cost() { let profit = if reward.reward < params.node.operator_cost() {
U128::from_num(0u128) U128::from_num(0u128)
} else { } else {
reward.reward - params.node.operator_cost() reward.reward - params.node.operator_cost()
}; };
let operator_base_reward = reward.reward.min(params.node.operator_cost()); let operator_base_reward = reward.reward.min(params.node.operator_cost());
// Div by zero checked above
let operator_reward = (self.profit_margin() let operator_reward = (self.profit_margin()
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma) + (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
* profit; * profit;
@@ -177,13 +177,6 @@ pub enum QueryMsg {
owner_address: String, owner_address: String,
proxy_address: Option<String>, proxy_address: Option<String>,
}, },
GetCheckpointsForMixnode {
mix_identity: IdentityKey,
},
GetMixnodeAtHeight {
mix_identity: IdentityKey,
height: u64,
},
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -25,11 +25,11 @@ impl NodeEpochRewards {
self.epoch_id self.epoch_id
} }
pub fn sigma(&self) -> U128 { pub fn sigma(&self) -> Uint128 {
self.result.sigma() self.result.sigma()
} }
pub fn lambda(&self) -> U128 { pub fn lambda(&self) -> Uint128 {
self.result.lambda() self.result.lambda()
} }
@@ -47,19 +47,20 @@ impl NodeEpochRewards {
pub fn node_profit(&self) -> U128 { pub fn node_profit(&self) -> U128 {
let reward = U128::from_num(self.reward().u128()); let reward = U128::from_num(self.reward().u128());
// if operating cost is higher then the reward node profit is 0 if reward < self.operator_cost() {
reward.saturating_sub(self.operator_cost()) U128::from_num(0u128)
} else {
reward - self.operator_cost()
}
} }
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> { pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
let reward = self.node_profit(); let reward = self.node_profit();
let operator_base_reward = reward.min(self.operator_cost()); let operator_base_reward = reward.min(self.operator_cost());
let div_by_zero_check = if let Some(value) = self.lambda().checked_div(self.sigma()) { let operator_reward = (profit_margin
value + (ONE - profit_margin) * U128::from_num(self.lambda().u128())
} else { / U128::from_num(self.sigma().u128()))
return Err(MixnetContractError::DivisionByZero); * reward;
};
let operator_reward = (profit_margin + (ONE - profit_margin) * div_by_zero_check) * reward;
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128)); let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
@@ -81,15 +82,9 @@ impl NodeEpochRewards {
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply()); let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply; let scaled_delegation_amount = delegation_amount / circulating_supply;
let delegator_reward = (ONE - profit_margin) * scaled_delegation_amount
let check_div_by_zero = / U128::from_num(self.sigma().u128())
if let Some(value) = scaled_delegation_amount.checked_div(self.sigma()) { * self.node_profit();
value
} else {
return Err(MixnetContractError::DivisionByZero);
};
let delegator_reward = (ONE - profit_margin) * check_div_by_zero * self.node_profit();
let reward = delegator_reward.max(U128::ZERO); let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() { if let Some(int_reward) = reward.checked_cast() {
@@ -206,11 +201,6 @@ impl RewardParams {
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size()) let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128()); - (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
if denom == 0 {
return U128::ZERO;
}
// Div by zero checked above
if self.in_active_set() { if self.in_active_set() {
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes) // work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
self.active_set_work_factor() / denom * self.rewarded_set_size() self.active_set_work_factor() / denom * self.rewarded_set_size()
+3 -9
View File
@@ -68,7 +68,6 @@ pub fn creating_dealing_for_3_parties(c: &mut Criterion) {
threshold, threshold,
epoch, epoch,
&receivers, &receivers,
None,
) )
}) })
}) })
@@ -90,7 +89,6 @@ pub fn verifying_dealing_made_for_3_parties_and_recovering_share(c: &mut Criteri
threshold, threshold,
epoch, epoch,
&receivers, &receivers,
None,
); );
let first_key = dks.get_mut(0).unwrap(); let first_key = dks.get_mut(0).unwrap();
@@ -101,7 +99,7 @@ pub fn verifying_dealing_made_for_3_parties_and_recovering_share(c: &mut Criteri
|b| { |b| {
b.iter(|| { b.iter(|| {
assert!(dealing assert!(dealing
.verify(&params, epoch, threshold, &receivers, None) .verify(&params, epoch, threshold, &receivers)
.is_ok()); .is_ok());
black_box(decrypt_share(first_key, 0, &dealing.ciphertexts, epoch, None).unwrap()); black_box(decrypt_share(first_key, 0, &dealing.ciphertexts, epoch, None).unwrap());
}) })
@@ -130,7 +128,6 @@ pub fn creating_dealing_for_20_parties(c: &mut Criterion) {
threshold, threshold,
epoch, epoch,
&receivers, &receivers,
None,
) )
}) })
}) })
@@ -153,7 +150,6 @@ pub fn verifying_dealing_made_for_20_parties_and_recovering_share(c: &mut Criter
threshold, threshold,
epoch, epoch,
&receivers, &receivers,
None,
); );
let first_key = dks.get_mut(0).unwrap(); let first_key = dks.get_mut(0).unwrap();
@@ -164,7 +160,7 @@ pub fn verifying_dealing_made_for_20_parties_and_recovering_share(c: &mut Criter
|b| { |b| {
b.iter(|| { b.iter(|| {
assert!(dealing assert!(dealing
.verify(&params, epoch, threshold, &receivers, None) .verify(&params, epoch, threshold, &receivers)
.is_ok()); .is_ok());
black_box(decrypt_share(first_key, 0, &dealing.ciphertexts, epoch, None).unwrap()); black_box(decrypt_share(first_key, 0, &dealing.ciphertexts, epoch, None).unwrap());
}) })
@@ -193,7 +189,6 @@ pub fn creating_dealing_for_100_parties(c: &mut Criterion) {
threshold, threshold,
epoch, epoch,
&receivers, &receivers,
None,
) )
}) })
}) })
@@ -216,7 +211,6 @@ pub fn verifying_dealing_made_for_100_parties_and_recovering_share(c: &mut Crite
threshold, threshold,
epoch, epoch,
&receivers, &receivers,
None,
); );
let first_key = dks.get_mut(0).unwrap(); let first_key = dks.get_mut(0).unwrap();
@@ -227,7 +221,7 @@ pub fn verifying_dealing_made_for_100_parties_and_recovering_share(c: &mut Crite
|b| { |b| {
b.iter(|| { b.iter(|| {
assert!(dealing assert!(dealing
.verify(&params, epoch, threshold, &receivers, None) .verify(&params, epoch, threshold, &receivers)
.is_ok()); .is_ok());
black_box(decrypt_share(first_key, 0, &dealing.ciphertexts, epoch, None).unwrap()); black_box(decrypt_share(first_key, 0, &dealing.ciphertexts, epoch, None).unwrap());
}) })
@@ -69,10 +69,9 @@ mod tests {
#[test] #[test]
fn wrong_prefix_fails() { fn wrong_prefix_fails() {
assert_eq!( assert_eq!(
Err(Bech32Error::WrongPrefix(format!( Err(Bech32Error::WrongPrefix(
"your bech32 address prefix should be {}, not punk", "your bech32 address prefix should be nymt, not punk".to_string()
DEFAULT_NETWORK.bech32_prefix() )),
))),
validate_bech32_prefix("punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0") validate_bech32_prefix("punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0")
) )
} }
@@ -81,9 +80,7 @@ mod tests {
fn correct_prefix_works() { fn correct_prefix_works() {
assert_eq!( assert_eq!(
Ok(()), Ok(()),
validate_bech32_prefix( validate_bech32_prefix("nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9")
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g"
)
) )
} }
} }
+2 -2
View File
@@ -3,8 +3,8 @@
fn main() { fn main() {
match option_env!("NETWORK") { match option_env!("NETWORK") {
None | Some("mainnet") => println!("cargo:rustc-cfg=network=\"mainnet\"",), Some("mainnet") => println!("cargo:rustc-cfg=network=\"mainnet\"",),
Some("sandbox") => println!("cargo:rustc-cfg=network=\"sandbox\"",), None | Some("sandbox") => println!("cargo:rustc-cfg=network=\"sandbox\"",),
Some("qa") => println!("cargo:rustc-cfg=network=\"qa\""), Some("qa") => println!("cargo:rustc-cfg=network=\"qa\""),
_ => panic!("No such network"), _ => panic!("No such network"),
} }
-7
View File
@@ -101,13 +101,6 @@ impl ValidatorDetails {
} }
} }
pub fn new_with_name(nymd_url: &str, api_url: Option<&str>) -> Self {
ValidatorDetails {
nymd_url: nymd_url.to_string(),
api_url: api_url.map(ToString::to_string),
}
}
pub fn nymd_url(&self) -> Url { pub fn nymd_url(&self) -> Url {
self.nymd_url self.nymd_url
.parse() .parse()
+1 -1
View File
@@ -21,6 +21,6 @@ pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hh
pub(crate) fn validators() -> Vec<ValidatorDetails> { pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new( vec![ValidatorDetails::new(
"https://rpc.nyx.nodes.guru/", "https://rpc.nyx.nodes.guru/",
Some("https://validator.nymtech.net/api/"), Some("https://validator.nymtech.net/api"),
)] )]
} }
+7 -9
View File
@@ -3,24 +3,22 @@
use crate::ValidatorDetails; use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "n"; pub(crate) const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unym"; pub const DENOM: &str = "unymt";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt17x6pt4msccvawgxjeg5nmnygttu56tftg5l6j3";
"n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep"; pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1t4dmskxea0avvrj8xtmu66hv7dkyg9s8059t3c";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
"n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000"); hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000"); hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq"; pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
pub(crate) fn validators() -> Vec<ValidatorDetails> { pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new( vec![ValidatorDetails::new(
"https://qa-validator.nymtech.net", "https://qa-validator.nymtech.net",
Some("https://qa-validator-api.nymtech.net/api"), Some("https://qa-validator.nymtech.net/api"),
)] )]
} }
-2
View File
@@ -1,7 +1,5 @@
pub mod msg;
pub mod request; pub mod request;
pub mod response; pub mod response;
pub use msg::*;
pub use request::*; pub use request::*;
pub use response::*; pub use response::*;
-63
View File
@@ -1,63 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::request::{Request, RequestError};
use crate::response::{Response, ResponseError};
#[derive(Debug)]
pub enum MessageError {
Request(RequestError),
Response(ResponseError),
NoData,
UnknownMessageType,
}
impl std::fmt::Display for MessageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MessageError::Request(r) => write!(f, "{}", r),
MessageError::Response(r) => write!(f, "{:?}", r),
MessageError::NoData => write!(f, "no data provided"),
MessageError::UnknownMessageType => write!(f, "unknown message type received"),
}
}
}
pub enum Message {
Request(Request),
Response(Response),
}
impl Message {
const REQUEST_FLAG: u8 = 0;
const RESPONSE_FLAG: u8 = 1;
pub fn try_from_bytes(b: &[u8]) -> Result<Message, MessageError> {
if b.is_empty() {
return Err(MessageError::NoData);
}
if b[0] == Self::REQUEST_FLAG {
Request::try_from_bytes(&b[1..])
.map(Message::Request)
.map_err(MessageError::Request)
} else if b[0] == Self::RESPONSE_FLAG {
Response::try_from_bytes(&b[1..])
.map(Message::Response)
.map_err(MessageError::Response)
} else {
Err(MessageError::UnknownMessageType)
}
}
pub fn into_bytes(self) -> Vec<u8> {
match self {
Self::Request(r) => std::iter::once(Self::REQUEST_FLAG)
.chain(r.into_bytes().iter().cloned())
.collect(),
Self::Response(r) => std::iter::once(Self::RESPONSE_FLAG)
.chain(r.into_bytes().iter().cloned())
.collect(),
}
}
}
+3 -4
View File
@@ -41,7 +41,7 @@ checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4"
[[package]] [[package]]
name = "bandwidth-claim" name = "bandwidth-claim"
version = "1.0.0" version = "1.0.0-rc.2"
dependencies = [ dependencies = [
"bandwidth-claim-contract", "bandwidth-claim-contract",
"config", "config",
@@ -227,7 +227,6 @@ dependencies = [
name = "config" name = "config"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cfg-if",
"handlebars", "handlebars",
"humantime-serde", "humantime-serde",
"log", "log",
@@ -981,9 +980,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.16" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "bandwidth-claim" name = "bandwidth-claim"
version = "1.0.0" version = "1.0.0-rc.2"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+3 -2
View File
@@ -1,8 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net> // Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// approximately 1 epoch (assuming 5s per block) // approximately 1 week (assuming 5s per block)
pub const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 720; // i.e. approximately quarter of the interval (there are 3600 * 60 * 7 = 604800 seconds in a week, i.e. ~604800 / 5 = 120960 blocks)
pub const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 120960;
pub const INTERVAL_REWARD_PERCENT: u8 = 2; // Used to calculate interval reward pool pub const INTERVAL_REWARD_PERCENT: u8 = 2; // Used to calculate interval reward pool
pub const SYBIL_RESISTANCE_PERCENT: u8 = 30; pub const SYBIL_RESISTANCE_PERCENT: u8 = 30;
+4 -24
View File
@@ -24,17 +24,14 @@ use crate::mixnet_contract_settings::queries::{
use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address; use crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address;
use crate::mixnodes::bonding_queries as mixnode_queries; use crate::mixnodes::bonding_queries as mixnode_queries;
use crate::mixnodes::bonding_queries::{ use crate::mixnodes::bonding_queries::query_mixnodes_paged;
query_checkpoints_for_mixnode, query_mixnode_at_height, query_mixnodes_paged,
};
use crate::mixnodes::layer_queries::query_layer_distribution; use crate::mixnodes::layer_queries::query_layer_distribution;
use crate::rewards::queries::{ use crate::rewards::queries::{
query_circulating_supply, query_reward_pool, query_rewarding_status, query_circulating_supply, query_reward_pool, query_rewarding_status,
}; };
use crate::rewards::storage as rewards_storage; use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{ use cosmwasm_std::{
entry_point, to_binary, Addr, Api, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128,
Uint128,
}; };
use mixnet_contract_common::mixnode::DelegationEvent; use mixnet_contract_common::mixnode::DelegationEvent;
use mixnet_contract_common::{ use mixnet_contract_common::{
@@ -57,10 +54,6 @@ pub const INITIAL_ACTIVE_SET_WORK_FACTOR: u8 = 10;
pub const DEFAULT_FIRST_INTERVAL_START: OffsetDateTime = pub const DEFAULT_FIRST_INTERVAL_START: OffsetDateTime =
time::macros::datetime!(2022-01-01 12:00 UTC); time::macros::datetime!(2022-01-01 12:00 UTC);
pub fn debug_with_visibility<S: Into<String>>(api: &dyn Api, msg: S) {
api.debug(&*format!("\n\n\n=========================================\n{}\n=========================================\n\n\n", msg.into()));
}
fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState { fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState {
ContractState { ContractState {
owner, owner,
@@ -394,19 +387,13 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
QueryMsg::DebugGetAllDelegationValues {} => to_binary( QueryMsg::DebugGetAllDelegationValues {} => to_binary(
&crate::delegations::queries::debug_query_all_delegation_values(deps.storage)?, &crate::delegations::queries::debug_query_all_delegation_values(deps.storage)?,
), ),
QueryMsg::GetCheckpointsForMixnode { mix_identity } => {
to_binary(&query_checkpoints_for_mixnode(deps, mix_identity)?)
}
QueryMsg::GetMixnodeAtHeight {
mix_identity,
height,
} => to_binary(&query_mixnode_at_height(deps, mix_identity, height)?),
}; };
Ok(query_res?) Ok(query_res?)
} }
fn deal_with_zero_delegations(deps: DepsMut<'_>) -> Result<(), ContractError> { #[entry_point]
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
// if there exists any delegation of 0 value, remove it // if there exists any delegation of 0 value, remove it
let zero_delegations = delegations() let zero_delegations = delegations()
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending) .range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
@@ -435,13 +422,6 @@ fn deal_with_zero_delegations(deps: DepsMut<'_>) -> Result<(), ContractError> {
.remove(deps.storage, delegation_event); .remove(deps.storage, delegation_event);
} }
Ok(())
}
#[entry_point]
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
deal_with_zero_delegations(deps)?;
Ok(Default::default()) Ok(Default::default())
} }
@@ -1,8 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::storage::{self, PENDING_DELEGATION_EVENTS}; use super::storage::{self, PENDING_DELEGATION_EVENTS};
// use crate::contract::debug_with_visibility;
// use crate::contract::debug_with_visibility;
use crate::error::ContractError; use crate::error::ContractError;
use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage; use crate::mixnodes::storage as mixnodes_storage;
@@ -30,13 +28,12 @@ pub fn try_reconcile_all_delegation_events(
return Err(ContractError::Unauthorized); return Err(ContractError::Unauthorized);
} }
_try_reconcile_all_delegation_events(deps.storage, deps.api) _try_reconcile_all_delegation_events(deps.storage)
} }
// TODO: Error handling? // TODO: Error handling?
pub(crate) fn _try_reconcile_all_delegation_events( pub(crate) fn _try_reconcile_all_delegation_events(
storage: &mut dyn Storage, storage: &mut dyn Storage,
api: &dyn Api,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
let pending_delegation_events = PENDING_DELEGATION_EVENTS let pending_delegation_events = PENDING_DELEGATION_EVENTS
.range(storage, None, None, Order::Ascending) .range(storage, None, None, Order::Ascending)
@@ -45,8 +42,6 @@ pub(crate) fn _try_reconcile_all_delegation_events(
let mut response = Response::new(); let mut response = Response::new();
// debug_with_visibility(api, "Reconciling delegation events");
for (key, delegation_event) in pending_delegation_events { for (key, delegation_event) in pending_delegation_events {
match delegation_event { match delegation_event {
DelegationEvent::Delegate(delegation) => { DelegationEvent::Delegate(delegation) => {
@@ -58,8 +53,7 @@ pub(crate) fn _try_reconcile_all_delegation_events(
response = response.add_event(event); response = response.add_event(event);
} }
DelegationEvent::Undelegate(pending_undelegate) => { DelegationEvent::Undelegate(pending_undelegate) => {
let undelegate_response = let undelegate_response = try_reconcile_undelegation(storage, &pending_undelegate)?;
try_reconcile_undelegation(storage, api, &pending_undelegate)?;
response = response.add_event(undelegate_response.event); response = response.add_event(undelegate_response.event);
if let Some(msg) = undelegate_response.bank_msg { if let Some(msg) = undelegate_response.bank_msg {
response = response.add_message(msg); response = response.add_message(msg);
@@ -107,7 +101,8 @@ pub(crate) fn try_delegate_to_mixnode(
let amount = validate_delegation_stake(info.funds)?; let amount = validate_delegation_stake(info.funds)?;
_try_delegate_to_mixnode( _try_delegate_to_mixnode(
deps, deps.storage,
deps.api,
env.block.height, env.block.height,
&mix_identity, &mix_identity,
info.sender.as_str(), info.sender.as_str(),
@@ -127,7 +122,8 @@ pub(crate) fn try_delegate_to_mixnode_on_behalf(
let amount = validate_delegation_stake(info.funds)?; let amount = validate_delegation_stake(info.funds)?;
_try_delegate_to_mixnode( _try_delegate_to_mixnode(
deps, deps.storage,
deps.api,
env.block.height, env.block.height,
&mix_identity, &mix_identity,
&delegate, &delegate,
@@ -177,18 +173,19 @@ pub(crate) fn try_reconcile_delegation(
} }
pub(crate) fn _try_delegate_to_mixnode( pub(crate) fn _try_delegate_to_mixnode(
deps: DepsMut<'_>, storage: &mut dyn Storage,
api: &dyn Api,
block_height: u64, block_height: u64,
mix_identity: &str, mix_identity: &str,
delegate: &str, delegate: &str,
amount: Coin, amount: Coin,
proxy: Option<Addr>, proxy: Option<Addr>,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
let delegate = deps.api.addr_validate(delegate)?; let delegate = api.addr_validate(delegate)?;
// check if the target node actually exists // check if the target node actually exists
if mixnodes_storage::mixnodes() if mixnodes_storage::mixnodes()
.may_load(deps.storage, mix_identity)? .may_load(storage, mix_identity)?
.is_none() .is_none()
{ {
return Err(ContractError::MixNodeBondNotFound { return Err(ContractError::MixNodeBondNotFound {
@@ -205,7 +202,7 @@ pub(crate) fn _try_delegate_to_mixnode(
); );
if storage::PENDING_DELEGATION_EVENTS if storage::PENDING_DELEGATION_EVENTS
.may_load(deps.storage, delegation.event_storage_key())? .may_load(storage, delegation.event_storage_key())?
.is_some() .is_some()
{ {
return Err(ContractError::DelegationEventAlreadyPending { return Err(ContractError::DelegationEventAlreadyPending {
@@ -216,7 +213,7 @@ pub(crate) fn _try_delegate_to_mixnode(
} }
storage::PENDING_DELEGATION_EVENTS.save( storage::PENDING_DELEGATION_EVENTS.save(
deps.storage, storage,
delegation.event_storage_key(), delegation.event_storage_key(),
&DelegationEvent::Delegate(delegation), &DelegationEvent::Delegate(delegation),
)?; )?;
@@ -256,13 +253,10 @@ pub struct ReconcileUndelegateResponse {
pub(crate) fn try_reconcile_undelegation( pub(crate) fn try_reconcile_undelegation(
storage: &mut dyn Storage, storage: &mut dyn Storage,
api: &dyn Api,
pending_undelegate: &PendingUndelegate, pending_undelegate: &PendingUndelegate,
) -> Result<ReconcileUndelegateResponse, ContractError> { ) -> Result<ReconcileUndelegateResponse, ContractError> {
let delegation_map = storage::delegations(); let delegation_map = storage::delegations();
// debug_with_visibility(api, "Reconciling undelegations");
let any_delegations = delegation_map let any_delegations = delegation_map
.prefix(pending_undelegate.storage_key()) .prefix(pending_undelegate.storage_key())
.keys(storage, None, None, cosmwasm_std::Order::Ascending) .keys(storage, None, None, cosmwasm_std::Order::Ascending)
@@ -286,13 +280,10 @@ pub(crate) fn try_reconcile_undelegation(
let reward = crate::rewards::transactions::calculate_delegator_reward( let reward = crate::rewards::transactions::calculate_delegator_reward(
storage, storage,
api,
pending_undelegate.proxy_storage_key(), pending_undelegate.proxy_storage_key(),
&pending_undelegate.mix_identity(), &pending_undelegate.mix_identity(),
)?; )?;
// debug_with_visibility(api, format!("Delegator reward: {}", reward));
// Might want to introduce paging here // Might want to introduce paging here
let delegation_heights = delegation_map let delegation_heights = delegation_map
.prefix(pending_undelegate.storage_key()) .prefix(pending_undelegate.storage_key())
@@ -316,23 +307,12 @@ pub(crate) fn try_reconcile_undelegation(
let mut total_delegation = Uint128::zero(); let mut total_delegation = Uint128::zero();
// debug_with_visibility(api, "Reducing accumulated rewards"); if crate::mixnodes::storage::mixnodes()
.may_load(storage, &pending_undelegate.mix_identity())?
.is_none()
{ {
if let Some(mut bond) = crate::mixnodes::storage::mixnodes() // Since the mixnode is no longer bonded the reward did not compound and we need to manually add it to the total
.may_load(storage, &pending_undelegate.mix_identity())? total_delegation = reward;
{
let remaining = bond.accumulated_rewards().saturating_sub(reward);
// debug_with_visibility(api, format!("Remaining accumulated rewards: {}", remaining));
bond.accumulated_rewards = Some(remaining);
crate::mixnodes::storage::mixnodes().save(
storage,
&pending_undelegate.mix_identity(),
&bond,
pending_undelegate.block_height(),
)?;
}
} }
for h in delegation_heights { for h in delegation_heights {
@@ -347,40 +327,6 @@ pub(crate) fn try_reconcile_undelegation(
)?; )?;
} }
mixnodes_storage::TOTAL_DELEGATION.update::<_, ContractError>(
storage,
&pending_undelegate.mix_identity(),
|total_node_delegation| {
// debug_with_visibility(api, "Setting total delegation");
let remaining = match total_node_delegation.unwrap().checked_sub(total_delegation) {
Ok(remaining) => remaining,
Err(_) => {
// debug_with_visibility(
// api,
// format!(
// "Overflowed delegation subsctraction, {} - {}",
// total_node_delegation.unwrap(),
// total_delegation
// ),
// );
return Err(ContractError::TotalDelegationSubOverflow {
mix_identity: pending_undelegate.mix_identity(),
total_node_delegation: total_node_delegation.unwrap().u128(),
to_subtract: total_delegation.u128(),
});
}
};
// debug_with_visibility(api, format!("Remaining total delegation: {}", remaining));
// the first unwrap is fine because the delegation information MUST exist, otherwise we would
// have never gotten here in the first place
// the second unwrap is also fine because we should NEVER underflow here,
// if we do, it means we have some serious error in our logic
Ok(remaining)
},
)?;
let total_funds = total_delegation + reward;
// don't add a bank message if it would have resulted in attempting to send 0 tokens // don't add a bank message if it would have resulted in attempting to send 0 tokens
let bank_msg = if total_delegation != Uint128::zero() { let bank_msg = if total_delegation != Uint128::zero() {
Some(BankMsg::Send { Some(BankMsg::Send {
@@ -389,19 +335,34 @@ pub(crate) fn try_reconcile_undelegation(
.as_ref() .as_ref()
.unwrap_or(&pending_undelegate.delegate()) .unwrap_or(&pending_undelegate.delegate())
.to_string(), .to_string(),
amount: coins(total_funds.u128(), DENOM), amount: coins(total_delegation.u128(), DENOM),
}) })
} else { } else {
None None
}; };
mixnodes_storage::TOTAL_DELEGATION.update::<_, ContractError>(
storage,
&pending_undelegate.mix_identity(),
|total_node_delegation| {
// the first unwrap is fine because the delegation information MUST exist, otherwise we would
// have never gotten here in the first place
// the second unwrap is also fine because we should NEVER underflow here,
// if we do, it means we have some serious error in our logic
Ok(total_node_delegation
.unwrap()
.checked_sub(total_delegation)
.unwrap())
},
)?;
let mut wasm_msg = None; let mut wasm_msg = None;
if let Some(proxy) = &pending_undelegate.proxy() { if let Some(proxy) = &pending_undelegate.proxy() {
let msg = Some(VestingContractExecuteMsg::TrackUndelegation { let msg = Some(VestingContractExecuteMsg::TrackUndelegation {
owner: pending_undelegate.delegate().as_str().to_string(), owner: pending_undelegate.delegate().as_str().to_string(),
mix_identity: pending_undelegate.mix_identity(), mix_identity: pending_undelegate.mix_identity(),
amount: Coin::new(total_funds.u128(), DENOM), amount: Coin::new(total_delegation.u128(), DENOM),
}); });
wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin()])?); wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin()])?);
@@ -411,11 +372,9 @@ pub(crate) fn try_reconcile_undelegation(
&pending_undelegate.delegate(), &pending_undelegate.delegate(),
&pending_undelegate.proxy(), &pending_undelegate.proxy(),
&pending_undelegate.mix_identity(), &pending_undelegate.mix_identity(),
total_funds, total_delegation,
); );
// debug_with_visibility(api, "Done");
Ok(ReconcileUndelegateResponse { Ok(ReconcileUndelegateResponse {
bank_msg, bank_msg,
wasm_msg, wasm_msg,
@@ -568,7 +527,7 @@ mod tests {
) )
.is_ok()); .is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let expected = Delegation::new( let expected = Delegation::new(
delegation_owner.clone(), delegation_owner.clone(),
@@ -647,7 +606,7 @@ mod tests {
) )
.is_ok()); .is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let expected = Delegation::new( let expected = Delegation::new(
delegation_owner.clone(), delegation_owner.clone(),
@@ -710,7 +669,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// let expected = Delegation::new( // let expected = Delegation::new(
// delegation_owner.clone(), // delegation_owner.clone(),
@@ -765,7 +724,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert_eq!( assert_eq!(
initial_height, initial_height,
@@ -786,7 +745,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let delegations = crate::delegations::queries::query_mixnode_delegation( let delegations = crate::delegations::queries::query_mixnode_delegation(
&deps.storage, &deps.storage,
@@ -831,7 +790,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert_eq!( assert_eq!(
initial_height, initial_height,
@@ -852,7 +811,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert_eq!( assert_eq!(
initial_height, initial_height,
@@ -940,7 +899,7 @@ mod tests {
) )
.is_ok()); .is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let expected1 = Delegation::new( let expected1 = Delegation::new(
delegation_owner.clone(), delegation_owner.clone(),
@@ -1005,7 +964,7 @@ mod tests {
identity.clone(), identity.clone(),
) )
.is_ok()); .is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// node's "total_delegation" is sum of both // node's "total_delegation" is sum of both
assert_eq!( assert_eq!(
@@ -1035,7 +994,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
@@ -1123,7 +1082,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let _delegation = query_mixnode_delegation( let _delegation = query_mixnode_delegation(
&deps.storage, &deps.storage,
@@ -1193,7 +1152,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
let delegation = query_mixnode_delegation( let delegation = query_mixnode_delegation(
&deps.storage, &deps.storage,
@@ -1228,7 +1187,7 @@ mod tests {
) )
); );
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert!(test_helpers::read_delegation( assert!(test_helpers::read_delegation(
&deps.storage, &deps.storage,
@@ -1261,7 +1220,7 @@ mod tests {
) )
.is_ok()); .is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
assert!(try_delegate_to_mixnode( assert!(try_delegate_to_mixnode(
deps.as_mut(), deps.as_mut(),
@@ -1271,7 +1230,7 @@ mod tests {
) )
.is_ok()); .is_ok());
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// sender1 undelegates // sender1 undelegates
try_remove_delegation_from_mixnode( try_remove_delegation_from_mixnode(
@@ -1282,7 +1241,7 @@ mod tests {
) )
.unwrap(); .unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage).unwrap();
// but total delegation should still equal to what sender2 sent // but total delegation should still equal to what sender2 sent
// node's "total_delegation" is sum of both // node's "total_delegation" is sum of both
assert_eq!( assert_eq!(
-11
View File
@@ -168,15 +168,4 @@ pub enum ContractError {
identity: String, identity: String,
kind: String, // delegation | undelegation kind: String, // delegation | undelegation
}, },
#[error("Attempted to subsctract more then the total delegation, this MUST never happen! mix: {mix_identity}, total_node_delegation {total_node_delegation}, to_subtract {to_subtract}")]
TotalDelegationSubOverflow {
mix_identity: String,
total_node_delegation: u128,
to_subtract: u128,
},
#[error("Profit margin can be updated only once during a rolling 30 day interval, last update was at {last_update_time} and current block time is {current_block_time}")]
UpdatePMTooSoon {
last_update_time: u64,
current_block_time: u64,
},
} }
@@ -1,33 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::storage::{self, StoredMixnodeBond}; use super::storage;
use cosmwasm_std::{Deps, Order, StdResult}; use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound; use cw_storage_plus::Bound;
use mixnet_contract_common::{ use mixnet_contract_common::{
IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse, IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse,
}; };
pub fn query_mixnode_at_height(
deps: Deps<'_>,
mix_identity: String,
height: u64,
) -> StdResult<Option<StoredMixnodeBond>> {
storage::mixnodes().may_load_at_height(deps.storage, &mix_identity, height)
}
pub fn query_checkpoints_for_mixnode(
deps: Deps<'_>,
mix_identity: IdentityKey,
) -> StdResult<Vec<u64>> {
Ok(storage::mixnodes()
.changelog()
.prefix(&mix_identity)
.keys(deps.storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.collect())
}
pub fn query_mixnodes_paged( pub fn query_mixnodes_paged(
deps: Deps<'_>, deps: Deps<'_>,
start_after: Option<IdentityKey>, start_after: Option<IdentityKey>,
+1 -6
View File
@@ -19,8 +19,6 @@ const MIXNODES_PK_CHANGELOG: &str = "mn__change";
const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno"; const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns"; const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
const LAST_PM_UPDATE_NAMESPACE: &str = "lpm";
// paged retrieval limits for all queries and transactions // paged retrieval limits for all queries and transactions
pub(crate) const BOND_PAGE_MAX_LIMIT: u32 = 75; pub(crate) const BOND_PAGE_MAX_LIMIT: u32 = 75;
pub(crate) const BOND_PAGE_DEFAULT_LIMIT: u32 = 50; pub(crate) const BOND_PAGE_DEFAULT_LIMIT: u32 = 50;
@@ -28,9 +26,6 @@ pub(crate) const BOND_PAGE_DEFAULT_LIMIT: u32 = 50;
pub(crate) const TOTAL_DELEGATION: Map<'_, IdentityKeyRef<'_>, Uint128> = pub(crate) const TOTAL_DELEGATION: Map<'_, IdentityKeyRef<'_>, Uint128> =
Map::new(TOTAL_DELEGATION_NAMESPACE); Map::new(TOTAL_DELEGATION_NAMESPACE);
pub(crate) const LAST_PM_UPDATE_TIME: Map<'_, IdentityKeyRef<'_>, u64> =
Map::new(LAST_PM_UPDATE_NAMESPACE);
pub(crate) struct MixnodeBondIndex<'a> { pub(crate) struct MixnodeBondIndex<'a> {
pub(crate) owner: UniqueIndex<'a, Addr, StoredMixnodeBond>, pub(crate) owner: UniqueIndex<'a, Addr, StoredMixnodeBond>,
@@ -60,7 +55,7 @@ pub(crate) fn mixnodes<'a>(
MIXNODES_PK_NAMESPACE, MIXNODES_PK_NAMESPACE,
MIXNODES_PK_CHECKPOINTS, MIXNODES_PK_CHECKPOINTS,
MIXNODES_PK_CHANGELOG, MIXNODES_PK_CHANGELOG,
Strategy::Selected, Strategy::Never,
indexes, indexes,
) )
} }
+4 -76
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::storage::{self, LAST_PM_UPDATE_TIME}; use super::storage;
use crate::error::ContractError; use crate::error::ContractError;
use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::layer_queries::query_layer_distribution; use crate::mixnodes::layer_queries::query_layer_distribution;
@@ -18,8 +18,6 @@ use mixnet_contract_common::MixNode;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_ucoin; use vesting_contract_common::one_ucoin;
const MIN_PM_UPDATE_INTERVAL: u64 = 60 * 60 * 24 * 30; // one month roughly
pub fn try_checkpoint_mixnodes( pub fn try_checkpoint_mixnodes(
storage: &mut dyn Storage, storage: &mut dyn Storage,
block_height: u64, block_height: u64,
@@ -154,8 +152,6 @@ fn _try_add_mixnode(
storage::TOTAL_DELEGATION.save(deps.storage, identity, &Uint128::zero())?; storage::TOTAL_DELEGATION.save(deps.storage, identity, &Uint128::zero())?;
} }
storage::LAST_PM_UPDATE_TIME.save(deps.storage, identity, &env.block.time.seconds())?;
mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?; mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?;
Ok(Response::new().add_event(new_mixnode_bonding_event( Ok(Response::new().add_event(new_mixnode_bonding_event(
@@ -195,7 +191,6 @@ pub(crate) fn _try_remove_mixnode(
crate::rewards::transactions::_try_compound_operator_reward( crate::rewards::transactions::_try_compound_operator_reward(
deps.storage, deps.storage,
deps.api,
env.block.height, env.block.height,
&owner, &owner,
None, None,
@@ -298,19 +293,6 @@ pub(crate) fn _try_update_mixnode_config(
}); });
} }
let last_update_time = storage::LAST_PM_UPDATE_TIME
.load(deps.storage, mixnode_bond.identity())
.unwrap_or(0);
let current_block_time = env.block.time.seconds();
if current_block_time - last_update_time < MIN_PM_UPDATE_INTERVAL {
return Err(ContractError::UpdatePMTooSoon {
last_update_time,
current_block_time,
});
}
// We don't have to check lower bound as its an u8 // We don't have to check lower bound as its an u8
if profit_margin_percent > 100 { if profit_margin_percent > 100 {
return Err(ContractError::InvalidProfitMarginPercent( return Err(ContractError::InvalidProfitMarginPercent(
@@ -333,8 +315,6 @@ pub(crate) fn _try_update_mixnode_config(
}, },
)?; )?;
LAST_PM_UPDATE_TIME.save(deps.storage, mixnode_bond.identity(), &current_block_time)?;
let mut response = Response::new(); let mut response = Response::new();
if let Some(proxy) = proxy { if let Some(proxy) = proxy {
@@ -381,8 +361,6 @@ fn validate_mixnode_pledge(
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use std::f64::MIN;
use super::*; use super::*;
use crate::contract::{execute, query, INITIAL_MIXNODE_PLEDGE}; use crate::contract::{execute, query, INITIAL_MIXNODE_PLEDGE};
use crate::error::ContractError; use crate::error::ContractError;
@@ -736,7 +714,6 @@ pub mod tests {
#[test] #[test]
fn updating_mixnode_config() { fn updating_mixnode_config() {
let sender = "bob"; let sender = "bob";
let mut env = mock_env();
let mut deps = test_helpers::init_contract(); let mut deps = test_helpers::init_contract();
let info = mock_info(sender, &[]); let info = mock_info(sender, &[]);
@@ -744,7 +721,7 @@ pub mod tests {
let msg = ExecuteMsg::UpdateMixnodeConfig { let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent: 10, profit_margin_percent: 10,
}; };
let ret = execute(deps.as_mut(), env.clone(), info.clone(), msg); let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!( assert_eq!(
ret, ret,
Err(ContractError::NoAssociatedMixNodeBond { Err(ContractError::NoAssociatedMixNodeBond {
@@ -773,14 +750,12 @@ pub mod tests {
.profit_margin_percent .profit_margin_percent
); );
env.block.time = env.block.time.plus_seconds(MIN_PM_UPDATE_INTERVAL + 1);
// try updating with an invalid value // try updating with an invalid value
let profit_margin_percent = 101; let profit_margin_percent = 101;
let msg = ExecuteMsg::UpdateMixnodeConfig { let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent, profit_margin_percent,
}; };
let ret = execute(deps.as_mut(), env.clone(), info.clone(), msg); let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!( assert_eq!(
ret, ret,
Err(ContractError::InvalidProfitMarginPercent( Err(ContractError::InvalidProfitMarginPercent(
@@ -792,7 +767,7 @@ pub mod tests {
let msg = ExecuteMsg::UpdateMixnodeConfig { let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent, profit_margin_percent,
}; };
execute(deps.as_mut(), env, info, msg).unwrap(); execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!( assert_eq!(
profit_margin_percent, profit_margin_percent,
storage::mixnodes() storage::mixnodes()
@@ -906,51 +881,4 @@ pub mod tests {
// change identity but reuse sphinx key // change identity but reuse sphinx key
assert!(try_add_mixnode(deps.as_mut(), mock_env(), info_bob, mixnode, sig2).is_err()); assert!(try_add_mixnode(deps.as_mut(), mock_env(), info_bob, mixnode, sig2).is_err());
} }
#[test]
fn updating_pm_too_often_fails() {
use super::MIN_PM_UPDATE_INTERVAL;
let mut deps = test_helpers::init_contract();
let mut env = mock_env();
let keypair1 = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng());
let sig1 = keypair1.private_key().sign_text("alice");
let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge());
let mixnode = MixNode {
host: "1.2.3.4".to_string(),
mix_port: 1234,
verloc_port: 1234,
http_api_port: 1234,
sphinx_key: crypto::asymmetric::encryption::KeyPair::new(&mut thread_rng())
.public_key()
.to_base58_string(),
identity_key: keypair1.public_key().to_base58_string(),
version: "v0.1.2.3".to_string(),
profit_margin_percent: 10,
};
assert!(try_add_mixnode(
deps.as_mut(),
mock_env(),
info_alice.clone(),
mixnode.clone(),
sig1
)
.is_ok());
env.block.time = env.block.time.plus_seconds(MIN_PM_UPDATE_INTERVAL - 1);
// fails if too soon after bonding
assert!(
try_update_mixnode_config(deps.as_mut(), env.clone(), info_alice.clone(), 20).is_err()
);
env.block.time = env.block.time.plus_seconds(2);
// succeds after some time
assert!(try_update_mixnode_config(deps.as_mut(), env, info_alice, 20).is_ok());
}
} }
+8 -11
View File
@@ -39,7 +39,7 @@ pub fn query_operator_reward(deps: Deps, owner: String) -> Result<Uint128, Contr
} }
}; };
super::transactions::calculate_operator_reward(deps.storage, deps.api, &owner_address, &bond) super::transactions::calculate_operator_reward(deps.storage, &owner_address, &bond)
} }
pub fn query_delegator_reward( pub fn query_delegator_reward(
@@ -48,20 +48,17 @@ pub fn query_delegator_reward(
mix_identity: IdentityKey, mix_identity: IdentityKey,
proxy: Option<String>, proxy: Option<String>,
) -> Result<Uint128, ContractError> { ) -> Result<Uint128, ContractError> {
let proxy = match proxy { let proxy = proxy.map(|p| {
Some(proxy) => Some( deps.api
deps.api .addr_validate(&p)
.addr_validate(&proxy) .map_err(|_| ContractError::InvalidAddress(p))
.map_err(|_| ContractError::InvalidAddress(proxy))?, .expect("proxy address is invalid")
), });
None => None,
};
let key = mixnet_contract_common::delegation::generate_storage_key( let key = mixnet_contract_common::delegation::generate_storage_key(
&deps.api.addr_validate(&owner)?, &deps.api.addr_validate(&owner)?,
proxy.as_ref(), proxy.as_ref(),
); );
super::transactions::calculate_delegator_reward(deps.storage, deps.api, key, &mix_identity) super::transactions::calculate_delegator_reward(deps.storage, key, &mix_identity)
} }
#[cfg(test)] #[cfg(test)]
+1 -1
View File
@@ -60,5 +60,5 @@ pub fn decr_reward_pool(
pub fn circulating_supply(storage: &dyn Storage) -> StdResult<Uint128> { pub fn circulating_supply(storage: &dyn Storage) -> StdResult<Uint128> {
let reward_pool = REWARD_POOL.load(storage)?; let reward_pool = REWARD_POOL.load(storage)?;
Ok(Uint128::new(TOTAL_SUPPLY).saturating_sub(reward_pool)) Ok(Uint128::new(TOTAL_SUPPLY) - reward_pool)
} }
+75 -436
View File
@@ -6,7 +6,6 @@ use super::storage::{
OPERATOR_REWARD_CLAIMED_HEIGHT, OPERATOR_REWARD_CLAIMED_HEIGHT,
}; };
use crate::constants; use crate::constants;
use crate::contract::debug_with_visibility;
use crate::delegations::storage as delegations_storage; use crate::delegations::storage as delegations_storage;
use crate::delegations::transactions::_try_delegate_to_mixnode; use crate::delegations::transactions::_try_delegate_to_mixnode;
use crate::error::ContractError; use crate::error::ContractError;
@@ -16,7 +15,6 @@ use crate::rewards::helpers;
use crate::support::helpers::is_authorized; use crate::support::helpers::is_authorized;
use config::defaults::DENOM; use config::defaults::DENOM;
use cosmwasm_std::{Addr, Api, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128}; use cosmwasm_std::{Addr, Api, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128};
use cw_storage_plus::Bound;
use mixnet_contract_common::events::{ use mixnet_contract_common::events::{
new_compound_delegator_reward_event, new_compound_operator_reward_event, new_compound_delegator_reward_event, new_compound_operator_reward_event,
new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event, new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event,
@@ -37,13 +35,8 @@ pub fn try_compound_operator_reward_on_behalf(
let proxy = deps.api.addr_validate(info.sender.as_str())?; let proxy = deps.api.addr_validate(info.sender.as_str())?;
let owner = deps.api.addr_validate(&owner)?; let owner = deps.api.addr_validate(&owner)?;
let reward = _try_compound_operator_reward( let reward =
deps.storage, _try_compound_operator_reward(deps.storage, env.block.height, &owner, Some(proxy))?;
deps.api,
env.block.height,
&owner,
Some(proxy),
)?;
Ok(Response::new().add_event(new_compound_operator_reward_event(&owner, reward))) Ok(Response::new().add_event(new_compound_operator_reward_event(&owner, reward)))
} }
@@ -54,15 +47,13 @@ pub fn try_compound_operator_reward(
info: MessageInfo, info: MessageInfo,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(info.sender.as_str())?; let owner = deps.api.addr_validate(info.sender.as_str())?;
let reward = let reward = _try_compound_operator_reward(deps.storage, env.block.height, &owner, None)?;
_try_compound_operator_reward(deps.storage, deps.api, env.block.height, &owner, None)?;
Ok(Response::new().add_event(new_compound_operator_reward_event(&owner, reward))) Ok(Response::new().add_event(new_compound_operator_reward_event(&owner, reward)))
} }
pub fn _try_compound_operator_reward( pub fn _try_compound_operator_reward(
storage: &mut dyn Storage, storage: &mut dyn Storage,
api: &dyn Api,
block_height: u64, block_height: u64,
owner: &Addr, owner: &Addr,
proxy: Option<Addr>, proxy: Option<Addr>,
@@ -81,9 +72,8 @@ pub fn _try_compound_operator_reward(
} }
let mut updated_bond = bond.clone(); let mut updated_bond = bond.clone();
let reward = calculate_operator_reward(storage, api, owner, &bond)?; let reward = calculate_operator_reward(storage, owner, &bond)?;
updated_bond.accumulated_rewards = updated_bond.accumulated_rewards = Some(updated_bond.accumulated_rewards() - reward);
Some(updated_bond.accumulated_rewards().saturating_sub(reward));
updated_bond.pledge_amount.amount += reward; updated_bond.pledge_amount.amount += reward;
mixnodes().replace( mixnodes().replace(
storage, storage,
@@ -104,7 +94,6 @@ pub fn _try_compound_operator_reward(
pub fn calculate_operator_reward( pub fn calculate_operator_reward(
storage: &dyn Storage, storage: &dyn Storage,
api: &dyn Api,
owner: &Addr, owner: &Addr,
bond: &StoredMixnodeBond, bond: &StoredMixnodeBond,
) -> Result<Uint128, ContractError> { ) -> Result<Uint128, ContractError> {
@@ -115,33 +104,26 @@ pub fn calculate_operator_reward(
let accumulated_rewards = mixnodes() let accumulated_rewards = mixnodes()
.changelog() .changelog()
.prefix(bond.identity()) .prefix(bond.identity())
.keys( .keys(storage, None, None, Order::Ascending)
storage,
Some(Bound::exclusive(last_claimed_height)),
None,
Order::Ascending,
)
.filter_map(|height| height.ok()) .filter_map(|height| height.ok())
.filter(|height| last_claimed_height <= *height)
.fold( .fold(
Ok(Uint128::zero()), Ok(Uint128::zero()),
|acc, height| -> Result<Uint128, ContractError> { |acc, height| -> Result<Uint128, ContractError> {
let accumulated_reward = acc?; let accumulated_reward = acc?;
if let Some(bond) = mixnodes() if let Some(bond) =
.may_load_at_height(storage, bond.identity().as_str(), height) mixnodes().may_load_at_height(storage, bond.identity().as_str(), height)?
.ok()
.flatten()
{ {
if let Some(ref epoch_rewards) = bond.epoch_rewards { if let Some(epoch_rewards) = bond.epoch_rewards {
let epoch_reward_params =
epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
// Compound rewards from previous heights // Compound rewards from previous heights
match epoch_rewards.operator_reward(bond.profit_margin()) { let reward_at_height = epoch_rewards.delegation_reward(
Ok(reward) => return Ok(accumulated_reward + reward), bond.pledge_amount().amount + accumulated_reward,
Err(err) => { bond.profit_margin(),
debug_with_visibility( epoch_reward_params,
api, )?;
format!("Failed to calculate operator reward: {:?}", err), return Ok(accumulated_reward + reward_at_height);
);
}
};
} }
}; };
Ok(accumulated_reward) Ok(accumulated_reward)
@@ -168,7 +150,8 @@ pub fn try_compound_delegator_reward_on_behalf(
let owner = deps.api.addr_validate(&owner)?; let owner = deps.api.addr_validate(&owner)?;
let reward = _try_compound_delegator_reward( let reward = _try_compound_delegator_reward(
env.block.height, env.block.height,
deps, deps.api,
deps.storage,
owner.as_str(), owner.as_str(),
&mix_identity, &mix_identity,
Some(proxy.clone()), Some(proxy.clone()),
@@ -193,7 +176,8 @@ pub fn try_compound_delegator_reward(
let owner = deps.api.addr_validate(info.sender.as_str())?; let owner = deps.api.addr_validate(info.sender.as_str())?;
let reward = _try_compound_delegator_reward( let reward = _try_compound_delegator_reward(
env.block.height, env.block.height,
deps, deps.api,
deps.storage,
owner.as_str(), owner.as_str(),
&mix_identity, &mix_identity,
None, None,
@@ -211,7 +195,8 @@ pub fn try_compound_delegator_reward(
pub fn _try_compound_delegator_reward( pub fn _try_compound_delegator_reward(
block_height: u64, block_height: u64,
mut deps: DepsMut<'_>, api: &dyn Api,
storage: &mut dyn Storage,
owner_address: &str, owner_address: &str,
mix_identity: &str, mix_identity: &str,
proxy: Option<Addr>, proxy: Option<Addr>,
@@ -219,25 +204,25 @@ pub fn _try_compound_delegator_reward(
let delegation_map = crate::delegations::storage::delegations(); let delegation_map = crate::delegations::storage::delegations();
let key = mixnet_contract_common::delegation::generate_storage_key( let key = mixnet_contract_common::delegation::generate_storage_key(
&deps.api.addr_validate(owner_address)?, &api.addr_validate(owner_address)?,
proxy.as_ref(), proxy.as_ref(),
); );
let reward = calculate_delegator_reward(deps.storage, deps.api, key.clone(), mix_identity)?; let reward = calculate_delegator_reward(storage, key.clone(), mix_identity)?;
let mut compounded_delegation = reward; let mut compounded_delegation = reward;
// Might want to introduce paging here // Might want to introduce paging here
let delegation_heights = delegation_map let delegation_heights = delegation_map
.prefix((mix_identity.to_string(), key.clone())) .prefix((mix_identity.to_string(), key.clone()))
.keys(deps.storage, None, None, cosmwasm_std::Order::Ascending) .keys(storage, None, None, cosmwasm_std::Order::Ascending)
.filter_map(|v| v.ok()) .filter_map(|v| v.ok())
.collect::<Vec<u64>>(); .collect::<Vec<u64>>();
for h in delegation_heights { for h in delegation_heights {
let delegation = let delegation =
delegation_map.load(deps.storage, (mix_identity.to_string(), key.clone(), h))?; delegation_map.load(storage, (mix_identity.to_string(), key.clone(), h))?;
compounded_delegation += delegation.amount.amount; compounded_delegation += delegation.amount.amount;
delegation_map.replace( delegation_map.replace(
deps.storage, storage,
(mix_identity.to_string(), key.clone(), h), (mix_identity.to_string(), key.clone(), h),
None, None,
Some(&delegation), Some(&delegation),
@@ -246,12 +231,13 @@ pub fn _try_compound_delegator_reward(
if compounded_delegation != Uint128::zero() { if compounded_delegation != Uint128::zero() {
_try_delegate_to_mixnode( _try_delegate_to_mixnode(
deps.branch(), storage,
api,
block_height, block_height,
mix_identity, mix_identity,
owner_address, owner_address,
Coin { Coin {
amount: compounded_delegation, amount: reward,
denom: DENOM.to_string(), denom: DENOM.to_string(),
}, },
proxy, proxy,
@@ -259,14 +245,15 @@ pub fn _try_compound_delegator_reward(
} }
{ {
if let Some(mut bond) = mixnodes().may_load(deps.storage, mix_identity)? { //TODO: Node exists all is well, life goes on, if it does not exist we'll just return the reward to the caller as there is nothing to do on the bond
bond.accumulated_rewards = Some(bond.accumulated_rewards().saturating_sub(reward)); if let Some(mut bond) = mixnodes().may_load(storage, mix_identity)? {
mixnodes().save(deps.storage, mix_identity, &bond, block_height)?; bond.accumulated_rewards = Some(bond.accumulated_rewards() - reward);
mixnodes().save(storage, mix_identity, &bond, block_height)?;
} }
} }
DELEGATOR_REWARD_CLAIMED_HEIGHT.save( DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
deps.storage, storage,
(key, mix_identity.to_string()), (key, mix_identity.to_string()),
&block_height, &block_height,
)?; )?;
@@ -279,7 +266,6 @@ pub fn _try_compound_delegator_reward(
// + last_reward_claimed height is correctly used // + last_reward_claimed height is correctly used
pub fn calculate_delegator_reward( pub fn calculate_delegator_reward(
storage: &dyn Storage, storage: &dyn Storage,
api: &dyn Api,
key: Vec<u8>, key: Vec<u8>,
mix_identity: &str, mix_identity: &str,
) -> Result<Uint128, ContractError> { ) -> Result<Uint128, ContractError> {
@@ -289,96 +275,45 @@ pub fn calculate_delegator_reward(
// Get delegations newer then last_claimed_height, it would be nice to also fold this into the iteration bellow but it should be ok for now, as // Get delegations newer then last_claimed_height, it would be nice to also fold this into the iteration bellow but it should be ok for now, as
// I doubt folks refresh their delegations often // I doubt folks refresh their delegations often
let mut delegations = delegations_storage::delegations() let delegations = delegations_storage::delegations()
.prefix((mix_identity.to_string(), key)) .prefix((mix_identity.to_string(), key))
.range( .range(storage, None, None, Order::Descending)
storage,
Some(Bound::exclusive(last_claimed_height)),
None,
Order::Descending,
)
.filter_map(|record| record.ok()) .filter_map(|record| record.ok())
.filter(|(height, _)| last_claimed_height <= *height)
.map(|(_, delegation)| delegation) .map(|(_, delegation)| delegation)
.collect::<Vec<Delegation>>(); .collect::<Vec<Delegation>>();
// Accumulate outside of the loop to gain some speed, on a log of checkpoints
let mut delegation_at_height = Uint128::zero();
// This is a bit gnarly, but we want to avoid loading all heights, the loading mixnodes, so we're doing it all in the iterator // This is a bit gnarly, but we want to avoid loading all heights, the loading mixnodes, so we're doing it all in the iterator
let accumulated_rewards = mixnodes() let accumulated_rewards = mixnodes()
.changelog() .changelog()
.prefix(mix_identity) .prefix(mix_identity)
.keys( .keys(storage, None, None, Order::Ascending)
storage,
Some(Bound::exclusive(last_claimed_height)),
None,
Order::Ascending,
)
.filter_map(|height| height.ok()) .filter_map(|height| height.ok())
// Get all checkpoints greater then last claimed delegation height .filter(|height| last_claimed_height <= *height)
.fold( .fold(
Ok(Uint128::zero()), Ok(Uint128::zero()),
|acc, height| -> Result<Uint128, ContractError> { |acc, height| -> Result<Uint128, ContractError> {
let accumulated_reward = acc?; let accumulated_reward = acc?;
delegation_at_height = delegations let delegation_at_height = delegations
.iter() .iter()
.filter(|d| d.block_height <= height) .filter(|d| height <= d.block_height)
.fold(delegation_at_height, |total, delegation| { .fold(Uint128::zero(), |total, delegation| {
total + delegation.amount.amount total + delegation.amount.amount
}); });
// Drop what we've processed
// This should be replaced with drain_filter once it stabilizes
delegations.retain(|d| d.block_height > height);
// debug_with_visibility(
// api,
// format!("delegation at height {} - {}", height, delegation_at_height),
// );
if delegation_at_height != Uint128::zero() { if delegation_at_height != Uint128::zero() {
// debug_with_visibility( if let Some(bond) =
// api, mixnodes().may_load_at_height(storage, mix_identity, height)?
// format!("Loading bond {} at height {}", mix_identity, height),
// );
if let Some(bond) = mixnodes()
.may_load_at_height(storage, mix_identity, height)
.ok()
.flatten()
{ {
if let Some(ref epoch_rewards) = bond.epoch_rewards { if let Some(epoch_rewards) = bond.epoch_rewards {
// Compound rewards from previous heights // Compound rewards from previous heights
match epoch_reward_params_for_id(storage, epoch_rewards.epoch_id()) { let epoch_reward_params =
Ok(params) => { epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
let reward_at_height = match epoch_rewards.delegation_reward( let reward_at_height = epoch_rewards.delegation_reward(
delegation_at_height + accumulated_reward, delegation_at_height + accumulated_reward,
bond.profit_margin(), bond.profit_margin(),
params, epoch_reward_params,
) { )?;
Ok(reward) => { return Ok(accumulated_reward + reward_at_height);
// debug_with_visibility(
// api,
// format!("Reward at height {} - {}", height, reward),
// );
reward
}
Err(err) => {
debug_with_visibility(
api,
format!(
"Error calculating reward at {} - {}",
height, err
),
);
Uint128::zero()
}
};
return Ok(accumulated_reward + reward_at_height);
}
Err(_err) => {
debug_with_visibility(
api,
format!("No epoch reward params for epoch {}", height),
);
}
}
} }
} }
}; };
@@ -520,9 +455,7 @@ pub(crate) fn try_reward_mixnode(
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::constants::EPOCHS_IN_INTERVAL; use crate::constants::EPOCHS_IN_INTERVAL;
use crate::delegations::transactions::{ use crate::delegations::transactions::try_delegate_to_mixnode;
_try_remove_delegation_from_mixnode, try_delegate_to_mixnode,
};
use crate::error::ContractError; use crate::error::ContractError;
use crate::interval::storage::{ use crate::interval::storage::{
current_epoch_reward_params, save_epoch, save_epoch_reward_params, current_epoch_reward_params, save_epoch, save_epoch_reward_params,
@@ -539,7 +472,7 @@ pub mod tests {
use az::CheckedCast; use az::CheckedCast;
use config::defaults::DENOM; use config::defaults::DENOM;
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, StdError, Timestamp, Uint128}; use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128};
use mixnet_contract_common::events::{ use mixnet_contract_common::events::{
must_find_attribute, BOND_TOO_FRESH_VALUE, NO_REWARD_REASON_KEY, must_find_attribute, BOND_TOO_FRESH_VALUE, NO_REWARD_REASON_KEY,
OPERATOR_REWARDING_EVENT_TYPE, OPERATOR_REWARDING_EVENT_TYPE,
@@ -875,13 +808,9 @@ pub mod tests {
} }
#[test] #[test]
fn test_reward_additivity_and_snapshots() { fn test_reward_additivity() {
use crate::constants::INTERVAL_REWARD_PERCENT; use crate::constants::INTERVAL_REWARD_PERCENT;
use crate::contract::INITIAL_REWARD_POOL; use crate::contract::INITIAL_REWARD_POOL;
use crate::mixnodes::transactions::try_add_mixnode;
use rand::thread_rng;
let mixnodes = crate::mixnodes::storage::mixnodes();
type U128 = fixed::types::U75F53; type U128 = fixed::types::U75F53;
@@ -891,81 +820,14 @@ pub mod tests {
.load(deps.as_ref().storage) .load(deps.as_ref().storage)
.unwrap(); .unwrap();
let rewarding_validator_address = current_state.rewarding_validator_address; let rewarding_validator_address = current_state.rewarding_validator_address;
let info = mock_info(rewarding_validator_address.as_str(), &[]);
crate::mixnodes::transactions::try_checkpoint_mixnodes(
&mut deps.storage,
env.block.height,
info.clone(),
)
.unwrap();
let checkpoints = mixnodes
.changelog()
.keys(&deps.storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.collect::<Vec<(IdentityKey, u64)>>();
assert_eq!(0, checkpoints.len());
let period_reward_pool = (INITIAL_REWARD_POOL / 100 / EPOCHS_IN_INTERVAL as u128) let period_reward_pool = (INITIAL_REWARD_POOL / 100 / EPOCHS_IN_INTERVAL as u128)
* INTERVAL_REWARD_PERCENT as u128; * INTERVAL_REWARD_PERCENT as u128;
assert_eq!(period_reward_pool, 6_944_444_444); assert_eq!(period_reward_pool, 6_944_444_444);
let circulating_supply = storage::circulating_supply(&deps.storage).unwrap().u128(); let circulating_supply = storage::circulating_supply(&deps.storage).unwrap().u128();
assert_eq!(circulating_supply, 750_000_000_000_000u128); assert_eq!(circulating_supply, 750_000_000_000_000u128);
let sender = Addr::unchecked("alice"); let node_owner: Addr = Addr::unchecked("alice");
let stake = coins(10_000_000_000, DENOM); let node_identity = test_helpers::add_mixnode(
let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng());
let owner_signature = keypair
.private_key()
.sign(sender.as_bytes())
.to_base58_string();
let legit_sphinx_key = crypto::asymmetric::encryption::KeyPair::new(&mut thread_rng());
let info = mock_info(sender.as_str(), &stake);
let node_identity_1 = keypair.public_key().to_base58_string();
env.block.height;
try_add_mixnode(
deps.as_mut(),
env.clone(),
info.clone(),
MixNode {
identity_key: node_identity_1.clone(),
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
},
owner_signature,
)
.unwrap();
// tick
env.block.height += 1;
let info = mock_info(rewarding_validator_address.as_str(), &[]);
crate::mixnodes::transactions::try_checkpoint_mixnodes(
&mut deps.storage,
env.block.height,
info.clone(),
)
.unwrap();
mixnodes
.assert_checkpointed(&deps.storage, env.block.height)
.unwrap();
let checkpoints = mixnodes
.changelog()
.keys(&deps.storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.collect::<Vec<(IdentityKey, u64)>>();
assert_eq!(checkpoints.len(), 1);
let node_owner: Addr = Addr::unchecked("johnny");
let node_identity_2 = test_helpers::add_mixnode(
node_owner.as_str(), node_owner.as_str(),
coins(10_000_000_000, DENOM), coins(10_000_000_000, DENOM),
deps.as_mut(), deps.as_mut(),
@@ -975,7 +837,7 @@ pub mod tests {
deps.as_mut(), deps.as_mut(),
mock_env(), mock_env(),
mock_info("alice_d1", &[coin(8000_000000, DENOM)]), mock_info("alice_d1", &[coin(8000_000000, DENOM)]),
node_identity_1.clone(), node_identity.clone(),
) )
.unwrap(); .unwrap();
@@ -983,10 +845,17 @@ pub mod tests {
deps.as_mut(), deps.as_mut(),
mock_env(), mock_env(),
mock_info("alice_d2", &[coin(2000_000000, DENOM)]), mock_info("alice_d2", &[coin(2000_000000, DENOM)]),
node_identity_1.clone(), node_identity.clone(),
) )
.unwrap(); .unwrap();
let node_owner: Addr = Addr::unchecked("bob");
let node_identity_2 = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10_000_000_000, DENOM),
deps.as_mut(),
);
try_delegate_to_mixnode( try_delegate_to_mixnode(
deps.as_mut(), deps.as_mut(),
mock_env(), mock_env(),
@@ -1026,16 +895,13 @@ pub mod tests {
) )
.unwrap(); .unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events( crate::delegations::transactions::_try_reconcile_all_delegation_events(&mut deps.storage)
&mut deps.storage, .unwrap();
&deps.api,
)
.unwrap();
let info = mock_info(rewarding_validator_address.as_ref(), &[]); let info = mock_info(rewarding_validator_address.as_ref(), &[]);
env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity_1) let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let mix_1_uptime = 100; let mix_1_uptime = 100;
@@ -1076,230 +942,6 @@ pub mod tests {
let mix_1_reward_result = mix_1.reward(&params); let mix_1_reward_result = mix_1.reward(&params);
let info = mock_info(rewarding_validator_address.as_str(), &[]);
crate::mixnodes::transactions::try_checkpoint_mixnodes(
&mut deps.storage,
env.block.height,
info.clone(),
)
.unwrap();
mixnodes
.assert_checkpointed(&deps.storage, env.block.height)
.unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
info.clone(),
node_identity_1.clone(),
node_reward_params,
)
.unwrap();
let mix_after_reward = mixnodes.may_load(&deps.storage, &node_identity_1).unwrap();
// println!("{:?}", mix_after_reward);
let checkpoints = mixnodes
.changelog()
.prefix(&node_identity_1)
.keys(&deps.storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.collect::<Vec<u64>>();
assert_eq!(checkpoints.len(), 2);
env.block.height += 10000;
env.block.time = env.block.time.plus_seconds(3601);
try_advance_epoch(
env.clone(),
&mut deps.storage,
rewarding_validator_address.to_string(),
)
.unwrap();
// After two snapshots we should see an increase in delegation
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info("alice_d1", &[coin(8000_000000, DENOM)]),
node_identity_1.clone(),
)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(
&mut deps.storage,
&deps.api,
)
.unwrap();
let info = mock_info(rewarding_validator_address.as_str(), &[]);
crate::mixnodes::transactions::try_checkpoint_mixnodes(
&mut deps.storage,
env.block.height,
info.clone(),
)
.unwrap();
mixnodes
.assert_checkpointed(&deps.storage, env.block.height)
.unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
info.clone(),
node_identity_1.clone(),
node_reward_params,
)
.unwrap();
let mix_after_reward_2 = mixnodes.may_load(&deps.storage, &node_identity_1).unwrap();
assert_ne!(mix_after_reward, mix_after_reward_2);
let checkpoints = mixnodes
.changelog()
.prefix(&node_identity_1)
.keys(&deps.storage, None, None, Order::Ascending)
.collect::<Vec<Result<u64, StdError>>>();
assert_eq!(checkpoints.len(), 3);
env.block.height += 10000;
env.block.time = env.block.time.plus_seconds(3601);
try_advance_epoch(
env.clone(),
&mut deps.storage,
rewarding_validator_address.to_string(),
)
.unwrap();
let info = mock_info(rewarding_validator_address.as_str(), &[]);
crate::mixnodes::transactions::try_checkpoint_mixnodes(
&mut deps.storage,
env.block.height,
info.clone(),
)
.unwrap();
mixnodes
.assert_checkpointed(&deps.storage, env.block.height)
.unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
info.clone(),
node_identity_1.clone(),
node_reward_params,
)
.unwrap();
let checkpoints = mixnodes
.changelog()
.prefix(&node_identity_1)
.keys(&deps.storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.collect::<Vec<u64>>();
assert_eq!(checkpoints.len(), 4);
let delegation_map = crate::delegations::storage::delegations();
let key = "alice_d1".as_bytes().to_vec();
let last_claimed_height = storage::DELEGATOR_REWARD_CLAIMED_HEIGHT
.load(&deps.storage, (key.clone(), node_identity_1.to_string()))
.unwrap_or(0);
assert_eq!(last_claimed_height, 0);
let viable_delegations = delegation_map
.prefix((node_identity_1.to_string(), key.clone()))
.range(&deps.storage, None, None, Order::Descending)
.filter_map(|record| record.ok())
.filter(|(height, _)| last_claimed_height <= *height)
.map(|(_, delegation)| delegation)
.collect::<Vec<Delegation>>();
assert_eq!(viable_delegations.len(), 2);
let viable_heights = mixnodes
.changelog()
.prefix(&node_identity_1)
.keys(&deps.storage, None, None, Order::Ascending)
.filter_map(|height| height.ok())
.filter(|height| last_claimed_height <= *height)
.collect::<Vec<u64>>();
// Should be equal to the number of checkpoints
assert_eq!(viable_heights.len(), 4);
for (i, h) in viable_heights.into_iter().enumerate() {
let delegation_at_height = viable_delegations
.iter()
.filter(|d| d.block_height <= h)
.fold(Uint128::zero(), |total, delegation| {
total + delegation.amount.amount
});
if i < 2 {
assert_eq!(delegation_at_height, Uint128::new(8000000000));
} else {
assert_eq!(delegation_at_height, Uint128::new(16000000000));
}
}
let alice_reward =
calculate_delegator_reward(&deps.storage, &deps.api, key.clone(), &node_identity_1)
.unwrap();
assert_eq!(alice_reward, Uint128::new(304552));
let mix_0 = mixnodes.load(&deps.storage, &node_identity_1).unwrap();
_try_compound_delegator_reward(
env.block.height,
deps.as_mut(),
"alice_d1",
&node_identity_1,
None,
)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(
&mut deps.storage,
&deps.api,
)
.unwrap();
let delegations = crate::delegations::storage::delegations()
.prefix((node_identity_1.to_string(), key.clone()))
.range(&deps.storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.map(|(_, delegation)| delegation)
.collect::<Vec<Delegation>>();
assert_eq!(delegations.len(), 1);
let delegation = delegations.first().unwrap();
assert_eq!(delegation.amount.amount, Uint128::new(16000000000 + 304552));
let mix_1 = mixnodes
.load(&deps.storage, &node_identity_1.clone())
.unwrap();
_try_remove_delegation_from_mixnode(deps.as_mut(), env, node_identity_1, "alice_d1", None)
.unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events(
&mut deps.storage,
&deps.api,
)
.unwrap();
assert_eq!(
mix_0.accumulated_rewards(),
mix_1.accumulated_rewards() + alice_reward
);
let operator_reward =
calculate_operator_reward(&deps.storage, &deps.api, &Addr::unchecked("alice"), &mix_1)
.unwrap();
assert_eq!(operator_reward, Uint128::new(352532));
assert_eq!( assert_eq!(
mix_1_reward_result.sigma(), mix_1_reward_result.sigma(),
U128::from_num(0.0000266666666666f64) U128::from_num(0.0000266666666666f64)
@@ -1369,11 +1011,8 @@ pub mod tests {
) )
.unwrap(); .unwrap();
crate::delegations::transactions::_try_reconcile_all_delegation_events( crate::delegations::transactions::_try_reconcile_all_delegation_events(&mut deps.storage)
&mut deps.storage, .unwrap();
&deps.api,
)
.unwrap();
let info = mock_info(rewarding_validator_address.as_ref(), &[]); let info = mock_info(rewarding_validator_address.as_ref(), &[]);
env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING; env.block.height += 2 * constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
+18 -5
View File
@@ -334,7 +334,6 @@ fn try_create_periodic_vesting_account(
if info.sender != ADMIN.load(deps.storage)? { if info.sender != ADMIN.load(deps.storage)? {
return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); return Err(ContractError::NotAdmin(info.sender.as_str().to_string()));
} }
let account_exists = account_from_address(owner_address, deps.storage, deps.api).is_ok(); let account_exists = account_from_address(owner_address, deps.storage, deps.api).is_ok();
if account_exists { if account_exists {
return Err(ContractError::AccountAlreadyExists( return Err(ContractError::AccountAlreadyExists(
@@ -345,7 +344,6 @@ fn try_create_periodic_vesting_account(
let vesting_spec = vesting_spec.unwrap_or_default(); let vesting_spec = vesting_spec.unwrap_or_default();
let coin = validate_funds(&info.funds)?; let coin = validate_funds(&info.funds)?;
let owner_address = deps.api.addr_validate(owner_address)?; let owner_address = deps.api.addr_validate(owner_address)?;
let staking_address = if let Some(staking_address) = staking_address { let staking_address = if let Some(staking_address) = staking_address {
Some(deps.api.addr_validate(&staking_address)?) Some(deps.api.addr_validate(&staking_address)?)
@@ -359,9 +357,6 @@ fn try_create_periodic_vesting_account(
let periods = populate_vesting_periods(start_time, vesting_spec); let periods = populate_vesting_periods(start_time, vesting_spec);
let start_time = Timestamp::from_seconds(start_time); let start_time = Timestamp::from_seconds(start_time);
let response = Response::new();
Account::new( Account::new(
owner_address.clone(), owner_address.clone(),
staking_address.clone(), staking_address.clone(),
@@ -371,6 +366,24 @@ fn try_create_periodic_vesting_account(
deps.storage, deps.storage,
)?; )?;
let mut response = Response::new();
let send_tokens_owner = BankMsg::Send {
to_address: owner_address.as_str().to_string(),
amount: vec![Coin::new(1_000_000, DENOM)],
};
response = response.add_message(send_tokens_owner);
if let Some(staking_address) = staking_address.as_ref() {
let send_tokens_staking = BankMsg::Send {
to_address: staking_address.clone().as_str().to_string(),
amount: vec![Coin::new(1_000_000, DENOM)],
};
response = response.add_message(send_tokens_staking);
}
Ok(response.add_event(new_periodic_vesting_account_event( Ok(response.add_event(new_periodic_vesting_account_event(
&owner_address, &owner_address,
&coin, &coin,
-2
View File
@@ -44,6 +44,4 @@ pub enum ContractError {
InvalidAddress(String), InvalidAddress(String),
#[error("VESTING ({}): Account already exists: {0}", line!())] #[error("VESTING ({}): Account already exists: {0}", line!())]
AccountAlreadyExists(String), AccountAlreadyExists(String),
#[error("VESTING ({}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", line!())]
MinVestingFunds { sent: u128, need: u128 },
} }
@@ -14,22 +14,24 @@ impl VestingAccount for Account {
env: &Env, env: &Env,
storage: &dyn Storage, storage: &dyn Storage,
) -> Result<Coin, ContractError> { ) -> Result<Coin, ContractError> {
// Returns 0 in case of underflow. Which is fine, as the amount of pledged and delegated tokens can be larger then vesting_coins due to rewards and vesting periods expiring // Returns 0 in case of underflow.
Ok(Coin { Ok(Coin {
amount: Uint128::new( amount: Uint128::new(
self.get_vesting_coins(block_time, env)? self.get_vesting_coins(block_time, env)?
.amount .amount
.u128() .u128()
.saturating_sub( .checked_sub(
self.get_delegated_vesting(block_time, env, storage)? self.get_delegated_vesting(block_time, env, storage)?
.amount .amount
.u128(), .u128(),
) )
.saturating_sub( .ok_or(ContractError::Underflow)?
.checked_sub(
self.get_pledged_vesting(block_time, env, storage)? self.get_pledged_vesting(block_time, env, storage)?
.amount .amount
.u128(), .u128(),
), )
.ok_or(ContractError::Underflow)?,
), ),
denom: DENOM.to_string(), denom: DENOM.to_string(),
}) })
-1
View File
@@ -77,7 +77,6 @@ mod tests {
assert_eq!(created_account_test_by_staking, created_account); assert_eq!(created_account_test_by_staking, created_account);
assert_eq!( assert_eq!(
created_account.load_balance(&deps.storage).unwrap(), created_account.load_balance(&deps.storage).unwrap(),
// One was liquidated
Uint128::new(1_000_000_000_000) Uint128::new(1_000_000_000_000)
); );
// Try create the same account again // Try create the same account again
+100 -116
View File
@@ -1,122 +1,106 @@
version: '3.7' version: '3.7'
x-bech32-prefix: &BECH32_PREFIX
x-network: &NETWORK nymt
BECH32_PREFIX: nymt x-wasmd-version: &WASMD_VERSION
DENOM: nymt v0.21.0
STAKE_DENOM: nyxt x-wasmd-commit-hash: &WASMD_COMMIT_HASH
WASMD_VERSION: v0.26.0 1d436638af7cacb5aeeb7248b57b085c64f3ae35
WASMD_COMMIT_HASH: dc5ef6fe84f0a5e3b0894692a18cc48fb5b00adf
services: services:
genesis_validator: genesis_validator:
build: build:
context: docker/validator context: docker/validator
args: *NETWORK args:
image: validator:latest BECH32_PREFIX: *BECH32_PREFIX
ports: WASMD_VERSION: *WASMD_VERSION
- "26657:26657" WASMD_COMMIT_HASH: *WASMD_COMMIT_HASH
- "1317:1317" image: validator:latest
container_name: genesis_validator ports:
volumes: - "26657:26657"
- "genesis_volume:/genesis_volume" - "1317:1317"
- "genesis_nymd:/root/.nymd" container_name: genesis_validator
environment: *NETWORK volumes:
networks: - "genesis_volume:/genesis_volume"
localnet: environment:
ipv4_address: 172.168.10.2 BECH32_PREFIX: *BECH32_PREFIX
command: [ "genesis" ] WASMD_VERSION: *WASMD_VERSION
secondary_validator: command: ["genesis"]
build: secondary_validator:
context: docker/validator build:
args: *NETWORK context: docker/validator
image: validator:latest args:
ports: BECH32_PREFIX: *BECH32_PREFIX
- "36657:26657" WASMD_VERSION: *WASMD_VERSION
- "2317:1317" image: validator:latest
volumes: volumes:
- "genesis_volume:/genesis_volume" - "genesis_volume:/genesis_volume:ro"
- "secondary_nymd:/root/.nymd" environment:
environment: *NETWORK BECH32_PREFIX: *BECH32_PREFIX
networks: WASMD_VERSION: *WASMD_VERSION
localnet: depends_on:
ipv4_address: 172.168.10.3 - "genesis_validator"
depends_on: command: ["secondary"]
- "genesis_validator" mixnet_contract:
command: [ "secondary" ] build: docker/mixnet_contract
# mixnet_contract: image: contract:latest
# build: docker/mixnet_contract volumes:
# image: contract:latest - ".:/nym"
# volumes: vesting_contract:
# - ".:/nym" build: docker/vesting_contract
# vesting_contract: image: vesting_contract:latest
# build: docker/vesting_contract volumes:
# image: vesting_contract:latest - ".:/nym"
# volumes: contract_uploader:
# - ".:/nym" build: docker/typescript_client
# contract_uploader: image: contract_uploader:typescript
# build: docker/typescript_client volumes:
# image: contract_uploader:typescript - "genesis_volume:/genesis_volume:ro"
# volumes: - "contract_volume:/contract_volume"
# - "genesis_volume:/genesis_volume:ro" - ".:/nym"
# - "contract_volume:/contract_volume" depends_on:
# - ".:/nym" - "genesis_validator"
# depends_on: - "secondary_validator"
# - "genesis_validator" - "mixnet_contract"
# - "secondary_validator" environment:
# - "mixnet_contract" BECH32_PREFIX: *BECH32_PREFIX
# environment: mnemonic_echo:
# BECH32_PREFIX: *BECH32_PREFIX build: docker/mnemonic_echo
mnemonic_echo: image: mnemonic_echo:latest
build: docker/mnemonic_echo volumes:
image: mnemonic_echo:latest - "genesis_volume:/genesis_volume:ro"
volumes: depends_on:
- "genesis_volume:/genesis_volume:ro" - "genesis_validator"
depends_on:
- "genesis_validator"
- "secondary_validator"
# mongo: mongo:
# image: mongo:latest image: mongo:latest
# command: command:
# - --storageEngine=wiredTiger - --storageEngine=wiredTiger
# volumes: volumes:
# - mongo_data:/data/db - mongo_data:/data/db
# block_explorer: block_explorer:
# build: build:
# context: https://github.com/forbole/big-dipper.git#v0.41.x-7 context: https://github.com/forbole/big-dipper.git#v0.41.x-7
# image: block_explorer:v0.41.x-7 image: block_explorer:v0.41.x-7
# ports: ports:
# - "3080:3000" - "3080:3000"
# depends_on: depends_on:
# - "mongo" - "mongo"
# environment: environment:
# ROOT_URL: ${APP_ROOT_URL:-http://localhost} ROOT_URL: ${APP_ROOT_URL:-http://localhost}
# MONGO_URL: mongodb://mongo:27017/meteor MONGO_URL: mongodb://mongo:27017/meteor
# PORT: 3000 PORT: 3000
# METEOR_SETTINGS: ${METEOR_SETTINGS} METEOR_SETTINGS: ${METEOR_SETTINGS}
# explorer: explorer:
# build: build:
# context: docker/explorer context: docker/explorer
# image: explorer:latest image: explorer:latest
# ports: ports:
# - "3040:3000" - "3040:3000"
# depends_on: depends_on:
# - "genesis_validator" - "genesis_validator"
# - "block_explorer" - "block_explorer"
volumes: volumes:
genesis_volume: genesis_volume:
genesis_nymd: contract_volume:
secondary_nymd: mongo_data:
# contract_volume:
# mongo_data:
networks:
localnet:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.168.10.0/25
+2 -9
View File
@@ -1,16 +1,9 @@
#!/bin/sh #!/bin/sh
# Wait for the mnemonic(s) to be generated # Wait for the mnemonic to be generated
while ! [ -s /genesis_volume/genesis_mnemonic ]; do while ! [ -s /genesis_volume/genesis_mnemonic ]; do
sleep 1 sleep 1
done done
while ! [ -s /genesis_volume/secondary_mnemonic ]; do echo "This is the current mnemonic:"
sleep 1
done
echo "This is the current genesis mnemonic:"
cat /genesis_volume/genesis_mnemonic cat /genesis_volume/genesis_mnemonic
echo "This is the current secondary mnemonic:"
cat /genesis_volume/secondary_mnemonic
+8 -31
View File
@@ -6,29 +6,17 @@ PASSPHRASE=passphrase
cd /root cd /root
if [ "$1" = "genesis" ]; then if [ "$1" = "genesis" ]; then
if [ ! -f "/root/.nymd/config/genesis.json" ]; then if [ ! -d "/root/.nymd" ]; then
./nymd init nymnet --chain-id nymnet 2> /dev/null ./nymd init nymnet --chain-id nymnet 2> /dev/null
# staking/governance token is hardcoded in config, change this sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025u'"${BECH32_PREFIX}"'"/' /root/.nymd/config/app.toml
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 '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/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/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/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/' /root/.nymd/config/config.toml
yes "${PASSPHRASE}" | ./nymd keys add node_admin 2>&1 >/dev/null | tail -n 1 > /genesis_volume/genesis_mnemonic
# create accounts ADDRESS=$(yes "${PASSPHRASE}" | ./nymd keys show node_admin -a)
yes "${PASSPHRASE}" | ./nymd keys add node_admin 2>&1 >/dev/null | tail -n 1 > /root/.nymd/mnemonic yes "${PASSPHRASE}" | ./nymd add-genesis-account "${ADDRESS}" 1000000000000000u${BECH32_PREFIX},1000000000000000stake
yes "${PASSPHRASE}" | ./nymd keys add secondary 2>&1 >/dev/null | tail -n 1 > /root/.nymd/secondary_mnemonic yes "${PASSPHRASE}" | ./nymd gentx node_admin 1000000000stake --chain-id nymnet 2> /dev/null
cp /root/.nymd/mnemonic /genesis_volume/genesis_mnemonic
cp /root/.nymd/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}"
yes "${PASSPHRASE}" | ./nymd gentx node_admin 1000000000u"${STAKE_DENOM}" --chain-id nymnet 2> /dev/null
./nymd collect-gentxs 2> /dev/null ./nymd collect-gentxs 2> /dev/null
./nymd validate-genesis > /dev/null ./nymd validate-genesis > /dev/null
cp /root/.nymd/config/genesis.json /genesis_volume/genesis.json cp /root/.nymd/config/genesis.json /genesis_volume/genesis.json
@@ -38,7 +26,7 @@ if [ "$1" = "genesis" ]; then
fi fi
./nymd start ./nymd start
elif [ "$1" = "secondary" ]; then elif [ "$1" = "secondary" ]; then
if [ ! -f "/root/.nymd/config/genesis.json" ]; then if [ ! -d "/root/.nymd" ]; then
./nymd init nymnet --chain-id nym-secondary 2> /dev/null ./nymd init nymnet --chain-id nym-secondary 2> /dev/null
# Wait until the genesis node writes the genesis.json to the shared volume # Wait until the genesis node writes the genesis.json to the shared volume
@@ -46,27 +34,16 @@ elif [ "$1" = "secondary" ]; then
sleep 1 sleep 1
done done
# wait for the actual validator to start up
sleep 5
cp /genesis_volume/genesis.json /root/.nymd/config/genesis.json 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_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/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 '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 '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/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/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/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/' /root/.nymd/config/config.toml
yes "${PASSPHRASE}" | ./nymd keys add node_admin 2> mnemonic > /dev/null
# 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 ./nymd 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
else else
echo "Validator already initialized, starting with the existing configuration." echo "Validator already initialized, starting with the existing configuration."
echo "If you want to re-init the validator, destroy the existing container" echo "If you want to re-init the validator, destroy the existing container"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "explorer-api" name = "explorer-api"
version = "1.0.1" version = "1.0.0-rc.2"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-1
View File
@@ -28,7 +28,6 @@ pub(crate) struct PrettyDetailedMixNodeBond {
pub owner: Addr, pub owner: Addr,
pub layer: Layer, pub layer: Layer,
pub mix_node: MixNode, pub mix_node: MixNode,
pub avg_uptime: Option<u8>,
} }
pub(crate) struct MixNodeCache { pub(crate) struct MixNodeCache {
-26
View File
@@ -9,9 +9,7 @@ use serde::Serialize;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use mixnet_contract_common::MixNodeBond; use mixnet_contract_common::MixNodeBond;
use validator_client::models::UptimeResponse;
use crate::cache::Cache;
use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond};
use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem}; use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem};
use crate::mix_nodes::CACHE_ENTRY_TTL; use crate::mix_nodes::CACHE_ENTRY_TTL;
@@ -78,16 +76,10 @@ impl MixNodesResult {
} }
} }
#[derive(Clone, Debug)]
pub(crate) struct MixNodeHealth {
avg_uptime: u8,
}
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct ThreadsafeMixNodesCache { pub(crate) struct ThreadsafeMixNodesCache {
mixnodes: Arc<RwLock<MixNodesResult>>, mixnodes: Arc<RwLock<MixNodesResult>>,
locations: Arc<RwLock<LocationCache>>, locations: Arc<RwLock<LocationCache>>,
mixnode_health: Arc<RwLock<Cache<MixNodeHealth>>>,
} }
impl ThreadsafeMixNodesCache { impl ThreadsafeMixNodesCache {
@@ -95,7 +87,6 @@ impl ThreadsafeMixNodesCache {
ThreadsafeMixNodesCache { ThreadsafeMixNodesCache {
mixnodes: Arc::new(RwLock::new(MixNodesResult::new())), mixnodes: Arc::new(RwLock::new(MixNodesResult::new())),
locations: Arc::new(RwLock::new(LocationCache::new())), locations: Arc::new(RwLock::new(LocationCache::new())),
mixnode_health: Arc::new(RwLock::new(Cache::new())),
} }
} }
@@ -103,7 +94,6 @@ impl ThreadsafeMixNodesCache {
ThreadsafeMixNodesCache { ThreadsafeMixNodesCache {
mixnodes: Arc::new(RwLock::new(MixNodesResult::new())), mixnodes: Arc::new(RwLock::new(MixNodesResult::new())),
locations: Arc::new(RwLock::new(locations)), locations: Arc::new(RwLock::new(locations)),
mixnode_health: Arc::new(RwLock::new(Cache::new())),
} }
} }
@@ -142,11 +132,9 @@ impl ThreadsafeMixNodesCache {
) -> Option<PrettyDetailedMixNodeBond> { ) -> Option<PrettyDetailedMixNodeBond> {
let mixnodes_guard = self.mixnodes.read().await; let mixnodes_guard = self.mixnodes.read().await;
let location_guard = self.locations.read().await; let location_guard = self.locations.read().await;
let mixnode_health_guard = self.mixnode_health.read().await;
let bond = mixnodes_guard.get_mixnode(identity_key); let bond = mixnodes_guard.get_mixnode(identity_key);
let location = location_guard.get(identity_key); let location = location_guard.get(identity_key);
let health = mixnode_health_guard.get(identity_key);
match bond { match bond {
Some(bond) => Some(PrettyDetailedMixNodeBond { Some(bond) => Some(PrettyDetailedMixNodeBond {
@@ -157,7 +145,6 @@ impl ThreadsafeMixNodesCache {
owner: bond.owner, owner: bond.owner,
layer: bond.layer, layer: bond.layer,
mix_node: bond.mix_node, mix_node: bond.mix_node,
avg_uptime: health.map(|m| m.avg_uptime),
}), }),
None => None, None => None,
} }
@@ -166,7 +153,6 @@ impl ThreadsafeMixNodesCache {
pub(crate) async fn get_detailed_mixnodes(&self) -> Vec<PrettyDetailedMixNodeBond> { pub(crate) async fn get_detailed_mixnodes(&self) -> Vec<PrettyDetailedMixNodeBond> {
let mixnodes_guard = self.mixnodes.read().await; let mixnodes_guard = self.mixnodes.read().await;
let location_guard = self.locations.read().await; let location_guard = self.locations.read().await;
let mixnode_health_guard = self.mixnode_health.read().await;
mixnodes_guard mixnodes_guard
.all_mixnodes .all_mixnodes
@@ -174,7 +160,6 @@ impl ThreadsafeMixNodesCache {
.map(|bond| { .map(|bond| {
let location = location_guard.get(&bond.mix_node.identity_key); let location = location_guard.get(&bond.mix_node.identity_key);
let copy = bond.clone(); let copy = bond.clone();
let health = mixnode_health_guard.get(&bond.mix_node.identity_key);
PrettyDetailedMixNodeBond { PrettyDetailedMixNodeBond {
location: location.and_then(|l| l.location.clone()), location: location.and_then(|l| l.location.clone()),
status: mixnodes_guard.determine_node_status(&bond.mix_node.identity_key), status: mixnodes_guard.determine_node_status(&bond.mix_node.identity_key),
@@ -183,7 +168,6 @@ impl ThreadsafeMixNodesCache {
owner: copy.owner, owner: copy.owner,
layer: copy.layer, layer: copy.layer,
mix_node: copy.mix_node, mix_node: copy.mix_node,
avg_uptime: health.map(|m| m.avg_uptime),
} }
}) })
.collect() .collect()
@@ -204,14 +188,4 @@ impl ThreadsafeMixNodesCache {
guard.active_mixnodes = active_nodes; guard.active_mixnodes = active_nodes;
guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL; guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL;
} }
pub(crate) async fn update_health_cache(&self, all_uptimes: Vec<UptimeResponse>) {
let mut mixnode_health = self.mixnode_health.write().await;
for uptime in all_uptimes {
let health = MixNodeHealth {
avg_uptime: uptime.avg_uptime,
};
mixnode_health.set(&uptime.identity, health);
}
}
} }
-30
View File
@@ -4,7 +4,6 @@
use std::future::Future; use std::future::Future;
use mixnet_contract_common::{GatewayBond, MixNodeBond}; use mixnet_contract_common::{GatewayBond, MixNodeBond};
use validator_client::models::UptimeResponse;
use validator_client::nymd::error::NymdError; use validator_client::nymd::error::NymdError;
use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse}; use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse};
use validator_client::ValidatorClientError; use validator_client::ValidatorClientError;
@@ -89,17 +88,6 @@ impl ExplorerApiTasks {
.await .await
} }
async fn retrieve_all_mixnode_avg_uptimes(
&self,
) -> Result<Vec<UptimeResponse>, ValidatorClientError> {
self.state
.inner
.validator_client
.0
.get_mixnode_avg_uptimes()
.await
}
async fn update_mixnode_cache(&self) { async fn update_mixnode_cache(&self) {
let all_bonds = self.retrieve_all_mixnodes().await; let all_bonds = self.retrieve_all_mixnodes().await;
let rewarded_nodes = self let rewarded_nodes = self
@@ -121,21 +109,6 @@ impl ExplorerApiTasks {
.await; .await;
} }
async fn update_mixnode_health_cache(&self) {
match self.retrieve_all_mixnode_avg_uptimes().await {
Ok(response) => {
self.state
.inner
.mixnodes
.update_health_cache(response)
.await
}
Err(e) => {
error!("Failed to get mixnode avg uptimes: {:?}", e)
}
}
}
async fn update_validators_cache(&self) { async fn update_validators_cache(&self) {
match self.retrieve_all_validators().await { match self.retrieve_all_validators().await {
Ok(response) => self.state.inner.validators.update_cache(response).await, Ok(response) => self.state.inner.validators.update_cache(response).await,
@@ -172,9 +145,6 @@ impl ExplorerApiTasks {
info!("Updating mix node cache..."); info!("Updating mix node cache...");
self.update_mixnode_cache().await; self.update_mixnode_cache().await;
info!("Updating mix node health cache...");
self.update_mixnode_health_cache().await;
info!("Done"); info!("Done");
} }
}); });
-14
View File
@@ -1,14 +0,0 @@
module.exports = {
extends: [
'@nymproject/eslint-config-react-typescript'
],
overrides: [
{
files: ['*.ts'],
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
}
}
]
}
+5
View File
@@ -0,0 +1,5 @@
{
"extends": [
"@nymproject/eslint-config-react-typescript"
]
}
-63
View File
@@ -1,63 +0,0 @@
/* eslint-disable no-param-reassign */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: '@storybook/react',
core: {
builder: 'webpack5',
},
// webpackFinal: async (config, { configType }) => {
// // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// // You can change the configuration based on that.
// // 'PRODUCTION' is used when building the static version of storybook.
webpackFinal: async (config) => {
config.module.rules.forEach((rule) => {
// look for SVG import rule and replace
// NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
if (rule.test?.toString().includes('svg')) {
rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
}
});
// handle asset loading with this
config.module.rules.unshift({
test: /\.svg(\?.*)?$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
});
config.resolve.extensions = ['.tsx', '.ts', '.js'];
config.resolve.plugins = [new TsconfigPathsPlugin()];
config.resolve.fallback = {
fs: false,
tls: false,
path: false,
http: false,
https: false,
stream: false,
crypto: false,
net: false,
zlib: false,
};
config.plugins.push(new ForkTsCheckerWebpackPlugin({
typescript: {
mode: 'write-references',
diagnosticOptions: {
semantic: true,
syntactic: true,
},
},
}));
// Return the altered config
return config;
},
features: {
emotionAlias: false,
},
};
-56
View File
@@ -1,56 +0,0 @@
/* eslint-disable react/react-in-jsx-scope */
import { NymNetworkExplorerThemeProvider } from '@nymproject/mui-theme';
import { Box } from '@mui/material';
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
const withThemeProvider = (Story, context) => (
<div style={{ display: 'grid', height: '100%', gridTemplateColumns: '50% 50%' }}>
<div>
<NymNetworkExplorerThemeProvider mode="light">
<Box
p={4}
sx={{
display: 'grid',
gridTemplateRows: '80vh 2rem',
background: (theme) => theme.palette.background.default,
color: (theme) => theme.palette.text.primary,
}}
>
<Box sx={{ overflowY: 'auto' }}>
<Story {...context} />
</Box>
<h4 style={{ textAlign: 'center' }}>Light mode</h4>
</Box>
</NymNetworkExplorerThemeProvider>
</div>
<div>
<NymNetworkExplorerThemeProvider mode="dark">
<Box
p={4}
sx={{
display: 'grid',
gridTemplateRows: '80vh 2rem',
background: (theme) => theme.palette.background.default,
color: (theme) => theme.palette.text.primary,
}}
>
<Box sx={{ overflowY: 'auto' }}>
<Story {...context} />
</Box>
<h4 style={{ textAlign: 'center' }}>Dark mode</h4>
</Box>
</NymNetworkExplorerThemeProvider>
</div>
</div>
);
export const decorators = [withThemeProvider];
+3 -13
View File
@@ -30,15 +30,8 @@
"use-clipboard-copy": "^0.2.0" "use-clipboard-copy": "^0.2.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0", "@nymproject/eslint-config-react-typescript": "^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-interactions": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9",
"@svgr/webpack": "^6.1.1", "@svgr/webpack": "^6.1.1",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0", "@testing-library/react": "^12.0.0",
@@ -102,13 +95,10 @@
"build:serve": "npx serve dist", "build:serve": "npx serve dist",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"tsc": "tsc --noEmit true", "tsc": "tsc",
"tsc:watch": "tsc --watch --noEmit true", "tsc:watch": "tsc --watch",
"lint": "eslint src", "lint": "eslint src",
"lint:fix": "eslint src --fix", "lint:fix": "eslint src --fix"
"prestorybook": "yarn --cwd .. build",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
-4
View File
@@ -18,7 +18,6 @@ import {
MixNodeResponse, MixNodeResponse,
MixNodeResponseItem, MixNodeResponseItem,
MixnodeStatus, MixnodeStatus,
MixNodeEconomicDynamicsStatsResponse,
StatsResponse, StatsResponse,
StatusResponse, StatusResponse,
SummaryOverviewResponse, SummaryOverviewResponse,
@@ -123,9 +122,6 @@ export class Api {
static fetchMixnodeDescriptionById = async (id: string): Promise<MixNodeDescriptionResponse> => static fetchMixnodeDescriptionById = async (id: string): Promise<MixNodeDescriptionResponse> =>
(await fetch(`${MIXNODE_API}/${id}/description`)).json(); (await fetch(`${MIXNODE_API}/${id}/description`)).json();
static fetchMixnodeEconomicDynamicsStatsById = async (id: string): Promise<MixNodeEconomicDynamicsStatsResponse> =>
(await fetch(`${MIXNODE_API}/${id}/economic-dynamics-stats`)).json();
static fetchStatusById = async (id: string): Promise<StatusResponse> => (await fetch(`${MIXNODE_PING}/${id}`)).json(); static fetchStatusById = async (id: string): Promise<StatusResponse> => (await fetch(`${MIXNODE_PING}/${id}`)).json();
static fetchUptimeStoryById = async (id: string): Promise<UptimeStoryResponse> => static fetchUptimeStoryById = async (id: string): Promise<UptimeStoryResponse> =>
+2 -3
View File
@@ -12,13 +12,12 @@ export type ColumnsType = {
headerAlign: string; headerAlign: string;
flex?: number; flex?: number;
width?: number; width?: number;
tooltipInfo?: string;
}; };
export interface UniversalTableProps<T = any> { export interface UniversalTableProps {
tableName: string; tableName: string;
columnsData: ColumnsType[]; columnsData: ColumnsType[];
rows: T[]; rows: any[];
} }
function formatCellValues(val: string | number, field: string) { function formatCellValues(val: string | number, field: string) {
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { Alert, Box, CircularProgress, useMediaQuery, Typography } from '@mui/material'; import { Alert, Box, CircularProgress, useMediaQuery } from '@mui/material';
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import Table from '@mui/material/Table'; import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody'; import TableBody from '@mui/material/TableBody';
@@ -98,7 +98,7 @@ export const BondBreakdownTable: React.FC = () => {
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow> <TableRow>
<TableCell align="left">Self</TableCell> <TableCell align="left">Pledge total</TableCell>
<TableCell align="left" data-testid="pledge-total-amount"> <TableCell align="left" data-testid="pledge-total-amount">
{bonds.pledges} {bonds.pledges}
</TableCell> </TableCell>
@@ -127,62 +127,21 @@ export const BondBreakdownTable: React.FC = () => {
sx={{ sx={{
maxHeight: 400, maxHeight: 400,
overflowY: 'scroll', overflowY: 'scroll',
p: 2,
background: theme.palette.background.paper,
}} }}
> >
<Box
sx={{
display: 'flex',
alignItems: 'baseline',
width: '100%',
p: 2,
borderBottom: `1px solid ${theme.palette.divider}`,
}}
data-testid="delegations-total-amount"
>
<Typography
sx={{
fontSize: 16,
fontWeight: 600,
}}
>
Delegations&nbsp;&nbsp;
</Typography>
<Typography
sx={{
fontSize: 12,
fontWeight: 400,
}}
>
{`(${delegations?.data?.length} delegators)`}
</Typography>
</Box>
<Table stickyHeader> <Table stickyHeader>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell <TableCell sx={{ fontWeight: 600, background: '#242C3D' }} align="left">
sx={{
fontWeight: 600,
background: theme.palette.background.paper,
}}
align="left"
>
Delegators Delegators
</TableCell> </TableCell>
<TableCell <TableCell sx={{ fontWeight: 600, background: '#242C3D' }} align="left">
sx={{
fontWeight: 600,
background: theme.palette.background.paper,
}}
align="left"
>
Stake Stake
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
fontWeight: 600, fontWeight: 600,
background: theme.palette.background.paper, background: '#242C3D',
width: '200px', width: '200px',
}} }}
align="left" align="left"
@@ -1,49 +0,0 @@
import { ColumnsType } from '../../DetailTable';
export const EconomicsInfoColumns: ColumnsType[] = [
{
field: 'estimatedTotalReward',
title: 'Estimated Total Reward',
flex: 1,
headerAlign: 'left',
tooltipInfo: 'Estimated reward per epoch for this profit margin if your node is selected in the active set.',
},
{
field: 'estimatedOperatorReward',
title: 'Estimated Operator Reward',
flex: 1,
headerAlign: 'left',
tooltipInfo: 'Estimated reward per epoch for this profit margin if your node is selected in the active set.',
},
{
field: 'selectionChance',
title: 'Active Set Probability',
flex: 1,
headerAlign: 'left',
tooltipInfo:
'Probability of getting selected in the reward set (active and standby nodes) in the next epoch. The more your stake, the higher the chances to be selected.',
},
{
field: 'stakeSaturation',
title: 'Stake Saturation',
flex: 1,
headerAlign: 'left',
tooltipInfo:
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set.',
},
{
field: 'profitMargin',
title: 'Profit Margin',
flex: 1,
headerAlign: 'left',
tooltipInfo:
'Percentage of the delegates rewards that the operator takes as fee before rewards are distributed to the delegates.',
},
{
field: 'avgUptime',
title: 'Avg. Uptime',
flex: 1,
headerAlign: 'left',
tooltipInfo: 'Nodes average uptime in the last 24h.',
},
];
@@ -1,31 +0,0 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { EconomicsProgress } from './EconomicsProgress';
export default {
title: 'Mix Node Detail/Economics/ProgressBar',
component: EconomicsProgress,
} as ComponentMeta<typeof EconomicsProgress>;
const Template: ComponentStory<typeof EconomicsProgress> = (args) => <EconomicsProgress {...args} />;
export const Empty = Template.bind({});
Empty.args = {};
export const OverThreshold = Template.bind({});
OverThreshold.args = {
threshold: 100,
value: 120,
};
export const UnderThreshold = Template.bind({});
UnderThreshold.args = {
threshold: 100,
value: 80,
};
export const OnThreshold = Template.bind({});
OnThreshold.args = {
threshold: 100,
value: 100,
};
@@ -1,38 +0,0 @@
import * as React from 'react';
import LinearProgress, { LinearProgressProps } from '@mui/material/LinearProgress';
import { useTheme } from '@mui/material/styles';
import { Box } from '@mui/system';
const parseToNumber = (value: number | undefined | string) =>
typeof value === 'string' ? parseInt(value || '', 10) : value || 0;
export const EconomicsProgress: React.FC<
LinearProgressProps & {
threshold?: number;
}
> = ({ threshold, ...props }) => {
const theme = useTheme();
const { value } = props;
const valueNumber: number = parseToNumber(value);
const thresholdNumber: number = parseToNumber(threshold);
const percentageColor = valueNumber > (threshold || 100) ? 'warning' : 'inherit';
const percentageToDisplay = Math.min(valueNumber, thresholdNumber);
return (
<Box
sx={{
width: 6 / 10,
color: valueNumber > (threshold || 100) ? theme.palette.warning.main : theme.palette.nym.wallet.fee,
}}
>
<LinearProgress
{...props}
variant="determinate"
color={percentageColor}
value={percentageToDisplay}
sx={{ width: '100%', borderRadius: '5px', backgroundColor: theme.palette.common.white }}
/>
</Box>
);
};
@@ -1,129 +0,0 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { DelegatorsInfoTable } from './Table';
import { EconomicsInfoColumns } from './Columns';
import { EconomicsInfoRowWithIndex } from './types';
export default {
title: 'Mix Node Detail/Economics',
component: DelegatorsInfoTable,
} as ComponentMeta<typeof DelegatorsInfoTable>;
const row: EconomicsInfoRowWithIndex = {
id: 1,
selectionChance: {
value: 'High',
},
avgUptime: {
value: '65 %',
},
estimatedOperatorReward: {
value: '80000.123456 NYM',
},
estimatedTotalReward: {
value: '80000.123456 NYM',
},
profitMargin: {
value: '10 %',
},
stakeSaturation: {
value: '80 %',
progressBarValue: 80,
},
};
const rowVeryHighProbabilitySelection: EconomicsInfoRowWithIndex = {
...row,
selectionChance: {
value: 'Very High',
},
};
const rowModerateProbabilitySelection: EconomicsInfoRowWithIndex = {
...row,
selectionChance: {
value: 'Moderate',
},
};
const rowLowProbabilitySelection: EconomicsInfoRowWithIndex = {
...row,
selectionChance: {
value: 'Low',
},
};
const rowVeryLowProbabilitySelection: EconomicsInfoRowWithIndex = {
...row,
selectionChance: {
value: 'Very Low',
},
};
const emptyRow: EconomicsInfoRowWithIndex = {
id: 1,
selectionChance: {
value: '-',
progressBarValue: 0,
},
avgUptime: {
value: '-',
},
estimatedOperatorReward: {
value: '-',
},
estimatedTotalReward: {
value: '-',
},
profitMargin: {
value: '-',
},
stakeSaturation: {
value: '-',
progressBarValue: 0,
},
};
const Template: ComponentStory<typeof DelegatorsInfoTable> = (args) => <DelegatorsInfoTable {...args} />;
export const Empty = Template.bind({});
Empty.args = {
rows: [emptyRow],
columnsData: EconomicsInfoColumns,
tableName: 'storybook',
};
export const selectionChanceVeryHigh = Template.bind({});
selectionChanceVeryHigh.args = {
rows: [rowVeryHighProbabilitySelection],
columnsData: EconomicsInfoColumns,
tableName: 'storybook',
};
export const selectionChanceHigh = Template.bind({});
selectionChanceHigh.args = {
rows: [row],
columnsData: EconomicsInfoColumns,
tableName: 'storybook',
};
export const selectionChanceModerate = Template.bind({});
selectionChanceModerate.args = {
rows: [rowModerateProbabilitySelection],
columnsData: EconomicsInfoColumns,
tableName: 'storybook',
};
export const selectionChanceLow = Template.bind({});
selectionChanceLow.args = {
rows: [rowLowProbabilitySelection],
columnsData: EconomicsInfoColumns,
tableName: 'storybook',
};
export const selectionChanceVeryLow = Template.bind({});
selectionChanceVeryLow.args = {
rows: [rowVeryLowProbabilitySelection],
columnsData: EconomicsInfoColumns,
tableName: 'storybook',
};
@@ -1,55 +0,0 @@
import { currencyToString } from '../../../utils/currency';
import { useMixnodeContext } from '../../../context/mixnode';
import { ApiState, MixNodeEconomicDynamicsStatsResponse } from '../../../typeDefs/explorer-api';
import { EconomicsInfoRowWithIndex } from './types';
const selectionChance = (economicDynamicsStats: ApiState<MixNodeEconomicDynamicsStatsResponse> | undefined) => {
const inclusionProbability = economicDynamicsStats?.data?.active_set_inclusion_probability;
switch (inclusionProbability) {
case 'High':
case 'Moderate':
case 'Low':
return inclusionProbability;
case 'VeryHigh':
return 'Very High';
case 'VeryLow':
return 'Very Low';
default:
return '-';
}
};
export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
const { economicDynamicsStats, mixNode } = useMixnodeContext();
const estimatedNodeRewards =
currencyToString((economicDynamicsStats?.data?.estimated_total_node_reward || '').toString()) || '-';
const estimatedOperatorRewards =
currencyToString((economicDynamicsStats?.data?.estimated_operator_reward || '').toString()) || '-';
const stakeSaturation = economicDynamicsStats?.data?.stake_saturation || '-';
const profitMargin = mixNode?.data?.mix_node.profit_margin_percent || '-';
const avgUptime = economicDynamicsStats?.data?.current_interval_uptime;
return {
id: 1,
estimatedTotalReward: {
value: estimatedNodeRewards,
},
estimatedOperatorReward: {
value: estimatedOperatorRewards,
},
selectionChance: {
value: selectionChance(economicDynamicsStats),
},
stakeSaturation: {
progressBarValue: typeof stakeSaturation === 'number' ? stakeSaturation * 100 : 0,
value: typeof stakeSaturation === 'number' ? `${(stakeSaturation * 100).toFixed(2)} %` : '-',
},
profitMargin: {
value: profitMargin ? `${profitMargin} %` : '-',
},
avgUptime: {
value: avgUptime ? `${avgUptime} %` : '-',
},
};
};
@@ -1,175 +0,0 @@
import * as React from 'react';
import {
IconButton,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
useMediaQuery,
} from '@mui/material';
import { Box } from '@mui/system';
import { styled, useTheme, Theme } from '@mui/material/styles';
import Tooltip, { tooltipClasses, TooltipProps } from '@mui/material/Tooltip';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from './types';
import { EconomicsProgress } from './EconomicsProgress';
import { cellStyles } from '../../Universal-DataGrid';
import { UniversalTableProps } from '../../DetailTable';
const tooltipBackGroundColor = '#A0AED1';
const threshold = 100;
const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
const progressBarValue = value?.progressBarValue || 0;
const fieldValue = value.value;
if (progressBarValue > 100) {
return theme.palette.warning.main;
}
if (field === 'selectionChance') {
switch (fieldValue) {
case 'High':
case 'Very High':
return theme.palette.nym.networkExplorer.selectionChance.overModerate;
case 'Moderate':
return theme.palette.nym.networkExplorer.selectionChance.moderate;
case 'Low':
case 'Very Low':
return theme.palette.nym.networkExplorer.selectionChance.underModerate;
default:
return theme.palette.nym.wallet.fee;
}
}
return theme.palette.nym.wallet.fee;
};
const formatCellValues = (value: EconomicsRowsType, field: string, theme: Theme) => {
const isTablet = useMediaQuery(theme.breakpoints.down('lg'));
if (value.progressBarValue) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', flexDirection: isTablet ? 'column' : 'row' }} id="field">
<Typography
sx={{
mr: isTablet ? 0 : 1,
mb: isTablet ? 1 : 0,
fontWeight: '600',
fontSize: '12px',
}}
id={field}
>
{value.value}
</Typography>
<EconomicsProgress threshold={threshold} value={value.progressBarValue} />
</Box>
);
}
return (
<Box sx={{ display: 'flex', alignItems: 'center' }} id="field">
<Typography sx={{ mr: 1, fontWeight: '600', fontSize: '12px' }} id={field}>
{value.value}
</Typography>
</Box>
);
};
export const DelegatorsInfoTable: React.FC<UniversalTableProps<EconomicsInfoRowWithIndex>> = ({
tableName,
columnsData,
rows,
}) => {
const theme = useTheme();
const CustomTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))({
[`& .${tooltipClasses.tooltip}`]: {
maxWidth: 230,
background: tooltipBackGroundColor,
color: theme.palette.nym.networkExplorer.nav.hover,
},
});
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label={tableName}>
<TableHead>
<TableRow>
{columnsData?.map(({ field, title, flex, tooltipInfo }) => (
<TableCell key={field} sx={{ fontSize: 14, fontWeight: 600, flex }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{tooltipInfo && (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<CustomTooltip
title={tooltipInfo}
id={field}
placement="top-start"
sx={{
'& .MuiTooltip-arrow': {
color: '#A0AED1',
},
}}
arrow
>
<IconButton
sx={{
padding: 0,
py: 1,
pr: 1,
}}
disableFocusRipple
disableRipple
>
<InfoOutlinedIcon
sx={{
height: '18px',
width: '18px',
}}
/>
</IconButton>
</CustomTooltip>
</Box>
)}
{title}
</Box>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows?.map((eachRow) => (
<TableRow key={eachRow.id} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
{columnsData?.map((_, index: number) => {
const { field } = columnsData[index];
const value: EconomicsRowsType = (eachRow as any)[field];
return (
<TableCell
key={_.title}
component="th"
scope="row"
variant="body"
sx={{
...cellStyles,
padding: 2,
width: 200,
fontSize: 12,
fontWeight: 600,
color: textColour(value, field, theme),
}}
data-testid={`${_.title.replace(/ /g, '-')}-value`}
>
{formatCellValues(value, columnsData[index].field, theme)}
</TableCell>
);
})}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};
@@ -1,3 +0,0 @@
export { DelegatorsInfoTable } from './Table';
export { EconomicsInfoColumns } from './Columns';
export { EconomicsInfoRows } from './Rows';
@@ -1,15 +0,0 @@
export type EconomicsRowsType = {
progressBarValue?: number;
value: string;
};
export interface EconomicsInfoRow {
estimatedTotalReward: EconomicsRowsType;
estimatedOperatorReward: EconomicsRowsType;
selectionChance: EconomicsRowsType;
stakeSaturation: EconomicsRowsType;
profitMargin: EconomicsRowsType;
avgUptime: EconomicsRowsType;
}
export type EconomicsInfoRowWithIndex = EconomicsInfoRow & { id: number };
@@ -11,8 +11,6 @@ export type MixnodeRowType = {
self_percentage: string; self_percentage: string;
host: string; host: string;
layer: string; layer: string;
profit_percentage: string;
avg_uptime: number;
}; };
export function mixnodeToGridRow(arrayOfMixnodes?: MixNodeResponse): MixnodeRowType[] { export function mixnodeToGridRow(arrayOfMixnodes?: MixNodeResponse): MixnodeRowType[] {
@@ -24,7 +22,6 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
const delegations = Number(item.total_delegation.amount) || 0; const delegations = Number(item.total_delegation.amount) || 0;
const totalBond = pledge + delegations; const totalBond = pledge + delegations;
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2); const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
const profitPercentage = item.mix_node.profit_margin_percent || 0;
return { return {
id: item.owner, id: item.owner,
status: item.status, status: item.status,
@@ -35,7 +32,5 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
self_percentage: selfPercentage, self_percentage: selfPercentage,
host: item?.mix_node?.host || '', host: item?.mix_node?.host || '',
layer: item?.layer || '', layer: item?.layer || '',
profit_percentage: `${profitPercentage}%`,
avg_uptime: item.avg_uptime,
}; };
} }
+3 -6
View File
@@ -22,12 +22,9 @@ export const Socials: React.FC<{ isFooter?: boolean }> = ({ isFooter }) => {
<IconButton component="a" href={TELEGRAM_LINK} target="_blank" data-testid="telegram"> <IconButton component="a" href={TELEGRAM_LINK} target="_blank" data-testid="telegram">
<TelegramIcon color={color} size={24} /> <TelegramIcon color={color} size={24} />
</IconButton> </IconButton>
{false && ( <IconButton component="a" href={DISCORD_LINK} target="_blank" data-testid="discord">
<IconButton component="a" href={DISCORD_LINK} target="_blank" data-testid="discord"> <DiscordIcon color={color} size={24} />
<DiscordIcon color={color} size={24} /> </IconButton>
</IconButton>
)}
<IconButton component="a" href={TWITTER_LINK} target="_blank" data-testid="twitter"> <IconButton component="a" href={TWITTER_LINK} target="_blank" data-testid="twitter">
<TwitterIcon color={color} size={24} /> <TwitterIcon color={color} size={24} />
</IconButton> </IconButton>
@@ -1,15 +0,0 @@
export type RowsType = {
value?: string | number;
visualProgressValue?: number;
};
export interface DelegatorsInfoRow {
estimated_total_reward: RowsType;
estimated_operator_reward: RowsType;
active_set_probability: RowsType;
stake_saturation: RowsType;
profit_margin: RowsType;
avg_uptime: RowsType;
}
export type DelegatorsInfoRowWithIndex = DelegatorsInfoRow & { id: number };

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