Compare commits

...

122 Commits

Author SHA1 Message Date
Mark Sinclair 23f0212a16 Update CHANGELOG 2022-09-02 15:35:06 +01:00
Mark Sinclair cb58a62ff7 wasm-client: fix up dependencies and feature flags so that wasm-pack build works 2022-09-02 15:32:57 +01:00
Mark Sinclair cc8be3bce2 wasm-client: change example to use the mainnet validator API 2022-09-02 15:32:46 +01:00
Jędrzej Stuczyński a2c6abd3dd Made nodes_to_remove field in mixnet MigrateMsg public 2022-09-01 17:10:17 +01:00
Bogdan-Ștefan Neacşu d289c46e87 Feature/nr err report (#1576)
* common/socks5: Use thiserror and add copyright notice

* Send allowlist failure msg back to socks5 client

* Add some serde unit tests

* Fix clippy after rustup update

* Update changelog
2022-08-31 16:27:02 +03:00
Bogdan-Ștefan Neacşu a6aba3defd Feature/validator api shutdown (#1573)
* Rewarded set updater shutdown (partial) handling

* Shutdown handling in monitor

* Remove shutdown from packet receiver

* Configurable shutdown timeout

* Select on test_run too

* Remove unnecessary await/async

* Add bias to shutdown select and concurrency for big tasks

* Put cpu-bound packet prep on separate thread, to avoid blocking

* Use a better fit timeout value

* Fix clippy warnings

* Update changelog

* Fix wasm client
2022-08-30 14:14:50 +03:00
Jędrzej Stuczyński 6557be3738 Delegator compoudning to bypass pending events (#1571)
* Delegator compoudning to bypass pending events

* Updated changelog
2022-08-30 11:56:13 +01:00
Bogdan-Ștefan Neacşu 7134755073 Feature/credential mode (#1570)
* Activate enabled cred mode for coconut feature

* Fix disable cred mode propagation bug
2022-08-26 16:45:32 +03:00
Sachin Kamath dd1420a65a Wallet - set gateway default port to 9000 (#1568)
* fix(wallet): client port to gateway defaults

* fix(wallet): refactor variable name
2022-08-26 13:28:58 +02:00
Jędrzej Stuczyński df1bc60464 Feature/vesting delegations queries (#1569)
* Added contract queries for vesting delegations

* Added the queries on NymdClient

* Added account_id to DelegationTimesResponse

* Returning raw u64 as opposed to wrapped Timestamp

* Updated changelog
2022-08-26 11:24:16 +01:00
Bogdan-Ștefan Neacşu 865e809342 Remove hard-coded values from credential client (#1566) 2022-08-25 17:52:43 +03:00
Jon Häggblad 51f9c1ca29 Connect: remove some sources of panics when initializing socks5-client (#1563)
* connect: remove panic when unable to load previous gateway conf

* connect: dont panic when cant get config file
2022-08-25 16:25:31 +02:00
Jon Häggblad 303b014a59 types: fix missing entries in SelectionChance (#1564) 2022-08-25 14:06:54 +02:00
Jon Häggblad e1e20fb13e inclusion-prob: make rng generic and predictable in tests (#1561) 2022-08-25 08:47:04 +02:00
Mark Sinclair 0c3c13ae88 Change nym-connect window title to NymConnect 2022-08-24 15:48:26 +01:00
Jon Häggblad 8c8b7d71d0 validator-api: create node status cache with selection probabilies (#1547)
* validator-api: create node status cache with selection probabilies

Create a node status cache to complement the contract cache. Initially
we store the simulated active set selection probabilities.

* validator-api: add validator cache watch channel

* changelog: add note

* validator-api: clippy fixes

* validator-api: fix clippy

* validator-api: additional fields to inclusion probabilities response

* selection chance: revert back to 3 buckets

* selection chance: revert buckets again

* rustfmt

* validator-api: remove the old get_mixnode_inclusion_probability

* node-status-cache: return error when refreshing

* inclusion-simulator: cap on wall clock time

* node status cache: tidy
2022-08-24 14:29:21 +02:00
Jon Häggblad 3163c5f054 gateway-client: clippy::question-mark 2022-08-24 09:51:47 +02:00
Jon Häggblad 4a1794b2f1 nymcoconut: fix named-arguments-used-positionally 2022-08-24 09:51:47 +02:00
Jon Häggblad 1898b8ed96 validator-api: add bounds checks in compute_mixnode_reward_estimation (#1559) 2022-08-24 09:30:12 +02:00
Mark Sinclair a23471859d GitHub Actions: fix up artifacts on nym-cli-publish 2022-08-23 20:19:57 +01:00
Mark Sinclair 9d8c9edf22 Add GitHub Action to build nym-cli on all platforms 2022-08-23 19:27:03 +01:00
Pierre Dommerc 5ea7b24efc fix(nym-wallet): bonding, enhance error handling (#1543)
open a dialog for user feedback when error occurs
add some better error logging
2022-08-23 17:53:57 +02:00
Jędrzej Stuczyński a43a24faa8 Exposed direct queries to smart contract on NymdClient (#1558)
* Exposed direct queries to smart contract on NymdClient

* Updated changelog
2022-08-23 15:45:43 +01:00
Jędrzej Stuczyński 39ee215005 Storing delegations using block timestamp as opposed to block height (#1544)
* Storing delegations using block timestamp as opposed to block height

* Updated changelog
2022-08-23 09:42:34 +01:00
Mark Sinclair ef7961f58e Update nym-release-publish.yml 2022-08-19 18:32:28 +01:00
Mark Sinclair e628338b33 GitHub Actions: add manual dispatch and artifact upload 2022-08-19 18:23:16 +01:00
Pierre Dommerc 1bb137f87f Nym-connect - service provider persistant storage (#1540)
* feat(nym-connect): local storage service provider

* feat(nym-connect): local storage service provider

* feat(nym-connect): local storage service provider

* Add some extra height to the window to stop the scrollbar apearing

* Show the service description when selecting a Service Provider

* Bump version

* Update changelog

* fix(nym-connect): hotfix

* fix(nym-connect): hotfix

* fix(nym-connect): wrong disabled state for connection button

Co-authored-by: Mark Sinclair <mmsinclair@gmail.com>
2022-08-18 18:53:03 +02:00
Dave Hrycyszyn 773f9e5ead Moving helpful comment into docs comment 2022-08-18 10:45:42 +01:00
Pierre Dommerc 79f695f138 Wallet: new UI for pending delegation (#1499)
* refactor(wallet-delegation): new delegation pending ui

* refactor(wallet-delegation): remove pending delegation table

* refactor(wallet-delegation): new pending delegation ui

* refactor(wallet-delegation): remove logs

* refactor(wallet-delegation): better typing

* refactor(wallet-balance): feedback review
2022-08-18 11:20:32 +02:00
Pierre Dommerc 3aabbcf876 fix(nym-wallet): make get_operator_rewards request success optional (#1542) 2022-08-17 17:21:36 +02:00
Jędrzej Stuczyński d96b7408db Allowing optional fee for top-level NymdClient (#1541)
* Allowing optional fee for top-level NymdClient

* Updated changelog

* Fixed usages of said methods in validator-api
2022-08-17 13:27:53 +01:00
Jon Häggblad 728b0f4549 inclusion-probability: new crate for simulation active set inclusion (#1534) 2022-08-16 14:19:55 +02:00
tommy 0a37c81709 remove not needed prior state of denom 2022-08-16 12:05:38 +02:00
Jędrzej Stuczyński 957cbb45b0 Chore/linter updates (#1530)
* Removed needless return

* Added Eq derivation where applicable for types with PartialEq
2022-08-12 17:56:49 +01:00
Pierre Dommerc 8ec074cb1f fix(wallet): typo (#1527) 2022-08-12 16:02:27 +02:00
Jess ab5740087f Update CHANGELOG.md 2022-08-12 14:55:39 +01:00
Jess 6af59c303e Update CHANGELOG.md 2022-08-12 14:54:09 +01:00
Jess 27b384e034 Update CHANGELOG.md 2022-08-12 14:53:40 +01:00
Dave Hrycyszyn 7f5ce3ffeb Removing wallet entries from main changelog 2022-08-12 12:58:49 +01:00
Dave Hrycyszyn 89b6667c75 Splitting changelogs into their own files 2022-08-12 12:50:17 +01:00
Tommy Verrall a94a9aeaf5 Update README.md 2022-08-11 16:18:06 +01:00
tommy 6bc8b88a20 WIP - validator-api-tests
- Test base line
- Jest / Typescript / Node
2022-08-11 17:14:04 +02:00
Fouad 21f3991714 update sample env (#1523) 2022-08-11 12:21:19 +01:00
Tommy Verrall cd8eba988a Merge pull request #1522 from nymtech/release/wallet-v1.0.8
Release/wallet v1.0.8
2022-08-11 11:45:53 +01:00
Tommy Verrall d2b3841bbd Update Cargo.toml 2022-08-11 11:45:28 +01:00
Tommy Verrall de877fb337 Merge pull request #1521 from nymtech/release/nym-binaries-1.0.2
Release/nym binaries 1.0.2
2022-08-11 11:42:36 +01:00
Tommy Verrall d4c2b9060f Update changelog for v1.0.2 -reference 2022-08-11 10:02:34 +01:00
Bogdan-Ștefan Neacşu 41ac866729 Feature/validator api panics (#1520)
* Coconut unwraps

* Fail softly on epoch queries

* Update changelog

* Retry a few times before failing as before
2022-08-11 11:58:17 +03:00
Raphaël Walther a7afd2a1c7 Added audit workflow 2022-08-11 10:41:57 +02:00
Jon Häggblad df03daf2cc socks5: remove pub mod not needed anymore (#1517)
* socks5: remove pub mod not needed anymore

* all: dedup parse_validators and move to common

* rustfmt
2022-08-10 21:14:12 +02:00
Raphaël Walther b3f5a4f496 Added audit workflow 2022-08-10 16:35:49 +02:00
Raphaël Walther d080d661f7 Added audit workflow 2022-08-10 16:29:54 +02:00
Raphaël Walther 6deb481e5d Added audit workflow 2022-08-10 16:09:05 +02:00
Raphaël Walther 5b98e18a4e Added audit workflow 2022-08-10 16:03:11 +02:00
Raphaël Walther 506a0da89c Added audit workflow 2022-08-10 16:00:50 +02:00
Bogdan-Ștefan Neacșu c7fdcf0a79 Change for default mainnet and fix typo 2022-08-10 13:40:28 +03:00
Fouad f7d38a7ec6 modal style updates (#1508)
remove commented code

remove unused props

fix props
2022-08-10 10:14:48 +01:00
tommy 8edc762df9 Update
- update the cargo toml
- amend the work flow file
- and the envs to mainnet (disclaimer add qa after)
2022-08-10 11:10:38 +02:00
Pierre Dommerc 4459aca933 fix(wallet-delegation): add error feedback on wrong address (#1510)
* fix(wallet-delegation): add error feedback on wrong address

* fix(wallet-delegation): use generic component for modal error
2022-08-10 09:31:14 +01:00
Pierre Dommerc 5b84c58985 feat(wallet-balance): hide vesting ui when vesting balance is empty (#1505)
* feat(wallet-balance): hide vesting ui when vesting balance is empty

* refactor(wallet-balance): feedback review
2022-08-10 09:29:01 +01:00
Fouad 4301d91f6c Feature/wallet style updates (#1506)
* update buttons hover colours

* fix nymcard title size

* style updates for accounts!

* fix delegations page lock-up on network switch
2022-08-10 09:28:03 +01:00
Fouad 03d28c115e delegations page style updates (#1507) 2022-08-10 09:27:03 +01:00
tommy 7b15f350cd add service-provider vesioning to match other binaries 2022-08-10 10:14:07 +02:00
tommy 2b4917b8b1 fix messaging on init for nym-client 2022-08-10 10:11:00 +02:00
Jędrzej Stuczyński de78ca8d9b Removed unnecessary to-owned conversion 2022-08-09 15:25:45 +01:00
Gala 58d09e382a Merge pull request #1504 from nymtech/smooth-filter-slider
Smooth filter slider
2022-08-09 14:59:11 +02:00
Gala 0cef12d05b Merge pull request #1502 from nymtech/1501-contrast-text
fixing text color
2022-08-09 14:58:07 +02:00
Jon Häggblad 30e73ee795 Explorer: tweak selection probability categories (#1503)
* validator-api: tweak SelectionChance

* wallet: update SelectionChance colorMap

* changelog: add note
2022-08-09 12:57:47 +02:00
Tommy Verrall d918b69664 Update .env.qa
Fix wrong denoms for the qa.env
2022-08-09 11:53:11 +01:00
tommy 921e558660 Update versions on binaries 2022-08-09 11:56:54 +02:00
Gala b3b8d2ab46 adding the value to the cursor 2022-08-08 17:03:05 +02:00
tommy d62638b8e2 update wallet version 2022-08-08 16:53:00 +02:00
Gala 67130a1289 adding smooth prop 2022-08-08 16:30:10 +02:00
Fouad 0dabff72bd Feature/bonding refactor (#1481)
* feat(wallet-bonding): bonding page, new bond form wip

* feat(wallet-bonding): add node table component

feat(wallet-bonding): new dialog component

* feat(wallet-bonding): node settings flow

* feat(wallet-bonding): bond more flow (done)

* feat(wallet): use confirmation modal component

* feat(wallet-bonding): node menu ui

* refactor(wallet-bonding): bonding flow with new gasFee estimation

* feat(wallet-bonding): unbond with gasFee and request

* refactor(wallet-bonding): switch to simpledialog component to keep modals consistency

* feat(wallet-bonding): fetch mixnode status

* update coin types in new bonding page

* fix displayed denom

* rebuild BondedNodeCard using existing shared components

* create reuseable ActionMenu component

* new mixnode form

* add gateway bond form

* check balance and fetch fee on bond mixnode request

* node settings

* get node description

* fix up rust request

* lint fixes + used NodeTypeSelector component

* temporarily remove estimated operator reward

* update return on rust function

* dont display node name UI if name doesnt exist

* rebase develop

* fix uppercase address bug

Co-authored-by: Mark Sinclair <mmsinclair@gmail.com>
Co-authored-by: pierre <dommerc.pierre@gmail.com>
2022-08-08 14:09:57 +01:00
Gala e8e2f195e6 fixing text color 2022-08-08 14:09:33 +02:00
Jędrzej Stuczyński fa354016e0 Removed useless into() conversion 2022-08-08 09:49:52 +01:00
Tommy Verrall 935ee765e9 Merge pull request #1498 from nymtech/feature/fix-envs
fix .env files for explorer.
2022-08-05 16:36:08 +01:00
tommy 4c8e59e6fc fix .env files for explorer. 2022-08-05 17:11:35 +02:00
Bogdan-Ștefan Neacşu 067f3e6f1a validator-api: handle SIGTERM (#1496)
* validator-api: handle SIGTERM

* Update CHANGELOG
2022-08-05 15:59:34 +03:00
Gala 6f09d46dce Merge pull request #1492 from nymtech/feature/delegating_ui_update
feat(wallet-delegation): new confirmation modal ui
2022-08-05 12:08:51 +02:00
Gala bdef48331b Merge pull request #1489 from nymtech/332-fix-vesting-ui
Wallet: Fixing dark mode colours in vesting shedule
2022-08-05 12:05:19 +02:00
Gala 51a6936e51 Merge pull request #1491 from nymtech/330-mix-wallet-ui
Wallet: Fix light mode bg and nav bar UI
2022-08-05 12:02:50 +02:00
Gala fd456d2952 Merge pull request #1490 from nymtech/334-ne-changes
NE: Filter by use routing score instead of stake parameter and adding filters info
2022-08-04 18:24:04 +02:00
Gala eee1abe593 removing extra styles 2022-08-04 18:15:12 +02:00
Gala fffad43937 Avoiding CAPS in button 2022-08-04 17:51:24 +02:00
Gala 3a79f43a8d styling 2022-08-04 17:23:32 +02:00
pierre 2e495f87ab refactor(wallet-delegation): ModalListItem component, pass colon in label value 2022-08-04 16:35:14 +02:00
Gala 57a9f18f5a fixing padding marging and wording 2022-08-04 16:30:33 +02:00
Gala 0c6a0a9cae Merge branch 'develop' into 334-ne-changes 2022-08-04 15:18:54 +02:00
Gala c80d8d354a adding nodes number info 2022-08-04 15:17:44 +02:00
Gala 3f544dbc69 adding Advanced filtering text and hover fix 2022-08-04 14:59:17 +02:00
pierre d1e1f15db0 Merge branch 'develop' into feature/delegating_ui_update 2022-08-04 13:26:41 +02:00
pierre 651c314182 feat(wallet-delegation): new confirmation modal ui 2022-08-04 13:20:00 +02:00
Dave Hrycyszyn b957b939cf Typo fix 2022-08-04 11:01:36 +01:00
Gala a57545521d some refactor 2022-08-04 11:28:04 +02:00
Gala da60606921 hover bg color in dark and light mode 2022-08-03 17:20:46 +02:00
Gala 14f9bf7234 left nav spacing 2022-08-03 16:50:02 +02:00
Gala c1fa92869a fix light bg color 2022-08-03 16:49:47 +02:00
Gala c8533e3ec8 cleaning 2022-08-03 14:15:03 +02:00
Gala 06c4dd601d filter by use routing score instead of stake parameter 2022-08-03 14:06:33 +02:00
Gala 4ff80bbab2 fixing dark mode progress bar 2022-08-03 14:02:50 +02:00
Gala d7220b1fec adding info text in filters and renaming 2022-08-03 13:42:48 +02:00
Gala d92df9ada3 fixing dark mode colours 2022-08-03 12:24:21 +02:00
Bogdan-Ștefan Neacşu 9c19ae322d Bump regex version to fix dependabot (#1488) 2022-08-03 12:45:21 +03:00
Bogdan-Ștefan Neacşu 07893828d8 Fix NC filter for domains suffix-only domains (#1487)
* Fix NC filter for domains suffix-only domains

* Update CHANGELOG

* Fix unit test for filter

Some domains might be composed of the suffix only.

There are no nonsense domains, as they can be defined even on the local
machine. The underlying library doesn't resolve them, but rather uses a
fixed list of public suffixes to assess the domains.

* Fix clippy
2022-08-03 11:58:20 +03:00
Pierre Dommerc 1167f50543 [Wallet] move Receive page in modal (#1484)
* feat(wallet): move receive page in modal

* feat(wallet-receive): some ui work

* feat(wallet): simple modal component

show or not the Ok button based on onOk props

* feat(wallet): fix sx props type imports
2022-08-02 12:04:47 +02:00
Drazen Urch ba1818a903 Chitchat example (#1464)
* Chitchat example

* Cleanup
2022-08-01 14:19:04 +02:00
Bogdan-Ștefan Neacşu e631219a73 explorer-api: handle SIGTERM (#1482)
* explorer-api: handle SIGTERM

* Update CHANGELOG
2022-08-01 13:30:31 +03:00
rachyandco 207c6cf2c7 Correcting a typo (#1475)
Co-authored-by: Rachyandco <alexis@nymtech.net>
2022-07-29 09:13:07 +01:00
Drazen Urch c5ece97872 Stake inflation mitigations (#1480)
* Return Err from compound transactions

* Remove malicious nodes migration

* Reduce total delegation, before adding to it

* Blacklist malicious nodes, prevent future bonding

* Blacklisted gets no reward, enable compound

* Add GetBlacklistedNodes message

* Rebase on develop

* Remove TODO
2022-07-28 15:19:31 +02:00
Bogdan-Ștefan Neacșu 8a2c95d044 Mixnode option doc fix 2022-07-26 15:00:27 +03:00
Bogdan-Ștefan Neacşu ba5e3d4efa Remove service entries instead of zeroing them (#1479) 2022-07-26 11:53:10 +03:00
Bogdan-Ștefan Neacşu c81623a61a Add gateway id in the gateway stats (#1478)
* Add gateway id in the gateway stats

* Update CHANGELOG
2022-07-25 16:59:45 +03:00
Bogdan-Ștefan Neacşu 8bb42c2b1b Add migration code for mixnet and vesting contracts (#1477) 2022-07-25 14:05:22 +03:00
Mark Sinclair 33e161bd59 GitHub Actions: make explorer deployment only a manual step 2022-07-22 12:19:58 +01:00
Mark Sinclair 0233499036 GitHub Actions: add prod deploy step for Network Explorer UI 2022-07-22 10:34:20 +01:00
Mark Sinclair a059a29173 nym-connect: update CHANGELOG and bump version to 1.0.1 2022-07-22 10:15:18 +01:00
Mark Sinclair 83c3398570 nym-connect: copy changes 2022-07-22 10:09:13 +01:00
Fouad 93f931459a fix type update issues with actions (bonding, delegating etc.) (#1469) 2022-07-22 09:40:06 +01:00
Bogdan-Ștefan Neacşu 5a7b19aeb6 Nym connect use config from env (mainnet defaulted (#1471) 2022-07-21 17:03:14 +03:00
Bogdan-Ștefan Neacşu b901655591 Fix message deserialization in socks5 client (#1470) 2022-07-21 15:36:38 +03:00
Drazen Urch a9fdbccb82 Universal compound rewards message that anyone can call (#1387) 2022-07-21 10:51:33 +02:00
280 changed files with 12873 additions and 2259 deletions
+36
View File
@@ -0,0 +1,36 @@
name: Daily security audit
on: workflow_dispatch
jobs:
security_audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
notification:
if: ${{ failure() }}
needs: security_audit
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym daily audit"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "test"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+12
View File
@@ -1,6 +1,7 @@
name: CI for Network Explorer
on:
workflow_dispatch:
push:
paths:
- 'explorer/**'
@@ -75,3 +76,14 @@ jobs:
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
@@ -166,8 +166,8 @@ jobs:
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nightly"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "test"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
+50
View File
@@ -0,0 +1,50 @@
name: Publish Nym CLI binaries
on:
workflow_dispatch:
release:
types: [created]
env:
NETWORK: mainnet
jobs:
publish-nym-cli:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-cli-`
if: startsWith(github.ref, 'refs/tags/nym-cli-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-cli-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build binary
run: make build-nym-cli
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-cli-${{ matrix.platform }}
path: |
target/release/nym-cli*
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/nym-cli
+21 -1
View File
@@ -1,5 +1,7 @@
name: Publish Nym binaries
on:
workflow_dispatch:
release:
types: [created]
@@ -18,7 +20,7 @@ jobs:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-binaries-`
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
@@ -35,8 +37,24 @@ jobs:
command: build
args: --workspace --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: my-artifact
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-validator-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/nym-client
@@ -45,3 +63,5 @@ jobs:
target/release/nym-socks5-client
target/release/nym-validator-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
+34 -245
View File
@@ -2,16 +2,28 @@
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [Unreleased]
### Added
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
### Changed
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
[#1541]: https://github.com/nymtech/nym/pull/1541
[#1558]: https://github.com/nymtech/nym/pull/1558
[#1585]: https://github.com/nymtech/nym/pull/1585
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
### Added
- socks5 client/websocket client: add `--force-register-gateway` flag, useful when rerunning init ([#1353])
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
- nym-connect: add ability to select network requester and gateway ([#1427])
- nym-connect: add ability to export gateway keys as JSON
- nym-connect: add auto updater
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
- explorer-api: learned how to sum the delegations by owner in a new endpoint.
- explorer-api: add apy values to `mix_nodes` endpoint
@@ -23,12 +35,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- validator-api: add Swagger to document the REST API ([#1249]).
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
- validator-api: add `uptime`, `estimated_operator_apy`, `estimated_delegators_apy` to `/mixnodes/detailed` endpoint ([#1393])
- validator-api: add node info cache storing simulated active set inclusion probabilities
- network-statistics: a new mixnet service that aggregates and exposes anonymized data about mixnet services ([#1328])
- mixnode: Added basic mixnode hardware reporting to the HTTP API ([#1308]).
- validator-api: endpoint, in coconut mode, for returning the validator-api cosmos address ([#1404]).
- validator-client: add `denom` argument and add simple test for querying an account balance
- gateway, validator-api: Checks for coconut credential double spending attempts, taking the coconut bandwidth contract as source of truth ([#1457])
- coconut-bandwidth-contract: Record the state of a coconut credential; create specific proposal for releasing funds ([#1457])
- inclusion-probability: add simulator for active set inclusion probability
### Fixed
@@ -39,10 +53,12 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- native & socks5 clients: rerun init will now reuse previous gateway configuration instead of failing ([#1353])
- native & socks5 clients: deduplicate big chunks of init logic
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
- network-requester: fix filter for suffix-only domains ([#1487])
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service; cleaner shutdown, without panics ([#1496], [#1573]).
### Changed
- nym-connect: reuse config id instead of creating a new id on each connection
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
- all: updated all `cosmwasm`-related dependencies to `1.0.0` and `cw-storage-plus` to `0.13.4` [[#1318]]
- all: updated `rocket` to `0.5.0-rc.2`.
@@ -53,6 +69,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- validator-api: fee payment for multisig operations comes from the gateway account instead of the validator APIs' accounts ([#1419])
- multisig-contract: Limit the proposal creating functionality to one address (coconut-bandwidth-contract address) ([#1457])
- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463])
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
- network explorer: tweak how active set probability is shown ([#1503])
- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520])
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
[#1249]: https://github.com/nymtech/nym/pull/1249
@@ -74,78 +94,16 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#1393]: https://github.com/nymtech/nym/pull/1393
[#1404]: https://github.com/nymtech/nym/pull/1404
[#1419]: https://github.com/nymtech/nym/pull/1419
[#1427]: https://github.com/nymtech/nym/pull/1427
[#1457]: https://github.com/nymtech/nym/pull/1457
[#1463]: https://github.com/nymtech/nym/pull/1463
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
- wallet: dark mode
- wallet: when simulating gas costs, an automatic adjustment is being used ([#1388]).
[#1388]: https://github.com/nymtech/nym/pull/1388
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
### Added
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
- mixnet-contract: Added staking_supply field to ContractStateParams.
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
### Fixed
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
[#1255]: https://github.com/nymtech/nym/pull/1255
[#1257]: https://github.com/nymtech/nym/pull/1257
[#1258]: https://github.com/nymtech/nym/pull/1258
[#1275]: https://github.com/nymtech/nym/pull/1275
[#1284]: https://github.com/nymtech/nym/pull/1284
[#1292]: https://github.com/nymtech/nym/pull/1292
[#1331]: https://github.com/nymtech/nym/pull/1331
[#1369]: https://github.com/nymtech/nym/pull/1369
[#1373]: https://github.com/nymtech/nym/pull/1373
## [nym-wallet-v1.0.6](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.6) (2022-06-21)
- wallet: undelegating now uses either the mixnet or vesting contract, or both, depending on how delegations were made
- wallet: redeeming and compounding now uses both the mixnet and vesting contract
- wallet: the wallet backend learned how to archive wallet files
- wallet: add ENABLE_QA_MODE environment variable to enable QA mode on built wallet
## [nym-wallet-v1.0.5](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.5) (2022-06-14)
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
- wallet: added support for multiple accounts ([#1265])
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
- wallet: require password to switch accounts
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
- wallet: new delegation and rewards UI
- wallet: show version in nav bar
- wallet: contract admin route put back
- wallet: staking_supply field to StateParams
- wallet: show transaction hash for redeeming or compounding rewards
[#1265]: https://github.com/nymtech/nym/pull/1265
[#1302]: https://github.com/nymtech/nym/pull/1302
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
### Changed
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
[#1478]: https://github.com/nymtech/nym/pull/1478
[#1482]: https://github.com/nymtech/nym/pull/1482
[#1487]: https://github.com/nymtech/nym/pull/1487
[#1496]: https://github.com/nymtech/nym/pull/1496
[#1503]: https://github.com/nymtech/nym/pull/1503
[#1520]: https://github.com/nymtech/nym/pull/1520
[#1573]: https://github.com/nymtech/nym/pull/1573
[#1576]: https://github.com/nymtech/nym/pull/1576
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
@@ -178,77 +136,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.3...nym-binaries-1.0.0)
## [nym-wallet-v1.0.3](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.3) (2022-04-25)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.2...nym-wallet-v1.0.3)
**Fixed bugs:**
- \[Issue\] Wallet 1.0.2 cannot send NYM tokens from a DelayedVestingAccount [\#1215](https://github.com/nymtech/nym/issues/1215)
- Main README not showing properly with GitHub dark mode [\#1211](https://github.com/nymtech/nym/issues/1211)
**Merged pull requests:**
- Bugfix - wallet undelegation for vesting accounts [\#1220](https://github.com/nymtech/nym/pull/1220) ([mmsinclair](https://github.com/mmsinclair))
- Bugfix/delegation reconcile [\#1219](https://github.com/nymtech/nym/pull/1219) ([jstuczyn](https://github.com/jstuczyn))
- Bugfix/query proxied pending delegations [\#1218](https://github.com/nymtech/nym/pull/1218) ([jstuczyn](https://github.com/jstuczyn))
- Using custom gas multiplier in the wallet [\#1217](https://github.com/nymtech/nym/pull/1217) ([jstuczyn](https://github.com/jstuczyn))
- Feature/vesting accounts support [\#1216](https://github.com/nymtech/nym/pull/1216) ([jstuczyn](https://github.com/jstuczyn))
- Release/1.0.0 rc.2 [\#1214](https://github.com/nymtech/nym/pull/1214) ([jstuczyn](https://github.com/jstuczyn))
- chore: fix dark mode rendering [\#1212](https://github.com/nymtech/nym/pull/1212) ([pwnfoo](https://github.com/pwnfoo))
- Feature/spend coconut [\#1210](https://github.com/nymtech/nym/pull/1210) ([neacsu](https://github.com/neacsu))
- Bugfix/unique sphinx key [\#1207](https://github.com/nymtech/nym/pull/1207) ([jstuczyn](https://github.com/jstuczyn))
- Add cache read and write timeouts [\#1206](https://github.com/nymtech/nym/pull/1206) ([durch](https://github.com/durch))
- Additional, more informative routes [\#1204](https://github.com/nymtech/nym/pull/1204) ([durch](https://github.com/durch))
- Feature/aggregated econ dynamics explorer endpoint [\#1203](https://github.com/nymtech/nym/pull/1203) ([jstuczyn](https://github.com/jstuczyn))
- Debugging validator [\#1198](https://github.com/nymtech/nym/pull/1198) ([durch](https://github.com/durch))
- wallet: expose additional validator configuration functionality to the frontend [\#1195](https://github.com/nymtech/nym/pull/1195) ([octol](https://github.com/octol))
- Update rewarding validator address [\#1193](https://github.com/nymtech/nym/pull/1193) ([durch](https://github.com/durch))
- Crypto part of the Groth's NIDKG [\#1182](https://github.com/nymtech/nym/pull/1182) ([jstuczyn](https://github.com/jstuczyn))
- fix unbond page [\#1180](https://github.com/nymtech/nym/pull/1180) ([tommyv1987](https://github.com/tommyv1987))
- Type safe bounds [\#1179](https://github.com/nymtech/nym/pull/1179) ([durch](https://github.com/durch))
- Fix delegation paging [\#1174](https://github.com/nymtech/nym/pull/1174) ([durch](https://github.com/durch))
- Update binaries to rc version [\#1172](https://github.com/nymtech/nym/pull/1172) ([tommyv1987](https://github.com/tommyv1987))
- Bump ansi-regex from 4.1.0 to 4.1.1 in /docker/typescript\_client/upload\_contract [\#1171](https://github.com/nymtech/nym/pull/1171) ([dependabot[bot]](https://github.com/apps/dependabot))
## [nym-binaries-1.0.0-rc.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.2) (2022-04-15)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.2...nym-binaries-1.0.0-rc.2)
## [nym-wallet-v1.0.2](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.2) (2022-04-05)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.1...nym-wallet-v1.0.2)
**Merged pull requests:**
- Wallet 1.0.2 visual tweaks [\#1197](https://github.com/nymtech/nym/pull/1197) ([mmsinclair](https://github.com/mmsinclair))
- Password for wallet with routes [\#1196](https://github.com/nymtech/nym/pull/1196) ([fmtabbara](https://github.com/fmtabbara))
- Add auto-updater to Nym Wallet [\#1194](https://github.com/nymtech/nym/pull/1194) ([mmsinclair](https://github.com/mmsinclair))
- Fix clippy warnings for beta toolchain [\#1191](https://github.com/nymtech/nym/pull/1191) ([octol](https://github.com/octol))
- wallet: expose validator urls to the frontend [\#1190](https://github.com/nymtech/nym/pull/1190) ([octol](https://github.com/octol))
- wallet: add test for decrypting stored wallet file [\#1189](https://github.com/nymtech/nym/pull/1189) ([octol](https://github.com/octol))
- Fix clippy warnings [\#1188](https://github.com/nymtech/nym/pull/1188) ([octol](https://github.com/octol))
- Password for wallet with routes [\#1187](https://github.com/nymtech/nym/pull/1187) ([mmsinclair](https://github.com/mmsinclair))
- wallet: add validate\_mnemonic [\#1186](https://github.com/nymtech/nym/pull/1186) ([octol](https://github.com/octol))
- wallet: support removing accounts from the wallet file [\#1185](https://github.com/nymtech/nym/pull/1185) ([octol](https://github.com/octol))
- Feature/adding discord [\#1184](https://github.com/nymtech/nym/pull/1184) ([gala1234](https://github.com/gala1234))
- wallet: config backend for validator selection [\#1183](https://github.com/nymtech/nym/pull/1183) ([octol](https://github.com/octol))
- Add storybook to wallet [\#1178](https://github.com/nymtech/nym/pull/1178) ([mmsinclair](https://github.com/mmsinclair))
- wallet: connection test nymd and api urls independently [\#1170](https://github.com/nymtech/nym/pull/1170) ([octol](https://github.com/octol))
- wallet: wire up account storage [\#1153](https://github.com/nymtech/nym/pull/1153) ([octol](https://github.com/octol))
- Feature/signature on deposit [\#1151](https://github.com/nymtech/nym/pull/1151) ([neacsu](https://github.com/neacsu))
## [nym-wallet-v1.0.1](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.1) (2022-04-05)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.1...nym-wallet-v1.0.1)
**Closed issues:**
- Check enabling bbbc simultaneously with open access. Estimate what it would take to make this the default compilation target. [\#1175](https://github.com/nymtech/nym/issues/1175)
- Get coconut credential for deposited tokens [\#1138](https://github.com/nymtech/nym/issues/1138)
- Make payments lazy [\#1135](https://github.com/nymtech/nym/issues/1135)
- Uptime on node selection for sets [\#1049](https://github.com/nymtech/nym/issues/1049)
## [nym-binaries-1.0.0-rc.1](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.1) (2022-03-28)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.0...nym-binaries-1.0.0-rc.1)
@@ -327,108 +218,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
## [nym-wallet-v1.0.0](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.0) (2022-02-03)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...nym-wallet-v1.0.0)
**Implemented enhancements:**
- \[Feature Request\] Please enable registration without need for Telegram account [\#1016](https://github.com/nymtech/nym/issues/1016)
- Fast mixnode launch with a pre-built ISO + VM software [\#1001](https://github.com/nymtech/nym/issues/1001)
**Fixed bugs:**
- \[Issue\] [\#1000](https://github.com/nymtech/nym/issues/1000)
- \[Issue\] `nym-client` requires multiple attempts to run a server [\#869](https://github.com/nymtech/nym/issues/869)
- De-'float'-ing `Interval` \(`Display` impl + `serde`\) [\#1065](https://github.com/nymtech/nym/pull/1065) ([jstuczyn](https://github.com/jstuczyn))
- display client address on wallet creation [\#1058](https://github.com/nymtech/nym/pull/1058) ([fmtabbara](https://github.com/fmtabbara))
**Closed issues:**
- Rewarded set inclusion probability API endpoint [\#1037](https://github.com/nymtech/nym/issues/1037)
- Update cw-storage-plus to 0.11 [\#1032](https://github.com/nymtech/nym/issues/1032)
- Change `u128` fields in `RewardEstimationResponse` to `u64` [\#1029](https://github.com/nymtech/nym/issues/1029)
- Test out the mainnet Gravity Bridge [\#1006](https://github.com/nymtech/nym/issues/1006)
- Add vesting contract interface to nym-wallet [\#959](https://github.com/nymtech/nym/issues/959)
- Mixnode crash [\#486](https://github.com/nymtech/nym/issues/486)
**Merged pull requests:**
- create custom urls for mainnet [\#1095](https://github.com/nymtech/nym/pull/1095) ([fmtabbara](https://github.com/fmtabbara))
- Wallet signing on MacOS [\#1093](https://github.com/nymtech/nym/pull/1093) ([mmsinclair](https://github.com/mmsinclair))
- Fix rust 2018 idioms warnings [\#1092](https://github.com/nymtech/nym/pull/1092) ([octol](https://github.com/octol))
- Prevent contract overwriting [\#1090](https://github.com/nymtech/nym/pull/1090) ([durch](https://github.com/durch))
- Logout operation [\#1087](https://github.com/nymtech/nym/pull/1087) ([jstuczyn](https://github.com/jstuczyn))
- Update to rust edition 2021 everywhere [\#1086](https://github.com/nymtech/nym/pull/1086) ([octol](https://github.com/octol))
- Tag contract errors, and print out lines for easier QA [\#1084](https://github.com/nymtech/nym/pull/1084) ([durch](https://github.com/durch))
- Feature/flexible vesting + utility queries [\#1083](https://github.com/nymtech/nym/pull/1083) ([durch](https://github.com/durch))
- Bump @openzeppelin/contracts from 4.3.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1082](https://github.com/nymtech/nym/pull/1082) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump nth-check from 2.0.0 to 2.0.1 in /clients/native/examples/js-examples/websocket [\#1081](https://github.com/nymtech/nym/pull/1081) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.5.1 to 1.5.4 in /clients/native/examples/js-examples/websocket [\#1080](https://github.com/nymtech/nym/pull/1080) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.1 to 1.14.7 in /clients/native/examples/js-examples/websocket [\#1079](https://github.com/nymtech/nym/pull/1079) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump nanoid from 3.1.23 to 3.2.0 in /clients/native/examples/js-examples/websocket [\#1078](https://github.com/nymtech/nym/pull/1078) ([dependabot[bot]](https://github.com/apps/dependabot))
- Setup basic test for mixnode stats reporting [\#1077](https://github.com/nymtech/nym/pull/1077) ([octol](https://github.com/octol))
- Make wallet\_address mandatory for mixnode init [\#1076](https://github.com/nymtech/nym/pull/1076) ([octol](https://github.com/octol))
- Tidy nym-mixnode module visibility [\#1075](https://github.com/nymtech/nym/pull/1075) ([octol](https://github.com/octol))
- Feature/wallet login with password [\#1074](https://github.com/nymtech/nym/pull/1074) ([fmtabbara](https://github.com/fmtabbara))
- Add trait to mock client dependency in DelayForwarder [\#1073](https://github.com/nymtech/nym/pull/1073) ([octol](https://github.com/octol))
- Bump rust-version to latest stable for nym-mixnode [\#1072](https://github.com/nymtech/nym/pull/1072) ([octol](https://github.com/octol))
- Fixes CI for our wasm build [\#1069](https://github.com/nymtech/nym/pull/1069) ([jstuczyn](https://github.com/jstuczyn))
- Add @octol as codeowner [\#1068](https://github.com/nymtech/nym/pull/1068) ([octol](https://github.com/octol))
- set-up inclusion probability [\#1067](https://github.com/nymtech/nym/pull/1067) ([fmtabbara](https://github.com/fmtabbara))
- Feature/wasm client [\#1066](https://github.com/nymtech/nym/pull/1066) ([neacsu](https://github.com/neacsu))
- Changed bech32\_prefix from punk to nymt [\#1064](https://github.com/nymtech/nym/pull/1064) ([jstuczyn](https://github.com/jstuczyn))
- Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet [\#1063](https://github.com/nymtech/nym/pull/1063) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet [\#1062](https://github.com/nymtech/nym/pull/1062) ([dependabot[bot]](https://github.com/apps/dependabot))
- Rework vesting contract storage [\#1061](https://github.com/nymtech/nym/pull/1061) ([durch](https://github.com/durch))
- Mixnet Contract constants extraction [\#1060](https://github.com/nymtech/nym/pull/1060) ([jstuczyn](https://github.com/jstuczyn))
- fix: make explorer footer year dynamic [\#1059](https://github.com/nymtech/nym/pull/1059) ([martinyung](https://github.com/martinyung))
- Add mnemonic just on creation, to display it [\#1057](https://github.com/nymtech/nym/pull/1057) ([neacsu](https://github.com/neacsu))
- Network Explorer: updates to API and UI to show the active set [\#1056](https://github.com/nymtech/nym/pull/1056) ([mmsinclair](https://github.com/mmsinclair))
- Made contract addresses for query NymdClient construction optional [\#1055](https://github.com/nymtech/nym/pull/1055) ([jstuczyn](https://github.com/jstuczyn))
- Introduced RPC query for total token supply [\#1053](https://github.com/nymtech/nym/pull/1053) ([jstuczyn](https://github.com/jstuczyn))
- Feature/tokio console [\#1052](https://github.com/nymtech/nym/pull/1052) ([durch](https://github.com/durch))
- Implemented beta clippy lint recommendations [\#1051](https://github.com/nymtech/nym/pull/1051) ([jstuczyn](https://github.com/jstuczyn))
- add new function to update profit percentage [\#1050](https://github.com/nymtech/nym/pull/1050) ([fmtabbara](https://github.com/fmtabbara))
- Upgrade Clap and use declarative argument parsing for nym-mixnode [\#1047](https://github.com/nymtech/nym/pull/1047) ([octol](https://github.com/octol))
- Feature/additional bond validation [\#1046](https://github.com/nymtech/nym/pull/1046) ([fmtabbara](https://github.com/fmtabbara))
- Fix clippy on relevant lints [\#1044](https://github.com/nymtech/nym/pull/1044) ([neacsu](https://github.com/neacsu))
- Bump shelljs from 0.8.4 to 0.8.5 in /contracts/basic-bandwidth-generation [\#1043](https://github.com/nymtech/nym/pull/1043) ([dependabot[bot]](https://github.com/apps/dependabot))
- Endpoint for rewarded set inclusion probabilities [\#1042](https://github.com/nymtech/nym/pull/1042) ([durch](https://github.com/durch))
- Bump follow-redirects from 1.14.4 to 1.14.7 in /contracts/basic-bandwidth-generation [\#1041](https://github.com/nymtech/nym/pull/1041) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet [\#1040](https://github.com/nymtech/nym/pull/1040) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/node settings update [\#1036](https://github.com/nymtech/nym/pull/1036) ([fmtabbara](https://github.com/fmtabbara))
- Migrate to cw-storage-plus 0.11.1 [\#1035](https://github.com/nymtech/nym/pull/1035) ([durch](https://github.com/durch))
- Bump @openzeppelin/contracts from 4.4.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1034](https://github.com/nymtech/nym/pull/1034) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/configurable wallet [\#1033](https://github.com/nymtech/nym/pull/1033) ([neacsu](https://github.com/neacsu))
- Feature/downcast reward estimation [\#1031](https://github.com/nymtech/nym/pull/1031) ([durch](https://github.com/durch))
- Wallet UI updates [\#1028](https://github.com/nymtech/nym/pull/1028) ([fmtabbara](https://github.com/fmtabbara))
- Remove migration code [\#1027](https://github.com/nymtech/nym/pull/1027) ([neacsu](https://github.com/neacsu))
- Chore/stricter dependency requirements [\#1025](https://github.com/nymtech/nym/pull/1025) ([jstuczyn](https://github.com/jstuczyn))
- Feature/validator api client endpoints [\#1024](https://github.com/nymtech/nym/pull/1024) ([jstuczyn](https://github.com/jstuczyn))
- Updated cosmrs to 0.4.1 [\#1023](https://github.com/nymtech/nym/pull/1023) ([jstuczyn](https://github.com/jstuczyn))
- Feature/testnet deploy scripts [\#1022](https://github.com/nymtech/nym/pull/1022) ([mfahampshire](https://github.com/mfahampshire))
- Changed wallet's client to a full validator client [\#1021](https://github.com/nymtech/nym/pull/1021) ([jstuczyn](https://github.com/jstuczyn))
- Fix 404 link [\#1020](https://github.com/nymtech/nym/pull/1020) ([RiccardoMasutti](https://github.com/RiccardoMasutti))
- Feature/additional mixnode endpoints [\#1019](https://github.com/nymtech/nym/pull/1019) ([jstuczyn](https://github.com/jstuczyn))
- Introduced denom check when trying to withdraw vested coins [\#1018](https://github.com/nymtech/nym/pull/1018) ([jstuczyn](https://github.com/jstuczyn))
- Add network defaults for qa [\#1017](https://github.com/nymtech/nym/pull/1017) ([neacsu](https://github.com/neacsu))
- Feature/expanded events [\#1015](https://github.com/nymtech/nym/pull/1015) ([jstuczyn](https://github.com/jstuczyn))
- update frontend to use new profit update api [\#1014](https://github.com/nymtech/nym/pull/1014) ([fmtabbara](https://github.com/fmtabbara))
- Feature/node state endpoint [\#1013](https://github.com/nymtech/nym/pull/1013) ([jstuczyn](https://github.com/jstuczyn))
- Feature/hourly set updates [\#1012](https://github.com/nymtech/nym/pull/1012) ([durch](https://github.com/durch))
- Feature/remove unused profit margin [\#1011](https://github.com/nymtech/nym/pull/1011) ([neacsu](https://github.com/neacsu))
- Feature/explorer node status [\#1010](https://github.com/nymtech/nym/pull/1010) ([jstuczyn](https://github.com/jstuczyn))
- Use serial integer instead of random [\#1009](https://github.com/nymtech/nym/pull/1009) ([durch](https://github.com/durch))
- Feature/configure profit [\#1008](https://github.com/nymtech/nym/pull/1008) ([neacsu](https://github.com/neacsu))
- Feature/fix gateway sign [\#1004](https://github.com/nymtech/nym/pull/1004) ([neacsu](https://github.com/neacsu))
- Fix clippy [\#1003](https://github.com/nymtech/nym/pull/1003) ([neacsu](https://github.com/neacsu))
- Update wallet version [\#998](https://github.com/nymtech/nym/pull/998) ([tommyv1987](https://github.com/tommyv1987))
- Fix wallet build instructions [\#997](https://github.com/nymtech/nym/pull/997) ([tommyv1987](https://github.com/tommyv1987))
- Make the separation between testnet-mode and erc20 bandwidth mode clearer [\#994](https://github.com/nymtech/nym/pull/994) ([neacsu](https://github.com/neacsu))
- Bump @openzeppelin/contracts from 3.4.0 to 4.4.1 in /contracts/basic-bandwidth-generation [\#983](https://github.com/nymtech/nym/pull/983) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/implicit runtime [\#973](https://github.com/nymtech/nym/pull/973) ([jstuczyn](https://github.com/jstuczyn))
- Differentiate staking and ownership [\#961](https://github.com/nymtech/nym/pull/961) ([durch](https://github.com/durch))
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
Generated
+30 -44
View File
@@ -894,6 +894,7 @@ dependencies = [
"cfg-if 0.1.10",
"clap 3.2.8",
"coconut-interface",
"config",
"credential-storage",
"credentials",
"crypto",
@@ -1606,6 +1607,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"task",
"thiserror",
"tokio",
"validator-client",
@@ -1902,10 +1904,6 @@ name = "futures-timer"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
dependencies = [
"gloo-timers",
"send_wrapper",
]
[[package]]
name = "futures-util"
@@ -2090,18 +2088,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "gloo-timers"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "group"
version = "0.10.0"
@@ -2495,6 +2481,15 @@ dependencies = [
"syn",
]
[[package]]
name = "inclusion-probability"
version = "0.1.0"
dependencies = [
"log",
"rand 0.8.5",
"thiserror",
]
[[package]]
name = "indenter"
version = "0.3.3"
@@ -2742,9 +2737,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.16"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if 1.0.0",
]
@@ -3055,7 +3050,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"clap 3.2.8",
"client-core",
@@ -3089,7 +3084,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"anyhow",
"async-trait",
@@ -3137,7 +3132,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"anyhow",
"bs58",
@@ -3177,7 +3172,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"async-trait",
"clap 2.34.0",
@@ -3205,7 +3200,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "0.1.0"
version = "1.0.2"
dependencies = [
"dirs",
"log",
@@ -3221,7 +3216,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"clap 3.2.8",
"client-core",
@@ -3282,7 +3277,7 @@ dependencies = [
[[package]]
name = "nym-validator-api"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"anyhow",
"async-trait",
@@ -3305,6 +3300,7 @@ dependencies = [
"gateway-client",
"getset",
"humantime-serde",
"inclusion-probability",
"log",
"mixnet-contract-common",
"multisig-contract-common",
@@ -3324,6 +3320,8 @@ dependencies = [
"serde",
"serde_json",
"sqlx",
"tap",
"task",
"thiserror",
"time 0.3.9",
"tokio",
@@ -4912,12 +4910,6 @@ dependencies = [
"pest",
]
[[package]]
name = "send_wrapper"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
[[package]]
name = "serde"
version = "1.0.136"
@@ -4979,9 +4971,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.79"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
dependencies = [
"itoa 1.0.1",
"ryu",
@@ -5197,6 +5189,7 @@ version = "0.1.0"
dependencies = [
"nymsphinx-addressing",
"ordered-buffer",
"thiserror",
]
[[package]]
@@ -5650,18 +5643,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.30"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
dependencies = [
"proc-macro2",
"quote",
@@ -6403,8 +6396,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if 1.0.0",
"serde",
"serde_json",
"wasm-bindgen-macro",
]
@@ -6500,15 +6491,12 @@ dependencies = [
"ethereum-types",
"futures",
"futures-timer",
"getrandom 0.2.6",
"headers",
"hex",
"js-sys",
"jsonrpc-core",
"log",
"parking_lot 0.11.2",
"pin-project",
"rand 0.8.5",
"reqwest",
"rlp",
"secp256k1",
@@ -6520,8 +6508,6 @@ dependencies = [
"tokio-stream",
"tokio-util 0.6.9",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web3-async-native-tls",
]
+9 -7
View File
@@ -22,22 +22,23 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
"common/bandwidth-claim-contract",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
"common/credential-storage",
"common/coconut-interface",
"common/config",
"common/credentials",
"common/crypto",
"common/crypto/dkg",
"common/execute",
"common/bandwidth-claim-contract",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
"common/credentials",
"common/crypto",
"common/crypto/dkg",
"common/execute",
"common/inclusion-probability",
"common/mixnode-common",
"common/network-defaults",
"common/nonexhaustive-delayqueue",
@@ -53,9 +54,9 @@ members = [
"common/nymsphinx/params",
"common/nymsphinx/types",
"common/pemstore",
"common/statistics",
"common/socks5/proxy-helpers",
"common/socks5/requests",
"common/statistics",
"common/task",
"common/topology",
"common/types",
@@ -76,6 +77,7 @@ default-members = [
"clients/socks5",
"gateway",
"service-providers/network-requester",
"service-providers/network-statistics",
"mixnode",
"validator-api",
"explorer-api",
+1 -1
View File
@@ -1,5 +1,5 @@
test: clippy-all cargo-test wasm fmt
test-all: test cargo-test-expensive
test: build clippy-all cargo-test wasm fmt
no-clippy: build cargo-test wasm fmt
happy: fmt clippy-happy test
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-all-connect
@@ -133,7 +133,6 @@ where
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient)
.await
.unwrap();
real_messages.push(RealMessage::new(
@@ -83,7 +83,6 @@ where
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, packet_recipient)
.await
.unwrap();
// if we have the ONLY strong reference to the ack data, it means it was removed from the
+1
View File
@@ -18,6 +18,7 @@ url = "2.2"
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-interface = { path = "../../common/coconut-interface" }
config = { path = "../../common/config" }
credentials = { path = "../../common/credentials" }
credential-storage = { path = "../../common/credential-storage" }
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
+3 -4
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Result;
use crate::{MNEMONIC, NYMD_URL};
use bip39::Mnemonic;
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use std::str::FromStr;
@@ -17,9 +16,9 @@ pub(crate) struct Client {
}
impl Client {
pub fn new() -> Self {
let nymd_url = Url::from_str(NYMD_URL).unwrap();
let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
pub fn new(nymd_url: &str, mnemonic: &str) -> Self {
let nymd_url = Url::from_str(nymd_url).unwrap();
let mnemonic = Mnemonic::from_str(mnemonic).unwrap();
let network_details = NymNetworkDetails::new_from_env();
let config = nymd::Config::try_from_nym_network_details(&network_details)
.expect("failed to construct valid validator client config with the provided network");
+13 -4
View File
@@ -6,7 +6,6 @@ use clap::{Args, Subcommand};
use pickledb::PickleDb;
use rand::rngs::OsRng;
use std::str::FromStr;
use url::Url;
use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters};
use credential_storage::storage::Storage;
@@ -20,7 +19,6 @@ use validator_client::nymd::tx::Hash;
use crate::client::Client;
use crate::error::{CredentialClientError, Result};
use crate::state::{KeyPair, RequestData, State};
use crate::SIGNER_AUTHORITIES;
#[derive(Subcommand)]
pub(crate) enum Commands {
@@ -39,6 +37,12 @@ pub(crate) trait Execute {
#[derive(Args, Clone)]
pub(crate) struct Deposit {
/// The nymd URL that should be used
#[clap(long)]
nymd_url: String,
/// A mnemonic for the account that does the deposit
#[clap(long)]
mnemonic: String,
/// The amount that needs to be deposited
#[clap(long)]
amount: u64,
@@ -51,7 +55,7 @@ impl Execute for Deposit {
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let client = Client::new();
let client = Client::new(&self.nymd_url, &self.mnemonic);
let tx_hash = client
.deposit(
self.amount,
@@ -96,6 +100,10 @@ pub(crate) struct GetCredential {
/// The hash of a successful deposit transaction
#[clap(long)]
tx_hash: String,
/// The URLs to the validator-api endpoints the are run as coconut signer authorities, separated
/// by comma (,)
#[clap(long)]
signer_authorities: String,
/// If we want to get the signature without attaching a blind sign request; it is expected that
/// there is already a signature stored on the signer
#[clap(long, parse(from_flag))]
@@ -108,7 +116,8 @@ impl Execute for GetCredential {
let mut state = db
.get::<State>(&self.tx_hash)
.ok_or(CredentialClientError::NoDeposit)?;
let urls = SIGNER_AUTHORITIES.map(|addr| Url::from_str(addr).unwrap());
let urls = config::parse_validators(&self.signer_authorities);
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = if self.__no_request {
+13 -8
View File
@@ -11,20 +11,24 @@ cfg_if::cfg_if! {
use commands::{Commands, Execute};
use error::Result;
use network_defaults::setup_env;
use clap::Parser;
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
pub const MNEMONIC: &str = "jazz fatigue diagram account outer wrist slide cherry mother grid network pause wolf pig round answer mail junior better hair dismiss toward access end";
pub const NYMD_URL: &str = "http://127.0.0.1:26657";
pub const CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
pub const SIGNER_AUTHORITIES: [&str; 1] = [
"http://127.0.0.1:8080",
];
#[derive(Parser)]
#[clap(author = "Nymtech", version, about)]
struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Path where the sqlite credental database will be located.
/// It should point to a $HOME/$CLIENT_ID/data/db.sqlite file of
/// the client that is supposed to use the credential.
#[clap(long)]
pub(crate) credential_db_path: std::path::PathBuf,
#[clap(subcommand)]
command: Commands,
}
@@ -32,8 +36,9 @@ cfg_if::cfg_if! {
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::parse();
setup_env(args.config_env_file.clone());
let shared_storage = credential_storage::initialise_storage(std::path::PathBuf::from("/tmp/credential.db")).await;
let shared_storage = credential_storage::initialise_storage(args.credential_db_path.clone()).await;
let mut db = match PickleDb::load(
"credential.db",
PickleDbDumpPolicy::AutoDump,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.0.1"
version = "1.0.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+4
View File
@@ -50,6 +50,10 @@ impl NymConfig for Config {
.join("clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
+3 -3
View File
@@ -199,9 +199,9 @@ impl NymClient {
Some(bandwidth_controller),
);
if self.config.get_base().get_disabled_credentials_mode() {
gateway_client.set_disabled_credentials_mode(true)
}
gateway_client
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
gateway_client
.authenticate_and_start()
.await
+4 -5
View File
@@ -46,10 +46,9 @@ pub(crate) struct Init {
fastmode: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private_key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -79,7 +78,7 @@ impl From<Init> for OverrideConfig {
port: init_config.port,
fastmode: init_config.fastmode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: init_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
+8 -17
View File
@@ -3,7 +3,6 @@
use crate::client::config::{Config, SocketType};
use clap::{Parser, Subcommand};
use url::Url;
#[cfg(not(feature = "coconut"))]
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
@@ -79,7 +78,7 @@ pub(crate) struct OverrideConfig {
port: Option<u16>,
fastmode: bool,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: bool,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -97,28 +96,17 @@ pub(crate) async fn execute(args: &Cli) {
}
}
fn parse_validators(raw: &str) -> Vec<Url> {
raw.split(',')
.map(|raw_validator| {
raw_validator
.trim()
.parse()
.expect("one of the provided validator api urls is invalid")
})
.collect()
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
if let Some(raw_validators) = args.validators {
config
.get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators));
.set_custom_validator_apis(config::parse_validators(&raw_validators));
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR)
.expect("api validator not set");
config
.get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators));
.set_custom_validator_apis(config::parse_validators(&raw_validators));
}
if args.disable_socket {
@@ -138,12 +126,15 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
.get_base_mut()
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
{
if args.enabled_credentials_mode {
config.get_base_mut().with_disabled_credentials(false)
}
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if let Some(eth_endpoint) = args.eth_endpoint {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
}
+5 -6
View File
@@ -35,10 +35,9 @@ pub(crate) struct Run {
port: Option<u16>,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private-key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -62,7 +61,7 @@ impl From<Run> for OverrideConfig {
port: run_config.port,
fastmode: false,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: run_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -82,7 +81,7 @@ fn version_check(cfg: &Config) -> bool {
if binary_version == config_version {
true
} else {
warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.0.1"
version = "1.0.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+4
View File
@@ -33,6 +33,10 @@ impl NymConfig for Config {
.join("socks5-clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
+3 -3
View File
@@ -198,9 +198,9 @@ impl NymClient {
Some(bandwidth_controller),
);
if self.config.get_base().get_disabled_credentials_mode() {
gateway_client.set_disabled_credentials_mode(true)
}
gateway_client
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
gateway_client
.authenticate_and_start()
.await
+4 -5
View File
@@ -46,10 +46,9 @@ pub(crate) struct Init {
fastmode: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private_key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -78,7 +77,7 @@ impl From<Init> for OverrideConfig {
port: init_config.port,
fastmode: init_config.fastmode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: init_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
+6 -14
View File
@@ -3,7 +3,7 @@
use crate::client::config::Config;
use clap::{Parser, Subcommand};
use url::Url;
use config::parse_validators;
pub mod init;
pub(crate) mod run;
@@ -78,7 +78,7 @@ pub(crate) struct OverrideConfig {
port: Option<u16>,
fastmode: bool,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: bool,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -96,17 +96,6 @@ pub(crate) async fn execute(args: &Cli) {
}
}
fn parse_validators(raw: &str) -> Vec<Url> {
raw.split(',')
.map(|raw_validator| {
raw_validator
.trim()
.parse()
.expect("one of the provided validator api urls is invalid")
})
.collect()
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
if let Some(raw_validators) = args.validators {
config
@@ -132,11 +121,14 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
{
if args.enabled_credentials_mode {
config.get_base_mut().with_disabled_credentials(false)
}
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if let Some(eth_endpoint) = args.eth_endpoint {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
}
+4 -5
View File
@@ -39,10 +39,9 @@ pub(crate) struct Run {
port: Option<u16>,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private-key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -65,7 +64,7 @@ impl From<Run> for OverrideConfig {
port: run_config.port,
fastmode: false,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: run_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
-5
View File
@@ -2,9 +2,4 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client;
// This is only used as we reach into the init functions in nym-connect. We need to refactor the
// init functions so that nym-connect can just call the same init function as the regular socks5
// client.
#[allow(unused)]
pub mod commands;
pub mod socks;
+14 -3
View File
@@ -5,7 +5,7 @@ use futures::StreamExt;
use log::*;
use nymsphinx::receiver::ReconstructedMessage;
use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
use socks5_requests::Response;
use socks5_requests::Message;
pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender,
@@ -44,12 +44,23 @@ impl MixnetResponseListener {
warn!("this message had a surb - we didn't do anything with it");
}
let response = match Response::try_from_bytes(&raw_message) {
let response = match Message::try_from_bytes(&raw_message) {
Err(err) => {
warn!("failed to parse received response - {:?}", err);
return;
}
Ok(data) => data,
Ok(Message::Request(_)) => {
warn!("unexpected request");
return;
}
Ok(Message::Response(data)) => data,
Ok(Message::NetworkRequesterResponse(r)) => {
error!(
"Network requester failed on connection id {} with error: {}",
r.connection_id, r.network_requester_error
);
return;
}
};
self.controller_sender
+1 -1
View File
@@ -32,7 +32,7 @@ credentials = { path = "../../common/credentials", optional = true }
crypto = { path = "../../common/crypto" }
nymsphinx = { path = "../../common/nymsphinx" }
topology = { path = "../../common/topology" }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
wasm-utils = { path = "../../common/wasm-utils" }
+1 -1
View File
@@ -25,7 +25,7 @@ async function main() {
set_panic_hook();
// validator server we will use to get topology from
const validator = "https://sandbox-validator.nymtech.net/api"; //"http://localhost:8081";
const validator = "https://validator.nymtech.net/api"; //"http://localhost:8081";
client = new NymClient(validator);
+1 -4
View File
@@ -132,9 +132,7 @@ impl NymClient {
bandwidth_controller,
);
if disabled_credentials_mode {
gateway_client.set_disabled_credentials_mode(true)
}
gateway_client.set_disabled_credentials_mode(disabled_credentials_mode);
gateway_client
.authenticate_and_start()
@@ -199,7 +197,6 @@ impl NymClient {
// don't bother with acks etc. for time being
let prepared_fragment = message_preparer
.prepare_chunk_for_sending(message_chunk, topology, &self.ack_key, &recipient)
.await
.unwrap();
console_warn!("packet is going to have round trip time of {:?}, but we're not going to do anything for acks anyway ", prepared_fragment.total_delay);
+4 -4
View File
@@ -15,8 +15,8 @@ log = "0.4"
thiserror = "1.0"
url = "2.2"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
secp256k1 = "0.20.3"
web3 = { version = "0.17.0", default-features = false }
secp256k1 = { version = "0.20.3", optional = true }
web3 = { version = "0.17.0", default-features = false, optional = true }
async-trait = { version = "0.1.51" }
# internal
@@ -73,5 +73,5 @@ features = ["js"]
[features]
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
wasm = ["web3/wasm", "web3/http", "web3/signing"]
default = ["web3/default"]
wasm = []
default = ["web3/default", "secp256k1"]
@@ -23,7 +23,7 @@ use {
},
};
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
use {
credentials::token::bandwidth::TokenCredential,
crypto::asymmetric::identity,
@@ -45,7 +45,7 @@ use {
},
};
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
Contract::from_json(
web3.eth(),
@@ -58,7 +58,7 @@ pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
.expect("Invalid json abi")
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
Contract::from_json(
web3.eth(),
@@ -76,11 +76,11 @@ pub struct BandwidthController<St: Storage> {
storage: St,
#[cfg(feature = "coconut")]
validator_endpoints: Vec<url::Url>,
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
contract: Contract<Http>,
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
erc20_contract: Contract<Http>,
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
eth_private_key: SecretKey,
}
@@ -96,7 +96,7 @@ where
}
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
pub fn new(
storage: St,
eth_endpoint: String,
@@ -120,7 +120,7 @@ where
})
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
async fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
self.storage
.insert_erc20_credential(
@@ -132,7 +132,7 @@ where
Ok(())
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
async fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
let data = self.storage.get_next_erc20_credential().await?;
let public_key = identity::PublicKey::from_base58_string(data.public_key).unwrap();
@@ -141,7 +141,7 @@ where
Ok(identity::KeyPair::from_keys(private_key, public_key))
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
async fn mark_keypair_as_spent(
&self,
keypair: &identity::KeyPair,
@@ -180,7 +180,7 @@ where
)?)
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
pub async fn prepare_token_credential(
&self,
gateway_identity: identity::PublicKey,
@@ -219,7 +219,7 @@ where
))
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
pub async fn buy_token_credential(
&self,
verification_key: identity::PublicKey,
@@ -348,7 +348,7 @@ where
}
}
#[cfg(not(feature = "coconut"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(test)]
mod tests {
use network_defaults::ETH_EVENT_NAME;
@@ -162,12 +162,10 @@ impl PartiallyDelegated {
.expect("stream sender was somehow dropped without sending anything!");
if let Some(res) = receive_res {
if let Err(err) = res {
// the receiver got an error. most likely a network one.
return Err(err);
} else {
panic!("This should have NEVER happened - returned a stream before receiving notification")
}
let _res = res?;
panic!(
"This should have NEVER happened - returned a stream before receiving notification"
)
}
// this call failing is incredibly unlikely, but not impossible.
@@ -24,7 +24,7 @@ use mixnet_contract_common::{
PagedMixDelegationsResponse, PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg,
RewardedSetUpdateDetails,
};
use serde::Serialize;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::time::SystemTime;
use vesting_contract_common::ExecuteMsg as VestingExecuteMsg;
@@ -282,6 +282,30 @@ impl<C> NymdClient<C> {
self.simulated_gas_multiplier = multiplier;
}
pub async fn query_contract_smart<M, T>(
&self,
contract: &AccountId,
query_msg: &M,
) -> Result<T, NymdError>
where
C: CosmWasmClient + Sync,
M: ?Sized + Serialize + Sync,
for<'a> T: Deserialize<'a>,
{
self.client.query_contract_smart(contract, query_msg).await
}
pub async fn query_contract_raw(
&self,
contract: &AccountId,
query_data: Vec<u8>,
) -> Result<Vec<u8>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.query_contract_raw(contract, query_data).await
}
pub fn wrap_contract_execute_message<M>(
&self,
contract_address: &AccountId,
@@ -893,7 +917,7 @@ impl<C> NymdClient<C> {
&self,
contract_address: &AccountId,
msg: &M,
fee: Fee,
fee: Option<Fee>,
memo: impl Into<String> + Send + 'static,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NymdError>
@@ -901,6 +925,7 @@ impl<C> NymdClient<C> {
C: SigningCosmWasmClient + Sync,
M: ?Sized + Serialize + Sync,
{
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
self.client
.execute(self.address(), contract_address, msg, fee, memo, funds)
.await
@@ -910,7 +935,7 @@ impl<C> NymdClient<C> {
&self,
contract_address: &AccountId,
msgs: I,
fee: Fee,
fee: Option<Fee>,
memo: impl Into<String> + Send + 'static,
) -> Result<ExecuteResult, NymdError>
where
@@ -918,6 +943,7 @@ impl<C> NymdClient<C> {
I: IntoIterator<Item = (M, Vec<Coin>)> + Send,
M: Serialize,
{
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
self.client
.execute_multiple(self.address(), contract_address, msgs, fee, memo)
.await
@@ -1006,6 +1032,29 @@ impl<C> NymdClient<C> {
.await
}
#[execute("mixnet")]
fn _compound_reward(
&self,
operator: Option<String>,
delegator: Option<String>,
mix_identity: Option<IdentityKey>,
proxy: Option<String>,
fee: Option<Fee>,
) -> (ExecuteMsg, Option<Fee>)
where
C: SigningCosmWasmClient + Sync,
{
(
ExecuteMsg::CompoundReward {
operator,
delegator,
mix_identity,
proxy,
},
fee,
)
}
#[execute("mixnet")]
fn _compound_operator_reward(&self, fee: Option<Fee>) -> (ExecuteMsg, Option<Fee>)
where
@@ -7,9 +7,11 @@ use crate::nymd::error::NymdError;
use crate::nymd::NymdClient;
use async_trait::async_trait;
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
use mixnet_contract_common::IdentityKey;
use vesting_contract::vesting::Account;
use vesting_contract_common::{
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
OriginalVestingResponse, Period, PledgeData, VestingDelegation,
};
#[async_trait]
@@ -70,6 +72,37 @@ pub trait VestingQueryClient {
&self,
vesting_account_address: &str,
) -> Result<Period, NymdError>;
async fn get_delegation_timestamps(
&self,
address: &str,
mix_identity: String,
) -> Result<DelegationTimesResponse, NymdError>;
async fn get_all_vesting_delegations_paged(
&self,
start_after: Option<(u32, IdentityKey, u64)>,
limit: Option<u32>,
) -> Result<AllDelegationsResponse, NymdError>;
async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NymdError> {
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.get_all_vesting_delegations_paged(start_after.take(), None)
.await?;
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
}
#[async_trait]
@@ -232,4 +265,29 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
.query_contract_smart(self.vesting_contract_address(), &request)
.await
}
async fn get_delegation_timestamps(
&self,
address: &str,
mix_identity: String,
) -> Result<DelegationTimesResponse, NymdError> {
let request = VestingQueryMsg::GetDelegationTimes {
address: address.to_string(),
mix_identity,
};
self.client
.query_contract_smart(self.vesting_contract_address(), &request)
.await
}
async fn get_all_vesting_delegations_paged(
&self,
start_after: Option<(u32, IdentityKey, u64)>,
limit: Option<u32>,
) -> Result<AllDelegationsResponse, NymdError> {
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
self.client
.query_contract_smart(self.vesting_contract_address(), &request)
.await
}
}
+35
View File
@@ -41,6 +41,30 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
Self::default_config_directory(id).join(Self::config_file_name())
}
// We provide a second set of functions that tries to not panic.
fn try_default_root_directory() -> Option<PathBuf>;
fn try_default_config_directory(id: Option<&str>) -> Option<PathBuf> {
if let Some(id) = id {
Self::try_default_root_directory().map(|d| d.join(id).join("config"))
} else {
Self::try_default_root_directory().map(|d| d.join("config"))
}
}
fn try_default_data_directory(id: Option<&str>) -> Option<PathBuf> {
if let Some(id) = id {
Self::try_default_root_directory().map(|d| d.join(id).join("data"))
} else {
Self::try_default_root_directory().map(|d| d.join("data"))
}
}
fn try_default_config_file_path(id: Option<&str>) -> Option<PathBuf> {
Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
}
fn root_directory(&self) -> PathBuf;
fn config_directory(&self) -> PathBuf;
fn data_directory(&self) -> PathBuf;
@@ -88,3 +112,14 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
}
pub fn parse_validators(raw: &str) -> Vec<url::Url> {
raw.split(',')
.map(|raw_validator| {
raw_validator
.trim()
.parse()
.expect("one of the provided validator api urls is invalid")
})
.collect()
}
@@ -37,7 +37,7 @@ impl SpendCredentialData {
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub enum SpendCredentialStatus {
InProgress,
Spent,
@@ -33,6 +33,12 @@ pub enum ExecuteMsg {
CompoundDelegatorReward {
mix_identity: IdentityKey,
},
CompoundReward {
operator: Option<String>,
delegator: Option<String>,
mix_identity: Option<IdentityKey>,
proxy: Option<String>,
},
BondMixnode {
mix_node: MixNode,
owner_signature: String,
@@ -116,6 +122,7 @@ pub enum ExecuteMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetBlacklistedNodes {},
GetCurrentOperatorCost {},
GetRewardingValidatorAddress {},
GetAllDelegationKeys {},
@@ -208,4 +215,29 @@ pub enum QueryMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
pub struct MigrateMsg {
pub mixnet_denom: String,
pub nodes_to_remove: Option<Vec<NodeToRemove>>,
}
impl MigrateMsg {
pub fn nodes_to_remove(&self) -> Vec<NodeToRemove> {
self.nodes_to_remove.clone().unwrap_or_default()
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct NodeToRemove {
owner: String,
proxy: Option<String>,
}
impl NodeToRemove {
pub fn owner(&self) -> &str {
&self.owner
}
pub fn proxy(&self) -> Option<&String> {
self.proxy.as_ref()
}
}
@@ -1,10 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Coin, Timestamp};
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
use mixnet_contract_common::IdentityKey;
pub mod events;
pub mod messages;
@@ -73,3 +74,35 @@ impl OriginalVestingResponse {
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
pub struct VestingDelegation {
pub account_id: u32,
pub mix_identity: IdentityKey,
pub block_timestamp: u64,
pub amount: Uint128,
}
impl VestingDelegation {
pub fn storage_key(&self) -> (u32, IdentityKey, u64) {
(
self.account_id,
self.mix_identity.clone(),
self.block_timestamp,
)
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
pub struct DelegationTimesResponse {
pub owner: Addr,
pub account_id: u32,
pub mix_identity: IdentityKey,
pub delegation_timestamps: Vec<u64>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)]
pub struct AllDelegationsResponse {
pub delegations: Vec<VestingDelegation>,
pub start_next_after: Option<(u32, IdentityKey, u64)>,
}
@@ -12,7 +12,9 @@ pub struct InitMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
pub struct MigrateMsg {
pub mix_denom: String,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)]
pub struct VestingSpecification {
@@ -168,4 +170,12 @@ pub enum QueryMsg {
address: String,
},
GetLockedPledgeCap {},
GetDelegationTimes {
address: String,
mix_identity: IdentityKey,
},
GetAllDelegations {
start_after: Option<(u32, IdentityKey, u64)>,
limit: Option<u32>,
},
}
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "inclusion-probability"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.17"
rand = "0.8.5"
thiserror = "1.0.32"
+11
View File
@@ -0,0 +1,11 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("The list of cumulative stake was unexpectedly empty")]
EmptyListCumulStake,
#[error("Sample point was unexpectedly out of bounds")]
SamplePointOutOfBounds,
#[error("Norm computation failed on different size arrays")]
NormDifferenceSizeArrays,
#[error("Computed probabilities are fewer than input number of nodes")]
ResultsShorterThanInput,
}
+426
View File
@@ -0,0 +1,426 @@
//! Active set inclusion probability simulator
use std::time::{Duration, Instant};
use error::Error;
use rand::Rng;
mod error;
const TOLERANCE_L2_NORM: f64 = 1e-4;
const TOLERANCE_MAX_NORM: f64 = 1e-4;
pub struct SelectionProbability {
pub active_set_probability: Vec<f64>,
pub reserve_set_probability: Vec<f64>,
pub samples: u64,
pub time: Duration,
pub delta_l2: f64,
pub delta_max: f64,
}
pub fn simulate_selection_probability_mixnodes<R>(
list_stake_for_mixnodes: &[u128],
active_set_size: usize,
reserve_set_size: usize,
max_samples: u64,
max_time: Duration,
rng: &mut R,
) -> Result<SelectionProbability, Error>
where
R: Rng + ?Sized,
{
log::trace!("Simulating mixnode active set selection probability");
// In case the active set size is larger than the number of bonded mixnodes, they all have 100%
// chance we don't have to go through with the simulation
if list_stake_for_mixnodes.len() <= active_set_size {
return Ok(SelectionProbability {
active_set_probability: vec![1.0; list_stake_for_mixnodes.len()],
reserve_set_probability: vec![0.0; list_stake_for_mixnodes.len()],
samples: 0,
time: Duration::ZERO,
delta_l2: 0.0,
delta_max: 0.0,
});
}
// Total number of existing (registered) nodes
let num_mixnodes = list_stake_for_mixnodes.len();
// Cumulative stake ordered by node index
let list_cumul = cumul_sum(list_stake_for_mixnodes);
// The computed probabilities
let mut active_set_probability = vec![0.0; num_mixnodes];
let mut reserve_set_probability = vec![0.0; num_mixnodes];
// Number sufficiently large to have a good approximation of selection probability
let mut samples = 0;
let mut delta_l2;
let mut delta_max;
// Make sure we bound the time we allow it to run
let start_time = Instant::now();
loop {
samples += 1;
let mut sample_active_mixnodes = Vec::new();
let mut sample_reserve_mixnodes = Vec::new();
let mut list_cumul_temp = list_cumul.clone();
let active_set_probability_previous = active_set_probability.clone();
// Select the active nodes for the epoch (hour)
while sample_active_mixnodes.len() < active_set_size
&& sample_active_mixnodes.len() < list_cumul_temp.len()
{
let candidate = sample_candidate(&list_cumul_temp, rng)?;
if !sample_active_mixnodes.contains(&candidate) {
sample_active_mixnodes.push(candidate);
remove_mixnode_from_cumul_stake(candidate, &mut list_cumul_temp);
}
}
// Select the reserve nodes for the epoch (hour)
while sample_reserve_mixnodes.len() < reserve_set_size
&& sample_reserve_mixnodes.len() + sample_active_mixnodes.len() < list_cumul_temp.len()
{
let candidate = sample_candidate(&list_cumul_temp, rng)?;
if !sample_reserve_mixnodes.contains(&candidate)
&& !sample_active_mixnodes.contains(&candidate)
{
sample_reserve_mixnodes.push(candidate);
remove_mixnode_from_cumul_stake(candidate, &mut list_cumul_temp);
}
}
// Sum up nodes being in active or reserve set
for active_mixnodes in sample_active_mixnodes {
active_set_probability[active_mixnodes] += 1.0;
}
for reserve_mixnodes in sample_reserve_mixnodes {
reserve_set_probability[reserve_mixnodes] += 1.0;
}
// Convergence critera only on active set.
// We devide by samples to get the average, that is not really part of the delta
// computation.
delta_l2 =
l2_diff(&active_set_probability, &active_set_probability_previous)? / (samples as f64);
delta_max =
max_diff(&active_set_probability, &active_set_probability_previous)? / (samples as f64);
if samples > 10 && delta_l2 < TOLERANCE_L2_NORM && delta_max < TOLERANCE_MAX_NORM
|| samples >= max_samples
{
break;
}
// Stop if we run out of time
if start_time.elapsed() > max_time {
log::debug!("Simulation ran out of time, stopping");
break;
}
}
// Divide occurrences with the number of samples once we're done to get the probabilities.
active_set_probability
.iter_mut()
.for_each(|x| *x /= samples as f64);
reserve_set_probability
.iter_mut()
.for_each(|x| *x /= samples as f64);
// Some sanity checks of the output
if active_set_probability.len() != num_mixnodes || reserve_set_probability.len() != num_mixnodes
{
return Err(Error::ResultsShorterThanInput);
}
Ok(SelectionProbability {
active_set_probability,
reserve_set_probability,
samples,
time: start_time.elapsed(),
delta_l2,
delta_max,
})
}
// Compute the cumulative sum
fn cumul_sum<'a>(list: impl IntoIterator<Item = &'a u128>) -> Vec<u128> {
let mut list_cumul = Vec::new();
let mut cumul = 0;
for entry in list {
cumul += entry;
list_cumul.push(cumul);
}
list_cumul
}
fn sample_candidate<R>(list_cumul: &[u128], rng: &mut R) -> Result<usize, Error>
where
R: Rng + ?Sized,
{
use rand::distributions::{Distribution, Uniform};
let uniform = Uniform::from(0..*list_cumul.last().ok_or(Error::EmptyListCumulStake)?);
let r = uniform.sample(rng);
let candidate = list_cumul
.iter()
.enumerate()
.find(|(_, x)| *x >= &r)
.ok_or(Error::SamplePointOutOfBounds)?
.0;
Ok(candidate)
}
// Update list of cumulative stake to reflect eliminating the picked node
fn remove_mixnode_from_cumul_stake(candidate: usize, list_cumul_stake: &mut [u128]) {
let prob_candidate = if candidate == 0 {
list_cumul_stake[0]
} else {
list_cumul_stake[candidate] - list_cumul_stake[candidate - 1]
};
for cumul in list_cumul_stake.iter_mut().skip(candidate) {
*cumul -= prob_candidate;
}
}
// Compute the difference in l2-norm
fn l2_diff(v1: &[f64], v2: &[f64]) -> Result<f64, Error> {
if v1.len() != v2.len() {
return Err(Error::NormDifferenceSizeArrays);
}
Ok(v1
.iter()
.zip(v2)
.map(|(&i1, &i2)| (i1 - i2).powi(2))
.sum::<f64>()
.sqrt())
}
// Compute the difference in max-norm
fn max_diff(v1: &[f64], v2: &[f64]) -> Result<f64, Error> {
if v1.len() != v2.len() {
return Err(Error::NormDifferenceSizeArrays);
}
Ok(v1
.iter()
.zip(v2)
.map(|(x, y)| (x - y).abs())
.fold(f64::NEG_INFINITY, f64::max))
}
#[cfg(test)]
mod tests {
use rand::{rngs::StdRng, SeedableRng};
use super::*;
fn test_rng() -> StdRng {
StdRng::seed_from_u64(42)
}
#[test]
fn compute_cumul_sum() {
let v = cumul_sum(&vec![1, 2, 3]);
assert_eq!(v, &[1, 3, 6]);
}
#[test]
fn remove_mixnode_from_cumul() {
let mut cumul_stake = vec![1, 2, 3, 4, 5, 6];
remove_mixnode_from_cumul_stake(3, &mut cumul_stake);
assert_eq!(cumul_stake, &[1, 2, 3, 3, 4, 5]);
}
#[test]
fn max_norm() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![2.0, 4.0, -6.0];
assert!((max_diff(&v1, &v2).unwrap() - 9.0).abs() < f64::EPSILON);
}
#[test]
fn ls_norm() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![2.0, 3.0, -2.0];
assert!((l2_diff(&v1, &v2).unwrap() - 5.196_152_422_706_632).abs() < 1e2 * f64::EPSILON);
}
// Replicate the results from the Python simulation code in https://github.com/nymtech/team-core/issues/114
#[test]
fn replicate_python_simulation() {
let active_set_size = 4;
let standby_set_size = 1;
// this has to contain the total stake per node
let list_mix = vec![
100, 100, 3000, 500_000, 100, 10, 10, 10, 10, 10, 30000, 500, 200, 52345,
];
let max_samples = 100_000;
let max_time = Duration::from_secs(10);
let mut rng = test_rng();
let SelectionProbability {
active_set_probability,
reserve_set_probability,
samples,
time,
delta_l2,
delta_max,
} = simulate_selection_probability_mixnodes(
&list_mix,
active_set_size,
standby_set_size,
max_samples,
max_time,
&mut rng,
)
.unwrap();
// Check that any possible test failure wasn't because we ran it on 1970s hardware, and the
// sampling aborted prematurely due to hitting `max_time`.
assert!(time < max_time);
// These values comes from running the python simulator for a very long time
let expected_active_set_probability = vec![
0.025_070_8,
0.025_073_2,
0.744_117,
0.999_999,
0.025_000_2,
0.002_524_4,
0.002_527_8,
0.002_528_6,
0.002_569_6,
0.002_513_6,
0.994,
0.125_482_8,
0.050_279_8,
0.998_313_2,
];
// The same check is used in the convergence criterion, and hence should be reflected in
// `delta_max` too.
assert!(
max_diff(&active_set_probability, &expected_active_set_probability).unwrap() < 1e-2
);
let expected_reserve_set_probability = vec![
0.076_392_4,
0.076_499,
0.204_893_6,
1e-06,
0.076_278_8,
0.007_720_6,
0.007_673,
0.007_700_2,
0.007_669_4,
0.007_731_2,
0.005_789_4,
0.368_465_6,
0.151_537_2,
0.001_648_6,
];
assert!(
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap() < 1e-2
);
// We converge around 20_000, add another 500 for some slack due to random values
assert_eq!(samples, 20_001);
assert!(delta_l2 < TOLERANCE_L2_NORM);
assert!(delta_max < TOLERANCE_MAX_NORM);
}
#[test]
fn fewer_nodes_than_active_set_size() {
let active_set_size = 10;
let standby_set_size = 3;
let list_mix = vec![100, 100, 3000];
let max_samples = 100_000;
let max_time = Duration::from_secs(10);
let mut rng = test_rng();
let SelectionProbability {
active_set_probability,
reserve_set_probability,
samples,
time: _,
delta_l2,
delta_max,
} = simulate_selection_probability_mixnodes(
&list_mix,
active_set_size,
standby_set_size,
max_samples,
max_time,
&mut rng,
)
.unwrap();
// These values comes from running the python simulator for a very long time
let expected_active_set_probability = vec![1.0, 1.0, 1.0];
let expected_reserve_set_probability = vec![0.0, 0.0, 0.0];
assert!(
max_diff(&active_set_probability, &expected_active_set_probability).unwrap()
< 1e1 * f64::EPSILON
);
assert!(
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap()
< 1e1 * f64::EPSILON
);
// We converge around 20_000, add another 500 for some slack due to random values
assert_eq!(samples, 0);
assert!(delta_l2 < f64::EPSILON);
assert!(delta_max < f64::EPSILON);
}
#[test]
fn fewer_nodes_than_reward_set_size() {
let active_set_size = 4;
let standby_set_size = 3;
let list_mix = vec![100, 100, 3000, 342, 3_498_234];
let max_samples = 100_000_000;
let max_time = Duration::from_secs(10);
let mut rng = test_rng();
let SelectionProbability {
active_set_probability,
reserve_set_probability,
samples,
time: _,
delta_l2,
delta_max,
} = simulate_selection_probability_mixnodes(
&list_mix,
active_set_size,
standby_set_size,
max_samples,
max_time,
&mut rng,
)
.unwrap();
// These values comes from running the python simulator for a very long time
let expected_active_set_probability = vec![0.546, 0.538, 0.999, 0.915, 1.0];
let expected_reserve_set_probability = vec![0.453, 0.461, 0.0005, 0.084, 0.0];
assert!(
max_diff(&active_set_probability, &expected_active_set_probability).unwrap() < 1e-2,
);
assert!(
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap() < 1e-2,
);
// We converge around 20_000, add another 500 for some slack due to random values
assert_eq!(samples, 20_001);
assert!(delta_l2 < TOLERANCE_L2_NORM);
assert!(delta_max < TOLERANCE_MAX_NORM);
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://127.0.0.1:8090";
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
pub const NYMD_VALIDATOR: &str = "https://rpc.nyx.nodes.guru/";
pub const API_VALIDATOR: &str = "https://validator.nymtech.net/api/";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
+1 -1
View File
@@ -42,7 +42,7 @@ pub enum CoconutError {
)]
DeserializationMinLength { min: usize, actual: usize },
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {object} or {modulus_target} % {modulus} == 0")]
DeserializationInvalidLength {
actual: usize,
target: usize,
+4 -6
View File
@@ -213,7 +213,7 @@ where
/// - compute vk_b = g^x || v_b
/// - compute sphinx_plaintext = SURB_ACK || g^x || v_b
/// - compute sphinx_packet = Sphinx(recipient, sphinx_plaintext)
pub async fn prepare_chunk_for_sending(
pub fn prepare_chunk_for_sending(
&mut self,
fragment: Fragment,
topology: &NymTopology,
@@ -222,8 +222,7 @@ where
) -> Result<PreparedFragment, NymTopologyError> {
// create an ack
let (ack_delay, surb_ack_bytes) = self
.generate_surb_ack(fragment.fragment_identifier(), topology, ack_key)
.await?
.generate_surb_ack(fragment.fragment_identifier(), topology, ack_key)?
.prepare_for_sending();
// TODO:
@@ -294,7 +293,7 @@ where
}
/// Construct an acknowledgement SURB for the given [`FragmentIdentifier`]
async fn generate_surb_ack(
fn generate_surb_ack(
&mut self,
fragment_id: FragmentIdentifier,
topology: &NymTopology,
@@ -357,8 +356,7 @@ where
// gateways could not distinguish reply packets from normal messages due to lack of said acks
// note: the ack delay is irrelevant since we do not know the delay of actual surb
let (_, surb_ack_bytes) = self
.generate_surb_ack(reply_id, topology, ack_key)
.await?
.generate_surb_ack(reply_id, topology, ack_key)?
.prepare_for_sending();
let zero_pad_len = self.packet_size.plaintext_size()
+1
View File
@@ -9,3 +9,4 @@ edition = "2021"
[dependencies]
nymsphinx-addressing = { path = "../../../common/nymsphinx/addressing" }
ordered-buffer = {path = "../ordered-buffer"}
thiserror = "1"
+5
View File
@@ -1,7 +1,12 @@
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod msg;
pub mod network_requester_response;
pub mod request;
pub mod response;
pub use msg::*;
pub use network_requester_response::*;
pub use request::*;
pub use response::*;
+28 -15
View File
@@ -1,36 +1,40 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
use crate::network_requester_response::{Error as NrError, NetworkRequesterResponse};
use crate::request::{Request, RequestError};
use crate::response::{Response, ResponseError};
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum MessageError {
#[error("{0}")]
Request(RequestError),
Response(ResponseError),
NoData,
UnknownMessageType,
}
impl std::fmt::Display for MessageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MessageError::Request(r) => write!(f, "{}", r),
MessageError::Response(r) => write!(f, "{:?}", r),
MessageError::NoData => write!(f, "no data provided"),
MessageError::UnknownMessageType => write!(f, "unknown message type received"),
}
}
#[error("{0:?}")]
Response(ResponseError),
#[error("{0}")]
NetworkRequesterResponseError(NrError),
#[error("no data")]
NoData,
#[error("unknown message type received")]
UnknownMessageType,
}
pub enum Message {
Request(Request),
Response(Response),
NetworkRequesterResponse(NetworkRequesterResponse),
}
impl Message {
const REQUEST_FLAG: u8 = 0;
const RESPONSE_FLAG: u8 = 1;
const NR_RESPONSE_FLAG: u8 = 2;
pub fn conn_id(&self) -> u64 {
match self {
@@ -39,6 +43,7 @@ impl Message {
Request::Send(conn_id, _, _) => *conn_id,
},
Message::Response(resp) => resp.connection_id,
Message::NetworkRequesterResponse(resp) => resp.connection_id,
}
}
@@ -49,6 +54,7 @@ impl Message {
Request::Send(_, data, _) => data.len(),
},
Message::Response(resp) => resp.data.len(),
Message::NetworkRequesterResponse(_) => 0,
}
}
@@ -65,6 +71,10 @@ impl Message {
Response::try_from_bytes(&b[1..])
.map(Message::Response)
.map_err(MessageError::Response)
} else if b[0] == Self::NR_RESPONSE_FLAG {
NetworkRequesterResponse::try_from_bytes(&b[1..])
.map(Message::NetworkRequesterResponse)
.map_err(MessageError::NetworkRequesterResponseError)
} else {
Err(MessageError::UnknownMessageType)
}
@@ -78,6 +88,9 @@ impl Message {
Self::Response(r) => std::iter::once(Self::RESPONSE_FLAG)
.chain(r.into_bytes().iter().cloned())
.collect(),
Self::NetworkRequesterResponse(r) => std::iter::once(Self::NR_RESPONSE_FLAG)
.chain(r.into_bytes().iter().cloned())
.collect(),
}
}
}
@@ -0,0 +1,112 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ConnectionId;
#[derive(Debug)]
pub struct NetworkRequesterResponse {
pub connection_id: ConnectionId,
pub network_requester_error: String,
}
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
#[error("no data provided")]
NoData,
#[error("not enough bytes to recover the connection id")]
ConnectionIdTooShort,
#[error("message is not utf8 encoded")]
MalformedErrorMessage(#[from] std::string::FromUtf8Error),
}
impl NetworkRequesterResponse {
pub fn new(connection_id: ConnectionId, network_requester_error: String) -> Self {
NetworkRequesterResponse {
connection_id,
network_requester_error,
}
}
pub fn try_from_bytes(b: &[u8]) -> Result<NetworkRequesterResponse, Error> {
if b.is_empty() {
return Err(Error::NoData);
}
if b.len() < 8 {
return Err(Error::ConnectionIdTooShort);
}
let mut connection_id_bytes = b.to_vec();
let network_requester_error_bytes = connection_id_bytes.split_off(8);
let connection_id = u64::from_be_bytes([
connection_id_bytes[0],
connection_id_bytes[1],
connection_id_bytes[2],
connection_id_bytes[3],
connection_id_bytes[4],
connection_id_bytes[5],
connection_id_bytes[6],
connection_id_bytes[7],
]);
let network_requester_error = String::from_utf8(network_requester_error_bytes)?;
Ok(NetworkRequesterResponse {
connection_id,
network_requester_error,
})
}
pub fn into_bytes(self) -> Vec<u8> {
self.connection_id
.to_be_bytes()
.iter()
.cloned()
.chain(self.network_requester_error.into_bytes().into_iter())
.collect()
}
}
#[cfg(test)]
mod network_requester_response_serde_tests {
use super::*;
#[test]
fn simple_serde() {
let conn_id = 42;
let network_requester_error = String::from("This is a test msg");
let response = NetworkRequesterResponse::new(conn_id, network_requester_error.clone());
let bytes = response.into_bytes();
let deserialized_response = NetworkRequesterResponse::try_from_bytes(&bytes).unwrap();
assert_eq!(conn_id, deserialized_response.connection_id);
assert_eq!(
network_requester_error,
deserialized_response.network_requester_error
);
}
#[test]
fn deserialization_errors() {
let err = NetworkRequesterResponse::try_from_bytes(&[]).err().unwrap();
assert_eq!(err, Error::NoData);
let bytes: [u8; 5] = [1, 2, 3, 4, 5];
let err = NetworkRequesterResponse::try_from_bytes(&bytes)
.err()
.unwrap();
assert_eq!(err, Error::ConnectionIdTooShort);
let bytes: Vec<u8> = 42u64
.to_be_bytes()
.into_iter()
.chain([0, 159, 146, 150].into_iter())
.collect();
let err = NetworkRequesterResponse::try_from_bytes(&bytes)
.err()
.unwrap();
assert!(matches!(err, Error::MalformedErrorMessage(_)));
}
}
+18 -24
View File
@@ -1,6 +1,9 @@
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nymsphinx_addressing::clients::{Recipient, RecipientFormattingError};
use std::convert::TryFrom;
use std::fmt::{self};
use thiserror::Error;
pub type ConnectionId = u64;
pub type RemoteAddress = String;
@@ -12,39 +15,30 @@ pub enum RequestFlag {
Send = 1,
}
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum RequestError {
#[error("not enough bytes to recover the length of the address")]
AddressLengthTooShort,
#[error("not enough bytes to recover the address")]
AddressTooShort,
#[error("not enough bytes to recover the connection id")]
ConnectionIdTooShort,
#[error("no data provided")]
NoData,
#[error("request of unknown type")]
UnknownRequestFlag,
#[error("too short return address")]
ReturnAddressTooShort,
#[error("malformed return address - {0}")]
MalformedReturnAddress(RecipientFormattingError),
}
impl fmt::Display for RequestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
match self {
RequestError::AddressLengthTooShort => {
write!(f, "not enough bytes to recover the length of the address")
}
RequestError::AddressTooShort => write!(f, "not enough bytes to recover the address"),
RequestError::ConnectionIdTooShort => {
write!(f, "not enough bytes to recover the connection id")
}
RequestError::NoData => write!(f, "no data provided"),
RequestError::UnknownRequestFlag => write!(f, "request of unknown type"),
RequestError::ReturnAddressTooShort => write!(f, "too short return address"),
RequestError::MalformedReturnAddress(recipient_err) => {
write!(f, "malformed return address - {}", recipient_err)
}
}
}
}
impl std::error::Error for RequestError {}
impl RequestError {
pub fn is_malformed_return(&self) -> bool {
matches!(self, RequestError::MalformedReturnAddress(_))
+8 -1
View File
@@ -1,8 +1,15 @@
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
use crate::ConnectionId;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Error, PartialEq, Eq)]
pub enum ResponseError {
#[error("not enough bytes to recover the connection id")]
ConnectionIdTooShort,
#[error("no data provided")]
NoData,
}
/// A remote network response retrieved by the Socks5 service provider. This
+6 -2
View File
@@ -34,12 +34,16 @@ pub enum StatsData {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct StatsGatewayData {
pub gateway_id: String,
pub inbox_count: u32,
}
impl StatsGatewayData {
pub fn new(inbox_count: u32) -> Self {
StatsGatewayData { inbox_count }
pub fn new(gateway_id: String, inbox_count: u32) -> Self {
StatsGatewayData {
gateway_id,
inbox_count,
}
}
}
+11 -2
View File
@@ -5,13 +5,14 @@ use std::time::Duration;
use tokio::sync::watch::{self, error::SendError};
const SHUTDOWN_TIMER_SECS: u64 = 5;
const DEFAULT_SHUTDOWN_TIMER_SECS: u64 = 5;
/// Used to notify other tasks to gracefully shutdown
#[derive(Debug)]
pub struct ShutdownNotifier {
notify_tx: watch::Sender<()>,
notify_rx: Option<watch::Receiver<()>>,
shutdown_timer_secs: u64,
}
impl Default for ShutdownNotifier {
@@ -20,11 +21,19 @@ impl Default for ShutdownNotifier {
Self {
notify_tx,
notify_rx: Some(notify_rx),
shutdown_timer_secs: DEFAULT_SHUTDOWN_TIMER_SECS,
}
}
}
impl ShutdownNotifier {
pub fn new(shutdown_timer_secs: u64) -> Self {
Self {
shutdown_timer_secs,
..Default::default()
}
}
pub fn subscribe(&self) -> ShutdownListener {
ShutdownListener::new(
self.notify_rx
@@ -50,7 +59,7 @@ impl ShutdownNotifier {
_ = tokio::signal::ctrl_c() => {
log::info!("Forcing shutdown");
}
_ = tokio::time::sleep(Duration::from_secs(SHUTDOWN_TIMER_SECS)) => {
_ = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs)) => {
log::info!("Timout reached, forcing shutdown");
},
}
+1 -1
View File
@@ -507,7 +507,7 @@ mod test {
for (expected, raw_display) in values {
let coin = DecCoin {
denom: Network::MAINNET.mix_denom().display.into(),
denom: Network::MAINNET.mix_denom().display,
amount: raw_display.parse().unwrap(),
};
let base = reg.attempt_convert_to_base_coin(coin).unwrap();
+51
View File
@@ -0,0 +1,51 @@
## Unreleased
### Added
- vesting-contract: added queries for delegation timestamps and paged query for all vesting delegations in the contract ([#1569])
### Changed
- mixnet-contract: compounding delegator rewards now happens instantaneously as opposed to having to wait for the current epoch to finish ([#1571])
### Fixed
- vesting-contract: the contract now correctly stores delegations with their timestamp as opposed to using block height ([#1544])
- mixnet-contract: compounding delegator rewards is now possible even if the associated mixnode had already unbonded ([#1571])
[#1544]: https://github.com/nymtech/nym/pull/1544
[#1569]: https://github.com/nymtech/nym/pull/1569
[#1569]: https://github.com/nymtech/nym/pull/1571
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
### Added
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
- mixnet-contract: Added staking_supply field to ContractStateParams.
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
### Fixed
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
[#1255]: https://github.com/nymtech/nym/pull/1255
[#1257]: https://github.com/nymtech/nym/pull/1257
[#1258]: https://github.com/nymtech/nym/pull/1258
[#1275]: https://github.com/nymtech/nym/pull/1275
[#1284]: https://github.com/nymtech/nym/pull/1284
[#1292]: https://github.com/nymtech/nym/pull/1292
[#1331]: https://github.com/nymtech/nym/pull/1331
[#1369]: https://github.com/nymtech/nym/pull/1369
[#1373]: https://github.com/nymtech/nym/pull/1373
+4 -4
View File
@@ -1392,18 +1392,18 @@ dependencies = [
[[package]]
name = "regex"
version = "1.5.4"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "rfc6979"
+76 -7
View File
@@ -27,16 +27,18 @@ use crate::mixnodes::bonding_queries::{
query_checkpoints_for_mixnode, query_mixnode_at_height, query_mixnodes_paged,
};
use crate::mixnodes::layer_queries::query_layer_distribution;
use crate::mixnodes::transactions::_try_remove_mixnode;
use crate::queued_migrations::migrate_config_from_env;
use crate::rewards::queries::{
query_circulating_supply, query_reward_pool, query_rewarding_status, query_staking_supply,
};
use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Api, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
Uint128,
Storage, Uint128,
};
use mixnet_contract_common::{
ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, NodeToRemove, QueryMsg,
};
use time::OffsetDateTime;
@@ -112,6 +114,19 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CompoundReward {
operator,
delegator,
mix_identity,
proxy,
} => crate::rewards::transactions::try_compound_reward(
deps,
env,
operator,
delegator,
mix_identity,
proxy,
),
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
try_update_rewarding_validator_address(deps, info, address)
}
@@ -127,7 +142,7 @@ pub fn execute(
owner_signature,
),
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(env, deps, info)
crate::mixnodes::transactions::try_remove_mixnode(&env, deps.storage, deps.api, info)
}
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
@@ -221,7 +236,13 @@ pub fn execute(
owner_signature,
),
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(env, deps, info, owner)
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(
&env,
deps.storage,
deps.api,
info,
owner,
)
}
ExecuteMsg::BondGatewayOnBehalf {
gateway,
@@ -280,7 +301,7 @@ pub fn execute(
)
}
ExecuteMsg::ReconcileDelegations {} => {
crate::delegations::transactions::try_reconcile_all_delegation_events(deps, info)
crate::delegations::transactions::try_reconcile_all_delegation_events(deps)
}
ExecuteMsg::CheckpointMixnodes {} => {
crate::mixnodes::transactions::try_checkpoint_mixnodes(
@@ -321,6 +342,9 @@ pub fn execute(
#[entry_point]
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let query_res = match msg {
QueryMsg::GetBlacklistedNodes {} => to_binary(
&crate::mixnodes::bonding_queries::get_blacklisted_nodes(deps),
),
QueryMsg::GetRewardingValidatorAddress {} => {
to_binary(&query_rewarding_validator_address(deps)?)
}
@@ -448,9 +472,54 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
Ok(query_res?)
}
fn blacklist_malicious_node(storage: &mut dyn Storage, owner: &Addr) -> Result<(), ContractError> {
let mixnode_bond = match crate::mixnodes::storage::mixnodes()
.idx
.owner
.item(storage, owner.clone())?
{
Some(record) => record.1,
None => {
return Err(ContractError::NoAssociatedMixNodeBond {
owner: owner.to_owned(),
})
}
};
crate::mixnodes::storage::MIXNODES_BOND_BLACKLIST.save(storage, mixnode_bond.identity(), &0)?;
Ok(())
}
// Removes nodes we've deemed malicious, returns the pledge to the owners, but does not send any rewards
fn remove_malicious_node(
storage: &mut dyn Storage,
api: &dyn Api,
env: &Env,
node: &NodeToRemove,
) -> Result<Response, ContractError> {
let proxy = node.proxy().map(|p| {
api.addr_validate(p)
.unwrap_or_else(|_| panic!("Invalid address: {}", p))
});
let owner_addr = api.addr_validate(node.owner())?;
blacklist_malicious_node(storage, &owner_addr)?;
_try_remove_mixnode(env, storage, api, node.owner(), proxy, false)
}
#[entry_point]
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
Ok(Default::default())
pub fn migrate(deps: DepsMut<'_>, env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
migrate_config_from_env(deps.storage, &msg)?;
let mut response = Response::new();
for node in msg.nodes_to_remove().iter() {
let mut sub_response = remove_malicious_node(deps.storage, deps.api, &env, node)
.unwrap_or_else(|_| panic!("Could not remove node: {:?}", node));
response.messages.append(&mut sub_response.messages);
response.attributes.append(&mut sub_response.attributes);
response.events.append(&mut sub_response.events);
}
Ok(response)
}
#[cfg(test)]
+35 -183
View File
@@ -19,16 +19,7 @@ use mixnet_contract_common::{Delegation, IdentityKey};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_ucoin;
pub fn try_reconcile_all_delegation_events(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, ContractError> {
let state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?;
// check if this is executed by the permitted validator, if not reject the transaction
if info.sender != state.rewarding_validator_address {
return Err(ContractError::Unauthorized);
}
pub fn try_reconcile_all_delegation_events(deps: DepsMut<'_>) -> Result<Response, ContractError> {
_try_reconcile_all_delegation_events(deps.storage, deps.api)
}
@@ -630,7 +621,14 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
let api = deps.api.clone();
try_remove_mixnode(
&mock_env(),
deps.as_mut().storage,
&api,
mock_info(mixnode_owner, &[]),
)
.unwrap();
assert_eq!(
Err(ContractError::MixNodeBondNotFound {
identity: identity.clone()
@@ -653,7 +651,14 @@ mod tests {
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
let api = deps.api.clone();
try_remove_mixnode(
&mock_env(),
deps.as_mut().storage,
&api,
mock_info(mixnode_owner, &[]),
)
.unwrap();
let identity = test_helpers::add_mixnode(
mixnode_owner,
tests::fixtures::good_mixnode_pledge(),
@@ -917,7 +922,14 @@ mod tests {
identity.clone(),
)
.unwrap();
try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
let api = deps.api.clone();
try_remove_mixnode(
&mock_env(),
deps.as_mut().storage,
&api,
mock_info(mixnode_owner, &[]),
)
.unwrap();
assert_eq!(
Err(ContractError::MixNodeBondNotFound {
identity: identity.clone()
@@ -1058,8 +1070,14 @@ mod tests {
.unwrap();
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
let api = deps.api.clone();
try_remove_mixnode(
&mock_env(),
deps.as_mut().storage,
&api,
mock_info(mixnode_owner, &[]),
)
.unwrap();
let expected = Delegation::new(
delegation_owner.clone(),
@@ -1084,179 +1102,13 @@ mod tests {
#[cfg(test)]
mod removing_mix_stake_delegation {
use super::*;
use crate::support::tests;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Addr;
use crate::support::tests;
use super::*;
// TODO: Probably delete due to reconciliation logic
//#[ignore]
//#[test]
//fn fails_if_delegation_never_existed() {
// let mut deps = test_helpers::init_contract();
// let env = mock_env();
// let mixnode_owner = "bob";
// let identity = test_helpers::add_mixnode(
// mixnode_owner,
// tests::fixtures::good_mixnode_pledge(),
// deps.as_mut(),
// );
// let delegation_owner = Addr::unchecked("sender");
// assert_eq!(
// Err(ContractError::NoMixnodeDelegationFound {
// identity: identity.clone(),
// address: delegation_owner.to_string(),
// }),
// try_remove_delegation_from_mixnode(
// deps.as_mut(),
// env,
// mock_info(delegation_owner.as_str(), &[]),
// identity,
// )
// );
//}
// TODO: Update to work with reconciliation
//#[ignore]
//#[test]
//fn succeeds_if_delegation_existed() {
// let mut deps = test_helpers::init_contract();
// let mixnode_owner = "bob";
// let env = mock_env();
// let identity = test_helpers::add_mixnode(
// mixnode_owner,
// tests::fixtures::good_mixnode_pledge(),
// deps.as_mut(),
// );
// let delegation_owner = Addr::unchecked("sender");
// try_delegate_to_mixnode(
// deps.as_mut(),
// mock_env(),
// mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
// identity.clone(),
// )
// .unwrap();
// _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
// let _delegation = query_mixnode_delegation(
// &deps.storage,
// &deps.api,
// identity.clone(),
// delegation_owner.clone().into_string(),
// None,
// )
// .unwrap();
// let expected_response = Response::new()
// .add_message(BankMsg::Send {
// to_address: delegation_owner.clone().into(),
// amount: coins(100, TEST_COIN_DENOM),
// })
// .add_event(new_undelegation_event(
// &delegation_owner,
// &None,
// &identity,
// Uint128::new(100),
// ));
// assert_eq!(
// Ok(expected_response),
// try_remove_delegation_from_mixnode(
// deps.as_mut(),
// env,
// mock_info(delegation_owner.as_str(), &[]),
// identity.clone(),
// )
// );
// assert!(storage::delegations()
// .may_load(
// &deps.storage,
// (identity.clone(), delegation_owner.as_bytes().to_vec(), 0),
// )
// .unwrap()
// .is_none());
// // and total delegation is cleared
// assert_eq!(
// Uint128::zero(),
// mixnodes_storage::TOTAL_DELEGATION
// .load(&deps.storage, &identity)
// .unwrap()
// )
//}
// TODO: Update to work with reconciliation
//#[ignore]
//#[test]
//fn succeeds_if_delegation_existed_even_if_node_unbonded() {
// let mut deps = test_helpers::init_contract();
// let mixnode_owner = "bob";
// let env = mock_env();
// let identity = test_helpers::add_mixnode(
// mixnode_owner,
// tests::fixtures::good_mixnode_pledge(),
// deps.as_mut(),
// );
// let delegation_owner = Addr::unchecked("sender");
// try_delegate_to_mixnode(
// deps.as_mut(),
// mock_env(),
// mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
// identity.clone(),
// )
// .unwrap();
// _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
// let _delegation = query_mixnode_delegation(
// &deps.storage,
// &deps.api,
// identity.clone(),
// delegation_owner.clone().into_string(),
// None,
// )
// .unwrap();
// let expected_response = Response::new()
// .add_message(BankMsg::Send {
// to_address: delegation_owner.clone().into(),
// amount: coins(100, TEST_COIN_DENOM),
// })
// .add_event(new_undelegation_event(
// &delegation_owner,
// &None,
// &identity,
// Uint128::new(100),
// ));
// try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
// assert_eq!(
// Ok(expected_response),
// try_remove_delegation_from_mixnode(
// deps.as_mut(),
// env,
// mock_info(delegation_owner.as_str(), &[]),
// identity.clone(),
// )
// );
// _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
// assert!(test_helpers::read_delegation(
// &deps.storage,
// identity,
// delegation_owner.as_bytes(),
// mock_env().block.height
// )
// .is_none());
//}
#[test]
fn total_delegation_is_preserved_if_only_some_undelegate() {
let mut deps = test_helpers::init_contract();
+7
View File
@@ -178,4 +178,11 @@ pub enum ContractError {
last_update_time: u64,
current_block_time: u64,
},
#[error("`mix_identity` is required when `delegator` is set")]
MissingMixIdentity,
#[error("Compounding has been disabled temporarily")]
CompoundDisabled,
#[error("Mixnode {identity} has been blacklisted on the network")]
MixnodeBlacklisted { identity: String },
}
+1
View File
@@ -9,5 +9,6 @@ mod gateways;
mod interval;
mod mixnet_contract_settings;
mod mixnodes;
mod queued_migrations;
mod rewards;
mod support;
@@ -8,6 +8,13 @@ use mixnet_contract_common::{
IdentityKey, MixNodeBond, MixOwnershipResponse, MixnodeBondResponse, PagedMixnodeResponse,
};
pub fn get_blacklisted_nodes(deps: Deps<'_>) -> Vec<IdentityKey> {
storage::MIXNODES_BOND_BLACKLIST
.keys(deps.storage, None, None, Order::Ascending)
.filter_map(|i| i.ok())
.collect()
}
pub fn query_mixnode_at_height(
deps: Deps<'_>,
mix_identity: String,
@@ -252,10 +259,13 @@ pub(crate) mod tests {
let res = query_owns_mixnode(deps.as_ref(), "fred".to_string()).unwrap();
assert!(res.mixnode.is_some());
let api = deps.api.clone();
// but after unbonding it, he doesn't own one anymore
crate::mixnodes::transactions::try_remove_mixnode(
env,
deps.as_mut(),
&env,
deps.as_mut().storage,
&api,
mock_info("fred", &[]),
)
.unwrap();
+4
View File
@@ -19,6 +19,7 @@ const MIXNODES_PK_CHECKPOINTS: &str = "mn__check";
const MIXNODES_PK_CHANGELOG: &str = "mn__change";
const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
const MIXNODES_BOND_BLACKLIST_NAMESPACE: &str = "mbb";
const LAST_PM_UPDATE_NAMESPACE: &str = "lpm";
@@ -32,6 +33,9 @@ pub(crate) const TOTAL_DELEGATION: Map<'_, IdentityKeyRef<'_>, Uint128> =
pub(crate) const LAST_PM_UPDATE_TIME: Map<'_, IdentityKeyRef<'_>, u64> =
Map::new(LAST_PM_UPDATE_NAMESPACE);
pub(crate) const MIXNODES_BOND_BLACKLIST: Map<'_, IdentityKeyRef<'_>, u8> =
Map::new(MIXNODES_BOND_BLACKLIST_NAMESPACE);
pub(crate) struct MixnodeBondIndex<'a> {
pub(crate) owner: UniqueIndex<'a, Addr, StoredMixnodeBond>,
+43 -27
View File
@@ -8,12 +8,12 @@ use crate::mixnodes::layer_queries::query_layer_distribution;
use crate::mixnodes::storage::StoredMixnodeBond;
use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature};
use cosmwasm_std::{
wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Storage, Uint128,
wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Storage, Uint128,
};
use mixnet_contract_common::events::{
new_checkpoint_mixnodes_event, new_mixnode_bonding_event, new_mixnode_unbonding_event,
};
use mixnet_contract_common::MixNode;
use mixnet_contract_common::{IdentityKeyRef, MixNode};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_ucoin;
@@ -87,6 +87,15 @@ pub fn try_add_mixnode_on_behalf(
)
}
pub fn is_blacklisted(
storage: &dyn Storage,
identity: IdentityKeyRef,
) -> Result<bool, ContractError> {
Ok(storage::MIXNODES_BOND_BLACKLIST
.may_load(storage, identity)?
.is_some())
}
fn _try_add_mixnode(
deps: DepsMut<'_>,
env: Env,
@@ -100,6 +109,12 @@ fn _try_add_mixnode(
// if the client has an active bonded mixnode or gateway, don't allow bonding
ensure_no_existing_bond(deps.storage, &owner)?;
if is_blacklisted(deps.storage, &mix_node.identity_key)? {
return Err(ContractError::MixnodeBlacklisted {
identity: mix_node.identity_key,
});
};
// We don't have to check lower bound as its an u8
if mix_node.profit_margin_percent > 100 {
return Err(ContractError::InvalidProfitMarginPercent(
@@ -167,45 +182,47 @@ fn _try_add_mixnode(
}
pub fn try_remove_mixnode_on_behalf(
env: Env,
deps: DepsMut<'_>,
env: &Env,
storage: &mut dyn Storage,
api: &dyn Api,
info: MessageInfo,
owner: String,
) -> Result<Response, ContractError> {
let proxy = info.sender;
_try_remove_mixnode(env, deps, &owner, Some(proxy))
_try_remove_mixnode(env, storage, api, &owner, Some(proxy), true)
}
pub fn try_remove_mixnode(
env: Env,
deps: DepsMut<'_>,
env: &Env,
storage: &mut dyn Storage,
api: &dyn Api,
info: MessageInfo,
) -> Result<Response, ContractError> {
_try_remove_mixnode(env, deps, info.sender.as_ref(), None)
_try_remove_mixnode(env, storage, api, info.sender.as_ref(), None, true)
}
pub(crate) fn _try_remove_mixnode(
env: Env,
deps: DepsMut<'_>,
env: &Env,
storage: &mut dyn Storage,
api: &dyn Api,
owner: &str,
proxy: Option<Addr>,
collect_rewards: bool,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(owner)?;
let owner = api.addr_validate(owner)?;
crate::rewards::transactions::_try_compound_operator_reward(
deps.storage,
deps.api,
env.block.height,
&owner,
None,
)?;
if collect_rewards {
crate::rewards::transactions::_try_compound_operator_reward(
storage,
api,
env.block.height,
&owner,
None,
)?;
}
// try to find the node of the sender
let mixnode_bond = match storage::mixnodes()
.idx
.owner
.item(deps.storage, owner.clone())?
{
let mixnode_bond = match storage::mixnodes().idx.owner.item(storage, owner.clone())? {
Some(record) => record.1,
None => return Err(ContractError::NoAssociatedMixNodeBond { owner }),
};
@@ -225,10 +242,10 @@ pub(crate) fn _try_remove_mixnode(
};
// remove the bond
storage::mixnodes().remove(deps.storage, mixnode_bond.identity(), env.block.height)?;
storage::mixnodes().remove(storage, mixnode_bond.identity(), env.block.height)?;
// decrement layer count
mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?;
mixnet_params_storage::decrement_layer_count(storage, mixnode_bond.layer)?;
let mut response = Response::new();
@@ -238,8 +255,7 @@ pub(crate) fn _try_remove_mixnode(
amount: mixnode_bond.pledge_amount(),
};
let track_unbond_message =
wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom(deps.storage)?)])?;
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom(storage)?)])?;
response = response.add_message(track_unbond_message);
}
+37
View File
@@ -0,0 +1,37 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, Response, Storage};
use cw_storage_plus::Item;
use mixnet_contract_common::{ContractStateParams, MigrateMsg};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::error::ContractError;
use crate::mixnet_contract_settings::models::ContractState;
use crate::mixnet_contract_settings::storage::CONTRACT_STATE;
pub fn migrate_config_from_env(
storage: &mut dyn Storage,
msg: &MigrateMsg,
) -> Result<Response, ContractError> {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct OldContractState {
pub owner: Addr,
pub rewarding_validator_address: Addr,
pub params: ContractStateParams,
}
const OLD_CONTRACT_STATE: Item<'_, OldContractState> = Item::new("config");
let old_state = OLD_CONTRACT_STATE.load(storage)?;
let new_state = ContractState {
owner: old_state.owner,
mix_denom: msg.mixnet_denom.clone(),
rewarding_validator_address: old_state.rewarding_validator_address,
params: old_state.params,
};
CONTRACT_STATE.save(storage, &new_state)?;
Ok(Default::default())
}
+92 -21
View File
@@ -8,16 +8,16 @@ use super::storage::{
use crate::constants;
use crate::contract::debug_with_visibility;
use crate::delegations::storage as delegations_storage;
use crate::delegations::transactions::_try_delegate_to_mixnode;
use crate::error::ContractError;
use crate::mixnet_contract_settings::storage::mix_denom;
use crate::mixnodes::storage::mixnodes;
use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond};
use crate::mixnodes::transactions::is_blacklisted;
use crate::rewards::helpers;
use crate::support::helpers::{is_authorized, operator_cost_at_epoch};
use cosmwasm_std::{
coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response,
Storage, Uint128,
StdResult, Storage, Uint128,
};
use cw_storage_plus::Bound;
use mixnet_contract_common::events::{
@@ -372,6 +372,56 @@ pub fn try_compound_delegator_reward_on_behalf(
)
}
pub fn try_compound_reward(
deps: DepsMut<'_>,
env: Env,
operator: Option<String>,
delegator: Option<String>,
mix_identity: Option<IdentityKey>,
proxy: Option<String>,
) -> Result<Response, ContractError> {
let proxy = proxy.and_then(|p| deps.api.addr_validate(&p).ok());
if let Some(operator_address) = operator {
let operator_address = deps.api.addr_validate(&operator_address)?;
let reward = _try_compound_operator_reward(
deps.storage,
deps.api,
env.block.height,
&operator_address,
proxy,
)?;
Ok(
Response::default().add_event(new_compound_operator_reward_event(
&operator_address,
reward,
)),
)
} else if let Some(delegator_address) = delegator {
if mix_identity.is_none() {
return Err(ContractError::MissingMixIdentity);
}
let delegator_address = deps.api.addr_validate(&delegator_address)?;
let reward = _try_compound_delegator_reward(
env.block.height,
deps,
delegator_address.as_str(),
mix_identity.as_ref().unwrap(),
proxy,
)?;
Ok(
Response::default().add_event(new_compound_delegator_reward_event(
&delegator_address,
&None,
reward,
&mix_identity.unwrap(),
)),
)
} else {
Ok(Response::default())
}
}
pub fn try_compound_delegator_reward(
deps: DepsMut<'_>,
env: Env,
@@ -399,32 +449,28 @@ pub fn try_compound_delegator_reward(
pub fn _try_compound_delegator_reward(
block_height: u64,
mut deps: DepsMut<'_>,
deps: DepsMut<'_>,
owner_address: &str,
mix_identity: &str,
proxy: Option<Addr>,
) -> Result<Uint128, ContractError> {
let delegation_map = crate::delegations::storage::delegations();
let mix_denom = mix_denom(deps.storage)?;
let key = mixnet_contract_common::delegation::generate_storage_key(
&deps.api.addr_validate(owner_address)?,
proxy.as_ref(),
);
let owner = deps.api.addr_validate(owner_address)?;
let key = mixnet_contract_common::delegation::generate_storage_key(&owner, proxy.as_ref());
let reward = calculate_delegator_reward(deps.storage, deps.api, key.clone(), mix_identity)?;
let mut compounded_delegation = reward;
let mut total_delegation_delegate = Uint128::zero();
// Might want to introduce paging here
let delegation_heights = delegation_map
.prefix((mix_identity.to_string(), key.clone()))
.keys(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.filter_map(|v| v.ok())
.collect::<Vec<u64>>();
.collect::<StdResult<Vec<_>>>()?;
for h in delegation_heights {
let delegation =
delegation_map.load(deps.storage, (mix_identity.to_string(), key.clone(), h))?;
compounded_delegation += delegation.amount.amount;
total_delegation_delegate += delegation.amount.amount;
delegation_map.replace(
deps.storage,
(mix_identity.to_string(), key.clone(), h),
@@ -433,25 +479,46 @@ pub fn _try_compound_delegator_reward(
)?;
}
let compounded_delegation = total_delegation_delegate + reward;
if compounded_delegation != Uint128::zero() {
_try_delegate_to_mixnode(
deps.branch(),
block_height,
mixnodes_storage::TOTAL_DELEGATION.update::<_, ContractError>(
deps.storage,
mix_identity,
owner_address,
|total_delegation| {
// since we know that the target node exists and because the total_delegation bucket
// entry is created whenever the node itself is added, the unwrap here is fine
// as the entry MUST exist
Ok(total_delegation.unwrap() + reward)
},
)?;
// let's simplify the entire procedure. Rather than creating a fresh delegation on the mixnode
// via `_try_delegate_to_mixnode` and then waiting for reconcile to happen,
// just save it directly to the storage right now.
// my reasoning for that is simple: `_try_delegate_to_mixnode` could fail if the node the
// delegator has delegated to no longer exists.
let delegation = Delegation::new(
owner,
mix_identity.into(),
Coin {
amount: compounded_delegation,
denom: mix_denom,
},
block_height,
proxy,
);
delegation_map.save(
deps.storage,
(mix_identity.into(), key.clone(), block_height),
&delegation,
)?;
}
{
if let Some(mut bond) = mixnodes().may_load(deps.storage, mix_identity)? {
bond.accumulated_rewards = Some(bond.accumulated_rewards().saturating_sub(reward));
mixnodes().save(deps.storage, mix_identity, &bond, block_height)?;
}
if let Some(mut bond) = mixnodes().may_load(deps.storage, mix_identity)? {
bond.accumulated_rewards = Some(bond.accumulated_rewards().saturating_sub(reward));
mixnodes().save(deps.storage, mix_identity, &bond, block_height)?;
}
DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
@@ -472,6 +539,10 @@ pub fn calculate_delegator_reward(
key: Vec<u8>,
mix_identity: &str,
) -> Result<Uint128, ContractError> {
if is_blacklisted(storage, mix_identity)? {
return Ok(Uint128::zero());
};
let last_claimed_height = storage::DELEGATOR_REWARD_CLAIMED_HEIGHT
.load(storage, (key.clone(), mix_identity.to_string()))
.unwrap_or(0);
+75 -5
View File
@@ -1,16 +1,18 @@
use crate::errors::ContractError;
use crate::queued_migrations::migrate_config_from_env;
use crate::storage::{
account_from_address, locked_pledge_cap, update_locked_pledge_cap, ADMIN,
MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
account_from_address, locked_pledge_cap, update_locked_pledge_cap, BlockTimestampSecs, ADMIN,
DELEGATIONS, MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
};
use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use cosmwasm_std::{
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Timestamp, Uint128,
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
QueryResponse, Response, StdResult, Timestamp, Uint128,
};
use cw_storage_plus::Bound;
use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
use vesting_contract_common::events::{
new_ownership_transfer_event, new_periodic_vesting_account_event,
@@ -21,7 +23,10 @@ use vesting_contract_common::events::{
use vesting_contract_common::messages::{
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
};
use vesting_contract_common::{OriginalVestingResponse, Period, PledgeData};
use vesting_contract_common::{
AllDelegationsResponse, DelegationTimesResponse, OriginalVestingResponse, Period, PledgeData,
VestingDelegation,
};
pub const INITIAL_LOCKED_PLEDGE_CAP: Uint128 = Uint128::new(100_000_000_000);
@@ -41,6 +46,7 @@ pub fn instantiate(
#[entry_point]
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
migrate_config_from_env(_deps, _env, _msg)?;
Ok(Response::default())
}
@@ -516,6 +522,13 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
QueryMsg::GetCurrentVestingPeriod { address } => {
to_binary(&try_get_current_vesting_period(&address, deps, env)?)
}
QueryMsg::GetDelegationTimes {
address,
mix_identity,
} => to_binary(&try_get_delegation_times(deps, &address, mix_identity)?),
QueryMsg::GetAllDelegations { start_after, limit } => {
to_binary(&try_get_all_delegations(deps, start_after, limit)?)
}
};
Ok(query_res?)
@@ -632,6 +645,63 @@ pub fn try_get_delegated_vesting(
account.get_delegated_vesting(block_time, &env, deps.storage)
}
pub fn try_get_delegation_times(
deps: Deps<'_>,
vesting_account_address: &str,
mix_identity: String,
) -> Result<DelegationTimesResponse, ContractError> {
let owner = deps.api.addr_validate(vesting_account_address)?;
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
let delegation_timestamps = DELEGATIONS
.prefix((account.storage_key(), mix_identity.clone()))
.keys(deps.storage, None, None, Order::Ascending)
.collect::<StdResult<Vec<_>>>()?;
Ok(DelegationTimesResponse {
owner,
account_id: account.storage_key(),
mix_identity,
delegation_timestamps,
})
}
pub fn try_get_all_delegations(
deps: Deps<'_>,
start_after: Option<(u32, IdentityKey, BlockTimestampSecs)>,
limit: Option<u32>,
) -> Result<AllDelegationsResponse, ContractError> {
let limit = limit.unwrap_or(100).min(200) as usize;
let start = start_after.map(Bound::exclusive);
let delegations = DELEGATIONS
.range(deps.storage, start, None, Order::Ascending)
.map(|kv| {
kv.map(
|((account_id, mix_identity, block_timestamp), amount)| VestingDelegation {
account_id,
mix_identity,
block_timestamp,
amount,
},
)
})
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = if delegations.len() < limit {
None
} else {
delegations
.last()
.map(|delegation| delegation.storage_key())
};
Ok(AllDelegationsResponse {
delegations,
start_next_after,
})
}
fn validate_funds(funds: &[Coin], mix_denom: String) -> Result<Coin, ContractError> {
if funds.is_empty() || funds[0].amount.is_zero() {
return Err(ContractError::EmptyFunds);
+1
View File
@@ -1,5 +1,6 @@
pub mod contract;
mod errors;
mod queued_migrations;
mod storage;
mod support;
mod traits;
@@ -0,0 +1,17 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{DepsMut, Env, Response};
use vesting_contract_common::MigrateMsg;
use crate::{errors::ContractError, storage::MIX_DENOM};
pub fn migrate_config_from_env(
deps: DepsMut<'_>,
_env: Env,
msg: MigrateMsg,
) -> Result<Response, ContractError> {
MIX_DENOM.save(deps.storage, &msg.mix_denom)?;
Ok(Default::default())
}
+4 -4
View File
@@ -5,7 +5,7 @@ use cw_storage_plus::{Item, Map};
use mixnet_contract_common::IdentityKey;
use vesting_contract_common::PledgeData;
type BlockHeight = u64;
pub(crate) type BlockTimestampSecs = u64;
pub const KEY: Item<'_, u32> = Item::new("key");
const ACCOUNTS: Map<'_, String, Account> = Map::new("acc");
@@ -14,7 +14,7 @@ const BALANCES: Map<'_, u32, Uint128> = Map::new("blc");
const WITHDRAWNS: Map<'_, u32, Uint128> = Map::new("wthd");
const BOND_PLEDGES: Map<'_, u32, PledgeData> = Map::new("bnd");
const GATEWAY_PLEDGES: Map<'_, u32, PledgeData> = Map::new("gtw");
pub const DELEGATIONS: Map<'_, (u32, IdentityKey, BlockHeight), Uint128> = Map::new("dlg");
pub const DELEGATIONS: Map<'_, (u32, IdentityKey, BlockTimestampSecs), Uint128> = Map::new("dlg");
pub const ADMIN: Item<'_, String> = Item::new("adm");
pub const MIXNET_CONTRACT_ADDRESS: Item<'_, String> = Item::new("mix");
pub const MIX_DENOM: Item<'_, String> = Item::new("den");
@@ -35,7 +35,7 @@ pub fn update_locked_pledge_cap(
}
pub fn save_delegation(
key: (u32, IdentityKey, BlockHeight),
key: (u32, IdentityKey, BlockTimestampSecs),
amount: Uint128,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
@@ -44,7 +44,7 @@ pub fn save_delegation(
}
pub fn remove_delegation(
key: (u32, IdentityKey, BlockHeight),
key: (u32, IdentityKey, BlockTimestampSecs),
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
DELEGATIONS.remove(storage, key);
@@ -88,7 +88,7 @@ impl DelegatingAccount for Account {
vec![coin.clone()],
)?;
self.track_delegation(
env.block.height,
env.block.time.seconds(),
mix_identity,
current_balance,
coin,
@@ -129,14 +129,14 @@ impl DelegatingAccount for Account {
fn track_delegation(
&self,
block_height: u64,
block_timestamp_secs: u64,
mix_identity: IdentityKey,
current_balance: Uint128,
delegation: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
save_delegation(
(self.storage_key(), mix_identity, block_height),
(self.storage_key(), mix_identity, block_timestamp_secs),
delegation.amount,
storage,
)?;
+18 -4
View File
@@ -3,7 +3,7 @@ use crate::errors::ContractError;
use crate::storage::{
load_balance, load_bond_pledge, load_gateway_pledge, load_withdrawn, remove_bond_pledge,
remove_delegation, remove_gateway_pledge, save_account, save_balance, save_bond_pledge,
save_gateway_pledge, save_withdrawn, DELEGATIONS, KEY,
save_gateway_pledge, save_withdrawn, BlockTimestampSecs, DELEGATIONS, KEY,
};
use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128};
use cw_storage_plus::Bound;
@@ -101,10 +101,9 @@ impl Account {
}
}
/// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
/// In case vesting is over it will always return NUM_VESTING_PERIODS.
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
// In case vesting is over it will always return NUM_VESTING_PERIODS.
if block_time.seconds() < self.periods.first().unwrap().start_time {
Period::Before
} else if self.periods.last().unwrap().end_time() < block_time {
@@ -262,4 +261,19 @@ impl Account {
.filter_map(|x| x.ok())
.fold(Uint128::zero(), |acc, (_key, val)| acc + val))
}
pub fn total_delegations_at_timestamp(
&self,
storage: &dyn Storage,
start_time: BlockTimestampSecs,
) -> Result<Uint128, ContractError> {
Ok(DELEGATIONS
.sub_prefix(self.storage_key())
.range(storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.filter(|((_mix, block_time), _amount)| *block_time <= start_time)
.fold(Uint128::zero(), |acc, ((_mix, _block_time), amount)| {
acc + amount
}))
}
}
@@ -1,7 +1,7 @@
use crate::errors::ContractError;
use crate::storage::{delete_account, save_account, DELEGATIONS, MIX_DENOM};
use crate::storage::{delete_account, save_account, MIX_DENOM};
use crate::traits::VestingAccount;
use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128};
use cosmwasm_std::{Addr, Coin, Env, Storage, Timestamp, Uint128};
use vesting_contract_common::{OriginalVestingResponse, Period};
use super::Account;
@@ -16,13 +16,6 @@ impl VestingAccount for Account {
+ self.get_pledged_vesting(None, env, storage)?.amount)
}
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
let current_balance = self.load_balance(storage)?;
let new_balance = current_balance + amount.amount;
self.save_balance(new_balance, storage)?;
Ok(())
}
fn locked_coins(
&self,
block_time: Option<Timestamp>,
@@ -141,14 +134,7 @@ impl VestingAccount for Account {
Period::In(idx) => self.periods[idx as usize].start_time,
};
let coin = DELEGATIONS
.sub_prefix(self.storage_key())
.range(storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.filter(|((_mix, block_time), _amount)| *block_time < start_time)
.fold(Uint128::zero(), |acc, ((_mix, _block_time), amount)| {
acc + amount
});
let coin = self.total_delegations_at_timestamp(storage, start_time)?;
let amount = Uint128::new(coin.u128().min(max_available.u128()));
@@ -158,6 +144,7 @@ impl VestingAccount for Account {
})
}
// TODO: why do we allow querying for block times in the past? - just use env.block.time all the time
fn get_delegated_vesting(
&self,
block_time: Option<Timestamp>,
@@ -166,9 +153,18 @@ impl VestingAccount for Account {
) -> Result<Coin, ContractError> {
let block_time = block_time.unwrap_or(env.block.time);
let delegated_free = self.get_delegated_free(Some(block_time), env, storage)?;
let total_delegations = self.total_delegations(storage)?;
let amount = total_delegations - delegated_free.amount;
let period = self.get_current_vesting_period(block_time);
let start_time = match period {
Period::Before => 0,
Period::After => u64::MAX,
Period::In(idx) => self.periods[idx as usize].start_time,
};
let delegations_before_start_time =
self.total_delegations_at_timestamp(storage, start_time)?;
let amount = delegations_before_start_time - delegated_free.amount;
Ok(Coin {
amount,
@@ -261,4 +257,11 @@ impl VestingAccount for Account {
save_account(self, storage)?;
Ok(())
}
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
let current_balance = self.load_balance(storage)?;
let new_balance = current_balance + amount.amount;
self.save_balance(new_balance, storage)?;
Ok(())
}
}
+169 -1
View File
@@ -44,10 +44,11 @@ mod tests {
use crate::traits::DelegatingAccount;
use crate::traits::VestingAccount;
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
use crate::vesting::{populate_vesting_periods, Account};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128};
use mixnet_contract_common::{Gateway, MixNode};
use vesting_contract_common::messages::ExecuteMsg;
use vesting_contract_common::messages::{ExecuteMsg, VestingSpecification};
use vesting_contract_common::Period;
#[test]
@@ -757,4 +758,171 @@ mod tests {
.unwrap();
assert_eq!(Uint128::zero(), bonded_vesting.amount);
}
#[test]
fn delegated_free() {
let mut deps = init_contract();
let mut env = mock_env();
let vesting_period_length_secs = 3600;
let account_creation_timestamp = 1650000000;
let account_creation_blockheight = 12345;
// this value is completely arbitrary, I just wanted to keep consistent
// (and make sure that if block timestamp increases so does the block height)
let blocks_per_period = 100;
env.block.height = account_creation_blockheight;
env.block.time = Timestamp::from_seconds(account_creation_timestamp);
// lets define some helper timestamps
// our account is set to be created after 2 vesting periods already passed
let vesting_start_blockheight = account_creation_blockheight - 2 * blocks_per_period;
let vesting_start_timestamp = account_creation_timestamp - 2 * vesting_period_length_secs;
let vesting_period2_start_blockheight = vesting_start_blockheight + blocks_per_period;
let vesting_period2_start_timestamp = vesting_start_timestamp + vesting_period_length_secs;
// this vesting period is currently in progress!
let vesting_period3_start_blockheight =
vesting_period2_start_blockheight + blocks_per_period;
let vesting_period3_start_timestamp =
vesting_period2_start_timestamp + vesting_period_length_secs;
// and this one is in the future! (in relation to account creation)
let vesting_period4_start_blockheight =
vesting_period3_start_blockheight + blocks_per_period;
let vesting_period4_start_timestamp =
vesting_period3_start_timestamp + vesting_period_length_secs;
// lets create our vesting account
let periods = populate_vesting_periods(
vesting_start_timestamp,
VestingSpecification::new(None, Some(vesting_period_length_secs), None),
);
let vesting_account = Account::new(
Addr::unchecked("owner"),
Some(Addr::unchecked("staking")),
Coin {
amount: Uint128::new(1_000_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
},
Timestamp::from_seconds(account_creation_timestamp),
periods,
deps.as_mut().storage,
)
.unwrap();
// time for some delegations
let mix_identity = "alice".to_string();
let delegation = Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
};
// delegate explicitly at the time the account was created
// (i.e. after 2 vesting periods already elapsed)
env.block.height = account_creation_blockheight;
env.block.time = Timestamp::from_seconds(account_creation_timestamp);
let ok = vesting_account.try_delegate_to_mixnode(
mix_identity.clone(),
delegation.clone(),
&env,
&mut deps.storage,
);
assert!(ok.is_ok());
let vested_coins = vesting_account
.get_vested_coins(None, &env, &deps.storage)
.unwrap();
let vesting_coins = vesting_account
.get_vesting_coins(None, &env, &deps.storage)
.unwrap();
assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000));
assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000));
let delegated_free = vesting_account
.get_delegated_free(None, &env, &deps.storage)
.unwrap();
let delegated_vesting = vesting_account
.get_delegated_vesting(None, &env, &deps.storage)
.unwrap();
// all good so far
assert_eq!(delegated_free.amount, Uint128::new(90_000_000_000));
assert_eq!(delegated_vesting.amount, Uint128::zero());
// some time passes, and we're now into the next vesting period, more of our coins got unlocked!
env.block.height = vesting_period4_start_blockheight;
env.block.time = Timestamp::from_seconds(vesting_period4_start_timestamp);
let vested_coins = vesting_account
.get_vested_coins(None, &env, &deps.storage)
.unwrap();
let vesting_coins = vesting_account
.get_vesting_coins(None, &env, &deps.storage)
.unwrap();
assert_eq!(vested_coins.amount, Uint128::new(375_000_000_000));
assert_eq!(vesting_coins.amount, Uint128::new(625_000_000_000));
// and nothing about our existing delegation changed
let delegated_free = vesting_account
.get_delegated_free(None, &env, &deps.storage)
.unwrap();
let delegated_vesting = vesting_account
.get_delegated_vesting(None, &env, &deps.storage)
.unwrap();
assert_eq!(delegated_free.amount, Uint128::new(90_000_000_000));
assert_eq!(delegated_vesting.amount, Uint128::zero());
// however, create a new delegation now in this brand new vesting period
let delegation = Coin {
amount: Uint128::new(50_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
};
let ok = vesting_account.try_delegate_to_mixnode(
mix_identity.clone(),
delegation.clone(),
&env,
&mut deps.storage,
);
assert!(ok.is_ok());
// we're still good here, we have delegated in total 140M from our vested tokens!
let delegated_free = vesting_account
.get_delegated_free(None, &env, &deps.storage)
.unwrap();
let delegated_vesting = vesting_account
.get_delegated_vesting(None, &env, &deps.storage)
.unwrap();
assert_eq!(delegated_free.amount, Uint128::new(140_000_000_000));
assert_eq!(delegated_vesting.amount, Uint128::zero());
// but let's ask now a different question:
// how many vested tokens have I had delegated during vesting period3? (i.e. after account creation)
let delegated_free = vesting_account
.get_delegated_free(
Some(Timestamp::from_seconds(vesting_period3_start_timestamp)),
&env,
&deps.storage,
)
.unwrap();
let delegated_vesting = vesting_account
.get_delegated_vesting(
Some(Timestamp::from_seconds(vesting_period3_start_timestamp)),
&env,
&deps.storage,
)
.unwrap();
// returns 90M as the 50M delegation didn't exist at this point of time
assert_eq!(delegated_free.amount, Uint128::new(90_000_000_000));
// the 50M delegation wasn't a thing here for VESTING tokens either
assert_eq!(delegated_vesting.amount, Uint128::zero());
}
}
+1 -1
View File
@@ -15,6 +15,6 @@ BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090"
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
API_VALIDATOR="https://validator.nymtech.net/api/"
+1
View File
@@ -26,4 +26,5 @@ tokio = {version = "1.19.1", features = ["full"] }
mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" }
network-defaults = { path = "../common/network-defaults" }
task = { path = "../common/task" }
validator-client = { path = "../common/client-libs/validator-client", features=["nymd-client"] }
@@ -1,4 +1,5 @@
use log::info;
use task::ShutdownListener;
use crate::country_statistics::country_nodes_distribution::CountryNodesDistribution;
use crate::COUNTRY_DATA_REFRESH_INTERVAL;
@@ -7,11 +8,12 @@ use crate::state::ExplorerApiStateContext;
pub(crate) struct CountryStatisticsDistributionTask {
state: ExplorerApiStateContext,
shutdown: ShutdownListener,
}
impl CountryStatisticsDistributionTask {
pub(crate) fn new(state: ExplorerApiStateContext) -> Self {
CountryStatisticsDistributionTask { state }
pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self {
CountryStatisticsDistributionTask { state, shutdown }
}
pub(crate) fn start(mut self) {
@@ -20,10 +22,15 @@ impl CountryStatisticsDistributionTask {
let mut interval_timer = tokio::time::interval(std::time::Duration::from_secs(
COUNTRY_DATA_REFRESH_INTERVAL,
));
loop {
// wait for the next interval tick
interval_timer.tick().await;
self.calculate_nodes_per_country().await;
while !self.shutdown.is_shutdown() {
tokio::select! {
_ = interval_timer.tick() => {
self.calculate_nodes_per_country().await;
}
_ = self.shutdown.recv() => {
trace!("Listener: Received shutdown");
}
}
}
});
}
@@ -5,15 +5,17 @@ use crate::mix_nodes::location::{GeoLocation, Location};
use crate::state::ExplorerApiStateContext;
use log::{info, warn};
use reqwest::Error as ReqwestError;
use task::ShutdownListener;
use thiserror::Error;
pub(crate) struct GeoLocateTask {
state: ExplorerApiStateContext,
shutdown: ShutdownListener,
}
impl GeoLocateTask {
pub(crate) fn new(state: ExplorerApiStateContext) -> Self {
GeoLocateTask { state }
pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self {
GeoLocateTask { state, shutdown }
}
pub(crate) fn start(mut self) {
@@ -27,10 +29,15 @@ impl GeoLocateTask {
info!("Spawning mix node locator task runner...");
tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(std::time::Duration::from_millis(50));
loop {
// wait for the next interval tick
interval_timer.tick().await;
self.locate_mix_nodes().await;
while !self.shutdown.is_shutdown() {
tokio::select! {
_ = interval_timer.tick() => {
self.locate_mix_nodes().await;
}
_ = self.shutdown.recv() => {
trace!("Listener: Received shutdown");
}
}
}
});
}
+47 -12
View File
@@ -6,6 +6,7 @@ extern crate rocket_okapi;
use clap::Parser;
use log::info;
use network_defaults::setup_env;
use task::ShutdownNotifier;
pub(crate) mod cache;
mod client;
@@ -50,29 +51,63 @@ impl ExplorerApi {
let validator_api_url = self.state.inner.validator_client.api_endpoint();
info!("Using validator API - {}", validator_api_url);
let shutdown = ShutdownNotifier::default();
// spawn concurrent tasks
crate::tasks::ExplorerApiTasks::new(self.state.clone()).start();
crate::tasks::ExplorerApiTasks::new(self.state.clone(), shutdown.subscribe()).start();
country_statistics::distribution::CountryStatisticsDistributionTask::new(
self.state.clone(),
shutdown.subscribe(),
)
.start();
country_statistics::geolocate::GeoLocateTask::new(self.state.clone()).start();
country_statistics::geolocate::GeoLocateTask::new(self.state.clone(), shutdown.subscribe())
.start();
// Rocket handles shutdown on it's own, but its shutdown handling should be incorporated
// with that of the rest of the tasks.
// Currently it's runtime is forcefully terminated once the explorer-api exits.
http::start(self.state.clone());
// wait for user to press ctrl+C
self.wait_for_interrupt().await
self.wait_for_interrupt(shutdown).await
}
async fn wait_for_interrupt(&self) {
if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
async fn wait_for_interrupt(&self, mut shutdown: ShutdownNotifier) {
wait_for_signal().await;
log::info!("Sending shutdown");
shutdown.signal_shutdown().ok();
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping explorer API");
}
}
#[cfg(unix)]
async fn wait_for_signal() {
use tokio::signal::unix::{signal, SignalKind};
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel");
let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel");
tokio::select! {
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
},
_ = sigterm.recv() => {
log::info!("Received SIGTERM");
}
info!(
"Received SIGINT - the mixnode will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
);
_ = sigquit.recv() => {
log::info!("Received SIGQUIT");
}
}
}
#[cfg(not(unix))]
async fn wait_for_signal() {
tokio::select! {
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
},
}
}
+21 -15
View File
@@ -4,6 +4,7 @@
use std::future::Future;
use mixnet_contract_common::GatewayBond;
use task::ShutdownListener;
use validator_client::models::MixNodeBondAnnotated;
use validator_client::nymd::error::NymdError;
use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse};
@@ -14,11 +15,12 @@ use crate::state::ExplorerApiStateContext;
pub(crate) struct ExplorerApiTasks {
state: ExplorerApiStateContext,
shutdown: ShutdownListener,
}
impl ExplorerApiTasks {
pub(crate) fn new(state: ExplorerApiStateContext) -> Self {
ExplorerApiTasks { state }
pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self {
ExplorerApiTasks { state, shutdown }
}
// a helper to remove duplicate code when grabbing active/rewarded/all mixnodes
@@ -128,24 +130,28 @@ impl ExplorerApiTasks {
}
}
pub(crate) fn start(self) {
pub(crate) fn start(mut self) {
info!("Spawning mix nodes task runner...");
tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(CACHE_REFRESH_RATE);
loop {
// wait for the next interval tick
interval_timer.tick().await;
while !self.shutdown.is_shutdown() {
tokio::select! {
_ = interval_timer.tick() => {
info!("Updating validator cache...");
self.update_validators_cache().await;
info!("Done");
info!("Updating validator cache...");
self.update_validators_cache().await;
info!("Done");
info!("Updating gateway cache...");
self.update_gateways_cache().await;
info!("Done");
info!("Updating gateway cache...");
self.update_gateways_cache().await;
info!("Done");
info!("Updating mix node cache...");
self.update_mixnode_cache().await;
info!("Updating mix node cache...");
self.update_mixnode_cache().await;
}
_ = self.shutdown.recv() => {
trace!("Listener: Received shutdown");
}
}
}
});
}
+6 -5
View File
@@ -1,5 +1,6 @@
EXPLORER_API_URL=https://sandbox-explorer.nymtech.net/api/v1
VALIDATOR_API_URL=https://sandbox-validator.nymtech.net
BIG_DIPPER_URL=https://sandbox-blocks.nymtech.net
CURRENCY_DENOM=unymt
CURRENCY_STAKING_DENOM=unyxt
EXPLORER_API_URL=https://explorer.nymtech.net/api/v1
VALIDATOR_API_URL=https://validator.nymtech.net
VALIDATOR_URL=https://rpc.nyx.nodes.guru
BIG_DIPPER_URL=https://blocks.nymtech.net
CURRENCY_DENOM=unym
CURRENCY_STAKING_DENOM=unyx
+4 -3
View File
@@ -1,5 +1,6 @@
EXPLORER_API_URL=https://qa-explorer.nymtech.net/api/v1
VALIDATOR_API_URL=https://qa-validator.nymtech.net
VALIDATOR_API_URL=https://qa-validator-api.nymtech.net
VALIDATOR_URL=https://qa-validator.nymtech.net
BIG_DIPPER_URL=https://qa-blocks.nymtech.net
CURRENCY_DENOM=unymt
CURRENCY_STAKING_DENOM=unyxt
CURRENCY_DENOM=unym
CURRENCY_STAKING_DENOM=unyx
+2 -1
View File
@@ -1,6 +1,7 @@
// master APIs
export const API_BASE_URL = process.env.EXPLORER_API_URL;
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
export const VALIDATOR_URL = process.env.VALIDATOR_URL;
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
// specific API routes
@@ -9,7 +10,7 @@ export const MIXNODE_PING = `${API_BASE_URL}/ping`;
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
export const VALIDATORS_API = `${VALIDATOR_URL}/validators`;
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
+19 -10
View File
@@ -6,7 +6,6 @@ import {
DialogContent,
DialogActions,
DialogTitle,
IconButton,
Slider,
Typography,
Box,
@@ -25,7 +24,9 @@ import { useIsMobile } from '../../hooks/useIsMobile';
const FilterItem = ({
label,
id,
tooltipInfo,
value,
isSmooth,
marks,
scale,
min,
@@ -36,12 +37,13 @@ const FilterItem = ({
}) => (
<Box sx={{ p: 2 }}>
<Typography gutterBottom>{label}</Typography>
<Typography fontSize={12}>{tooltipInfo}</Typography>
<Slider
value={value}
onChange={(e: Event, newValue: number | number[]) => onChange(id, newValue as number[])}
valueLabelDisplay="off"
valueLabelDisplay={isSmooth ? 'auto' : 'off'}
marks={marks}
step={null}
step={isSmooth ? 1 : null}
scale={scale}
min={min}
max={max}
@@ -50,7 +52,7 @@ const FilterItem = ({
);
export const Filters = () => {
const { filterMixnodes, fetchMixnodes } = useMainContext();
const { filterMixnodes, fetchMixnodes, mixnodes } = useMainContext();
const { status } = useParams<{ status: MixnodeStatusWithAll | undefined }>();
const isMobile = useIsMobile();
@@ -127,19 +129,26 @@ export const Filters = () => {
<Alert
severity="info"
variant={isMobile ? 'standard' : 'outlined'}
sx={{ color: (t) => t.palette.info.light }}
action={
<Button size="small" onClick={onClearFilters}>
Clear
CLEAR FILTERS
</Button>
}
sx={{ width: 300 }}
>
Filters applied
{mixnodes?.data?.length} mixnodes matched your criteria
</Alert>
</Snackbar>
<IconButton size="large" onClick={handleToggleShowFilters}>
<Tune />
</IconButton>
<Button
size="large"
variant="text"
color="inherit"
endIcon={<Tune />}
onClick={handleToggleShowFilters}
sx={{ textTransform: 'none' }}
>
Advanced filters
</Button>
<Dialog open={showFilters} onClose={handleToggleShowFilters} maxWidth="md" fullWidth>
<DialogTitle>Mixnode filters</DialogTitle>
<DialogContent dividers>
+21 -54
View File
@@ -5,6 +5,7 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
label: 'Profit margin (%)',
id: EnumFilterKey.profitMargin,
value: [0, 100],
isSmooth: true,
marks: [
{ label: '0', value: 0 },
{ label: '10', value: 10 },
@@ -18,6 +19,8 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
{ label: '90', value: 90 },
{ label: '100', value: 100 },
],
tooltipInfo:
'As a delegator you want to chose nodes with lower profit margin, meaning more payout for their delegators',
},
stakeSaturation: {
label: 'Stake saturation (%)',
@@ -43,65 +46,29 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
},
],
max: upperSaturationValue,
tooltipInfo: "Select nodes with <100% saturation. Any additional stake above 100% saturation won't get rewards",
},
stake: {
label: 'Stake (NYM)',
id: EnumFilterKey.stake,
value: [20, 90],
min: 20,
max: 90,
routingScore: {
label: 'Routing score (%)',
id: EnumFilterKey.routingScore,
value: [0, 100],
marks: [
{
value: 0,
label: '1',
},
{
value: 10,
label: '10',
},
{
value: 20,
label: '100',
},
{
value: 30,
label: '1k',
},
{
value: 40,
label: '10k',
},
{
value: 50,
label: '100k',
},
{
value: 60,
label: '1M',
},
{
value: 70,
label: '10M',
},
{
value: 80,
label: '100M',
},
{
value: 90,
label: '1B',
},
{ label: '0', value: 0 },
{ label: '10', value: 10 },
{ label: '20', value: 20 },
{ label: '30', value: 30 },
{ label: '40', value: 40 },
{ label: '50', value: 50 },
{ label: '60', value: 60 },
{ label: '70', value: 70 },
{ label: '80', value: 80 },
{ label: '90', value: 90 },
{ label: '100', value: 100 },
],
tooltipInfo: 'The higher the routing score the better the performance of the node and so its rewards',
},
});
const formatStakeValuesToMinorDenom = ([value_1, value_2]: number[]) => {
const lowerValue = 10 ** (value_1 / 10) * 1_000_000;
const upperValue = 10 ** (value_2 / 10) * 1_000_000;
return [lowerValue, upperValue];
};
const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
const lowerValue = value_1 / 100;
const upperValue = value_2 / 100;
@@ -110,7 +77,7 @@ const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
};
export const formatOnSave = (filters: TFilters) => ({
stake: formatStakeValuesToMinorDenom(filters.stake.value),
routingScore: filters.routingScore.value,
profitMargin: filters.profitMargin.value,
stakeSaturation: formatStakeSaturationValues(filters.stakeSaturation.value),
});
+2 -2
View File
@@ -102,8 +102,8 @@ export const MainContextProvider: React.FC = ({ children }) => {
m.mix_node.profit_margin_percent <= filters.profitMargin[1] &&
m.stake_saturation >= filters.stakeSaturation[0] &&
m.stake_saturation <= filters.stakeSaturation[1] &&
+m.pledge_amount.amount + +m.total_delegation.amount >= filters.stake[0] &&
+m.pledge_amount.amount + +m.total_delegation.amount <= filters.stake[1],
m.avg_uptime >= filters.routingScore[0] &&
m.avg_uptime <= filters.routingScore[1],
);
setMixnodes({ data: filtered, isLoading: false });
};
+3 -1
View File
@@ -4,17 +4,19 @@ import { Mark } from '@mui/base';
export enum EnumFilterKey {
profitMargin = 'profitMargin',
stakeSaturation = 'stakeSaturation',
stake = 'stake',
routingScore = 'routingScore',
}
export type TFilterItem = {
label: string;
id: EnumFilterKey;
value: number[];
isSmooth?: boolean;
marks: Mark[];
min?: number;
max?: number;
scale?: (value: number) => number;
tooltipInfo?: string;
};
export type TFilters = { [key in EnumFilterKey]: TFilterItem };
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "1.0.1"
version = "1.0.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Mixnet Gateway"
edition = "2021"
+3 -3
View File
@@ -52,7 +52,7 @@ pub struct Init {
mnemonic: Option<String>,
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
enabled_credentials_mode: Option<bool>,
@@ -83,7 +83,7 @@ impl From<Init> for OverrideConfig {
validators: init_config.validators,
mnemonic: init_config.mnemonic,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: init_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -177,7 +177,7 @@ mod tests {
mnemonic: None,
statistics_service_url: None,
enabled_statistics: None,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: None,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
eth_endpoint: "".to_string(),
+9 -23
View File
@@ -6,11 +6,11 @@ use std::{process, str::FromStr};
use crate::{config::Config, Cli};
use clap::Subcommand;
use colored::Colorize;
use config::parse_validators;
use crypto::bech32_address_validation;
use network_defaults::var_names::{
API_VALIDATOR, BECH32_PREFIX, CONFIGURED, NYMD_VALIDATOR, STATISTICS_SERVICE_DOMAIN_ADDRESS,
};
use url::Url;
pub(crate) mod init;
pub(crate) mod node_details;
@@ -52,7 +52,7 @@ pub(crate) struct OverrideConfig {
validators: Option<String>,
mnemonic: Option<String>,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: Option<bool>,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -69,17 +69,6 @@ pub(crate) async fn execute(args: Cli) {
}
}
fn parse_validators(raw: &str) -> Vec<Url> {
raw.split(',')
.map(|raw_validator| {
raw_validator
.trim()
.parse()
.expect("one of the provided validator api urls is invalid")
})
.collect()
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
let mut was_host_overridden = false;
if let Some(host) = args.host {
@@ -129,8 +118,8 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
config = config.with_custom_validator_apis(parse_validators(&raw_validators))
}
if let Some(raw_validators) = args.validators {
config = config.with_custom_validator_nymd(parse_validators(&raw_validators));
if let Some(ref raw_validators) = args.validators {
config = config.with_custom_validator_nymd(parse_validators(raw_validators));
} else if std::env::var(CONFIGURED).is_ok() {
let raw_validators = std::env::var(NYMD_VALIDATOR).expect("nymd validator not set");
config = config.with_custom_validator_nymd(parse_validators(&raw_validators))
@@ -157,18 +146,15 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
config = config.with_eth_endpoint(String::from(DEFAULT_ETH_ENDPOINT));
}
// We set the disabled credentials mode flag if we either compile without 'eth', or if there is a flag we
// can read from, which is when we build with 'eth' (and without 'coconut').
if cfg!(not(feature = "eth")) {
config = config.with_disabled_credentials_mode(true);
#[cfg(any(feature = "eth", feature = "coconut"))]
{
if let Some(enabled_credentials_mode) = args.enabled_credentials_mode {
config = config.with_disabled_credentials_mode(!enabled_credentials_mode);
}
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if let Some(enabled_credentials_mode) = args.enabled_credentials_mode {
config = config.with_disabled_credentials_mode(enabled_credentials_mode);
}
if let Some(raw_validators) = args.validators {
config = config.with_custom_validator_nymd(parse_validators(&raw_validators));
}
+2 -2
View File
@@ -52,7 +52,7 @@ pub struct Run {
mnemonic: Option<String>,
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
enabled_credentials_mode: Option<bool>,
@@ -83,7 +83,7 @@ impl From<Run> for OverrideConfig {
validators: run_config.validators,
mnemonic: run_config.mnemonic,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[cfg(any(feature = "eth", feature = "coconut"))]
enabled_credentials_mode: run_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
+5
View File
@@ -66,6 +66,10 @@ impl NymConfig for Config {
.join("gateways")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("gateways"))
}
fn root_directory(&self) -> PathBuf {
self.gateway.nym_root_directory.clone()
}
@@ -123,6 +127,7 @@ impl Config {
self
}
#[cfg(any(feature = "eth", feature = "coconut"))]
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
self.gateway.disabled_credentials_mode = disabled_credentials_mode;
self
+1
View File
@@ -336,6 +336,7 @@ where
if self.config.get_enabled_statistics() {
let statistics_service_url = self.config.get_statistics_service_url();
let stats_collector = GatewayStatisticsCollector::new(
self.identity_keypair.public_key().to_base58_string(),
active_clients_store.clone(),
statistics_service_url,
);
+11 -2
View File
@@ -14,13 +14,19 @@ use statistics_common::{
use crate::node::client_handling::active_clients::ActiveClientsStore;
pub(crate) struct GatewayStatisticsCollector {
gateway_id: String,
active_clients_store: ActiveClientsStore,
statistics_service_url: Url,
}
impl GatewayStatisticsCollector {
pub fn new(active_clients_store: ActiveClientsStore, statistics_service_url: Url) -> Self {
pub fn new(
gateway_id: String,
active_clients_store: ActiveClientsStore,
statistics_service_url: Url,
) -> Self {
GatewayStatisticsCollector {
gateway_id,
active_clients_store,
statistics_service_url,
}
@@ -35,7 +41,10 @@ impl StatisticsCollector for GatewayStatisticsCollector {
timestamp: DateTime<Utc>,
) -> StatsMessage {
let inbox_count = self.active_clients_store.size() as u32;
let stats_data = vec![StatsData::Gateway(StatsGatewayData { inbox_count })];
let stats_data = vec![StatsData::Gateway(StatsGatewayData::new(
self.gateway_id.clone(),
inbox_count,
))];
StatsMessage {
stats_data,
interval_seconds: interval.as_secs() as u32,
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.0.1"
version = "1.0.2"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+4 -13
View File
@@ -6,9 +6,11 @@ use std::process;
use crate::{config::Config, Cli};
use clap::Subcommand;
use colored::Colorize;
use config::defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED};
use config::{
defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED},
parse_validators,
};
use crypto::bech32_address_validation;
use url::Url;
mod describe;
mod init;
@@ -61,17 +63,6 @@ pub(crate) async fn execute(args: Cli) {
}
}
fn parse_validators(raw: &str) -> Vec<Url> {
raw.split(',')
.map(|raw_validator| {
raw_validator
.trim()
.parse()
.expect("one of the provided validator api urls is invalid")
})
.collect()
}
fn override_config(mut config: Config, args: OverrideConfig) -> Config {
let mut was_host_overridden = false;
if let Some(host) = args.host {
+4
View File
@@ -96,6 +96,10 @@ impl NymConfig for Config {
.join("mixnodes")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("mixnodes"))
}
fn root_directory(&self) -> PathBuf {
self.mixnode.nym_root_directory.clone()
}
+1 -1
View File
@@ -24,7 +24,7 @@ fn long_version_static() -> &'static str {
#[derive(Parser)]
#[clap(author = "Nymtech", version, about, long_version = long_version_static())]
struct Cli {
/// Path pointing to an env file that configures the gateway.
/// Path pointing to an env file that configures the mixnode.
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,

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