Compare commits

...

105 Commits

Author SHA1 Message Date
Jędrzej Stuczyński ca6cea4acf Added #[serde(default)] on fields we dont care about anymore 2022-01-26 11:10:57 +00:00
Tommy Verrall 2da6fdc2e8 Merge pull request #1067 from nymtech/feature/wallet-inclusion-probability
set-up inclusion probability
2022-01-25 17:05:01 +00:00
Jędrzej Stuczyński f7574924a8 Mixnet Contract constants extraction (#1060)
* Extracted constants that could realistically be controlled by governance to constants.rs

Also made interval control be more explicit in the contract

* Extracted active set work factor to a constant

* Required type changes in wallet code

* [ci skip] Generate TS types

* Missing change in test code

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2022-01-25 16:46:46 +00:00
fmtabbara 5d07115706 refinements and small bug fix 2022-01-25 13:58:15 +00:00
fmtabbara 9e994dfd55 set-up inclusion probability 2022-01-25 11:51:36 +00:00
Jon Häggblad 59bc7cb53d Upgrade Clap and use declarative argument parsing for nym-mixnode (#1047)
* mixnode: upgrade clap and use declarative cli parsing

* mixnode: map argument to enum for sign

* mixnode: address review comments
2022-01-25 12:51:26 +01:00
Jędrzej Stuczyński 655ff9bffb De-'float'-ing Interval (Display impl + serde) (#1065)
* Updated time to 0.3.6

* Changed Display impl for Interval so it doesnt use floats

* Explicit rfc3339 datetime serialization

* Typo

* Changed 'visit_borrwed_str' to 'visit_str'
2022-01-25 11:17:44 +00:00
Bogdan-Ștefan Neacşu a03cb1ef9f Feature/wasm client (#1066)
* Fix wasm client

* Re-enable CI on wasm client

There is an `unused-unit` lint that will fail for now, but this is
regarded as a false positive and should eventualy get fixed:

https://github.com/rustwasm/wasm-bindgen/issues/2774

* The wasm tests would be run under native arch
2022-01-24 18:48:44 +02:00
Jędrzej Stuczyński 60f965ec52 Made contract addresses for query NymdClient construction optional (#1055)
Similarly as it is the case when creating SigningNymdClient
2022-01-24 15:43:20 +00:00
Jędrzej Stuczyński 8d26e48a5b Introduced RPC query for total token supply (#1053)
* Introduced RPC query for total token supply

* Cargo fmt
2022-01-24 15:43:10 +00:00
Jędrzej Stuczyński 94527ab594 Changed bech32_prefix from punk to nymt (#1064) 2022-01-24 12:40:36 +00:00
Tommy Verrall e312a28aad Merge pull request #1059 from martinyung/develop
fix: make explorer footer year dynamic
2022-01-24 11:39:42 +00:00
dependabot[bot] e84a0c4438 Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet (#1062)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.30 to 3.2.0.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.30...3.2.0)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-24 10:18:11 +00:00
dependabot[bot] 6f1a0d987d Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet (#1063)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.30 to 3.2.0.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.30...3.2.0)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-24 10:18:02 +00:00
Yung Chun Ern Martin 3caa4c15ca fix: make explorer footer year dynamic 2022-01-22 00:11:13 +08:00
Tommy Verrall 741131f376 Merge pull request #1058 from nymtech/bug-fix/display-client-address
display client address on wallet creation
2022-01-21 15:54:59 +00:00
fmtabbara ae6f161cd6 display client address on wallet creation 2022-01-21 15:49:01 +00:00
Tommy Verrall b940c87d64 Merge pull request #1057 from nymtech/feature/fix_wallet_mnemonic
Add mnemonic just on creation, to display it
2022-01-21 15:41:28 +00:00
Drazen Urch fe6c685ab1 Feature/hourly set updates (#1012)
* Rename function/variables mixnodes->set

* Stub utility interface

* Rewarded set contract interface

* Move epoch to common, epoch to contract

* Move epoch to the chain

* Rewarded set validator-api

* [ci skip] Generate TS types

* Epoch queries

* Moved new code to a new module

* Restored cosmwasm dependencies to their beta.3 versions for better compatibility with the rest of the codebase

* Rewarded set write reorganisation

* Stub for validator api module  responsible for rewarded set updates

* Reorganised validator api cache

* Pending contract changes

* Relevant updates to the validator client

* Updating rewarded set based on contract state

* Advancing/Setting current epoch in the contract

* Using blocktime as 'now' at startup

* Adjusted validator-api side rewarding code

* Contract cleanup + query for epoch rewarded set heights

* [ci skip] Generate TS types

* Simplified rewarder processing loop and initial sync

* [ci skip] Generate TS types

* Fixed EXISTING query-related unit tests

* Fixed existing unit tests for rewarding-related transactions

* Cargo fmt

* Removed some dead code

* Using cosmwasm 1.0.0-beta3 for compatibility [with cw-storage-plus and rest of codebase]

* Missing TryInto import

* Additional storage and query related unit tests + a bug fix

* Transaction-related unit tests + bug fixes

* Required migration code

* Update common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs

Co-authored-by: Drazen Urch <drazen@urch.eu>

* Update common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs

Co-authored-by: Drazen Urch <drazen@urch.eu>

* Constant renaming

* Changed determining previous epoch return type to Option<Epoch> if they would precede the genesis

* Exposed the new endpoint to the wallet

* Epoch-related unit tests fixes

* Recommended #[must_use] on next_epoch method

* Renamed all epoch occurences to interval

As they refer to the 'rewarding interval'

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2022-01-21 14:04:23 +00:00
Bogdan-Ștefan Neacșu c64c36022f Add mnemonic just on creation, to display it 2022-01-21 14:57:42 +02:00
Mark Sinclair e52fe65985 Network Explorer: updates to API and UI to show the active set (#1056)
* Add identicons package

* Tidy up styling and move methods into component directories with better naming

* Add mixnode status colours to theme

* Mixnode status and icon components

* Add status to mixnode types

* Add API method to get mixnode details

* Add mixnode details to state

* Add status and name+description section to mixnode detail page

* Wrap with div instead of p

* Limit width of description and link to new tab

* Limit length of link button and truncate with elipsis

* Replace `filter` with `find`

* Move mix node detail components to a location that is better named

* Refactor mixnode detail state and separate into an independent context from main state.

This prevents the mixnode detail page from showing stale data when switching between mix nodes.

* Tidy up mixnode detail page adding new state provider and a guard component to handle loading, error and not found states

* Layout changes to mixnode description header section

* Add methods to Explorer API client to get a mixnode by id, active set by status and overview summary

* Add color prop to StatsCard and make count optional

* Add optional start and end children to TableToolbar

* Tidy up naming

* Add summary overview and getting mixnodes by active set status to main state

* Add mix node status overview cards

* Add mix node status to routes

* Mixnode list has a dropdown component to select the active set status

* Clean up caching code

* Add resource to get a single mixnode by id

* Add API resources to get `active`, `inactive` and `standby` mixnodes

* Add mixnode summary to API

* Add overview summary endpoint to API

* Fix OpenAPI/swagger base url

* Make clippy happy

* Add method to get validators

* Add methods to get active and rewarded mixnodes

* Fix naming

* Move client creation to crate root

* Move cache to module

* Delete unused files

* Add validators API resource

* Add gateways API resource

* Move tasks to crate root

* Add new HTTP resources for validators and gateways to routes

* Tidy up naming and locations for mixnodes

* Add validator and gateways to state, and tidy up naming

* Add gateways and validator modules to main

* Overview shows validator and gateway summaries from state

* Bundle variable weight Open Sans fonts

* Fix up font weights and sizes

* Fix up typing

* Fix up social icons

* Fix navbar colour

* Fix paper colour in dark mode and border radius

* Fix up stats card

* Tidy up Nym icons

* Fix up overview

* Fix up spacing and padding for overview

* Add light mode shades that are darker for mixnode status values

* Review feedback
2022-01-21 11:28:59 +00:00
Tommy Verrall fea64d4d4f Merge pull request #1052 from nymtech/feature/tokio-console
Feature/tokio console
2022-01-20 12:23:29 +00:00
durch 6ff02bc2a1 Fix wallet clippy lints 2022-01-20 11:31:35 +01:00
durch 8b166f12f8 Instrument tokio console 2022-01-20 11:17:33 +01:00
Jędrzej Stuczyński ecdbe034fa Implemented beta clippy lint recommendations (#1051) 2022-01-19 20:32:48 +01:00
Tommy Verrall 3e46c8630d Merge pull request #1050 from nymtech/update/validator-client-profit-percentage
add new function to update profit percentage
2022-01-19 16:18:47 +00:00
fmtabbara 93e962524a update types 2022-01-19 14:05:43 +00:00
dependabot[bot] 5b6c1c032c Bump shelljs in /contracts/basic-bandwidth-generation (#1043)
Bumps [shelljs](https://github.com/shelljs/shelljs) from 0.8.4 to 0.8.5.
- [Release notes](https://github.com/shelljs/shelljs/releases)
- [Changelog](https://github.com/shelljs/shelljs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/shelljs/shelljs/compare/v0.8.4...v0.8.5)

---
updated-dependencies:
- dependency-name: shelljs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-19 14:00:19 +00:00
dependabot[bot] 135f1a6e7f Bump follow-redirects in /contracts/basic-bandwidth-generation (#1041)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.4 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.4...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-19 14:00:12 +00:00
dependabot[bot] c61f89144e Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet (#1040)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.5 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.5...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-19 14:00:06 +00:00
Tommy Verrall 67fe368e65 Merge pull request #1033 from nymtech/feature/configurable_wallet
Feature/configurable wallet
2022-01-19 10:36:17 +00:00
Bogdan-Ștefan Neacşu 522229459b Fix clippy on relevant lints (#1044)
* Fix clippy on relevant lints

return_self_not_must_use still produces errors, but that will be
auto-fixed once the change to move it to pedantic is released to beta
channel

* Run fmt
2022-01-18 21:37:51 +01:00
Drazen Urch e3d8b71ea2 Endpoint for rewarded set inclusion probabilities (#1042)
* Add validator-api endpoint

* Add validator-client method

* Make it a bit nicer

* Address review comments

* Cap probability at 1.
2022-01-18 21:26:15 +01:00
Fouad 4f98fde362 Merge pull request #1046 from nymtech/feature/additional-bond-validation
Feature/additional bond validation
2022-01-18 12:57:14 +00:00
fmtabbara aa75e54419 dont display warnings on successful bond or delegate 2022-01-18 11:45:27 +00:00
fmtabbara 5190a541a6 add warning to delegate page 2022-01-18 11:30:56 +00:00
fmtabbara 3a39fff006 PR updates 2022-01-18 10:16:36 +00:00
fmtabbara 0e302b83ab add warning for unbonding 2022-01-17 18:42:13 +00:00
Fouad 0d0637fe19 Merge pull request #1036 from nymtech/feature/node-settings-update
Feature/node settings update
2022-01-17 18:16:24 +00:00
fmtabbara 1f0c0090dc merge develop 2022-01-17 17:53:14 +00:00
fmtabbara 4f960330b1 Merge branch 'develop' into feature/additional-bond-validation
merge develop
2022-01-17 17:36:41 +00:00
fmtabbara a273980aa0 refactor 2022-01-17 17:34:39 +00:00
neacsu 13a55f00d8 [ci skip] Generate TS types 2022-01-17 15:50:06 +00:00
Bogdan-Ștefan Neacșu 4feec780d4 Use fn new() for Account 2022-01-17 17:39:21 +02:00
Bogdan-Ștefan Neacșu 35c044c340 Store all clients and discard mnemonic 2022-01-17 17:24:52 +02:00
Bogdan-Ștefan Neacșu ac5539a0fa Export ts type file 2022-01-17 14:44:43 +02:00
Bogdan-Ștefan Neacșu 9c569cbb62 Expose switch_network to frontend 2022-01-17 14:13:15 +02:00
Bogdan-Ștefan Neacșu 72485cacd3 Pass possible network values using Network type
Export a Network type to TS and make seamless transitions between this
type and the network defaults one. We may have more networks supported
in the backend then in the frontend at a certain moment in time.
2022-01-17 13:38:10 +02:00
Drazen Urch 56d36d2c46 Migrate to cw-storage-plus 0.11.1 (#1035) 2022-01-14 20:59:40 +01:00
Drazen Urch 8fb54dd4e7 Feature/downcast reward estimation (#1031)
* Downcast u128 to u64

* fmt

* Fix status

* fmt
2022-01-14 20:57:51 +01:00
dependabot[bot] 44d59ff8c2 Bump @openzeppelin/contracts in /contracts/basic-bandwidth-generation (#1034)
Bumps [@openzeppelin/contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) from 4.4.1 to 4.4.2.
- [Release notes](https://github.com/OpenZeppelin/openzeppelin-contracts/releases)
- [Changelog](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md)
- [Commits](https://github.com/OpenZeppelin/openzeppelin-contracts/compare/v4.4.1...v4.4.2)

---
updated-dependencies:
- dependency-name: "@openzeppelin/contracts"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-14 15:50:57 +00:00
fmtabbara a8caf19f8c additional balance check before bonding 2022-01-14 14:32:16 +00:00
fmtabbara 53b44fb9c6 remove unused import 2022-01-13 17:30:11 +00:00
fmtabbara c0959e853e spacing updates 2022-01-13 17:07:18 +00:00
fmtabbara 144df00782 fix conflicts and refine UI 2022-01-13 16:40:59 +00:00
Fouad be4708cc84 Merge pull request #1028 from nymtech/feature/ui-updates
Wallet UI updates
2022-01-13 15:45:36 +00:00
fmtabbara d5cddec03c merge develop 2022-01-13 15:36:06 +00:00
fmtabbara 7c26cab4e6 get updated mixnode details after bonding/unbonding txs 2022-01-13 15:32:29 +00:00
Bogdan-Ștefan Neacșu f0bcf8c36f Update wallet backend 2022-01-13 17:20:17 +02:00
Bogdan-Ștefan Neacșu ac2f0a172e Store multiple network values in one place 2022-01-13 17:20:17 +02:00
Bogdan-Ștefan Neacșu 898070bc87 Rename some defaults 2022-01-13 17:20:17 +02:00
Bogdan-Ștefan Neacșu cc707660aa Expose both default and specific network values 2022-01-13 17:20:17 +02:00
fmtabbara 31624cf4e4 fix Rust u128 -> TS BiInt issue 2022-01-13 11:22:04 +00:00
fmtabbara e6a69170a4 raise mixnodedetails to main context 2022-01-13 11:22:04 +00:00
Bogdan-Ștefan Neacşu bc5e19514e Remove migration code (#1027) 2022-01-13 11:59:56 +01:00
fmtabbara 5c76b8483e add new function to update profit percentage 2022-01-13 10:10:42 +00:00
fmtabbara a9526c216f [ci skip] Generate TS types 2022-01-12 21:49:08 +00:00
fmtabbara 903af16a6b update settings state 2022-01-12 21:39:12 +00:00
fmtabbara 0de79b6953 create fee component 2022-01-12 21:38:06 +00:00
fmtabbara fd2fafb52a [ci skip] Generate TS types 2022-01-12 20:39:36 +00:00
fmtabbara fadb5b4ff9 update ui and state 2022-01-12 20:30:56 +00:00
fmtabbara 955583d0f0 merge develop 2022-01-12 16:04:48 +00:00
Fouad 3924c53d09 Merge pull request #1014 from nymtech/feature/update-profit-percentage
update frontend to use new profit update api
2022-01-12 15:33:28 +00:00
fmtabbara c0025ee9c6 update getfee function 2022-01-12 15:30:47 +00:00
fmtabbara 7dd0516b63 only request when settings open 2022-01-12 14:07:46 +00:00
fmtabbara d3cd3c9157 Merge branch 'develop' into feature/update-profit-percentage
merge develop
2022-01-12 12:49:59 +00:00
fmtabbara 83680473e0 Merge branch 'develop' into feature/additional-node-details
merge develop
2022-01-12 11:38:08 +00:00
fmtabbara 7f9a9f7a0a update node icon 2022-01-12 11:03:48 +00:00
fmtabbara e7ccb38502 use fullwidth input 2022-01-12 10:13:44 +00:00
Bogdan-Ștefan Neacşu 1f4c19d396 Add network defaults for qa (#1017)
* Add network defaults for qa

* update contract addresses

- have not updated the bandwith credential address - currently vesting

Co-authored-by: Tommy Verrall <tommyvez@protonmail.com>
2022-01-12 09:56:20 +01:00
fmtabbara 64842f40d7 integrate with new api 2022-01-11 19:41:16 +00:00
fmtabbara 2ec18721fc Merge branch 'develop' into feature/ui-updates
merge develop
2022-01-11 18:14:11 +00:00
fmtabbara 2ef1ac452f fix bug - send from address not showing 2022-01-11 18:11:46 +00:00
Jędrzej Stuczyński 6b3700aefe Feature/expanded events (#1015)
* Expanded emitted events for delegation-related transactions

* Expanded emitted events for gateway-related transactions

* Expanded emitted events for mixnode-related transactions

* Expanded emitted events for settings update transaction

* Expanded emitted events for rewarding-related transactions

* Fixed attribute look up in tests

* Making linter happier

* Reorganised cosmwasm contract-related modules

* Introduced similar event handling to the vesting contract
2022-01-11 16:56:12 +00:00
Jędrzej Stuczyński e2e06df4e6 Feature/validator api client endpoints (#1024)
* Moved mixnode status route to node status api module

* Introduced validator-api endpoint for estimating mixnode's reward

* Stake saturation endpoint

* kebab-cased coconut routes

* Created separate crate for validator API models

* Additional routes in validator API client

* Introduced support for new queries in the wallet

* Typescript type derivation

* Fixed up date in license notice
2022-01-11 16:37:07 +00:00
Jędrzej Stuczyński 835d4f46f6 Introduced denom check when trying to withdraw vested coins (#1018)
* Introduced denom check when trying to withdraw vested coins

* Using correct denom in the relevant unit test
2022-01-11 16:31:39 +00:00
Jędrzej Stuczyński d71ef635e2 Restricted blake3 dependency (#1025) 2022-01-11 16:22:55 +00:00
Bogdan-Ștefan Neacşu 6e3773a095 Feature/remove unused profit margin (#1011)
* Remove unused field, to avoid confusion

* Add migration code

* Update tests

* Make clippy happy
2022-01-11 16:05:15 +01:00
Jędrzej Stuczyński 050d370396 Updated cosmrs to 0.4.1 (#1023) 2022-01-11 11:26:34 +00:00
Jędrzej Stuczyński 29340ed00c Feature/additional mixnode endpoints (#1019)
* Moved mixnode status route to node status api module

* Introduced validator-api endpoint for estimating mixnode's reward

* Stake saturation endpoint
2022-01-11 09:38:39 +00:00
fmtabbara 2b062b3e5b add link to network explorer 2022-01-10 23:00:17 +00:00
fmtabbara b405adb9e5 add icons to card headers and balance padding 2022-01-10 22:36:12 +00:00
fmtabbara 5c3c0ac39e remove border lines and grey bg for card component 2022-01-10 21:17:47 +00:00
fmtabbara 1cc06ef349 update get_approx_fee to new function name _outdated_get_approx_fee 2022-01-10 17:31:43 +00:00
fmtabbara 2bef1603ab style updates 2022-01-10 17:27:01 +00:00
Mx 11a458a43d Merge pull request #1020 from RiccardoMasutti/patch-1
Fix 404 link
2022-01-10 18:00:58 +01:00
Jędrzej Stuczyński 1fbf37e0ec Changed wallet's client to a full validator client (#1021)
So that it could use validator API calls in the near future
2022-01-10 11:17:08 +00:00
Riccardo Masutti bc8efda08f Add wallet build instructions
Added wallet link
2022-01-09 16:00:17 +01:00
Riccardo Masutti cecd0b2b0a Fix 404 link 2022-01-09 15:56:45 +01:00
Jędrzej Stuczyński 62fa2ae5e4 Feature/node state endpoint (#1013)
* Introduced route to check mixnode's status (active/standby/inactive/not_found)

* Restored default validator API caching interval to a more sane value

* Changed status route
2022-01-07 11:57:38 +00:00
fmtabbara db2ce8070c display mixnode update fee 2022-01-06 16:16:32 +00:00
fmtabbara 70138ff54a update frontend to use new profit update api 2022-01-06 13:00:50 +00:00
Bogdan-Ștefan Neacşu 30e93c33bb Feature/configure profit (#1008)
* Introduce a method to update mixnode configuration

Right now, only for profit_margin_percent

* Check that the new profit margin is valid

* Extend a bit the test coverage of mixnode update

* Create validator client function

* [ci skip] Generate TS types

* Update wallet

* Update the bond height as well, as if a rebond was made

Co-authored-by: neacsu <neacsu@users.noreply.github.com>
2022-01-06 13:03:14 +01:00
Jędrzej Stuczyński ec4955f814 Feature/explorer node status (#1010)
* Restored mixnode refresh rate to a more sane value

* Moved PrettyMixNodeBondWithLocation to models.rs

* Renaming

* Exposed ability to query for rewarded mixnodes in the validator client

* Reorganised mix_nodes module

* Determining mixnode status (active/standby/inactive)

* Moved LocationCache to separate lock

* Minor cleanup

* Changed serialization case of status enum

* Made clippy happier

* Slightly better grammar
2022-01-06 10:38:14 +00:00
Drazen Urch e013517348 Use serial integer instead of random (#1009)
* Use serial integer instead of random

* [ci skip] Generate TS types

* cargo fmt

* Return u32
2022-01-05 15:28:17 +01:00
301 changed files with 9980 additions and 5133 deletions
+12 -15
View File
@@ -19,30 +19,27 @@ jobs:
override: true
components: rustfmt, clippy
# token credentials (non-coconut) don't work for wasm right now
# - uses: actions-rs/cargo@v1
# with:
# command: build
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
- uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
- uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
# - uses: actions-rs/cargo@v1
# with:
# command: test
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
- uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path clients/webassembly/Cargo.toml
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path clients/webassembly/Cargo.toml -- --check
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
# - uses: actions-rs/cargo@v1
# with:
# command: clippy
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
- uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
Generated
+622 -184
View File
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -17,7 +17,6 @@ members = [
"clients/native/websocket-requests",
"clients/socks5",
"clients/tauri-client/src-tauri",
"clients/webassembly",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
@@ -26,7 +25,9 @@ members = [
"common/credentials",
"common/crypto",
"common/bandwidth-claim-contract",
"common/mixnet-contract",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/mixnode-common",
"common/network-defaults",
"common/nonexhaustive-delayqueue",
@@ -52,12 +53,12 @@ members = [
"mixnode",
"service-providers/network-requester",
"validator-api",
"validator-api/validator-api-requests",
]
default-members = [
"clients/native",
"clients/socks5",
# "clients/webassembly",
"gateway",
"service-providers/network-requester",
"mixnode",
@@ -65,4 +66,4 @@ default-members = [
"explorer-api",
]
exclude = ["explorer", "contracts", "tokenomics-py"]
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"]
+2 -1
View File
@@ -21,7 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
### Developing
@@ -79,9 +79,9 @@ impl KeyManager {
))?;
let gateway_shared_key: SharedKeys =
pemstore::load_key(&client_pathfinder.gateway_shared_key().to_owned())?;
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
let ack_key: AckKey = pemstore::load_key(&client_pathfinder.ack_key().to_owned())?;
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
// after all for consistency sake?
@@ -59,7 +59,7 @@ impl ReplyKeyStorage {
) -> Result<(), ReplyKeyStorageError> {
let digest = encryption_key.compute_digest();
let insertion_result = match self.db.insert(digest.to_vec(), encryption_key.to_bytes()) {
let insertion_result = match self.db.insert(digest, encryption_key.to_bytes()) {
Err(e) => Err(ReplyKeyStorageError::DbWriteError(e)),
Ok(existing_key) => {
if existing_key.is_some() {
@@ -79,7 +79,7 @@ impl ReplyKeyStorage {
&self,
key_digest: EncryptionKeyDigest,
) -> Result<Option<SurbEncryptionKey>, ReplyKeyStorageError> {
let removal_result = match self.db.remove(&key_digest.to_vec()) {
let removal_result = match self.db.remove(key_digest) {
Err(e) => Err(ReplyKeyStorageError::DbReadError(e)),
Ok(existing_key) => {
Ok(existing_key.map(|existing_key| self.read_encryption_key(existing_key)))
@@ -35,7 +35,7 @@ async fn send_file_with_reply() {
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
let recipient = get_self_address(&mut ws_stream).await;
println!("our full address is: {}", recipient.to_string());
println!("our full address is: {}", recipient);
let read_data = std::fs::read("examples/dummy_file").unwrap();
@@ -83,7 +83,7 @@ async fn send_file_without_reply() {
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
let recipient = get_self_address(&mut ws_stream).await;
println!("our full address is: {}", recipient.to_string());
println!("our full address is: {}", recipient);
let read_data = std::fs::read("examples/dummy_file").unwrap();
@@ -36,7 +36,7 @@ async fn send_text_with_reply() {
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
let recipient = get_self_address(&mut ws_stream).await;
println!("our full address is: {}", recipient.to_string());
println!("our full address is: {}", recipient);
let send_request = json!({
"type" : "send",
@@ -76,7 +76,7 @@ async fn send_text_without_reply() {
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
let recipient = get_self_address(&mut ws_stream).await;
println!("our full address is: {}", recipient.to_string());
println!("our full address is: {}", recipient);
let send_request = json!({
"type" : "send",
+11 -3
View File
@@ -180,8 +180,8 @@ export default class ValidatorClient implements INymClient {
return this.client.getSybilResistancePercent(this.mixnetContract);
}
public async getEpochRewardPercent(): Promise<number> {
return this.client.getEpochRewardPercent(this.mixnetContract);
public async getIntervalRewardPercent(): Promise<number> {
return this.client.getIntervalRewardPercent(this.mixnetContract);
}
public async getAllNymdMixnodes(): Promise<MixNodeBond[]> {
@@ -433,6 +433,14 @@ export default class ValidatorClient implements INymClient {
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixIdentity, fee, memo);
}
public async updateMixnodeConfig(
mixIdentity: string,
fee: StdFee | 'auto' | number,
profitPercentage: number,
): Promise<ExecuteResult> {
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixIdentity, profitPercentage, fee);
}
public async updateContractStateParams(
newParams: ContractStateParams,
fee?: StdFee | 'auto' | number,
@@ -441,4 +449,4 @@ export default class ValidatorClient implements INymClient {
this.assertSigning();
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
}
}
}
+2 -9
View File
@@ -18,7 +18,6 @@ import {
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingIntervalResponse,
RewardingStatus,
} from './types';
@@ -79,12 +78,6 @@ export default class NymdQuerier implements INymdQuery {
});
}
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
return this.client.queryContractSmart(mixnetContractAddress, {
current_rewarding_interval: {},
});
}
getAllNetworkDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
@@ -155,9 +148,9 @@ export default class NymdQuerier implements INymdQuery {
});
}
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.client.queryContractSmart(mixnetContractAddress, {
get_epoch_reward_percent: {},
get_interval_reward_percent: {},
});
}
+3 -9
View File
@@ -28,7 +28,6 @@ import {
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingIntervalResponse,
RewardingStatus,
} from './types';
import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier';
@@ -63,7 +62,6 @@ export interface INymdQuery {
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse>;
getAllNetworkDelegationsPaged(
mixnetContractAddress: string,
@@ -87,7 +85,7 @@ export interface INymdQuery {
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
getRewardPool(mixnetContractAddress: string): Promise<string>;
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
getEpochRewardPercent(mixnetContractAddress: string): Promise<number>;
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
getRewardingStatus(
mixnetContractAddress: string,
@@ -138,10 +136,6 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nymdQuerier.getStateParams(mixnetContractAddress);
}
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
@@ -184,8 +178,8 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress);
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
+23 -7
View File
@@ -31,7 +31,6 @@ import {
PagedGatewayResponse,
PagedMixDelegationsResponse,
PagedMixnodeResponse,
RewardingIntervalResponse,
RewardingStatus,
} from './types';
import ValidatorApiQuerier from './validator-api-querier';
@@ -178,6 +177,13 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
memo?: string,
): Promise<ExecuteResult>;
updateMixnodeConfig(
mixnetContractAddress: string,
mixIdentity: string,
profitMarginPercent: number,
fee: StdFee | 'auto' | number,
): Promise<ExecuteResult>;
updateContractStateParams(
mixnetContractAddress: string,
newParams: ContractStateParams,
@@ -250,10 +256,6 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nymdQuerier.getStateParams(mixnetContractAddress);
}
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
mixnetContractAddress: string,
limit?: number,
@@ -296,8 +298,8 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress);
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
@@ -448,6 +450,20 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
);
}
updateMixnodeConfig(
mixnetContractAddress: string,
mixIdentity: string,
profitMarginPercent: number,
fee: StdFee | 'auto' | number,
): Promise<ExecuteResult> {
return this.execute(
this.clientAddress,
mixnetContractAddress,
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_identity: mixIdentity } },
fee,
);
}
updateContractStateParams(
mixnetContractAddress: string,
newParams: ContractStateParams,
+1 -6
View File
@@ -43,12 +43,6 @@ export type ContractStateParams = {
mixnode_active_set_size: number;
};
export type RewardingIntervalResponse = {
current_rewarding_interval_starting_block: number;
current_rewarding_interval_nonce: number;
rewarding_in_progress: boolean;
};
export type LayerDistribution = {
gateways: number;
layer1: number;
@@ -135,6 +129,7 @@ export type MixNode = {
sphinx_key: string;
identity_key: string;
version: string;
profit_margin_percent: number;
};
export type GatewayBond = {
+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" }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
wasm-utils = { path = "../../common/wasm-utils" }
+1 -2
View File
@@ -3,7 +3,6 @@
use crypto::asymmetric::{encryption, identity};
use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::GatewayClient;
use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::clients::Recipient;
@@ -111,7 +110,7 @@ impl NymClient {
let testnet_mode = self.testnet_mode;
#[cfg(feature = "coconut")]
let bandwidth_controller = Some(BandwidthController::new(
let bandwidth_controller = Some(gateway_client::bandwidth::BandwidthController::new(
vec![self.validator_server.clone()],
*self.identity.public_key(),
));
+5 -7
View File
@@ -15,6 +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 }
# internal
credentials = { path = "../../credentials" }
@@ -36,12 +38,6 @@ features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.secp256k1]
version = "0.20.3"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.web3]
version = "0.17.0"
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2"
@@ -70,4 +66,6 @@ features = ["js"]
#url = "2.1"
[features]
coconut = ["gateway-requests/coconut", "coconut-interface"]
coconut = ["gateway-requests/coconut", "coconut-interface"]
wasm = ["web3/wasm", "web3/http", "web3/signing"]
default = ["web3/default"]
@@ -203,7 +203,7 @@ impl BandwidthController {
&self.eth_private_key,
)
.await?;
if Some(U64::from(0)) == recipt.status {
if Some(U64::from(0u64)) == recipt.status {
Err(GatewayClientError::BurnTokenError(
web3::Error::InvalidResponse(format!(
"Transaction status is 0 (failure): {:?}",
+23 -8
View File
@@ -9,17 +9,18 @@ rust-version = "1.56"
[dependencies]
base64 = "0.13"
mixnet-contract = { path="../../../common/mixnet-contract" }
vesting-contract = { path="../../../contracts/vesting" }
serde = { version="1", features=["derive"] }
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract = { path = "../../../contracts/vesting" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version="0.11", features=["json"] }
reqwest = { version = "0.11", features = ["json"] }
thiserror = "1"
log = "0.4"
url = { version = "2.2", features = ["serde"] }
coconut-interface = { path = "../../coconut-interface" }
network-defaults = { path = "../../network-defaults" }
validator-api-requests = { path = "../../../validator-api/validator-api-requests" }
# required for nymd-client
# at some point it might be possible to make it wasm-compatible
@@ -27,14 +28,28 @@ network-defaults = { path = "../../network-defaults" }
async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
config = { path = "../../config", optional = true }
#cosmrs = { version = "0.3", features = ["rpc", "bip32", "cosmwasm"], optional = true }
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev="e5a1872083abb3d88fa62dda966e7f5408deba58", features = ["rpc", "bip32", "cosmwasm"], optional = true }
cosmrs = { version = "0.4.1", features = [
"rpc",
"bip32",
"cosmwasm",
], optional = true }
prost = { version = "0.9", default-features = false, optional = true }
flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0-beta3", optional = true }
ts-rs = {version = "5.1", optional = true}
ts-rs = { version = "5.1", optional = true }
[features]
nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"]
nymd-client = [
"async-trait",
"bip39",
"config",
"cosmrs",
"prost",
"flate2",
"sha2",
"itertools",
"cosmwasm-std",
]
typescript-types = ["ts-rs", "validator-api-requests/ts-rs"]
+230 -24
View File
@@ -6,21 +6,30 @@ use crate::nymd::{
error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
};
#[cfg(feature = "nymd-client")]
use mixnet_contract::ContractStateParams;
use mixnet_contract_common::ContractStateParams;
use crate::{validator_api, ValidatorClientError};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
#[cfg(feature = "nymd-client")]
use mixnet_contract::{
Delegation, MixnetContractVersion, MixnodeRewardingStatusResponse, RewardingIntervalResponse,
use mixnet_contract_common::{
Delegation, Interval, MixnetContractVersion, MixnodeRewardingStatusResponse,
};
use mixnet_contract_common::{
GatewayBond, IdentityKey, IdentityKeyRef, MixNodeBond, RewardedSetNodeStatus,
RewardedSetUpdateDetails,
};
use mixnet_contract::{GatewayBond, MixNodeBond};
use std::collections::{HashMap, HashSet};
#[cfg(feature = "nymd-client")]
use std::str::FromStr;
use url::Url;
use validator_api_requests::models::{
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
StakeSaturationResponse,
};
#[cfg(feature = "nymd-client")]
#[must_use]
pub struct Config {
api_url: Url,
nymd_url: Url,
@@ -30,6 +39,7 @@ pub struct Config {
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
rewarded_set_page_limit: Option<u32>,
}
#[cfg(feature = "nymd-client")]
@@ -48,6 +58,7 @@ impl Config {
mixnode_page_limit: None,
gateway_page_limit: None,
mixnode_delegations_page_limit: None,
rewarded_set_page_limit: None,
}
}
@@ -65,6 +76,11 @@ impl Config {
self.mixnode_delegations_page_limit = limit;
self
}
pub fn with_rewarded_set_page_limit(mut self, limit: Option<u32>) -> Config {
self.rewarded_set_page_limit = limit;
self
}
}
#[cfg(feature = "nymd-client")]
@@ -76,6 +92,7 @@ pub struct Client<C> {
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
rewarded_set_page_limit: Option<u32>,
// ideally they would have been read-only, but unfortunately rust doesn't have such features
pub validator_api: validator_api::Client,
@@ -104,6 +121,7 @@ impl Client<SigningNymdClient> {
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
rewarded_set_page_limit: None,
validator_api: validator_api_client,
nymd: nymd_client,
})
@@ -127,14 +145,14 @@ impl Client<QueryNymdClient> {
let validator_api_client = validator_api::Client::new(config.api_url.clone());
let nymd_client = NymdClient::connect(
config.nymd_url.as_str(),
config.mixnet_contract_address.clone().unwrap_or_else(|| {
Some(config.mixnet_contract_address.clone().unwrap_or_else(|| {
cosmrs::AccountId::from_str(network_defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS)
.unwrap()
}),
config.vesting_contract_address.clone().unwrap_or_else(|| {
})),
Some(config.vesting_contract_address.clone().unwrap_or_else(|| {
cosmrs::AccountId::from_str(network_defaults::DEFAULT_VESTING_CONTRACT_ADDRESS)
.unwrap()
}),
})),
)?;
Ok(Client {
@@ -144,6 +162,7 @@ impl Client<QueryNymdClient> {
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
rewarded_set_page_limit: config.rewarded_set_page_limit,
validator_api: validator_api_client,
nymd: nymd_client,
})
@@ -152,8 +171,8 @@ impl Client<QueryNymdClient> {
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
self.nymd = NymdClient::connect(
new_endpoint.as_ref(),
self.mixnet_contract_address.clone().unwrap(),
self.vesting_contract_address.clone().unwrap(),
self.mixnet_contract_address.clone(),
self.vesting_contract_address.clone(),
)?;
Ok(())
}
@@ -179,6 +198,18 @@ impl<C> Client<C> {
Ok(self.validator_api.get_mixnodes().await?)
}
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes().await?)
}
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes().await?)
}
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.validator_api.get_gateways().await?)
}
@@ -197,18 +228,9 @@ impl<C> Client<C> {
Ok(self.nymd.get_mixnet_contract_version().await?)
}
pub async fn get_current_rewarding_interval(
&self,
) -> Result<RewardingIntervalResponse, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_current_rewarding_interval().await?)
}
pub async fn get_rewarding_status(
&self,
mix_identity: mixnet_contract::IdentityKey,
mix_identity: mixnet_contract_common::IdentityKey,
rewarding_interval_nonce: u32,
) -> Result<MixnodeRewardingStatusResponse, ValidatorClientError>
where
@@ -227,6 +249,13 @@ impl<C> Client<C> {
Ok(self.nymd.get_reward_pool().await?.u128())
}
pub async fn get_current_interval(&self) -> Result<Interval, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_current_interval().await?)
}
pub async fn get_circulating_supply(&self) -> Result<u128, ValidatorClientError>
where
C: CosmWasmClient + Sync,
@@ -241,14 +270,136 @@ impl<C> Client<C> {
Ok(self.nymd.get_sybil_resistance_percent().await?)
}
pub async fn get_epoch_reward_percent(&self) -> Result<u8, ValidatorClientError>
pub async fn get_active_set_work_factor(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_epoch_reward_percent().await?)
Ok(self.nymd.get_active_set_work_factor().await?)
}
pub async fn get_interval_reward_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_interval_reward_percent().await?)
}
pub async fn get_current_rewarded_set_update_details(
&self,
) -> Result<RewardedSetUpdateDetails, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self
.nymd
.query_current_rewarded_set_update_details()
.await?)
}
// basically handles paging for us
pub async fn get_all_nymd_rewarded_set_mixnode_identities(
&self,
) -> Result<Vec<(IdentityKey, RewardedSetNodeStatus)>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut identities = Vec::new();
let mut start_after = None;
let mut height = None;
loop {
let mut paged_response = self
.nymd
.get_rewarded_set_identities_paged(
start_after.take(),
self.rewarded_set_page_limit,
height,
)
.await?;
identities.append(&mut paged_response.identities);
if height.is_none() {
// keep using the same height (the first query happened at the most recent height)
height = Some(paged_response.at_height)
}
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(identities)
}
pub async fn get_nymd_rewarded_and_active_sets(
&self,
) -> Result<Vec<(MixNodeBond, RewardedSetNodeStatus)>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
let rewarded_set_identities = self
.get_all_nymd_rewarded_set_mixnode_identities()
.await?
.into_iter()
.collect::<HashMap<_, _>>();
Ok(all_mixnodes
.into_iter()
.filter_map(|node| {
rewarded_set_identities
.get(node.identity())
.map(|status| (node, *status))
})
.collect())
}
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
pub async fn get_nymd_rewarded_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
let rewarded_set_identities = self
.get_all_nymd_rewarded_set_mixnode_identities()
.await?
.into_iter()
.map(|(identity, _status)| identity)
.collect::<HashSet<_>>();
Ok(all_mixnodes
.into_iter()
.filter(|node| rewarded_set_identities.contains(node.identity()))
.collect())
}
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
pub async fn get_nymd_active_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
let active_set_identities = self
.get_all_nymd_rewarded_set_mixnode_identities()
.await?
.into_iter()
.filter_map(|(identity, status)| {
if status.is_active() {
Some(identity)
} else {
None
}
})
.collect::<HashSet<_>>();
Ok(all_mixnodes
.into_iter()
.filter(|node| active_set_identities.contains(node.identity()))
.collect())
}
pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
@@ -297,8 +448,8 @@ impl<C> Client<C> {
pub async fn get_all_nymd_single_mixnode_delegations(
&self,
identity: mixnet_contract::IdentityKey,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
identity: IdentityKey,
) -> Result<Vec<Delegation>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
@@ -420,6 +571,12 @@ impl ApiClient {
Ok(self.validator_api.get_active_mixnodes().await?)
}
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes().await?)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?)
}
@@ -428,6 +585,55 @@ impl ApiClient {
Ok(self.validator_api.get_gateways().await?)
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_gateway_core_status_count(identity, since)
.await?)
}
pub async fn get_mixnode_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_mixnode_core_status_count(identity, since)
.await?)
}
pub async fn get_mixnode_status(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
Ok(self.validator_api.get_mixnode_status(identity).await?)
}
pub async fn get_mixnode_reward_estimation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<RewardEstimationResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_mixnode_reward_estimation(identity)
.await?)
}
pub async fn get_mixnode_stake_saturation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<StakeSaturationResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_mixnode_stake_saturation(identity)
.await?)
}
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
@@ -9,6 +9,7 @@ pub mod validator_api;
pub use crate::client::ApiClient;
pub use crate::error::ValidatorClientError;
pub use validator_api_requests::*;
#[cfg(feature = "nymd-client")]
pub use client::{Client, Config};
@@ -13,6 +13,7 @@ use cosmrs::proto::cosmos::auth::v1beta1::{
};
use cosmrs::proto::cosmos::bank::v1beta1::{
QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse,
QueryTotalSupplyRequest, QueryTotalSupplyResponse,
};
use cosmrs::proto::cosmos::tx::v1beta1::{
SimulateRequest, SimulateResponse as ProtoSimulateResponse,
@@ -27,6 +28,7 @@ use cosmrs::tendermint::abci::Code as AbciCode;
use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{tx, AccountId, Coin, Denom, Tx};
use cosmwasm_std::Coin as CosmWasmCoin;
use prost::Message;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
@@ -162,6 +164,43 @@ pub trait CosmWasmClient: rpc::Client {
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
}
// this is annoyingly and inconsistently returning `Vec<CosmWasmCoin>` rather than
// Vec<Coin>, since cosmrs::Coin can't deal with IBC denoms.
// Presumably after https://github.com/cosmos/cosmos-rust/issues/173 is resolved,
// the code could be adjusted
async fn get_total_supply(&self) -> Result<Vec<CosmWasmCoin>, NymdError> {
let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap());
let mut supply = Vec::new();
let mut pagination = None;
loop {
let req = QueryTotalSupplyRequest { pagination };
let mut res = self
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
.await?;
supply.append(&mut res.supply);
if let Some(pagination_info) = res.pagination {
pagination = Some(create_pagination(pagination_info.next_key))
} else {
break;
}
}
supply
.into_iter()
.map(|coin| {
coin.amount.parse().map(|amount| CosmWasmCoin {
denom: coin.denom,
amount,
})
})
.collect::<Result<_, _>>()
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
}
async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError> {
Ok(self.tx(id, false).await?)
}
@@ -160,10 +160,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
{
let init_msg = cosmwasm::MsgInstantiateContract {
sender: sender_address.clone(),
admin: options
.as_mut()
.map(|options| options.admin.take())
.flatten(),
admin: options.as_mut().and_then(|options| options.admin.take()),
code_id,
// now this is a weird one. the protobuf files say this field is optional,
// but if you omit it, the initialisation will fail CheckTx
@@ -20,6 +20,7 @@ pub enum Operation {
BondMixnodeOnBehalf,
UnbondMixnode,
UnbondMixnodeOnBehalf,
UpdateMixnodeConfig,
DelegateToMixnode,
DelegateToMixnodeOnBehalf,
UndelegateFromMixnode,
@@ -40,6 +41,10 @@ pub enum Operation {
WithdrawVestedCoins,
TrackUndelegation,
CreatePeriodicVestingAccount,
AdvanceCurrentInterval,
WriteRewardedSet,
ClearRewardedSet,
}
pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin {
@@ -57,6 +62,7 @@ impl fmt::Display for Operation {
Operation::BondMixnode => f.write_str("BondMixnode"),
Operation::BondMixnodeOnBehalf => f.write_str("BondMixnodeOnBehalf"),
Operation::UnbondMixnode => f.write_str("UnbondMixnode"),
Operation::UpdateMixnodeConfig => f.write_str("UpdateMixnodeConfig"),
Operation::UnbondMixnodeOnBehalf => f.write_str("UnbondMixnodeOnBehalf"),
Operation::BondGateway => f.write_str("BondGateway"),
Operation::BondGatewayOnBehalf => f.write_str("BondGatewayOnBehalf"),
@@ -76,6 +82,9 @@ impl fmt::Display for Operation {
Operation::WithdrawVestedCoins => f.write_str("WithdrawVestedCoins"),
Operation::TrackUndelegation => f.write_str("TrackUndelegation"),
Operation::CreatePeriodicVestingAccount => f.write_str("CreatePeriodicVestingAccount"),
Operation::AdvanceCurrentInterval => f.write_str("AdvanceCurrentInterval"),
Operation::WriteRewardedSet => f.write_str("WriteRewardedSet"),
Operation::ClearRewardedSet => f.write_str("ClearRewardedSet"),
}
}
}
@@ -94,6 +103,7 @@ impl Operation {
Operation::BondMixnodeOnBehalf => 200_000u64.into(),
Operation::UnbondMixnode => 175_000u64.into(),
Operation::UnbondMixnodeOnBehalf => 175_000u64.into(),
Operation::UpdateMixnodeConfig => 175_000u64.into(),
Operation::DelegateToMixnode => 175_000u64.into(),
Operation::DelegateToMixnodeOnBehalf => 175_000u64.into(),
Operation::UndelegateFromMixnode => 175_000u64.into(),
@@ -112,6 +122,9 @@ impl Operation {
Operation::WithdrawVestedCoins => 175_000u64.into(),
Operation::TrackUndelegation => 175_000u64.into(),
Operation::CreatePeriodicVestingAccount => 175_000u64.into(),
Operation::AdvanceCurrentInterval => 175_000u64.into(),
Operation::WriteRewardedSet => 175_000u64.into(),
Operation::ClearRewardedSet => 175_000u64.into(),
}
}
@@ -13,12 +13,12 @@ use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl};
use cosmwasm_std::{Coin, Uint128};
pub use fee::gas_price::GasPrice;
use fee::helpers::Operation;
use mixnet_contract::{
use mixnet_contract_common::{
ContractStateParams, Delegation, ExecuteMsg, Gateway, GatewayBond, GatewayOwnershipResponse,
IdentityKey, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse,
IdentityKey, Interval, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse,
MixnetContractVersion, MixnodeRewardingStatusResponse, PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
PagedMixnodeResponse, QueryMsg, RewardingIntervalResponse,
PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails,
};
use serde::Serialize;
use std::convert::TryInto;
@@ -27,9 +27,12 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
pub use crate::nymd::fee::Fee;
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse;
pub use cosmrs::rpc::HttpClient as QueryNymdClient;
pub use cosmrs::rpc::Paging;
pub use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::hash;
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::{self, Gas};
pub use cosmrs::Coin as CosmosCoin;
@@ -57,16 +60,16 @@ pub struct NymdClient<C> {
impl NymdClient<QueryNymdClient> {
pub fn connect<U>(
endpoint: U,
mixnet_contract_address: AccountId,
vesting_contract_address: AccountId,
mixnet_contract_address: Option<AccountId>,
vesting_contract_address: Option<AccountId>,
) -> Result<NymdClient<QueryNymdClient>, NymdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
Ok(NymdClient {
client: QueryNymdClient::new(endpoint)?,
mixnet_contract_address: Some(mixnet_contract_address),
vesting_contract_address: Some(vesting_contract_address),
mixnet_contract_address,
vesting_contract_address,
client_address: None,
custom_gas_limits: Default::default(),
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
@@ -234,6 +237,17 @@ impl<C> NymdClient<C> {
.map(|block| block.block_id.hash)
}
pub async fn get_validators(
&self,
height: u64,
paging: Paging,
) -> Result<ValidatorResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
Ok(self.client.validators(height as u32, paging).await?)
}
pub async fn get_balance(
&self,
address: &AccountId,
@@ -255,6 +269,13 @@ impl<C> NymdClient<C> {
self.get_balance(address, self.denom()?).await
}
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_total_supply().await
}
pub async fn get_contract_settings(&self) -> Result<ContractStateParams, NymdError>
where
C: CosmWasmClient + Sync,
@@ -275,35 +296,65 @@ impl<C> NymdClient<C> {
.await
}
pub async fn get_current_rewarding_interval(
&self,
) -> Result<RewardingIntervalResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::CurrentRewardingInterval {};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn get_rewarding_status(
&self,
mix_identity: mixnet_contract::IdentityKey,
rewarding_interval_nonce: u32,
mix_identity: mixnet_contract_common::IdentityKey,
interval_id: u32,
) -> Result<MixnodeRewardingStatusResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetRewardingStatus {
mix_identity,
rewarding_interval_nonce,
interval_id,
};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn query_current_rewarded_set_height(&self) -> Result<u64, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetCurrentRewardedSetHeight {};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn query_current_rewarded_set_update_details(
&self,
) -> Result<RewardedSetUpdateDetails, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetRewardedSetUpdateDetails {};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn get_rewarded_set_identities_paged(
&self,
start_after: Option<IdentityKey>,
page_limit: Option<u32>,
height: Option<u64>,
) -> Result<PagedRewardedSetResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetRewardedSet {
height,
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn get_layer_distribution(&self) -> Result<LayerDistribution, NymdError>
where
C: CosmWasmClient + Sync,
@@ -314,6 +365,16 @@ impl<C> NymdClient<C> {
.await
}
pub async fn get_current_interval(&self) -> Result<Interval, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetCurrentInterval {};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn get_reward_pool(&self) -> Result<Uint128, NymdError>
where
C: CosmWasmClient + Sync,
@@ -344,11 +405,21 @@ impl<C> NymdClient<C> {
.await
}
pub async fn get_epoch_reward_percent(&self) -> Result<u8, NymdError>
pub async fn get_active_set_work_factor(&self) -> Result<u8, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetEpochRewardPercent {};
let request = QueryMsg::GetActiveSetWorkFactor {};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
}
pub async fn get_interval_reward_percent(&self) -> Result<u8, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetIntervalRewardPercent {};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
.await
@@ -773,6 +844,31 @@ impl<C> NymdClient<C> {
.await
}
/// Update the configuration of a mixnode. Right now, only possible for profit margin.
pub async fn update_mixnode_config(
&self,
profit_margin_percent: u8,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
let req = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
self.client
.execute(
self.address(),
self.mixnet_contract_address()?,
&req,
fee,
"Updating mixnode configuration from rust!",
Vec::new(),
)
.await
}
/// Delegates specified amount of stake to particular mixnode.
pub async fn delegate_to_mixnode(
&self,
@@ -1076,41 +1172,38 @@ impl<C> NymdClient<C> {
.await
}
pub async fn begin_mixnode_rewarding(
&self,
rewarding_interval_nonce: u32,
) -> Result<ExecuteResult, NymdError>
pub async fn advance_current_interval(&self) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.operation_fee(Operation::BeginMixnodeRewarding);
let fee = self.operation_fee(Operation::AdvanceCurrentInterval);
let req = ExecuteMsg::BeginMixnodeRewarding {
rewarding_interval_nonce,
};
let req = ExecuteMsg::AdvanceCurrentInterval {};
self.client
.execute(
self.address(),
self.mixnet_contract_address()?,
&req,
fee,
"Beginning mixnode rewarding procedure",
"Advancing current interval",
Vec::new(),
)
.await
}
pub async fn finish_mixnode_rewarding(
pub async fn write_rewarded_set(
&self,
rewarding_interval_nonce: u32,
rewarded_set: Vec<IdentityKey>,
expected_active_set_size: u32,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.operation_fee(Operation::FinishMixnodeRewarding);
let fee = self.operation_fee(Operation::WriteRewardedSet);
let req = ExecuteMsg::FinishMixnodeRewarding {
rewarding_interval_nonce,
let req = ExecuteMsg::WriteRewardedSet {
rewarded_set,
expected_active_set_size,
};
self.client
.execute(
@@ -1118,7 +1211,7 @@ impl<C> NymdClient<C> {
self.mixnet_contract_address()?,
&req,
fee,
"Finishing mixnode rewarding procedure",
"Writing rewarded set",
Vec::new(),
)
.await
@@ -8,7 +8,7 @@ use crate::nymd::fee::helpers::Operation;
use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient};
use async_trait::async_trait;
use cosmwasm_std::Coin;
use mixnet_contract::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
use vesting_contract::messages::ExecuteMsg as VestingExecuteMsg;
#[async_trait]
@@ -133,6 +133,7 @@ impl DirectSecp256k1HdWallet {
}
}
#[must_use]
pub struct DirectSecp256k1HdWalletBuilder {
/// The password to use when deriving a BIP39 seed from a mnemonic.
bip39_password: String,
@@ -1,16 +1,25 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::validator_api::error::ValidatorAPIError;
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use mixnet_contract::{GatewayBond, MixNodeBond};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use url::Url;
use validator_api_requests::models::{
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
};
pub mod error;
pub(crate) mod routes;
type PathSegments<'a> = &'a [&'a str];
type Params<'a, K, V> = &'a [(K, V)];
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
pub struct Client {
url: Url,
@@ -30,24 +39,33 @@ impl Client {
self.url = new_url
}
async fn query_validator_api<T>(&self, path: PathSegments<'_>) -> Result<T, ValidatorAPIError>
async fn query_validator_api<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, ValidatorAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path);
let url = create_api_url(&self.url, path, params);
Ok(self.reqwest_client.get(url).send().await?.json().await?)
}
async fn post_validator_api<B, T>(
async fn post_validator_api<B, T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, ValidatorAPIError>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path);
let url = create_api_url(&self.url, path, params);
Ok(self
.reqwest_client
.post(url)
@@ -59,18 +77,176 @@ impl Client {
}
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES])
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
.await
}
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS])
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await
}
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE])
self.query_validator_api(
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
NO_PARAMS,
)
.await
}
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
NO_PARAMS,
)
.await
}
pub async fn get_probs_mixnode_rewarded(
&self,
mixnode_id: &str,
) -> Result<HashMap<String, f32>, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::MIXNODES,
routes::REWARDED,
routes::INCLUSION_CHANCE,
mixnode_id,
],
NO_PARAMS,
)
.await
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
if let Some(since) = since {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::GATEWAY,
identity,
CORE_STATUS_COUNT,
],
&[(SINCE_ARG, since.to_string())],
)
.await
} else {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::GATEWAY,
identity,
],
NO_PARAMS,
)
.await
}
}
pub async fn get_mixnode_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
if let Some(since) = since {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
CORE_STATUS_COUNT,
],
&[(SINCE_ARG, since.to_string())],
)
.await
} else {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
],
NO_PARAMS,
)
.await
}
}
pub async fn get_mixnode_status(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::STATUS,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_reward_estimation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::REWARD_ESTIMATION,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_stake_saturation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::STAKE_SATURATION,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_inclusion_probability(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::INCLUSION_CHANCE,
],
NO_PARAMS,
)
.await
}
pub async fn blind_sign(
@@ -79,6 +255,7 @@ impl Client {
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
self.post_validator_api(
&[routes::API_VERSION, routes::COCONUT_BLIND_SIGN],
NO_PARAMS,
request_body,
)
.await
@@ -87,13 +264,20 @@ impl Client {
pub async fn get_coconut_verification_key(
&self,
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY])
.await
self.query_validator_api(
&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY],
NO_PARAMS,
)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
let mut path_segments = url
.path_segments_mut()
@@ -108,6 +292,10 @@ fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
// and can be dropped
drop(path_segments);
if !params.is_empty() {
url.query_pairs_mut().extend_pairs(params);
}
url
}
@@ -122,51 +310,66 @@ mod tests {
// works with 1 segment
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["foo"]).as_str()
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
);
// works with 2 segments
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "bar"]).as_str()
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
);
// works with leading slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["/foo"]).as_str()
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["/foo", "bar"]).as_str()
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "/bar"]).as_str()
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
);
// works with trailing slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["foo/"]).as_str()
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo/", "bar"]).as_str()
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["foo", "bar/"]).as_str()
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
);
// works with both leading and trailing slash
assert_eq!(
"http://foomp.com/foo",
create_api_url(&base_url, &["/foo/"]).as_str()
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar",
create_api_url(&base_url, &["/foo/", "/bar/"]).as_str()
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
);
// adds params
assert_eq!(
"http://foomp.com/foo/bar?foomp=baz",
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
);
assert_eq!(
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
create_api_url(
&base_url,
&["/foo/", "/bar/"],
&[("arg1", "val1"), ("arg2", "val2")]
)
.as_str()
);
}
}
@@ -8,6 +8,19 @@ pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways";
pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded";
pub const COCONUT_BLIND_SIGN: &str = "blind_sign";
pub const COCONUT_VERIFICATION_KEY: &str = "verification_key";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode";
pub const GATEWAY: &str = "gateway";
pub const CORE_STATUS_COUNT: &str = "core-status-count";
pub const SINCE_ARG: &str = "since";
pub const STATUS: &str = "status";
pub const REWARD_ESTIMATION: &str = "reward-estimation";
pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
@@ -0,0 +1,10 @@
[package]
name = "contracts-common"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0-beta3"
@@ -0,0 +1,30 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::Event;
/// Looks up value of particular attribute in the provided event. If it fails to find it,
/// the function panics.
///
/// # Arguments
///
/// * `event`: event to search through.
/// * `key`: key associated with the particular attribute
pub fn must_find_attribute(event: &Event, key: &str) -> String {
may_find_attribute(event, key).unwrap()
}
/// Looks up value of particular attribute in the provided event. Returns None if it does not exist.
///
/// # Arguments
///
/// * `event`: event to search through.
/// * `key`: key associated with the particular attribute
pub fn may_find_attribute(event: &Event, key: &str) -> Option<String> {
for attr in &event.attributes {
if attr.key == key {
return Some(attr.value.clone());
}
}
None
}
@@ -0,0 +1,4 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod events;
@@ -1,5 +1,5 @@
[package]
name = "mixnet-contract"
name = "mixnet-contract-common"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
@@ -14,10 +14,17 @@ serde_repr = "0.1"
schemars = "0.8"
ts-rs = { version = "5.1", optional = true }
thiserror = "1.0"
network-defaults = { path = "../network-defaults" }
network-defaults = { path = "../../network-defaults" }
fixed = { version = "1.1", features = ["serde"] }
az = "1.1"
log = "0.4.14"
time = { version = "0.3.6", features = ["parsing", "formatting"] }
contracts-common = { path = "../contracts-common" }
[dev-dependencies]
time = { version = "0.3.5", features = ["serde", "macros"] }
[features]
default = []
@@ -0,0 +1,338 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mixnode::NodeRewardResult;
use crate::{ContractStateParams, Delegation, IdentityKeyRef, Interval, Layer};
use cosmwasm_std::{Addr, Coin, Event, Uint128};
pub use contracts_common::events::*;
// event types
pub const DELEGATION_EVENT_TYPE: &str = "delegation";
pub const UNDELEGATION_EVENT_TYPE: &str = "undelegation";
pub const GATEWAY_BONDING_EVENT_TYPE: &str = "gateway_bonding";
pub const GATEWAY_UNBONDING_EVENT_TYPE: &str = "gateway_unbonding";
pub const MIXNODE_BONDING_EVENT_TYPE: &str = "mixnode_bonding";
pub const MIXNODE_UNBONDING_EVENT_TYPE: &str = "mixnode_unbonding";
pub const SETTINGS_UPDATE_EVENT_TYPE: &str = "settings_update";
pub const OPERATOR_REWARDING_EVENT_TYPE: &str = "mix_rewarding";
pub const MIX_DELEGATORS_REWARDING_EVENT_TYPE: &str = "mix_delegators_rewarding";
pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set";
pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval";
// attributes that are used in multiple places
pub const OWNER_KEY: &str = "owner";
pub const AMOUNT_KEY: &str = "amount";
pub const PROXY_KEY: &str = "proxy";
// event-specific attributes
// delegation/undelegation
pub const DELEGATOR_KEY: &str = "delegator";
pub const DELEGATION_TARGET_KEY: &str = "delegation_target";
pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height";
// bonding/unbonding
pub const NODE_IDENTITY_KEY: &str = "identity";
pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer";
// settings change
pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge";
pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge";
pub const OLD_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "old_mixnode_rewarded_set_size";
pub const OLD_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "old_mixnode_active_set_size";
pub const OLD_ACTIVE_SET_WORK_FACTOR_KEY: &str = "old_active_set_work_factor";
pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge";
pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge";
pub const NEW_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "new_mixnode_rewarded_set_size";
pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size";
// rewarding
pub const INTERVAL_ID_KEY: &str = "interval_id";
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
pub const LAMBDA_KEY: &str = "lambda";
pub const SIGMA_KEY: &str = "sigma";
pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations";
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh";
pub const ZERO_UPTIME_VALUE: &str = "zero_uptime";
// rewarded set update
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
pub const REWARDED_SET_SIZE_KEY: &str = "rewarded_set_size";
pub const NODES_IN_REWARDED_SET_KEY: &str = "nodes_in_rewarded_set";
pub const CURRENT_INTERVAL_ID_KEY: &str = "current_interval";
pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval";
pub fn new_delegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
mix_identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
}
pub fn new_undelegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
old_delegation: &Delegation,
mix_identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event
.add_attribute(AMOUNT_KEY, old_delegation.amount.to_string())
.add_attribute(
DELEGATION_HEIGHT_KEY,
old_delegation.block_height.to_string(),
)
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
}
pub fn new_gateway_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_gateway_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
assigned_layer: Layer,
) -> Event {
let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_settings_update_event(
old_params: &ContractStateParams,
new_params: &ContractStateParams,
) -> Event {
let mut event = Event::new(SETTINGS_UPDATE_EVENT_TYPE);
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
event = event
.add_attribute(
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
old_params.minimum_mixnode_pledge,
)
.add_attribute(
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
new_params.minimum_mixnode_pledge,
)
}
if old_params.minimum_gateway_pledge != new_params.minimum_gateway_pledge {
event = event
.add_attribute(
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
old_params.minimum_gateway_pledge,
)
.add_attribute(
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
new_params.minimum_gateway_pledge,
)
}
if old_params.mixnode_rewarded_set_size != new_params.mixnode_rewarded_set_size {
event = event
.add_attribute(
OLD_MIXNODE_REWARDED_SET_SIZE_KEY,
old_params.mixnode_rewarded_set_size.to_string(),
)
.add_attribute(
NEW_MIXNODE_REWARDED_SET_SIZE_KEY,
new_params.mixnode_rewarded_set_size.to_string(),
)
}
if old_params.mixnode_active_set_size != new_params.mixnode_active_set_size {
event = event
.add_attribute(
OLD_MIXNODE_ACTIVE_SET_SIZE_KEY,
old_params.mixnode_active_set_size.to_string(),
)
.add_attribute(
NEW_MIXNODE_ACTIVE_SET_SIZE_KEY,
new_params.mixnode_active_set_size.to_string(),
)
}
event
}
pub fn new_not_found_mix_operator_rewarding_event(
interval_id: u32,
identity: IdentityKeyRef,
) -> Event {
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE)
}
pub fn new_too_fresh_bond_mix_operator_rewarding_event(
interval_id: u32,
identity: IdentityKeyRef,
) -> Event {
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(NO_REWARD_REASON_KEY, BOND_TOO_FRESH_VALUE)
}
pub fn new_zero_uptime_mix_operator_rewarding_event(
interval_id: u32,
identity: IdentityKeyRef,
) -> Event {
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE)
}
pub fn new_mix_operator_rewarding_event(
interval_id: u32,
identity: IdentityKeyRef,
node_reward_result: NodeRewardResult,
operator_reward: Uint128,
delegation_rewards_distributed: Uint128,
further_delegations: bool,
) -> Event {
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(
TOTAL_MIXNODE_REWARD_KEY,
node_reward_result.reward().to_string(),
)
.add_attribute(LAMBDA_KEY, node_reward_result.lambda().to_string())
.add_attribute(SIGMA_KEY, node_reward_result.sigma().to_string())
.add_attribute(OPERATOR_REWARD_KEY, operator_reward)
.add_attribute(
DISTRIBUTED_DELEGATION_REWARDS_KEY,
delegation_rewards_distributed,
)
.add_attribute(
FURTHER_DELEGATIONS_TO_REWARD_KEY,
further_delegations.to_string(),
)
}
pub fn new_mix_delegators_rewarding_event(
interval_id: u32,
identity: IdentityKeyRef,
delegation_rewards_distributed: Uint128,
further_delegations: bool,
) -> Event {
Event::new(MIX_DELEGATORS_REWARDING_EVENT_TYPE)
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(
DISTRIBUTED_DELEGATION_REWARDS_KEY,
delegation_rewards_distributed,
)
.add_attribute(
FURTHER_DELEGATIONS_TO_REWARD_KEY,
further_delegations.to_string(),
)
}
// note that when this event is emitted, we'll know the current block height
pub fn new_change_rewarded_set_event(
active_set_size: u32,
rewarded_set_size: u32,
nodes_in_rewarded_set: u32,
current_interval_id: u32,
) -> Event {
Event::new(CHANGE_REWARDED_SET_EVENT_TYPE)
.add_attribute(ACTIVE_SET_SIZE_KEY, active_set_size.to_string())
.add_attribute(REWARDED_SET_SIZE_KEY, rewarded_set_size.to_string())
.add_attribute(NODES_IN_REWARDED_SET_KEY, nodes_in_rewarded_set.to_string())
.add_attribute(CURRENT_INTERVAL_ID_KEY, current_interval_id.to_string())
}
pub fn new_advance_interval_event(interval: Interval) -> Event {
Event::new(ADVANCE_INTERVAL_EVENT_TYPE)
.add_attribute(NEW_CURRENT_INTERVAL_KEY, interval.to_string())
}
@@ -0,0 +1,365 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
use std::time::Duration;
use time::OffsetDateTime;
// internally, since version 0.3.6, time uses deserialize_any for deserialization, which can't be handled
// by serde wasm. We could just downgrade to 0.3.5 and call it a day, but then it would break
// when we decided to upgrade it at some point in the future. And then it would have been more problematic
// to fix it, since the data would have already been stored inside the contract.
// Hence, an explicit workaround to use string representation of Rfc3339-formatted datetime.
pub(crate) mod string_rfc3339_offset_date_time {
use serde::de::Visitor;
use serde::ser::Error;
use serde::{Deserializer, Serialize, Serializer};
use std::fmt::Formatter;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
struct Rfc3339OffsetDateTimeVisitor;
impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("an rfc3339 `OffsetDateTime`")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
OffsetDateTime::parse(value, &Rfc3339).map_err(E::custom)
}
}
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(Rfc3339OffsetDateTimeVisitor)
}
pub(crate) fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
datetime
.format(&Rfc3339)
.map_err(S::Error::custom)?
.serialize(serializer)
}
}
/// Representation of rewarding interval.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
pub struct Interval {
id: u32,
#[serde(with = "string_rfc3339_offset_date_time")]
start: OffsetDateTime,
length: Duration,
}
impl Interval {
/// Creates new interval instance.
pub const fn new(id: u32, start: OffsetDateTime, length: Duration) -> Self {
Interval { id, start, length }
}
/// Returns the next interval.
#[must_use]
pub fn next_interval(&self) -> Self {
Interval {
id: self.id + 1,
start: self.end(),
length: self.length,
}
}
/// Returns the last interval.
pub fn previous_interval(&self) -> Option<Self> {
if self.id > 0 {
Some(Interval {
id: self.id - 1,
start: self.start - self.length,
length: self.length,
})
} else {
None
}
}
/// Determines whether the provided datetime is contained within the interval
///
/// # Arguments
///
/// * `datetime`: specified datetime
pub fn contains(&self, datetime: OffsetDateTime) -> bool {
self.start <= datetime && datetime <= self.end()
}
/// Determines whether the provided unix timestamp is contained within the interval
///
/// # Arguments
///
/// * `timestamp`: specified timestamp
pub fn contains_timestamp(&self, timestamp: i64) -> bool {
self.start_unix_timestamp() <= timestamp && timestamp <= self.end_unix_timestamp()
}
/// Returns new instance of [Interval] such that the provided datetime would be within
/// its duration.
///
/// # Arguments
///
/// * `now`: current datetime
pub fn current(&self, now: OffsetDateTime) -> Option<Self> {
let mut candidate = *self;
if now > self.start {
loop {
if candidate.contains(now) {
return Some(candidate);
}
candidate = candidate.next_interval();
}
} else {
loop {
if candidate.contains(now) {
return Some(candidate);
}
candidate = candidate.previous_interval()?;
}
}
}
/// Returns new instance of [Interval] such that the provided unix timestamp would be within
/// its duration.
///
/// # Arguments
///
/// * `now_unix`: current unix time
pub fn current_with_timestamp(&self, now_unix: i64) -> Option<Self> {
let mut candidate = *self;
if now_unix > self.start_unix_timestamp() {
loop {
if candidate.contains_timestamp(now_unix) {
return Some(candidate);
}
candidate = candidate.next_interval();
}
} else {
loop {
if candidate.contains_timestamp(now_unix) {
return Some(candidate);
}
candidate = candidate.previous_interval()?;
}
}
}
/// Checks whether this interval has already finished
///
/// # Arguments
///
/// * `now`: current datetime
pub fn has_elapsed(&self, now: OffsetDateTime) -> bool {
self.end() < now
}
/// Returns id of this interval
pub const fn id(&self) -> u32 {
self.id
}
/// Determines amount of time left until this interval finishes.
///
/// # Arguments
///
/// * `now`: current datetime
pub fn until_end(&self, now: OffsetDateTime) -> Option<Duration> {
let remaining = self.end() - now;
if remaining.is_negative() {
None
} else {
remaining.try_into().ok()
}
}
/// Returns the starting datetime of this interval.
pub const fn start(&self) -> OffsetDateTime {
self.start
}
/// Returns the length of this interval.
pub const fn length(&self) -> Duration {
self.length
}
/// Returns the ending datetime of this interval.
pub fn end(&self) -> OffsetDateTime {
self.start + self.length
}
/// Returns the unix timestamp of the start of this interval.
pub const fn start_unix_timestamp(&self) -> i64 {
self.start().unix_timestamp()
}
/// Returns the unix timestamp of the end of this interval.
pub fn end_unix_timestamp(&self) -> i64 {
self.end().unix_timestamp()
}
}
impl Display for Interval {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let length = self.length().as_secs();
let full_hours = length / 3600;
let rem = length % 3600;
write!(
f,
"Interval {}: {} - {} ({}h {}s)",
self.id,
self.start(),
self.end(),
full_hours,
rem
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn previous_interval() {
let interval = Interval {
id: 1,
start: time::macros::datetime!(2021-08-23 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
let expected = Interval {
id: 0,
start: time::macros::datetime!(2021-08-22 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
assert_eq!(expected, interval.previous_interval().unwrap());
let genesis_interval = Interval {
id: 0,
start: time::macros::datetime!(2021-08-23 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
assert!(genesis_interval.previous_interval().is_none());
}
#[test]
fn next_interval() {
let interval = Interval {
id: 0,
start: time::macros::datetime!(2021-08-23 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
let expected = Interval {
id: 1,
start: time::macros::datetime!(2021-08-24 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
assert_eq!(expected, interval.next_interval())
}
#[test]
fn checking_for_datetime_inclusion() {
let interval = Interval {
id: 100,
start: time::macros::datetime!(2021-08-23 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
// it must contain its own boundaries
assert!(interval.contains(interval.start));
assert!(interval.contains(interval.end()));
let in_the_midle = interval.start + Duration::from_secs(interval.length.as_secs() / 2);
assert!(interval.contains(in_the_midle));
assert!(!interval.contains(interval.next_interval().end()));
assert!(!interval.contains(interval.previous_interval().unwrap().start()));
}
#[test]
fn determining_current_interval() {
let first_interval = Interval {
id: 100,
start: time::macros::datetime!(2021-08-23 12:00 UTC),
length: Duration::from_secs(24 * 60 * 60),
};
// interval just before
let fake_now = first_interval.start - Duration::from_secs(123);
assert_eq!(
first_interval.previous_interval(),
first_interval.current(fake_now)
);
// this interval (start boundary)
assert_eq!(
first_interval,
first_interval.current(first_interval.start).unwrap()
);
// this interval (in the middle)
let fake_now = first_interval.start + Duration::from_secs(123);
assert_eq!(first_interval, first_interval.current(fake_now).unwrap());
// this interval (end boundary)
assert_eq!(
first_interval,
first_interval.current(first_interval.end()).unwrap()
);
// next interval
let fake_now = first_interval.end() + Duration::from_secs(123);
assert_eq!(
first_interval.next_interval(),
first_interval.current(fake_now).unwrap()
);
// few intervals in the past
let fake_now = first_interval.start()
- first_interval.length
- first_interval.length
- first_interval.length;
assert_eq!(
first_interval
.previous_interval()
.unwrap()
.previous_interval()
.unwrap()
.previous_interval()
.unwrap(),
first_interval.current(fake_now).unwrap()
);
// few intervals in the future
let fake_now = first_interval.end()
+ first_interval.length
+ first_interval.length
+ first_interval.length;
assert_eq!(
first_interval
.next_interval()
.next_interval()
.next_interval(),
first_interval.current(fake_now).unwrap()
);
}
}
@@ -3,7 +3,9 @@
mod delegation;
pub mod error;
pub mod events;
mod gateway;
mod interval;
pub mod mixnode;
mod msg;
mod types;
@@ -16,6 +18,9 @@ pub use delegation::{
PagedMixDelegationsResponse,
};
pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse};
pub use mixnode::{Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
pub use interval::Interval;
pub use mixnode::{
Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse, RewardedSetNodeStatus,
};
pub use msg::*;
pub use types::*;
@@ -5,7 +5,7 @@ use crate::{IdentityKey, SphinxKey};
use az::CheckedCast;
use cosmwasm_std::{coin, Addr, Coin, Uint128};
use log::error;
use network_defaults::DEFAULT_OPERATOR_EPOCH_COST;
use network_defaults::DEFAULT_OPERATOR_INTERVAL_COST;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -18,6 +18,19 @@ fixed::const_fixed_from_int! {
const ONE: U128 = 1;
}
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
pub enum RewardedSetNodeStatus {
Active,
Standby,
}
impl RewardedSetNodeStatus {
pub fn is_active(&self) -> bool {
matches!(self, RewardedSetNodeStatus::Active)
}
}
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
pub struct MixNode {
@@ -53,6 +66,16 @@ pub enum Layer {
Three = 3,
}
impl From<Layer> for String {
fn from(layer: Layer) -> Self {
if layer == Layer::Gateway {
"gateway".to_string()
} else {
(layer as u8).to_string()
}
}
}
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct NodeRewardParams {
period_reward_pool: Uint128,
@@ -123,7 +146,7 @@ impl NodeRewardParams {
}
pub fn operator_cost(&self) -> U128 {
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_EPOCH_COST as u128)
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
@@ -243,7 +266,7 @@ impl DelegatorRewardParams {
}
}
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct NodeRewardResult {
reward: U128,
lambda: U128,
@@ -315,7 +338,7 @@ impl MixNodeBond {
&self.mix_node
}
pub fn total_stake(&self) -> Option<u128> {
pub fn total_bond(&self) -> Option<u128> {
if self.pledge_amount.denom != self.total_delegation.denom {
None
} else {
@@ -327,6 +350,11 @@ impl MixNodeBond {
self.total_delegation.clone()
}
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 {
self.total_bond_to_circulating_supply(circulating_supply)
* U128::from_num(rewarded_set_size)
}
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply)
}
@@ -20,6 +20,9 @@ pub enum ExecuteMsg {
owner_signature: String,
},
UnbondMixnode {},
UpdateMixnodeConfig {
profit_margin_percent: u8,
},
BondGateway {
gateway: Gateway,
owner_signature: String,
@@ -35,28 +38,18 @@ pub enum ExecuteMsg {
mix_identity: IdentityKey,
},
BeginMixnodeRewarding {
// nonce of the current rewarding interval
rewarding_interval_nonce: u32,
},
FinishMixnodeRewarding {
// nonce of the current rewarding interval
rewarding_interval_nonce: u32,
},
RewardMixnode {
identity: IdentityKey,
// percentage value in range 0-100
params: NodeRewardParams,
// nonce of the current rewarding interval
rewarding_interval_nonce: u32,
// id of the current rewarding interval
interval_id: u32,
},
RewardNextMixDelegators {
mix_identity: IdentityKey,
// nonce of the current rewarding interval
rewarding_interval_nonce: u32,
// id of the current rewarding interval
interval_id: u32,
},
DelegateToMixnodeOnBehalf {
mix_identity: IdentityKey,
@@ -82,6 +75,11 @@ pub enum ExecuteMsg {
UnbondGatewayOnBehalf {
owner: String,
},
WriteRewardedSet {
rewarded_set: Vec<IdentityKey>,
expected_active_set_size: u32,
},
AdvanceCurrentInterval {},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -103,7 +101,6 @@ pub enum QueryMsg {
address: String,
},
StateParams {},
CurrentRewardingInterval {},
// gets all [paged] delegations in the entire network
// TODO: do we even want that?
GetAllNetworkDelegations {
@@ -134,12 +131,25 @@ pub enum QueryMsg {
LayerDistribution {},
GetRewardPool {},
GetCirculatingSupply {},
GetEpochRewardPercent {},
GetIntervalRewardPercent {},
GetSybilResistancePercent {},
GetActiveSetWorkFactor {},
GetRewardingStatus {
mix_identity: IdentityKey,
rewarding_interval_nonce: u32,
interval_id: u32,
},
GetRewardedSet {
height: Option<u64>,
start_after: Option<IdentityKey>,
limit: Option<u32>,
},
GetRewardedSetHeightsForInterval {
interval_id: u32,
},
GetRewardedSetUpdateDetails {},
GetCurrentRewardedSetHeight {},
GetCurrentInterval {},
GetRewardedSetRefreshBlocks {},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::mixnode::DelegatorRewardParams;
use crate::Layer;
use crate::{Layer, RewardedSetNodeStatus};
use cosmwasm_std::{Addr, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -27,19 +27,12 @@ impl LayerDistribution {
}
}
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
pub struct RewardingIntervalResponse {
pub current_rewarding_interval_starting_block: u64,
pub current_rewarding_interval_nonce: u32,
pub rewarding_in_progress: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ContractStateParams {
// so currently epoch_length is being unused and validator API performs rewarding
// based on its own epoch length config value. I guess that's fine for time being
// so currently interval_length is being unused and validator API performs rewarding
// based on its own interval length config value. I guess that's fine for time being
// however, in the future, the contract constant should be controlling it instead.
// pub epoch_length: u32, // length of a rewarding epoch/interval, expressed in hours
// pub interval_length: u32, // length of a rewarding interval/interval, expressed in hours
pub minimum_mixnode_pledge: Uint128, // minimum amount a mixnode must pledge to get into the system
pub minimum_gateway_pledge: Uint128, // minimum amount a gateway must pledge to get into the system
@@ -50,7 +43,6 @@ pub struct ContractStateParams {
// subset of rewarded mixnodes that are actively receiving mix traffic
// used to handle shorter-term (e.g. hourly) fluctuations of demand
pub mixnode_active_set_size: u32,
pub active_set_work_factor: u8,
}
impl Display for ContractStateParams {
@@ -75,11 +67,6 @@ impl Display for ContractStateParams {
f,
"mixnode active set size: {}",
self.mixnode_active_set_size
)?;
write!(
f,
"mixnode active set work factor: {}",
self.active_set_work_factor
)
}
}
@@ -136,3 +123,23 @@ pub struct MixnetContractVersion {
pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str;
pub type SphinxKey = String;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedRewardedSetResponse {
pub identities: Vec<(IdentityKey, RewardedSetNodeStatus)>,
pub start_next_after: Option<IdentityKey>,
pub at_height: u64,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct RewardedSetUpdateDetails {
pub refresh_rate_blocks: u64,
pub last_refreshed_block: u64,
pub current_height: u64,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct IntervalRewardedSetHeightsResponse {
pub interval_id: u32,
pub heights: Vec<u64>,
}
@@ -0,0 +1,9 @@
[package]
name = "vesting-contract-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0-beta3"
@@ -0,0 +1,133 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, Coin, Event, Timestamp};
// event types
pub const WITHDRAW_EVENT_TYPE: &str = "vested_coins_withdraw";
pub const OWNERSHIP_TRANSFER_EVENT_TYPE: &str = "ownership_transfer";
pub const STAKING_ADDRESS_UPDATE_EVENT_TYPE: &str = "staking_address_update";
pub const NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE: &str = "new_periodic_vesting_account";
pub const VESTING_DELEGATION_EVENT_TYPE: &str = "vesting_delegation";
pub const VESTING_UNDELEGATION_EVENT_TYPE: &str = "vesting_undelegation";
pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding";
pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding";
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation";
// attributes that are used in multiple places
pub const OWNER_KEY: &str = "owner";
pub const AMOUNT_KEY: &str = "amount";
// event-specific attributes
// withdraw
pub const REMAINING_SPENDABLE_KEY: &str = "remaining_spendable";
// ownership transfer
pub const FROM_ACCOUNT_KEY: &str = "from";
pub const TO_ACCOUNT_KEY: &str = "to";
pub const NO_VALUE_VALUE: &str = "none";
// new vesting account
pub const START_TIME_KEY: &str = "start_time";
pub const STAKING_ADDRESS_KEY: &str = "staking_address";
// OPEN QUESTION: would it make sense to also emit amount of vesting/locked coins here?
// however, then it would require additional storage reads.
pub fn new_vested_coins_withdraw_event(
address: &Addr,
amount: &Coin,
remaining_spendable: &Coin,
) -> Event {
Event::new(WITHDRAW_EVENT_TYPE)
.add_attribute(OWNER_KEY, address)
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(REMAINING_SPENDABLE_KEY, remaining_spendable.to_string())
}
pub fn new_ownership_transfer_event(from: &Addr, to: &Addr) -> Event {
Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE)
.add_attribute(FROM_ACCOUNT_KEY, from)
.add_attribute(TO_ACCOUNT_KEY, to)
}
pub fn new_staking_address_update_event(from: &Option<Addr>, to: &Option<Addr>) -> Event {
let mut event = Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE);
if let Some(from) = from {
event = event.add_attribute(FROM_ACCOUNT_KEY, from)
} else {
event = event.add_attribute(FROM_ACCOUNT_KEY, NO_VALUE_VALUE);
}
if let Some(to) = to {
event = event.add_attribute(TO_ACCOUNT_KEY, to)
} else {
event = event.add_attribute(TO_ACCOUNT_KEY, NO_VALUE_VALUE);
}
event
}
pub fn new_periodic_vesting_account_event(
owner_address: &Addr,
amount: &Coin,
staking_address: &Option<Addr>,
start_time: Timestamp,
) -> Event {
let mut event = Event::new(NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner_address)
.add_attribute(AMOUNT_KEY, amount.to_string());
if let Some(staking_address) = staking_address {
event = event.add_attribute(STAKING_ADDRESS_KEY, staking_address);
}
event.add_attribute(START_TIME_KEY, start_time.to_string())
}
// In most cases the events are rather barebone as there's no point in attaching
// bunch of data to them as it would be redundant. It is because in most cases when the event is emitted
// a call to the mixnet contract is made that throws another event with relevant attributes already attached.
pub fn new_vesting_gateway_bonding_event() -> Event {
Event::new(VESTING_GATEWAY_BONDING_EVENT_TYPE)
}
pub fn new_vesting_gateway_unbonding_event() -> Event {
Event::new(VESTING_GATEWAY_UNBONDING_EVENT_TYPE)
}
pub fn new_vesting_mixnode_bonding_event() -> Event {
Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE)
}
pub fn new_vesting_mixnode_unbonding_event() -> Event {
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
}
pub fn new_vesting_delegation_event() -> Event {
Event::new(VESTING_DELEGATION_EVENT_TYPE)
}
pub fn new_vesting_undelegation_event() -> Event {
Event::new(VESTING_UNDELEGATION_EVENT_TYPE)
}
pub fn new_track_mixnode_unbond_event() -> Event {
Event::new(TRACK_MIXNODE_UNBOND_EVENT_TYPE)
}
pub fn new_track_gateway_unbond_event() -> Event {
Event::new(TRACK_GATEWAY_UNBOND_EVENT_TYPE)
}
pub fn new_track_undelegation_event() -> Event {
Event::new(TRACK_UNDELEGATION_EVENT_TYPE)
}
@@ -0,0 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod events;
+1 -1
View File
@@ -27,7 +27,7 @@ pub struct BandwidthVoucherAttributes {
pub binding_number: PrivateAttribute,
// the value (e.g., bandwidth) encoded in this voucher
pub voucher_value: PublicAttribute,
// a field with public information, e.g., type of voucher, epoch etc.
// a field with public information, e.g., type of voucher, interval etc.
pub voucher_info: PublicAttribute,
}
+1 -1
View File
@@ -9,7 +9,7 @@ edition = "2018"
[dependencies]
aes = { version = "0.7.4", features = ["ctr"] }
bs58 = "0.4.0"
blake3 = { version = "1.0.0", features = ["traits-preview"] }
blake3 = { version = "~1.2.0", features = ["traits-preview"] }
digest = "0.9.0"
generic-array = "0.14"
hkdf = "0.11.0"
+1 -1
View File
@@ -22,7 +22,7 @@ where
let hkdf = Hkdf::<D>::new(salt, ikm);
let mut okm = vec![0u8; okm_length];
hkdf.expand(info.unwrap_or_else(|| &[]), &mut okm)?;
hkdf.expand(info.unwrap_or(&[]), &mut okm)?;
Ok(okm)
}
+1
View File
@@ -77,6 +77,7 @@ impl Config {
}
}
#[must_use]
pub struct ConfigBuilder(Config);
impl ConfigBuilder {
+1 -1
View File
@@ -7,7 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cfg-if = "1.0.0"
hex-literal = "0.3.3"
serde = {version = "1.0", features = ["derive"]}
url = "2.2"
time = { version = "0.3", features = ["macros"] }
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::{milhon, qa, sandbox, ValidatorDetails};
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum Network {
MILHON,
QA,
SANDBOX,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct NetworkDetails {
bech32_prefix: String,
denom: String,
mixnet_contract_address: String,
vesting_contract_address: String,
bandwidth_claim_contract_address: String,
rewarding_validator_address: String,
validators: Vec<ValidatorDetails>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SupportedNetworks {
networks: HashMap<Network, NetworkDetails>,
}
impl SupportedNetworks {
pub fn new(support: Vec<Network>) -> Self {
let mut networks = HashMap::new();
for network in support {
match network {
Network::MILHON => networks.insert(
Network::MILHON,
NetworkDetails {
bech32_prefix: String::from(milhon::BECH32_PREFIX),
denom: String::from(milhon::DENOM),
mixnet_contract_address: String::from(milhon::MIXNET_CONTRACT_ADDRESS),
vesting_contract_address: String::from(milhon::VESTING_CONTRACT_ADDRESS),
bandwidth_claim_contract_address: String::from(
milhon::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
),
rewarding_validator_address: String::from(
milhon::REWARDING_VALIDATOR_ADDRESS,
),
validators: milhon::validators(),
},
),
Network::QA => networks.insert(
Network::QA,
NetworkDetails {
bech32_prefix: String::from(qa::BECH32_PREFIX),
denom: String::from(qa::DENOM),
mixnet_contract_address: String::from(qa::MIXNET_CONTRACT_ADDRESS),
vesting_contract_address: String::from(qa::VESTING_CONTRACT_ADDRESS),
bandwidth_claim_contract_address: String::from(
qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
),
rewarding_validator_address: String::from(qa::REWARDING_VALIDATOR_ADDRESS),
validators: qa::validators(),
},
),
Network::SANDBOX => networks.insert(
Network::SANDBOX,
NetworkDetails {
bech32_prefix: String::from(sandbox::BECH32_PREFIX),
denom: String::from(sandbox::DENOM),
mixnet_contract_address: String::from(sandbox::MIXNET_CONTRACT_ADDRESS),
vesting_contract_address: String::from(sandbox::VESTING_CONTRACT_ADDRESS),
bandwidth_claim_contract_address: String::from(
sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
),
rewarding_validator_address: String::from(
sandbox::REWARDING_VALIDATOR_ADDRESS,
),
validators: sandbox::validators(),
},
),
};
}
SupportedNetworks { networks }
}
pub fn bech32_prefix(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.bech32_prefix.as_str())
}
pub fn denom(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.denom.as_str())
}
pub fn mixnet_contract_address(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.mixnet_contract_address.as_str())
}
pub fn vesting_contract_address(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.vesting_contract_address.as_str())
}
pub fn bandwidth_claim_contract_address(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.bandwidth_claim_contract_address.as_str())
}
pub fn rewarding_validator_address(&self, network: Network) -> Option<&str> {
self.networks
.get(&network)
.map(|network_details| network_details.rewarding_validator_address.as_str())
}
pub fn validators(&self, network: Network) -> Option<&Vec<ValidatorDetails>> {
self.networks
.get(&network)
.map(|network_details| &network_details.validators)
}
}
+65 -33
View File
@@ -1,20 +1,65 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::time::Duration;
use time::OffsetDateTime;
use url::Url;
pub mod all;
pub mod eth_contract;
#[cfg(network = "milhon")]
pub mod milhon;
#[cfg(network = "sandbox")]
pub mod sandbox;
mod milhon;
mod qa;
mod sandbox;
#[cfg(network = "milhon")]
pub use milhon::*;
#[cfg(network = "sandbox")]
pub use sandbox::*;
cfg_if::cfg_if! {
if #[cfg(network = "milhon")] {
pub const BECH32_PREFIX: &str = milhon::BECH32_PREFIX;
pub const DENOM: &str = milhon::DENOM;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = milhon::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = milhon::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = milhon::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = milhon::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
milhon::validators()
}
pub fn default_network() -> all::Network {
all::Network::MILHON
}
} else if #[cfg(network = "qa")] {
pub const BECH32_PREFIX: &str = qa::BECH32_PREFIX;
pub const DENOM: &str = qa::DENOM;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = qa::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = qa::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR: &str = qa::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
qa::validators()
}
pub fn default_network() -> all::Network {
all::Network::QA
}
} else if #[cfg(network = "sandbox")] {
pub const BECH32_PREFIX: &str = sandbox::BECH32_PREFIX;
pub const DENOM: &str = sandbox::DENOM;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = sandbox::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = sandbox::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR: &str = sandbox::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
sandbox::validators()
}
pub fn default_network() -> all::Network {
all::Network::SANDBOX
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ValidatorDetails {
@@ -47,25 +92,6 @@ impl ValidatorDetails {
}
}
#[cfg(network = "milhon")]
pub fn default_validators() -> Vec<ValidatorDetails> {
vec![
ValidatorDetails::new(
"https://testnet-milhon-validator1.nymtech.net",
Some("https://testnet-milhon-validator1.nymtech.net/api"),
),
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
]
}
#[cfg(network = "sandbox")]
pub fn default_validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
"https://sandbox-validator.nymtech.net",
Some("https://sandbox-validator.nymtech.net/api"),
)]
}
pub fn default_nymd_endpoints() -> Vec<Url> {
default_validators()
.iter()
@@ -80,6 +106,13 @@ pub fn default_api_endpoints() -> Vec<Url> {
.collect()
}
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
// Name of the event triggered by the eth contract. If the event name is changed,
// this would also need to be changed; It is currently tested against the json abi
pub const ETH_EVENT_NAME: &str = "Burned";
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
// Ethereum constants used for token bridge
/// How much bandwidth (in bytes) one token can buy
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
@@ -117,10 +150,9 @@ pub const DEFAULT_VALIDATOR_API_PORT: u16 = 8080;
pub const VALIDATOR_API_VERSION: &str = "v1";
// REWARDING
pub const DEFAULT_FIRST_EPOCH_START: OffsetDateTime = time::macros::datetime!(2021-08-23 12:00 UTC);
pub const DEFAULT_EPOCH_LENGTH: Duration = Duration::from_secs(24 * 60 * 60 * 30); // 30 days
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate epoch costs to Nyms. We'll also assume a cost of 40$ per epoch(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
pub const DEFAULT_OPERATOR_EPOCH_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
// TODO: is there a way to get this from the chain
pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000;
+18 -12
View File
@@ -1,17 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const BECH32_PREFIX: &str = "punk";
pub const DENOM: &str = "upunk";
use crate::ValidatorDetails;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
pub(crate) const BECH32_PREFIX: &str = "punk";
pub(crate) const DENOM: &str = "upunk";
// Name of the event triggered by the eth contract. If the event name is changed,
// this would also need to be changed; It is currently tested against the json abi
pub const ETH_EVENT_NAME: &str = "Burned";
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![
ValidatorDetails::new(
"https://testnet-milhon-validator1.nymtech.net",
Some("https://testnet-milhon-validator1.nymtech.net/api"),
),
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
]
}
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "nymt";
pub(crate) const DENOM: &str = "unymt";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
"https://qa-validator.nymtech.net",
Some("https://qa-validator.nymtech.net/api"),
)]
}
+15 -12
View File
@@ -1,17 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt";
use crate::ValidatorDetails;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
pub(crate) const BECH32_PREFIX: &str = "nymt";
pub(crate) const DENOM: &str = "unymt";
// Name of the event triggered by the eth contract. If the event name is changed,
// this would also need to be changed; It is currently tested against the json abi
pub const ETH_EVENT_NAME: &str = "Burned";
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
"https://sandbox-validator.nymtech.net",
Some("https://sandbox-validator.nymtech.net/api"),
)]
}
+1 -1
View File
@@ -25,7 +25,7 @@ crypto = { path = "../crypto" }
topology = { path = "../topology" }
[dev-dependencies]
mixnet-contract = { path = "../mixnet-contract" }
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2
+2 -7
View File
@@ -54,6 +54,7 @@ impl From<NymTopologyError> for PreparationError {
/// an optional reply-SURB, padding it to appropriate length, encrypting its content,
/// and chunking into appropriate size [`Fragment`]s.
#[cfg_attr(not(target_arch = "wasm32"), derive(Clone))]
#[must_use]
pub struct MessagePreparer<R: CryptoRng + Rng> {
/// Instance of a cryptographically secure random number generator.
rng: R,
@@ -386,13 +387,7 @@ where
// (note: surb_ack_bytes contains SURB_ACK_FIRST_HOP || SURB_ACK_DATA )
let packet_payload: Vec<_> = surb_ack_bytes
.into_iter()
.chain(
reply_surb
.encryption_key()
.compute_digest()
.to_vec()
.into_iter(),
)
.chain(reply_surb.encryption_key().compute_digest().iter().copied())
.chain(reply_content.into_iter())
.collect();
+2 -1
View File
@@ -58,6 +58,7 @@ impl MessageReceiver {
}
/// Allows setting non-default number of expected mix hops in the network.
#[must_use]
pub fn with_mix_hops(mut self, hops: u8) -> Self {
self.num_mix_hops = hops;
self
@@ -186,7 +187,7 @@ impl Default for MessageReceiver {
mod message_receiver {
use super::*;
use crypto::asymmetric::identity;
use mixnet_contract::Layer;
use mixnet_contract_common::Layer;
use nymsphinx_addressing::clients::Recipient;
use rand::rngs::OsRng;
use std::collections::HashMap;
+1 -1
View File
@@ -13,7 +13,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
## internal
crypto = { path = "../crypto" }
mixnet-contract = { path = "../mixnet-contract" }
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nymsphinx-addressing = { path = "../nymsphinx/addressing" }
nymsphinx-types = { path = "../nymsphinx/types" }
version-checker = { path = "../version-checker" }
+1
View File
@@ -9,6 +9,7 @@ pub trait Versioned: Clone {
}
pub trait VersionFilterable<T> {
#[must_use]
fn filter_by_version(&self, expected_version: &str) -> Self;
}
+1 -1
View File
@@ -3,7 +3,7 @@
use crate::{filter, NetworkAddress};
use crypto::asymmetric::{encryption, identity};
use mixnet_contract::GatewayBond;
use mixnet_contract_common::GatewayBond;
use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress};
use nymsphinx_types::Node as SphinxNode;
use std::convert::{TryFrom, TryInto};
+4 -2
View File
@@ -3,7 +3,7 @@
use crate::filter::VersionFilterable;
use log::warn;
use mixnet_contract::{GatewayBond, MixNodeBond};
use mixnet_contract_common::{GatewayBond, MixNodeBond};
use nymsphinx_addressing::nodes::NodeIdentity;
use nymsphinx_types::Node as SphinxNode;
use rand::Rng;
@@ -206,10 +206,12 @@ impl NymTopology {
true
}
#[must_use]
pub fn filter_system_version(&self, expected_version: &str) -> Self {
self.filter_node_versions(expected_version, expected_version)
}
#[must_use]
pub fn filter_node_versions(
&self,
expected_mix_version: &str,
@@ -272,7 +274,7 @@ mod converting_mixes_to_vec {
use crypto::asymmetric::{encryption, identity};
use super::*;
use mixnet_contract::Layer;
use mixnet_contract_common::Layer;
#[test]
fn returns_a_vec_with_hashmap_values() {
+1 -1
View File
@@ -3,7 +3,7 @@
use crate::{filter, NetworkAddress};
use crypto::asymmetric::{encryption, identity};
use mixnet_contract::{Layer, MixNodeBond};
use mixnet_contract_common::{Layer, MixNodeBond};
use nymsphinx_addressing::nodes::NymNodeRoutingAddress;
use nymsphinx_types::Node as SphinxNode;
use std::convert::{TryFrom, TryInto};
+126 -132
View File
@@ -17,9 +17,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.49"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a03e93e97a28fbc9f42fbc5ba0886a3c67eb637b476dbee711f80a6ffe8223d"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "arrayref"
@@ -41,9 +41,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "az"
version = "1.1.2"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325"
checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4"
[[package]]
name = "bandwidth-claim"
@@ -123,7 +123,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array 0.14.4",
"generic-array 0.14.5",
]
[[package]]
@@ -143,9 +143,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
[[package]]
name = "bumpalo"
version = "3.8.0"
version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "byte-tools"
@@ -155,9 +155,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytemuck"
version = "1.7.2"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
[[package]]
name = "byteorder"
@@ -209,7 +209,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array 0.14.4",
"generic-array 0.14.5",
]
[[package]]
@@ -236,6 +236,13 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "contracts-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
]
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta3"
@@ -320,13 +327,13 @@ dependencies = [
"config",
"digest 0.9.0",
"ed25519-dalek",
"generic-array 0.14.4",
"generic-array 0.14.5",
"hkdf",
"hmac",
"log",
"nymsphinx-types",
"pemstore",
"rand 0.7.3",
"rand",
"subtle-encoding",
"x25519-dalek",
]
@@ -337,7 +344,7 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03"
dependencies = [
"generic-array 0.14.4",
"generic-array 0.14.5",
"rand_core 0.6.3",
"subtle 2.4.1",
"zeroize",
@@ -359,7 +366,7 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
dependencies = [
"generic-array 0.14.4",
"generic-array 0.14.5",
"subtle 2.4.1",
]
@@ -387,9 +394,9 @@ dependencies = [
[[package]]
name = "cw-storage-plus"
version = "0.10.3"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b8b840947313c1a1cccf056836cd79a60b4526bdcd6582995be37dc97be4ae"
checksum = "7d7ee1963302b0ac2a9d42fe0faec826209c17452bfd36fbfd9d002a88929261"
dependencies = [
"cosmwasm-std",
"schemars",
@@ -398,9 +405,9 @@ dependencies = [
[[package]]
name = "der"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4"
dependencies = [
"const-oid",
]
@@ -420,7 +427,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array 0.14.4",
"generic-array 0.14.5",
]
[[package]]
@@ -458,7 +465,7 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"rand",
"serde",
"sha2",
"zeroize",
@@ -486,7 +493,7 @@ checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b"
dependencies = [
"crypto-bigint",
"ff",
"generic-array 0.14.4",
"generic-array 0.14.5",
"group",
"pkcs8",
"rand_core 0.6.3",
@@ -532,9 +539,9 @@ dependencies = [
[[package]]
name = "fixed"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d333a26ec13a023c6dff4b7584de4d323cfee2e508f5dd2bbee6669e4f7efdf"
checksum = "80a9a8cb2e34880a498f09367089339bda5e12d6f871640f947850f7113058c0"
dependencies = [
"az",
"bytemuck",
@@ -564,9 +571,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "0.14.4"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
@@ -592,17 +599,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
name = "getset"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9"
dependencies = [
"proc-macro-error",
"proc-macro2",
@@ -612,9 +617,9 @@ dependencies = [
[[package]]
name = "git2"
version = "0.13.24"
version = "0.13.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "845e007a28f1fcac035715988a234e8ec5458fd825b20a20c7dec74237ef341f"
checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6"
dependencies = [
"bitflags",
"libc",
@@ -715,9 +720,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.4.8"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "jobserver"
@@ -763,15 +768,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.107"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "libgit2-sys"
version = "0.12.25+1.3.0"
version = "0.12.26+1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68169ef08d6519b2fe133ecc637408d933c0174b23b80bb2f79828966fbaab"
checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494"
dependencies = [
"cc",
"libc",
@@ -833,21 +838,6 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "mixnet-contract"
version = "0.1.0"
dependencies = [
"az",
"cosmwasm-std",
"fixed",
"log",
"network-defaults",
"schemars",
"serde",
"serde_repr",
"thiserror",
]
[[package]]
name = "mixnet-contracts"
version = "0.1.0"
dependencies = [
"bs58",
"config",
@@ -857,23 +847,41 @@ dependencies = [
"crypto",
"cw-storage-plus",
"fixed",
"mixnet-contract",
"rand 0.7.3",
"rand_chacha 0.2.2",
"mixnet-contract-common",
"rand",
"rand_chacha",
"schemars",
"serde",
"thiserror",
"time 0.3.6",
"vergen",
"vesting-contract",
]
[[package]]
name = "mixnet-contract-common"
version = "0.1.0"
dependencies = [
"az",
"contracts-common",
"cosmwasm-std",
"fixed",
"log",
"network-defaults",
"schemars",
"serde",
"serde_repr",
"thiserror",
"time 0.3.6",
]
[[package]]
name = "network-defaults"
version = "0.1.0"
dependencies = [
"cfg-if",
"hex-literal",
"serde",
"time 0.3.5",
"url",
]
@@ -897,6 +905,15 @@ dependencies = [
"libm",
]
[[package]]
name = "num_threads"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52"
dependencies = [
"libc",
]
[[package]]
name = "nymsphinx-types"
version = "0.1.0"
@@ -906,9 +923,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "opaque-debug"
@@ -1001,15 +1018,15 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.22"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "ppv-lite86"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
@@ -1037,9 +1054,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
@@ -1052,9 +1069,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
@@ -1067,21 +1084,9 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_chacha",
"rand_core 0.5.1",
"rand_hc 0.2.0",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc 0.3.1",
"rand_hc",
]
[[package]]
@@ -1094,16 +1099,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.3",
]
[[package]]
name = "rand_core"
version = "0.5.1"
@@ -1129,7 +1124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56"
dependencies = [
"num-traits",
"rand 0.7.3",
"rand",
]
[[package]]
@@ -1141,15 +1136,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core 0.6.3",
]
[[package]]
name = "regex"
version = "1.5.4"
@@ -1176,21 +1162,21 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "ryu"
version = "1.0.5"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "schemars"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271ac0c667b8229adf70f0f957697c96fafd7486ab7481e15dc5e45e3e6a4368"
checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3"
dependencies = [
"dyn-clone",
"schemars_derive",
@@ -1200,9 +1186,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ebda811090b257411540779860bc09bf321bc587f58d2c5864309d1566214e7"
checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b"
dependencies = [
"proc-macro2",
"quote",
@@ -1218,27 +1204,27 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-json-wasm"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50eef3672ec8fa45f3457fd423ba131117786784a895548021976117c1ded449"
checksum = "042ac496d97e5885149d34139bad1d617192770d7eb8f1866da2317ff4501853"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
"proc-macro2",
"quote",
@@ -1258,9 +1244,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.70"
version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3"
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
dependencies = [
"itoa",
"ryu",
@@ -1292,9 +1278,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.9.8"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
@@ -1330,7 +1316,7 @@ dependencies = [
"hmac",
"lioness",
"log",
"rand 0.7.3",
"rand",
"rand_distr",
"sha2",
"subtle 2.4.1",
@@ -1374,9 +1360,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.81"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [
"proc-macro2",
"quote",
@@ -1427,11 +1413,13 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
checksum = "c8d54b9298e05179c335de2b9645d061255bcd5155f843b3e328d2cfe0a5b413"
dependencies = [
"itoa",
"libc",
"num_threads",
"time-macros",
]
@@ -1467,9 +1455,9 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "ucd-trie"
@@ -1530,9 +1518,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vergen"
version = "5.1.18"
version = "5.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d48696c0fbbdafd9553e14c4584b4b9583931e9474a3ae506f1872b890d0b47"
checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e"
dependencies = [
"anyhow",
"cfg-if",
@@ -1547,9 +1535,9 @@ dependencies = [
[[package]]
name = "version_check"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vesting-contract"
@@ -1558,12 +1546,18 @@ dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"getrandom 0.2.3",
"mixnet-contract",
"rand 0.8.4",
"mixnet-contract-common",
"schemars",
"serde",
"thiserror",
"vesting-contract-common",
]
[[package]]
name = "vesting-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
]
[[package]]
+9 -9
View File
@@ -1556,9 +1556,9 @@
}
},
"@openzeppelin/contracts": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.1.tgz",
"integrity": "sha512-o+pHCf/yMLSlV5MkDQEzEQL402i6SoRnktru+0rdSxVEFZcTzzGhZCAtZjUFyKGazMSv1TilzMg+RbED1N8XHQ=="
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.2.tgz",
"integrity": "sha512-NyJV7sJgoGYqbtNUWgzzOGW4T6rR19FmX1IJgXGdapGPWsuMelGJn9h03nos0iqfforCbCB0iYIR0MtIuIFLLw=="
},
"@openzeppelin/test-helpers": {
"version": "0.5.15",
@@ -5059,9 +5059,9 @@
}
},
"follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"dev": true
},
"for-each": {
@@ -17880,9 +17880,9 @@
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
"shelljs": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
"dev": true,
"requires": {
"glob": "^7.0.0",
@@ -12,7 +12,7 @@
"dependencies": {
"@nomiclabs/hardhat-truffle5": "^2.0.2",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/contracts": "^4.4.1",
"@openzeppelin/contracts": "^4.4.2",
"@openzeppelin/test-helpers": "^0.5.15",
"dotenv": "^10.0.0",
"find-config": "^1.0.0"
+4 -3
View File
@@ -1,5 +1,5 @@
[package]
name = "mixnet-contracts"
name = "mixnet-contract"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2018"
@@ -16,18 +16,19 @@ exclude = [
crate-type = ["cdylib", "rlib"]
[dependencies]
mixnet-contract = { path = "../../common/mixnet-contract" }
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
config = { path = "../../common/config"}
vesting-contract = { path = "../vesting" }
cosmwasm-std = "1.0.0-beta3"
cosmwasm-storage = "1.0.0-beta3"
cw-storage-plus = "0.10.3"
cw-storage-plus = "0.11.1"
bs58 = "0.4.0"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
time = { version = "0.3", features = ["macros"] }
[dev-dependencies]
cosmwasm-schema = "1.0.0-beta3"
+1 -1
View File
@@ -4,7 +4,7 @@
extern crate mixnet_contract;
use cosmwasm_schema::{export_schema, remove_schemas, schema_for};
use mixnet_contract::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg};
use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg};
use std::env::current_dir;
use std::fs::create_dir_all;
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::time::Duration;
// approximately 1 week (assuming 5s per block)
// i.e. approximately quarter of the interval (there are 3600 * 60 * 7 = 604800 seconds in a week, i.e. ~604800 / 5 = 120960 blocks)
pub const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 120960;
pub const INTERVAL_REWARD_PERCENT: u8 = 2; // Used to calculate interval reward pool
pub const SYBIL_RESISTANCE_PERCENT: u8 = 30;
pub const ACTIVE_SET_WORK_FACTOR: u8 = 10;
// TODO: this, in theory, represents "epoch" length.
// However, since the blocktime is not EXACTLY 5s, we can't really guarantee 720 epochs in interval
// and we can't change this easily to `Duration`, because then the entire rewarded set storage
// would be messed up... (as we look up stuff "by blocks")
pub const REWARDED_SET_REFRESH_BLOCKS: u64 = 720; // with blocktime being approximately 5s, it should be roughly 1h
pub const REWARDING_INTERVAL_LENGTH: Duration = Duration::from_secs(60 * 60 * 720); // 720h, i.e. 30 days
+143 -48
View File
@@ -1,6 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::constants::{
ACTIVE_SET_WORK_FACTOR, INTERVAL_REWARD_PERCENT, REWARDING_INTERVAL_LENGTH,
SYBIL_RESISTANCE_PERCENT,
};
use crate::delegations::queries::query_all_network_delegations_paged;
use crate::delegations::queries::query_delegator_delegations_paged;
use crate::delegations::queries::query_mixnode_delegation;
@@ -8,8 +12,13 @@ use crate::delegations::queries::query_mixnode_delegations_paged;
use crate::error::ContractError;
use crate::gateways::queries::query_gateways_paged;
use crate::gateways::queries::query_owns_gateway;
use crate::interval::queries::{
query_current_interval, query_current_rewarded_set_height, query_rewarded_set,
query_rewarded_set_heights_for_interval, query_rewarded_set_refresh_minimum_blocks,
query_rewarded_set_update_details,
};
use crate::interval::storage as interval_storage;
use crate::mixnet_contract_settings::models::ContractState;
use crate::mixnet_contract_settings::queries::query_rewarding_interval;
use crate::mixnet_contract_settings::queries::{
query_contract_settings_params, query_contract_version,
};
@@ -17,13 +26,17 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::bonding_queries as mixnode_queries;
use crate::mixnodes::bonding_queries::query_mixnodes_paged;
use crate::mixnodes::layer_queries::query_layer_distribution;
use crate::rewards::queries::query_reward_pool;
use crate::rewards::queries::{query_circulating_supply, query_rewarding_status};
use crate::rewards::queries::{
query_circulating_supply, query_reward_pool, query_rewarding_status,
};
use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128,
};
use mixnet_contract::{ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use mixnet_contract_common::{
ContractStateParams, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg, QueryMsg,
};
use time::OffsetDateTime;
/// Constant specifying minimum of coin required to bond a gateway
pub const INITIAL_GATEWAY_PLEDGE: Uint128 = Uint128::new(100_000_000);
@@ -35,15 +48,12 @@ pub const INITIAL_MIXNODE_REWARDED_SET_SIZE: u32 = 200;
pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
pub const INITIAL_REWARD_POOL: u128 = 250_000_000_000_000;
pub const EPOCH_REWARD_PERCENT: u8 = 2; // Used to calculate epoch reward pool
pub const DEFAULT_SYBIL_RESISTANCE_PERCENT: u8 = 30;
pub const DEFAULT_ACTIVE_SET_WORK_FACTOR: u8 = 10;
pub const INITIAL_ACTIVE_SET_WORK_FACTOR: u8 = 10;
fn default_initial_state(
owner: Addr,
rewarding_validator_address: Addr,
env: Env,
) -> ContractState {
pub const DEFAULT_FIRST_INTERVAL_START: OffsetDateTime =
time::macros::datetime!(2022-01-01 12:00 UTC);
fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState {
ContractState {
owner,
rewarding_validator_address,
@@ -52,11 +62,7 @@ fn default_initial_state(
minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE,
mixnode_rewarded_set_size: INITIAL_MIXNODE_REWARDED_SET_SIZE,
mixnode_active_set_size: INITIAL_MIXNODE_ACTIVE_SET_SIZE,
active_set_work_factor: DEFAULT_ACTIVE_SET_WORK_FACTOR,
},
rewarding_interval_starting_block: env.block.height,
latest_rewarding_interval_nonce: 0,
rewarding_in_progress: false,
}
}
@@ -73,11 +79,15 @@ pub fn instantiate(
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
let rewarding_validator_address = deps.api.addr_validate(&msg.rewarding_validator_address)?;
let state = default_initial_state(info.sender, rewarding_validator_address, env);
let state = default_initial_state(info.sender, rewarding_validator_address);
let rewarding_interval =
Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH);
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?;
mixnet_params_storage::LAYERS.save(deps.storage, &Default::default())?;
rewards_storage::REWARD_POOL.save(deps.storage, &Uint128::new(INITIAL_REWARD_POOL))?;
interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?;
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
Ok(Response::default())
}
@@ -104,6 +114,14 @@ pub fn execute(
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, info)
}
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
} => crate::mixnodes::transactions::try_update_mixnode_config(
deps,
env,
info,
profit_margin_percent,
),
ExecuteMsg::BondGateway {
gateway,
owner_signature,
@@ -125,14 +143,14 @@ pub fn execute(
ExecuteMsg::RewardMixnode {
identity,
params,
rewarding_interval_nonce,
interval_id,
} => crate::rewards::transactions::try_reward_mixnode(
deps,
env,
info,
identity,
params,
rewarding_interval_nonce,
interval_id,
),
ExecuteMsg::DelegateToMixnode { mix_identity } => {
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
@@ -144,29 +162,14 @@ pub fn execute(
mix_identity,
)
}
ExecuteMsg::BeginMixnodeRewarding {
rewarding_interval_nonce,
} => crate::rewards::transactions::try_begin_mixnode_rewarding(
deps,
env,
info,
rewarding_interval_nonce,
),
ExecuteMsg::FinishMixnodeRewarding {
rewarding_interval_nonce,
} => crate::rewards::transactions::try_finish_mixnode_rewarding(
deps,
info,
rewarding_interval_nonce,
),
ExecuteMsg::RewardNextMixDelegators {
mix_identity,
rewarding_interval_nonce,
interval_id,
} => crate::rewards::transactions::try_reward_next_mixnode_delegators(
deps,
info,
mix_identity,
rewarding_interval_nonce,
interval_id,
),
ExecuteMsg::DelegateToMixnodeOnBehalf {
mix_identity,
@@ -217,11 +220,24 @@ pub fn execute(
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
}
ExecuteMsg::WriteRewardedSet {
rewarded_set,
expected_active_set_size,
} => crate::interval::transactions::try_write_rewarded_set(
deps,
env,
info,
rewarded_set,
expected_active_set_size,
),
ExecuteMsg::AdvanceCurrentInterval {} => {
crate::interval::transactions::try_advance_interval(env, deps.storage)
}
}
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let query_res = match msg {
QueryMsg::GetContractVersion {} => to_binary(&query_contract_version()),
QueryMsg::GetMixNodes { start_after, limit } => {
@@ -235,7 +251,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
}
QueryMsg::OwnsGateway { address } => to_binary(&query_owns_gateway(deps, address)?),
QueryMsg::StateParams {} => to_binary(&query_contract_settings_params(deps)?),
QueryMsg::CurrentRewardingInterval {} => to_binary(&query_rewarding_interval(deps)?),
QueryMsg::LayerDistribution {} => to_binary(&query_layer_distribution(deps)?),
QueryMsg::GetMixnodeDelegations {
mix_identity,
@@ -266,22 +281,102 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
} => to_binary(&query_mixnode_delegation(deps, mix_identity, delegator)?),
QueryMsg::GetRewardPool {} => to_binary(&query_reward_pool(deps)?),
QueryMsg::GetCirculatingSupply {} => to_binary(&query_circulating_supply(deps)?),
QueryMsg::GetEpochRewardPercent {} => to_binary(&EPOCH_REWARD_PERCENT),
QueryMsg::GetSybilResistancePercent {} => to_binary(&DEFAULT_SYBIL_RESISTANCE_PERCENT),
QueryMsg::GetIntervalRewardPercent {} => to_binary(&INTERVAL_REWARD_PERCENT),
QueryMsg::GetSybilResistancePercent {} => to_binary(&SYBIL_RESISTANCE_PERCENT),
QueryMsg::GetActiveSetWorkFactor {} => to_binary(&ACTIVE_SET_WORK_FACTOR),
QueryMsg::GetRewardingStatus {
mix_identity,
rewarding_interval_nonce,
} => to_binary(&query_rewarding_status(
deps,
mix_identity,
rewarding_interval_nonce,
interval_id,
} => to_binary(&query_rewarding_status(deps, mix_identity, interval_id)?),
QueryMsg::GetRewardedSet {
height,
start_after,
limit,
} => to_binary(&query_rewarded_set(
deps.storage,
height,
start_after,
limit,
)?),
QueryMsg::GetRewardedSetHeightsForInterval { interval_id } => to_binary(
&query_rewarded_set_heights_for_interval(deps.storage, interval_id)?,
),
QueryMsg::GetRewardedSetUpdateDetails {} => {
to_binary(&query_rewarded_set_update_details(env, deps.storage)?)
}
QueryMsg::GetCurrentRewardedSetHeight {} => {
to_binary(&query_current_rewarded_set_height(deps.storage)?)
}
QueryMsg::GetCurrentInterval {} => to_binary(&query_current_interval(deps.storage)?),
QueryMsg::GetRewardedSetRefreshBlocks {} => {
to_binary(&query_rewarded_set_refresh_minimum_blocks())
}
};
Ok(query_res?)
}
#[entry_point]
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
use cw_storage_plus::Item;
use serde::{Deserialize, Serialize};
// needed migration:
/*
1. removal of rewarding_interval_starting_block field from ContractState
2. removal of latest_rewarding_interval_nonce field from ContractState
3. removal of rewarding_in_progress field from ContractState
4. interval_storage::CURRENT_INTERVAL.save(deps.storage, &Interval::default())?;
5. interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
6. removal of active_set_work_factor fields from ContractStateParams
*/
#[derive(Serialize, Deserialize)]
pub struct OldContractStateParams {
pub minimum_mixnode_pledge: Uint128,
pub minimum_gateway_pledge: Uint128,
pub mixnode_rewarded_set_size: u32,
pub mixnode_active_set_size: u32,
#[serde(default)]
pub active_set_work_factor: u8,
}
#[derive(Serialize, Deserialize)]
struct OldContractState {
pub owner: Addr, // only the owner account can update state
pub rewarding_validator_address: Addr,
pub params: OldContractStateParams,
#[serde(default)]
pub rewarding_interval_starting_block: u64,
#[serde(default)]
pub latest_rewarding_interval_nonce: u32,
#[serde(default)]
pub rewarding_in_progress: bool,
}
let old_contract_state: Item<OldContractState> = Item::new("config");
let old_state = old_contract_state.load(deps.storage)?;
let new_params = mixnet_contract_common::ContractStateParams {
minimum_mixnode_pledge: old_state.params.minimum_mixnode_pledge,
minimum_gateway_pledge: old_state.params.minimum_mixnode_pledge,
mixnode_rewarded_set_size: old_state.params.mixnode_rewarded_set_size,
mixnode_active_set_size: old_state.params.mixnode_active_set_size,
};
let new_state = crate::mixnet_contract_settings::models::ContractState {
owner: old_state.owner,
rewarding_validator_address: old_state.rewarding_validator_address,
params: new_params,
};
let rewarding_interval =
Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH);
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?;
interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?;
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
Ok(Default::default())
}
@@ -292,14 +387,14 @@ pub mod tests {
use config::defaults::DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
use mixnet_contract::PagedMixnodeResponse;
use mixnet_contract_common::PagedMixnodeResponse;
#[test]
fn initialize_contract() {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {
rewarding_validator_address: config::defaults::REWARDING_VALIDATOR_ADDRESS.to_string(),
rewarding_validator_address: config::defaults::DEFAULT_REWARDING_VALIDATOR.to_string(),
};
let info = mock_info("creator", &[]);
+9 -13
View File
@@ -7,10 +7,10 @@ use cosmwasm_std::Deps;
use cosmwasm_std::Order;
use cosmwasm_std::StdResult;
use cw_storage_plus::{Bound, PrimaryKey};
use mixnet_contract::PagedAllDelegationsResponse;
use mixnet_contract::PagedDelegatorDelegationsResponse;
use mixnet_contract::PagedMixDelegationsResponse;
use mixnet_contract::{Delegation, IdentityKey};
use mixnet_contract_common::{
Delegation, IdentityKey, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
PagedMixDelegationsResponse,
};
pub(crate) fn query_all_network_delegations_paged(
deps: Deps,
@@ -140,7 +140,7 @@ pub(crate) mod tests {
#[cfg(test)]
mod querying_for_mixnode_delegations_paged {
use super::*;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
#[test]
fn retrieval_obeys_limits() {
@@ -281,7 +281,7 @@ pub(crate) mod tests {
mod querying_for_all_mixnode_delegations_paged {
use super::*;
use crate::support::tests::test_helpers;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
#[test]
fn retrieval_obeys_limits() {
@@ -444,7 +444,7 @@ pub(crate) mod tests {
// add delegation from a different address
let delegation = Delegation::new(
delegation_owner2.clone(),
delegation_owner2,
node_identity1.clone(),
coin(1234, DENOM),
1234,
@@ -474,7 +474,7 @@ pub(crate) mod tests {
// add delegation for a different node
let delegation = Delegation::new(
delegation_owner1.clone(),
node_identity2.clone(),
node_identity2,
coin(1234, DENOM),
1234,
None,
@@ -493,11 +493,7 @@ pub(crate) mod tests {
identity: node_identity1.clone(),
address: Addr::unchecked(delegation_owner1.clone())
}),
query_mixnode_delegation(
deps.as_ref(),
node_identity1.clone(),
delegation_owner1.to_string()
)
query_mixnode_delegation(deps.as_ref(), node_identity1, delegation_owner1.to_string())
)
}
+11 -11
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex};
use mixnet_contract::{Addr, Delegation, IdentityKey};
use mixnet_contract_common::{Addr, Delegation, IdentityKey};
// storage prefixes
const DELEGATION_PK_NAMESPACE: &str = "dl";
@@ -17,9 +17,9 @@ pub(crate) const DELEGATION_PAGE_DEFAULT_LIMIT: u32 = 250;
type PrimaryKey = Vec<u8>;
pub(crate) struct DelegationIndex<'a> {
pub(crate) owner: MultiIndex<'a, (Addr, PrimaryKey), Delegation>,
pub(crate) owner: MultiIndex<'a, Addr, Delegation>,
pub(crate) mixnode: MultiIndex<'a, (IdentityKey, PrimaryKey), Delegation>,
pub(crate) mixnode: MultiIndex<'a, IdentityKey, Delegation>,
}
impl<'a> IndexList<Delegation> for DelegationIndex<'a> {
@@ -46,12 +46,12 @@ impl<'a> IndexList<Delegation> for DelegationIndex<'a> {
pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> {
let indexes = DelegationIndex {
owner: MultiIndex::new(
|d, pk| (d.owner.clone(), pk),
|d| d.owner.clone(),
DELEGATION_PK_NAMESPACE,
DELEGATION_OWNER_IDX_NAMESPACE,
),
mixnode: MultiIndex::new(
|d, pk| (d.node_identity.clone(), pk),
|d| d.node_identity.clone(),
DELEGATION_PK_NAMESPACE,
DELEGATION_MIXNODE_IDX_NAMESPACE,
),
@@ -64,7 +64,7 @@ pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, Delega
mod tests {
use crate::delegations::storage;
use cosmwasm_std::Addr;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
#[cfg(test)]
mod reverse_mix_delegations {
@@ -74,7 +74,7 @@ mod tests {
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::{coin, Order};
use cw_storage_plus::PrimaryKey;
use mixnet_contract::Delegation;
use mixnet_contract_common::Delegation;
#[test]
fn reverse_mix_delegation_exists() {
@@ -94,7 +94,7 @@ mod tests {
storage::delegations()
.save(
&mut deps.storage,
(node_identity.clone(), delegation_owner.clone()).joined_key(),
(node_identity, delegation_owner.clone()).joined_key(),
&dummy_data,
)
.unwrap();
@@ -131,7 +131,7 @@ mod tests {
// add delegation for a different node
let dummy_data = Delegation::new(
delegation_owner1.clone(),
node_identity2.clone(),
node_identity2,
delegation.clone(),
mock_env().block.height,
None,
@@ -156,14 +156,14 @@ mod tests {
let dummy_data = Delegation::new(
delegation_owner2.clone(),
node_identity1.clone(),
delegation.clone(),
delegation,
mock_env().block.height,
None,
);
storage::delegations()
.save(
&mut deps.storage,
(node_identity1.clone(), delegation_owner2.clone()).joined_key(),
(node_identity1.clone(), delegation_owner2).joined_key(),
&dummy_data,
)
.unwrap();
@@ -7,8 +7,8 @@ use crate::support::helpers::generate_storage_key;
use config::defaults::DENOM;
use cosmwasm_std::{coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use cw_storage_plus::PrimaryKey;
use mixnet_contract::Delegation;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event};
use mixnet_contract_common::{Delegation, IdentityKey};
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
@@ -113,16 +113,21 @@ pub(crate) fn _try_delegate_to_mixnode(
}
None => Delegation::new(
delegate.to_owned(),
mix_identity,
amount,
mix_identity.clone(),
amount.clone(),
env.block.height,
proxy,
proxy.clone(),
),
})
},
)?;
Ok(Response::default())
Ok(Response::new().add_event(new_delegation_event(
&delegate,
&proxy,
&amount,
&mix_identity,
)))
}
pub(crate) fn try_remove_delegation_from_mixnode(
@@ -204,14 +209,19 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
let msg = Some(VestingContractExecuteMsg::TrackUndelegation {
owner: delegate.as_str().to_string(),
mix_identity: mix_identity.clone(),
amount: old_delegation.amount,
amount: old_delegation.amount.clone(),
});
let track_undelegation_msg = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_undelegation_msg);
}
Ok(response)
Ok(response.add_event(new_undelegation_event(
&delegate,
&proxy,
&old_delegation,
&mix_identity,
)))
}
}
}
@@ -530,7 +540,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env1,
mock_info(delegation_owner1.as_str(), &[delegation1.clone()]),
mock_info(delegation_owner1.as_str(), &[delegation1]),
identity.clone(),
)
.unwrap();
@@ -544,7 +554,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env2,
mock_info(delegation_owner2.as_str(), &[delegation2.clone()]),
mock_info(delegation_owner2.as_str(), &[delegation2]),
identity.clone(),
)
.unwrap();
@@ -699,7 +709,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &vec![delegation_amount.clone()]),
mock_info(delegation_owner.as_str(), &[delegation_amount.clone()]),
identity.clone(),
)
.unwrap();
@@ -722,6 +732,7 @@ mod tests {
#[cfg(test)]
mod removing_mix_stake_delegation {
use crate::delegations::queries::query_mixnode_delegation;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
@@ -774,11 +785,27 @@ mod tests {
identity.clone(),
)
.unwrap();
assert_eq!(
Ok(Response::new().add_message(BankMsg::Send {
let delegation = query_mixnode_delegation(
deps.as_ref(),
identity.clone(),
delegation_owner.clone().into_string(),
)
.unwrap();
let expected_response = Response::new()
.add_message(BankMsg::Send {
to_address: delegation_owner.clone().into(),
amount: coins(100, DENOM),
})),
})
.add_event(new_undelegation_event(
&delegation_owner,
&None,
&delegation,
&identity,
));
assert_eq!(
Ok(expected_response),
try_remove_delegation_from_mixnode(
deps.as_mut(),
mock_info(delegation_owner.as_str(), &[]),
@@ -819,12 +846,27 @@ mod tests {
identity.clone(),
)
.unwrap();
try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
assert_eq!(
Ok(Response::new().add_message(BankMsg::Send {
let delegation = query_mixnode_delegation(
deps.as_ref(),
identity.clone(),
delegation_owner.clone().into_string(),
)
.unwrap();
let expected_response = Response::new()
.add_message(BankMsg::Send {
to_address: delegation_owner.clone().into(),
amount: coins(100, DENOM),
})),
})
.add_event(new_undelegation_event(
&delegation_owner,
&None,
&delegation,
&identity,
));
try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
assert_eq!(
Ok(expected_response),
try_remove_delegation_from_mixnode(
deps.as_mut(),
mock_info(delegation_owner.as_str(), &[]),
@@ -853,7 +895,7 @@ mod tests {
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner1.as_str(), &[delegation1.clone()]),
mock_info(delegation_owner1.as_str(), &[delegation1]),
identity.clone(),
)
.is_ok());
+28 -11
View File
@@ -3,7 +3,7 @@
use config::defaults::DENOM;
use cosmwasm_std::{Addr, StdError};
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
use thiserror::Error;
/// Custom errors for contract failure conditions.
@@ -42,7 +42,7 @@ pub enum ContractError {
#[error("No coin was sent for the bonding, you must send {}", DENOM)]
NoBondFound,
#[error("Provided active set size is bigger than the demanded set")]
#[error("Provided active set size is bigger than the rewarded set")]
InvalidActiveSetSize,
#[error("Provided active set size is zero")]
@@ -75,14 +75,8 @@ pub enum ContractError {
#[error("We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}")]
OutOfFunds { to_remove: u128, reward_pool: u128 },
#[error("Received invalid rewarding interval nonce. Expected {expected}, received {received}")]
InvalidRewardingIntervalNonce { received: u32, expected: u32 },
#[error("Rewarding distribution is currently in progress")]
RewardingInProgress,
#[error("Rewarding distribution is currently not in progress")]
RewardingNotInProgress,
#[error("Received invalid interval id. Expected {expected}, received {received}")]
InvalidIntervalId { received: u32, expected: u32 },
#[error("Mixnode {identity} has already been rewarded during the current rewarding interval")]
MixnodeAlreadyRewarded { identity: IdentityKey },
@@ -105,6 +99,29 @@ pub enum ContractError {
#[error("Provided ed25519 signature did not verify correctly")]
InvalidEd25519Signature,
#[error("Profit margin percent needs to be an integer in range [0, 100], recieved {0}")]
#[error("Profit margin percent needs to be an integer in range [0, 100], received {0}")]
InvalidProfitMarginPercent(u8),
#[error("Rewarded set height not set, was rewarding set determined?")]
RewardSetHeightMapEmpty,
#[error("Received unexpected value for the active set. Got: {received}, expected: {expected}")]
UnexpectedActiveSetSize { received: u32, expected: u32 },
#[error("Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}")]
UnexpectedRewardedSetSize { received: u32, expected: u32 },
#[error("There hasn't been sufficient delay since last rewarded set update. It was last updated at height {last_update}. The delay is {minimum_delay}. The current block height is {current_height}")]
TooFrequentRewardedSetUpdate {
last_update: u64,
minimum_delay: u64,
current_height: u64,
},
#[error("Can't change to the desired interval as it's not in progress yet. It starts at {interval_start} and finishes at {interval_end}, while the current block time is {current_block_time}")]
IntervalNotInProgress {
current_block_time: u64,
interval_start: i64,
interval_end: i64,
},
}
+3 -1
View File
@@ -5,7 +5,9 @@ use super::storage;
use crate::mixnodes::storage::{BOND_PAGE_DEFAULT_LIMIT, BOND_PAGE_MAX_LIMIT}; // Keeps gateway and mixnode retrieval in sync by re-using the constant. Could be split into its own constant.
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use mixnet_contract::{GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse};
use mixnet_contract_common::{
GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse,
};
pub(crate) fn query_gateways_paged(
deps: Deps,
+3 -5
View File
@@ -3,7 +3,7 @@
use cosmwasm_std::Addr;
use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex};
use mixnet_contract::{GatewayBond, IdentityKeyRef};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
// storage prefixes
const GATEWAYS_PK_NAMESPACE: &str = "gt";
@@ -41,9 +41,7 @@ mod tests {
use cosmwasm_std::StdResult;
use cosmwasm_std::Storage;
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract::GatewayBond;
use mixnet_contract::IdentityKey;
use mixnet_contract::{Gateway, IdentityKeyRef};
use mixnet_contract_common::{Gateway, GatewayBond, IdentityKey, IdentityKeyRef};
// currently this is only used in tests but may become useful later on
pub(crate) fn read_gateway_pledge_amount(
@@ -87,7 +85,7 @@ mod tests {
let gateway_bond = GatewayBond {
pledge_amount: coin(pledge_amount, DENOM),
owner: node_owner.clone(),
owner: node_owner,
block_height: 12_345,
gateway: Gateway {
identity_key: node_identity.clone(),
+35 -30
View File
@@ -9,7 +9,8 @@ use config::defaults::DENOM;
use cosmwasm_std::{
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
};
use mixnet_contract::{Gateway, GatewayBond, Layer};
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
use mixnet_contract_common::{Gateway, GatewayBond, Layer};
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_gateway(
@@ -97,12 +98,24 @@ pub(crate) fn _try_add_gateway(
&gateway.identity_key,
)?;
let bond = GatewayBond::new(pledge, owner, env.block.height, gateway, proxy);
let gateway_identity = gateway.identity_key.clone();
let bond = GatewayBond::new(
pledge.clone(),
owner.clone(),
env.block.height,
gateway,
proxy.clone(),
);
storage::gateways().save(deps.storage, bond.identity(), &bond)?;
mixnet_params_storage::increment_layer_count(deps.storage, Layer::Gateway)?;
Ok(Response::new())
Ok(Response::new().add_event(new_gateway_bonding_event(
&owner,
&proxy,
&pledge,
&gateway_identity,
)))
}
pub fn try_remove_gateway_on_behalf(
@@ -155,23 +168,24 @@ pub(crate) fn _try_remove_gateway(
// decrement layer count
mixnet_params_storage::decrement_layer_count(deps.storage, Layer::Gateway)?;
let mut response = Response::new()
.add_message(return_tokens)
.add_attribute("action", "unbond")
.add_attribute("address", owner.clone())
.add_attribute("gateway_bond", gateway_bond.to_string());
let mut response = Response::new().add_message(return_tokens);
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondGateway {
owner: owner.as_str().to_string(),
amount: gateway_bond.pledge_amount,
amount: gateway_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_unbond_message);
}
Ok(response)
Ok(response.add_event(new_gateway_unbonding_event(
&owner,
&proxy,
&gateway_bond.pledge_amount,
gateway_bond.identity(),
)))
}
fn validate_gateway_pledge(
@@ -212,12 +226,10 @@ pub mod tests {
use crate::support::tests;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use cosmwasm_std::attr;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, BankMsg, Response};
use cosmwasm_std::{from_binary, Addr, Uint128};
use mixnet_contract::Gateway;
use mixnet_contract::{ExecuteMsg, PagedGatewayResponse, QueryMsg};
use mixnet_contract_common::{ExecuteMsg, Gateway, PagedGatewayResponse, QueryMsg};
#[test]
fn gateway_add() {
@@ -454,19 +466,6 @@ pub mod tests {
let msg = ExecuteMsg::UnbondGateway {};
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
// we should see log messages come back showing an unbond message
let expected_attributes = vec![
attr("action", "unbond"),
attr("address", "fred"),
attr(
"gateway_bond",
format!(
"amount: {} {}, owner: fred, identity: {}",
INITIAL_GATEWAY_PLEDGE, DENOM, fred_identity
),
),
];
// we should see a funds transfer from the contract back to fred
let expected_message = BankMsg::Send {
to_address: String::from(info.sender),
@@ -474,11 +473,17 @@ pub mod tests {
};
// run the executor and check that we got back the correct results
let expected = Response::new()
.add_attributes(expected_attributes)
.add_message(expected_message);
let expected_response =
Response::new()
.add_message(expected_message)
.add_event(new_gateway_unbonding_event(
&Addr::unchecked("fred"),
&None,
&tests::fixtures::good_gateway_pledge()[0],
&fred_identity,
));
assert_eq!(remove_fred, expected);
assert_eq!(expected_response, remove_fred);
// only 1 node now exists, owned by bob:
let gateway_bonds = tests::queries::get_gateways(&mut deps);
+6
View File
@@ -0,0 +1,6 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod storage;
pub mod transactions;
+425
View File
@@ -0,0 +1,425 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use crate::error::ContractError;
use cosmwasm_std::{Env, Order, StdResult, Storage};
use cw_storage_plus::Bound;
use mixnet_contract_common::{
IdentityKey, Interval, IntervalRewardedSetHeightsResponse, PagedRewardedSetResponse,
RewardedSetNodeStatus, RewardedSetUpdateDetails,
};
pub fn query_current_interval(storage: &dyn Storage) -> Result<Interval, ContractError> {
Ok(storage::CURRENT_INTERVAL.load(storage)?)
}
pub(crate) fn query_rewarded_set_refresh_minimum_blocks() -> u64 {
crate::constants::REWARDED_SET_REFRESH_BLOCKS
}
pub fn query_rewarded_set_heights_for_interval(
storage: &dyn Storage,
interval_id: u32,
) -> Result<IntervalRewardedSetHeightsResponse, ContractError> {
// I don't think we have to deal with paging here as at most we're going to have 720 values here
// and I think the validators are capable of performing 720 storage reads at once if they're only
// reading u64 (+ u8) values...
let heights = storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
.prefix(interval_id)
.range(storage, None, None, Order::Ascending)
.map(|val| val.map(|(height, _)| height))
.collect::<StdResult<Vec<_>>>()?;
Ok(IntervalRewardedSetHeightsResponse {
interval_id,
heights,
})
}
// note: I have removed the `query_rewarded_set_for_interval`, because I don't think it's appropriate
// for the contract to go through so much data (i.e. all "rewarded" sets of particular interval) in one go.
// To achieve the same result, the client would have to instead first call `query_rewarded_set_heights_for_interval`
// to learn the heights used in given interval and then for each of them `query_rewarded_set` for that particular height.
pub fn query_current_rewarded_set_height(storage: &dyn Storage) -> Result<u64, ContractError> {
Ok(storage::CURRENT_REWARDED_SET_HEIGHT.load(storage)?)
}
fn query_rewarded_set_at_height(
storage: &dyn Storage,
height: u64,
start_after: Option<IdentityKey>,
limit: u32,
) -> Result<Vec<(IdentityKey, RewardedSetNodeStatus)>, ContractError> {
let start = start_after.map(Bound::exclusive);
let rewarded_set = storage::REWARDED_SET
.prefix(height)
.range(storage, start, None, Order::Ascending)
.take(limit as usize)
.collect::<StdResult<_>>()?;
Ok(rewarded_set)
}
pub fn query_rewarded_set(
storage: &dyn Storage,
height: Option<u64>,
start_after: Option<IdentityKey>,
limit: Option<u32>,
) -> Result<PagedRewardedSetResponse, ContractError> {
let height = match height {
Some(height) => height,
None => query_current_rewarded_set_height(storage)?,
};
let limit = limit
.unwrap_or(storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT)
.min(storage::REWARDED_NODE_MAX_PAGE_LIMIT);
// query for an additional element to determine paging requirements
let mut paged_result = query_rewarded_set_at_height(storage, height, start_after, limit + 1)?;
if paged_result.len() > limit as usize {
paged_result.truncate(limit as usize);
Ok(PagedRewardedSetResponse {
start_next_after: paged_result.last().map(|res| res.0.clone()),
identities: paged_result,
at_height: height,
})
} else {
Ok(PagedRewardedSetResponse {
identities: paged_result,
start_next_after: None,
at_height: height,
})
}
}
// this was all put together into the same query so that all information would be synced together
pub fn query_rewarded_set_update_details(
env: Env,
storage: &dyn Storage,
) -> Result<RewardedSetUpdateDetails, ContractError> {
Ok(RewardedSetUpdateDetails {
refresh_rate_blocks: query_rewarded_set_refresh_minimum_blocks(),
last_refreshed_block: query_current_rewarded_set_height(storage)?,
current_height: env.block.height,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::interval::storage::REWARDED_NODE_MAX_PAGE_LIMIT;
use crate::support::tests::test_helpers;
use cosmwasm_std::testing::mock_env;
fn store_rewarded_nodes(
storage: &mut dyn Storage,
height: u64,
active_set: u32,
rewarded_set: u32,
) -> Vec<IdentityKey> {
let identities = (0..rewarded_set)
.map(|i| format!("identity{:04}", i))
.collect::<Vec<_>>();
storage::save_rewarded_set(storage, height, active_set, identities.clone()).unwrap();
identities
}
#[test]
fn querying_for_rewarded_set_heights_for_interval() {
let mut deps = test_helpers::init_contract();
// no data
assert!(
query_rewarded_set_heights_for_interval(deps.as_ref().storage, 0)
.unwrap()
.heights
.is_empty()
);
// 100 heights
for i in 0..100 {
storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
.save(deps.as_mut().storage, (1, i), &0u8)
.unwrap();
}
let expected = (0..100).collect::<Vec<_>>();
assert_eq!(
expected,
query_rewarded_set_heights_for_interval(deps.as_ref().storage, 1)
.unwrap()
.heights
);
// 100 heights for different interval
for i in 200..300 {
storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
.save(deps.as_mut().storage, (10, i), &0u8)
.unwrap();
}
let expected = (200..300).collect::<Vec<_>>();
assert_eq!(
expected,
query_rewarded_set_heights_for_interval(deps.as_ref().storage, 10)
.unwrap()
.heights
)
}
#[test]
fn querying_for_rewarded_set_at_height() {
let mut deps = test_helpers::init_contract();
// store some nodes
let identities1 = store_rewarded_nodes(deps.as_mut().storage, 1, 100, 200);
let identities2 = store_rewarded_nodes(deps.as_mut().storage, 2, 50, 200);
let identities3 = store_rewarded_nodes(deps.as_mut().storage, 3, 150, 200);
let identities4 = store_rewarded_nodes(deps.as_mut().storage, 4, 300, 500);
let identities5 = store_rewarded_nodes(deps.as_mut().storage, 5, 500, 500);
// expected2 and 3 are basically sanity checks to ensure changing active set size (increase or decrease)
// doesn't affect the ordering
let expected1 = identities1
.into_iter()
.enumerate()
.map(|(i, identity)| {
if i < 100 {
(identity, RewardedSetNodeStatus::Active)
} else {
(identity, RewardedSetNodeStatus::Standby)
}
})
.collect::<Vec<_>>();
assert_eq!(
expected1,
query_rewarded_set_at_height(deps.as_ref().storage, 1, None, 1000).unwrap()
);
let expected2 = identities2
.into_iter()
.enumerate()
.map(|(i, identity)| {
if i < 50 {
(identity, RewardedSetNodeStatus::Active)
} else {
(identity, RewardedSetNodeStatus::Standby)
}
})
.collect::<Vec<_>>();
assert_eq!(
expected2,
query_rewarded_set_at_height(deps.as_ref().storage, 2, None, 1000).unwrap()
);
let expected3 = identities3
.into_iter()
.enumerate()
.map(|(i, identity)| {
if i < 150 {
(identity, RewardedSetNodeStatus::Active)
} else {
(identity, RewardedSetNodeStatus::Standby)
}
})
.collect::<Vec<_>>();
assert_eq!(
expected3,
query_rewarded_set_at_height(deps.as_ref().storage, 3, None, 1000).unwrap()
);
// check limit and paging
// active: 300, rewarded: 500
let first_100 = identities4
.iter()
.take(100)
.map(|identity| (identity.clone(), RewardedSetNodeStatus::Active))
.collect::<Vec<_>>();
assert_eq!(
first_100,
query_rewarded_set_at_height(deps.as_ref().storage, 4, None, 100).unwrap()
);
let expected_single1 = vec![("identity0299".to_string(), RewardedSetNodeStatus::Active)];
let expected_single2 = vec![("identity0300".to_string(), RewardedSetNodeStatus::Standby)];
assert_eq!(
expected_single1,
query_rewarded_set_at_height(
deps.as_ref().storage,
4,
Some("identity0298".to_string()),
1
)
.unwrap()
);
assert_eq!(
expected_single2,
query_rewarded_set_at_height(
deps.as_ref().storage,
4,
Some("identity0299".to_string()),
1
)
.unwrap()
);
let last_100 = identities4
.iter()
.skip(400)
.map(|identity| (identity.clone(), RewardedSetNodeStatus::Standby))
.collect::<Vec<_>>();
assert_eq!(
last_100,
query_rewarded_set_at_height(
deps.as_ref().storage,
4,
Some("identity0399".to_string()),
100
)
.unwrap()
);
// all nodes are in the active set
let expected5 = identities5
.into_iter()
.map(|identity| (identity, RewardedSetNodeStatus::Active))
.collect::<Vec<_>>();
assert_eq!(
expected5,
query_rewarded_set_at_height(deps.as_ref().storage, 5, None, 1000).unwrap()
);
}
#[test]
fn querying_for_rewarded_set() {
let mut deps = test_helpers::init_contract();
let current_height = 123;
let other_height = 456;
let different_height = 789;
storage::CURRENT_REWARDED_SET_HEIGHT
.save(deps.as_mut().storage, &current_height)
.unwrap();
let identities1 = store_rewarded_nodes(deps.as_mut().storage, current_height, 50, 100);
let identities2 = store_rewarded_nodes(deps.as_mut().storage, other_height, 100, 200);
let identities3 = store_rewarded_nodes(
deps.as_mut().storage,
different_height,
storage::REWARDED_NODE_MAX_PAGE_LIMIT,
storage::REWARDED_NODE_MAX_PAGE_LIMIT * 2,
);
// if height is not set, current height is used, else it's just passed
let expected1 = PagedRewardedSetResponse {
identities: identities1
.into_iter()
.enumerate()
.map(|(i, identity)| {
if i < 50 {
(identity, RewardedSetNodeStatus::Active)
} else {
(identity, RewardedSetNodeStatus::Standby)
}
})
.collect::<Vec<_>>(),
start_next_after: None,
at_height: current_height,
};
let expected2 = PagedRewardedSetResponse {
identities: identities2
.into_iter()
.enumerate()
.map(|(i, identity)| {
if i < 100 {
(identity, RewardedSetNodeStatus::Active)
} else {
(identity, RewardedSetNodeStatus::Standby)
}
})
.collect::<Vec<_>>(),
start_next_after: None,
at_height: other_height,
};
assert_eq!(
Ok(expected1),
query_rewarded_set(deps.as_ref().storage, None, None, None)
);
assert_eq!(
Ok(expected2),
query_rewarded_set(deps.as_ref().storage, Some(other_height), None, None)
);
// if limit is not set, a default one is used instead
let expected3 = PagedRewardedSetResponse {
identities: identities3
.iter()
.take(storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT as usize)
.cloned()
.map(|identity| (identity, RewardedSetNodeStatus::Active))
.collect::<Vec<_>>(),
start_next_after: Some(format!(
"identity{:04}",
storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT - 1
)),
at_height: different_height,
};
assert_eq!(
Ok(expected3),
query_rewarded_set(deps.as_ref().storage, Some(different_height), None, None)
);
// limit cannot be larger that pre-defined maximum
let expected4 = PagedRewardedSetResponse {
identities: identities3
.iter()
.take(storage::REWARDED_NODE_MAX_PAGE_LIMIT as usize)
.cloned()
.map(|identity| (identity, RewardedSetNodeStatus::Active))
.collect::<Vec<_>>(),
start_next_after: Some(format!(
"identity{:04}",
storage::REWARDED_NODE_MAX_PAGE_LIMIT - 1
)),
at_height: different_height,
};
assert_eq!(
Ok(expected4),
query_rewarded_set(
deps.as_ref().storage,
Some(different_height),
None,
Some(REWARDED_NODE_MAX_PAGE_LIMIT * 100)
)
);
}
#[test]
fn querying_for_rewarded_set_update_details() {
let env = mock_env();
let mut deps = test_helpers::init_contract();
let current_height = 123;
storage::CURRENT_REWARDED_SET_HEIGHT
.save(deps.as_mut().storage, &current_height)
.unwrap();
// returns whatever is in the correct environment
assert_eq!(
RewardedSetUpdateDetails {
refresh_rate_blocks: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
last_refreshed_block: current_height,
current_height: env.block.height
},
query_rewarded_set_update_details(env, deps.as_ref().storage).unwrap()
)
}
}
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{StdResult, Storage};
use cw_storage_plus::{Item, Map};
use mixnet_contract_common::{IdentityKey, Interval, RewardedSetNodeStatus};
// type aliases for better reasoning for storage keys
// (I found it helpful)
type BlockHeight = u64;
type IntervalId = u32;
// TODO: those values need to be verified
pub(crate) const REWARDED_NODE_DEFAULT_PAGE_LIMIT: u32 = 1000;
pub(crate) const REWARDED_NODE_MAX_PAGE_LIMIT: u32 = 1500;
pub(crate) const CURRENT_INTERVAL: Item<Interval> = Item::new("cep");
pub(crate) const CURRENT_REWARDED_SET_HEIGHT: Item<BlockHeight> = Item::new("crh");
// I've changed the `()` data to an `u8` as after serializing `()` is represented as "null",
// taking more space than a single digit u8. If we don't care about what's there, why not go with more efficient approach? : )
pub(crate) const REWARDED_SET_HEIGHTS_FOR_INTERVAL: Map<(IntervalId, BlockHeight), u8> =
Map::new("rsh");
// pub(crate) const REWARDED_SET: Map<(u64, IdentityKey), NodeStatus> = Map::new("rs");
pub(crate) const REWARDED_SET: Map<(BlockHeight, IdentityKey), RewardedSetNodeStatus> =
Map::new("rs");
pub(crate) fn save_rewarded_set(
storage: &mut dyn Storage,
height: BlockHeight,
active_set_size: u32,
entries: Vec<IdentityKey>,
) -> StdResult<()> {
for (i, identity) in entries.into_iter().enumerate() {
// first k nodes are active
let set_status = if i < active_set_size as usize {
RewardedSetNodeStatus::Active
} else {
RewardedSetNodeStatus::Standby
};
REWARDED_SET.save(storage, (height, identity), &set_status)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::test_helpers;
#[test]
fn saving_rewarded_set() {
let mut deps = test_helpers::init_contract();
let active_set_size = 100;
let mut nodes = Vec::new();
for i in 0..1000 {
nodes.push(format!("identity{:04}", i))
}
save_rewarded_set(deps.as_mut().storage, 1234, active_set_size, nodes).unwrap();
// first k nodes MUST BE active
for i in 0..1000 {
let identity = format!("identity{:04}", i);
if i < active_set_size {
assert_eq!(
RewardedSetNodeStatus::Active,
REWARDED_SET
.load(deps.as_ref().storage, (1234, identity))
.unwrap()
)
} else {
assert_eq!(
RewardedSetNodeStatus::Standby,
REWARDED_SET
.load(deps.as_ref().storage, (1234, identity))
.unwrap()
)
}
}
}
}
@@ -0,0 +1,308 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use crate::error::ContractError;
use crate::error::ContractError::IntervalNotInProgress;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, Storage};
use mixnet_contract_common::events::{new_advance_interval_event, new_change_rewarded_set_event};
use mixnet_contract_common::IdentityKey;
pub fn try_write_rewarded_set(
deps: DepsMut,
env: Env,
info: MessageInfo,
rewarded_set: Vec<IdentityKey>,
active_set_size: u32,
) -> 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);
}
// sanity check to make sure the sending validator is in sync with the contract state
// (i.e. so that we'd known that top k nodes are actually expected to be active)
if active_set_size != state.params.mixnode_active_set_size {
return Err(ContractError::UnexpectedActiveSetSize {
received: active_set_size,
expected: state.params.mixnode_active_set_size,
});
}
if rewarded_set.len() as u32 > state.params.mixnode_rewarded_set_size {
return Err(ContractError::UnexpectedRewardedSetSize {
received: rewarded_set.len() as u32,
expected: state.params.mixnode_rewarded_set_size,
});
}
let last_update = storage::CURRENT_REWARDED_SET_HEIGHT.load(deps.storage)?;
let block_height = env.block.height;
if last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS > block_height {
return Err(ContractError::TooFrequentRewardedSetUpdate {
last_update,
minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
current_height: block_height,
});
}
let current_interval = storage::CURRENT_INTERVAL.load(deps.storage)?.id();
let num_nodes = rewarded_set.len();
storage::save_rewarded_set(deps.storage, block_height, active_set_size, rewarded_set)?;
storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL.save(
deps.storage,
(current_interval, block_height),
&0u8,
)?;
storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &block_height)?;
Ok(Response::new().add_event(new_change_rewarded_set_event(
state.params.mixnode_active_set_size,
state.params.mixnode_rewarded_set_size,
num_nodes as u32,
current_interval,
)))
}
pub fn try_advance_interval(
env: Env,
storage: &mut dyn Storage,
) -> Result<Response, ContractError> {
// in theory, we could have just changed the state and relied on its reversal upon failed
// execution, but better safe than sorry and do not modify the state at all unless we know
// all checks have succeeded.
let current_interval = storage::CURRENT_INTERVAL.load(storage)?;
let next_interval = current_interval.next_interval();
if next_interval.start_unix_timestamp() > env.block.time.seconds() as i64 {
// the reason for this check is as follows:
// nobody, even trusted validators, should be able to continuously keep advancing intervals,
// because otherwise it would be possible for them to continuously keep rewarding nodes.
//
// Therefore, even if "trusted" validator, responsible for rewarding, is malicious,
// they can't send rewards more often than every `REWARDED_SET_REFRESH_BLOCKS`
// and changing this value requires going through governance and having agreement of
// the super-majority of the validators (by stake)
return Err(IntervalNotInProgress {
current_block_time: env.block.time.seconds(),
interval_start: next_interval.start_unix_timestamp(),
interval_end: next_interval.end_unix_timestamp(),
});
}
storage::CURRENT_INTERVAL.save(storage, &next_interval)?;
Ok(Response::new().add_event(new_advance_interval_event(next_interval)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::test_helpers;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::Timestamp;
use mixnet_contract_common::{Interval, RewardedSetNodeStatus};
use std::time::Duration;
use time::OffsetDateTime;
#[test]
fn writing_rewarded_set() {
let mut env = mock_env();
let mut deps = test_helpers::init_contract();
let current_state = mixnet_params_storage::CONTRACT_STATE
.load(deps.as_mut().storage)
.unwrap();
let authorised_sender = mock_info(current_state.rewarding_validator_address.as_str(), &[]);
let full_rewarded_set = (0..current_state.params.mixnode_rewarded_set_size)
.map(|i| format!("identity{:04}", i))
.collect::<Vec<_>>();
let last_update = 123;
storage::CURRENT_REWARDED_SET_HEIGHT
.save(deps.as_mut().storage, &last_update)
.unwrap();
// can only be performed by the permitted validator
let dummy_sender = mock_info("dummy_sender", &[]);
assert_eq!(
Err(ContractError::Unauthorized),
try_write_rewarded_set(
deps.as_mut(),
env.clone(),
dummy_sender,
full_rewarded_set.clone(),
current_state.params.mixnode_active_set_size
)
);
// the sender must use the same active set size as the one defined in the contract
assert_eq!(
Err(ContractError::UnexpectedActiveSetSize {
received: 123,
expected: current_state.params.mixnode_active_set_size
}),
try_write_rewarded_set(
deps.as_mut(),
env.clone(),
authorised_sender.clone(),
full_rewarded_set.clone(),
123
)
);
// the sender cannot provide more nodes than the rewarded set size
let mut bigger_set = full_rewarded_set.clone();
bigger_set.push("another_node".to_string());
assert_eq!(
Err(ContractError::UnexpectedRewardedSetSize {
received: current_state.params.mixnode_rewarded_set_size + 1,
expected: current_state.params.mixnode_rewarded_set_size
}),
try_write_rewarded_set(
deps.as_mut(),
env.clone(),
authorised_sender.clone(),
bigger_set,
current_state.params.mixnode_active_set_size
)
);
// cannot be performed too soon after a previous update
env.block.height = last_update + 1;
assert_eq!(
Err(ContractError::TooFrequentRewardedSetUpdate {
last_update,
minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
current_height: last_update + 1,
}),
try_write_rewarded_set(
deps.as_mut(),
env.clone(),
authorised_sender.clone(),
full_rewarded_set.clone(),
current_state.params.mixnode_active_set_size
)
);
// after successful rewarded set write, all internal storage structures are updated appropriately
env.block.height = last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS;
let expected_response = Response::new().add_event(new_change_rewarded_set_event(
current_state.params.mixnode_active_set_size,
current_state.params.mixnode_rewarded_set_size,
full_rewarded_set.len() as u32,
0,
));
assert_eq!(
Ok(expected_response),
try_write_rewarded_set(
deps.as_mut(),
env.clone(),
authorised_sender,
full_rewarded_set.clone(),
current_state.params.mixnode_active_set_size
)
);
for (i, rewarded_node) in full_rewarded_set.into_iter().enumerate() {
if (i as u32) < current_state.params.mixnode_active_set_size {
assert_eq!(
RewardedSetNodeStatus::Active,
storage::REWARDED_SET
.load(deps.as_ref().storage, (env.block.height, rewarded_node))
.unwrap()
)
} else {
assert_eq!(
RewardedSetNodeStatus::Standby,
storage::REWARDED_SET
.load(deps.as_ref().storage, (env.block.height, rewarded_node))
.unwrap()
)
}
}
assert!(storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
.has(deps.as_ref().storage, (0, env.block.height)));
assert_eq!(
env.block.height,
storage::CURRENT_REWARDED_SET_HEIGHT
.load(deps.as_ref().storage)
.unwrap()
);
}
#[test]
fn advancing_interval() {
let mut env = mock_env();
let mut deps = test_helpers::init_contract();
// 1609459200 = 2021-01-01
// 1640995200 = 2022-01-01
// 1641081600 = 2022-01-02
// 1643673600 = 2022-02-01
// 1672531200 = 2023-01-01
let current_interval = Interval::new(
0,
OffsetDateTime::from_unix_timestamp(1640995200).unwrap(),
Duration::from_secs(60 * 60 * 720),
);
let next_interval = current_interval.next_interval();
storage::CURRENT_INTERVAL
.save(deps.as_mut().storage, &current_interval)
.unwrap();
// fails if the current interval hasn't finished yet i.e. the new interval hasn't begun
env.block.time = Timestamp::from_seconds(1641081600);
assert_eq!(
Err(ContractError::IntervalNotInProgress {
current_block_time: 1641081600,
interval_start: next_interval.start_unix_timestamp(),
interval_end: next_interval.end_unix_timestamp()
}),
try_advance_interval(env.clone(), deps.as_mut().storage)
);
// same if the current blocktime is set to BEFORE the first interval has even begun
// (say we decided to set the first interval to be some time in the future at initialisation)
env.block.time = Timestamp::from_seconds(1609459200);
assert_eq!(
Err(ContractError::IntervalNotInProgress {
current_block_time: 1609459200,
interval_start: next_interval.start_unix_timestamp(),
interval_end: next_interval.end_unix_timestamp()
}),
try_advance_interval(env.clone(), deps.as_mut().storage)
);
// works otherwise
// interval that has just finished
env.block.time =
Timestamp::from_seconds(next_interval.start_unix_timestamp() as u64 + 10000);
let expected_new_interval = current_interval.next_interval();
let expected_response =
Response::new().add_event(new_advance_interval_event(expected_new_interval));
assert_eq!(
Ok(expected_response),
try_advance_interval(env.clone(), deps.as_mut().storage)
);
// interval way back in the past (i.e. 'somebody' failed to advance it for a long time)
env.block.time = Timestamp::from_seconds(1672531200);
storage::CURRENT_INTERVAL
.save(deps.as_mut().storage, &current_interval)
.unwrap();
let expected_new_interval = current_interval.next_interval();
let expected_response =
Response::new().add_event(new_advance_interval_event(expected_new_interval));
assert_eq!(
Ok(expected_response),
try_advance_interval(env.clone(), deps.as_mut().storage)
);
}
}
+2
View File
@@ -1,10 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod constants;
pub mod contract;
mod delegations;
mod error;
mod gateways;
mod interval;
mod mixnet_contract_settings;
mod mixnodes;
mod rewards;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::Addr;
use mixnet_contract::ContractStateParams;
use mixnet_contract_common::ContractStateParams;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -11,11 +11,4 @@ pub struct ContractState {
pub owner: Addr, // only the owner account can update state
pub rewarding_validator_address: Addr,
pub params: ContractStateParams,
// keep track of the changes to the current rewarding interval,
// i.e. at which block has the latest rewarding occurred
// and whether another run is already in progress
pub rewarding_interval_starting_block: u64,
pub latest_rewarding_interval_nonce: u32,
pub rewarding_in_progress: bool,
}
@@ -3,7 +3,7 @@
use super::storage;
use cosmwasm_std::{Deps, StdResult};
use mixnet_contract::{ContractStateParams, MixnetContractVersion, RewardingIntervalResponse};
use mixnet_contract_common::{ContractStateParams, MixnetContractVersion};
pub(crate) fn query_contract_settings_params(deps: Deps) -> StdResult<ContractStateParams> {
storage::CONTRACT_STATE
@@ -11,16 +11,6 @@ pub(crate) fn query_contract_settings_params(deps: Deps) -> StdResult<ContractSt
.map(|settings| settings.params)
}
pub(crate) fn query_rewarding_interval(deps: Deps) -> StdResult<RewardingIntervalResponse> {
let state = storage::CONTRACT_STATE.load(deps.storage)?;
Ok(RewardingIntervalResponse {
current_rewarding_interval_starting_block: state.rewarding_interval_starting_block,
current_rewarding_interval_nonce: state.latest_rewarding_interval_nonce,
rewarding_in_progress: state.rewarding_in_progress,
})
}
pub(crate) fn query_contract_version() -> MixnetContractVersion {
// as per docs
// env! macro will expand to the value of the named environment variable at
@@ -55,11 +45,7 @@ pub(crate) mod tests {
minimum_gateway_pledge: 456u128.into(),
mixnode_rewarded_set_size: 1000,
mixnode_active_set_size: 500,
active_set_work_factor: 10,
},
rewarding_interval_starting_block: 123,
latest_rewarding_interval_nonce: 0,
rewarding_in_progress: false,
};
storage::CONTRACT_STATE
@@ -5,8 +5,7 @@ use crate::mixnet_contract_settings::models::ContractState;
use cosmwasm_std::StdResult;
use cosmwasm_std::Storage;
use cw_storage_plus::Item;
use mixnet_contract::Layer;
use mixnet_contract::LayerDistribution;
use mixnet_contract_common::{Layer, LayerDistribution};
pub(crate) const CONTRACT_STATE: Item<ContractState> = Item::new("config");
pub(crate) const LAYERS: Item<LayerDistribution> = Item::new("layers");
@@ -6,7 +6,8 @@ use crate::error::ContractError;
use cosmwasm_std::DepsMut;
use cosmwasm_std::MessageInfo;
use cosmwasm_std::Response;
use mixnet_contract::ContractStateParams;
use mixnet_contract_common::events::new_settings_update_event;
use mixnet_contract_common::ContractStateParams;
pub(crate) fn try_update_contract_settings(
deps: DepsMut,
@@ -34,10 +35,12 @@ pub(crate) fn try_update_contract_settings(
return Err(ContractError::InvalidActiveSetSize);
}
let response = Response::new().add_event(new_settings_update_event(&state.params, &params));
state.params = params;
storage::CONTRACT_STATE.save(deps.storage, &state)?;
Ok(Response::default())
Ok(response)
}
#[cfg(test)]
@@ -49,7 +52,7 @@ pub mod tests {
use crate::support::tests::test_helpers;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Response;
use mixnet_contract::ContractStateParams;
use mixnet_contract_common::ContractStateParams;
#[test]
fn updating_contract_settings() {
@@ -60,17 +63,15 @@ pub mod tests {
minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE,
mixnode_rewarded_set_size: 100,
mixnode_active_set_size: 50,
active_set_work_factor: 10,
};
let initial_params = storage::CONTRACT_STATE
.load(deps.as_ref().storage)
.unwrap()
.params;
// sanity check to ensure new_params are different than the default ones
assert_ne!(
new_params,
storage::CONTRACT_STATE
.load(deps.as_ref().storage)
.unwrap()
.params
);
assert_ne!(new_params, initial_params);
// cannot be updated from non-owner account
let info = mock_info("not-the-creator", &[]);
@@ -80,7 +81,10 @@ pub mod tests {
// but works fine from the creator account
let info = mock_info("creator", &[]);
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
assert_eq!(res, Ok(Response::default()));
assert_eq!(
res,
Ok(Response::new().add_event(new_settings_update_event(&initial_params, &new_params)))
);
// and the state is actually updated
let current_state = storage::CONTRACT_STATE.load(deps.as_ref().storage).unwrap();
@@ -90,21 +94,21 @@ pub mod tests {
let info = mock_info("creator", &[]);
let mut new_params = current_state.params.clone();
new_params.mixnode_rewarded_set_size = new_params.mixnode_active_set_size - 1;
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
let res = try_update_contract_settings(deps.as_mut(), info, new_params);
assert_eq!(Err(ContractError::InvalidActiveSetSize), res);
// error is thrown for 0 size rewarded set
let info = mock_info("creator", &[]);
let mut new_params = current_state.params.clone();
new_params.mixnode_rewarded_set_size = 0;
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
let res = try_update_contract_settings(deps.as_mut(), info, new_params);
assert_eq!(Err(ContractError::ZeroRewardedSet), res);
// error is thrown for 0 size active set
let info = mock_info("creator", &[]);
let mut new_params = current_state.params.clone();
let mut new_params = current_state.params;
new_params.mixnode_active_set_size = 0;
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
let res = try_update_contract_settings(deps.as_mut(), info, new_params);
assert_eq!(Err(ContractError::ZeroActiveSet), res);
}
}
@@ -4,7 +4,9 @@
use super::storage;
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use mixnet_contract::{IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
use mixnet_contract_common::{
IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse,
};
pub fn query_mixnodes_paged(
deps: Deps,
@@ -3,7 +3,7 @@
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use cosmwasm_std::{Deps, StdResult};
use mixnet_contract::LayerDistribution;
use mixnet_contract_common::LayerDistribution;
pub(crate) fn query_layer_distribution(deps: Deps) -> StdResult<LayerDistribution> {
mixnet_params_storage::LAYERS.load(deps.storage)
+3 -8
View File
@@ -4,7 +4,7 @@
use config::defaults::DENOM;
use cosmwasm_std::{StdResult, Storage, Uint128};
use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex};
use mixnet_contract::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond};
use mixnet_contract_common::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
@@ -49,7 +49,6 @@ pub(crate) struct StoredMixnodeBond {
pub layer: Layer,
pub block_height: u64,
pub mix_node: MixNode,
pub profit_margin_percent: Option<u8>,
pub proxy: Option<Addr>,
}
@@ -60,7 +59,6 @@ impl StoredMixnodeBond {
layer: Layer,
block_height: u64,
mix_node: MixNode,
profit_margin_percent: Option<u8>,
proxy: Option<Addr>,
) -> Self {
StoredMixnodeBond {
@@ -69,7 +67,6 @@ impl StoredMixnodeBond {
layer,
block_height,
mix_node,
profit_margin_percent,
proxy,
}
}
@@ -141,8 +138,7 @@ mod tests {
use config::defaults::DENOM;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract::IdentityKey;
use mixnet_contract::MixNode;
use mixnet_contract_common::{IdentityKey, MixNode};
#[test]
fn mixnode_single_read_retrieval() {
@@ -173,14 +169,13 @@ mod tests {
let mixnode_bond = StoredMixnodeBond {
pledge_amount: coin(pledge_value, DENOM),
owner: node_owner.clone(),
owner: node_owner,
layer: Layer::One,
block_height: 12_345,
mix_node: MixNode {
identity_key: node_identity.clone(),
..tests::fixtures::mix_node_fixture()
},
profit_margin_percent: None,
proxy: None,
};
+141 -33
View File
@@ -11,7 +11,8 @@ use config::defaults::DENOM;
use cosmwasm_std::{
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
};
use mixnet_contract::MixNode;
use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event};
use mixnet_contract_common::MixNode;
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_mixnode(
@@ -109,13 +110,12 @@ fn _try_add_mixnode(
let layer = layer_distribution.choose_with_fewest();
let stored_bond = StoredMixnodeBond::new(
pledge_amount,
owner,
pledge_amount.clone(),
owner.clone(),
layer,
env.block.height,
mix_node,
None,
proxy,
proxy.clone(),
);
// technically we don't have to set the total_delegation bucket, but it makes things easier
@@ -133,7 +133,13 @@ fn _try_add_mixnode(
mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?;
Ok(Response::new())
Ok(Response::new().add_event(new_mixnode_bonding_event(
&owner,
&proxy,
&pledge_amount,
identity,
stored_bond.layer,
)))
}
pub fn try_remove_mixnode_on_behalf(
@@ -186,22 +192,60 @@ pub(crate) fn _try_remove_mixnode(
// decrement layer count
mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?;
let mut response = Response::new()
.add_message(return_tokens)
.add_attribute("action", "unbond")
.add_attribute("mixnode_bond", mixnode_bond.to_string());
let mut response = Response::new().add_message(return_tokens);
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondMixnode {
owner: owner.as_str().to_string(),
amount: mixnode_bond.pledge_amount,
amount: mixnode_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_unbond_message);
}
Ok(response)
Ok(response.add_event(new_mixnode_unbonding_event(
&owner,
&proxy,
&mixnode_bond.pledge_amount,
mixnode_bond.identity(),
)))
}
pub(crate) fn try_update_mixnode_config(
deps: DepsMut,
env: Env,
info: MessageInfo,
profit_margin_percent: u8,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(info.sender.as_ref())?;
let mix_identity = storage::mixnodes()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(ContractError::NoAssociatedMixNodeBond { owner })?
.1
.identity()
.clone();
// We don't have to check lower bound as its an u8
if profit_margin_percent > 100 {
return Err(ContractError::InvalidProfitMarginPercent(
profit_margin_percent,
));
}
storage::mixnodes().update(deps.storage, &mix_identity, |mixnode_bond_opt| {
mixnode_bond_opt
.map(|mut mixnode_bond| {
mixnode_bond.mix_node.profit_margin_percent = profit_margin_percent;
mixnode_bond.block_height = env.block.height;
mixnode_bond
})
.ok_or(ContractError::NoBondFound)
})?;
Ok(Response::new())
}
fn validate_mixnode_pledge(
@@ -242,13 +286,12 @@ pub mod tests {
use crate::support::tests;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use cosmwasm_std::attr;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, BankMsg, Response};
use cosmwasm_std::{from_binary, Addr, Uint128};
use mixnet_contract::Layer;
use mixnet_contract::MixNode;
use mixnet_contract::{ExecuteMsg, LayerDistribution, PagedMixnodeResponse, QueryMsg};
use mixnet_contract_common::{
ExecuteMsg, Layer, LayerDistribution, MixNode, PagedMixnodeResponse, QueryMsg,
};
#[test]
fn mixnode_add() {
@@ -507,18 +550,6 @@ pub mod tests {
let msg = ExecuteMsg::UnbondMixnode {};
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
// we should see log messages come back showing an unbond message
let expected_attributes = vec![
attr("action", "unbond"),
attr(
"mixnode_bond",
format!(
"amount: {}{}, owner: fred, identity: {}",
INITIAL_MIXNODE_PLEDGE, DENOM, fred_identity
),
),
];
// we should see a funds transfer from the contract back to fred
let expected_message = BankMsg::Send {
to_address: String::from(info.sender),
@@ -526,11 +557,17 @@ pub mod tests {
};
// run the executor and check that we got back the correct results
let expected = Response::new()
.add_attributes(expected_attributes)
.add_message(expected_message);
let expected_response =
Response::new()
.add_message(expected_message)
.add_event(new_mixnode_unbonding_event(
&Addr::unchecked("fred"),
&None,
&tests::fixtures::good_gateway_pledge()[0],
&fred_identity,
));
assert_eq!(remove_fred, expected);
assert_eq!(expected_response, remove_fred);
// only 1 node now exists, owned by bob:
let mix_node_bonds = tests::queries::get_mix_nodes(&mut deps);
@@ -587,6 +624,77 @@ pub mod tests {
);
}
#[test]
fn updating_mixnode_config() {
let sender = "bob";
let mut deps = test_helpers::init_contract();
let info = mock_info(sender, &[]);
// try updating a non existing mixnode bond
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent: 10,
};
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!(
ret,
Err(ContractError::NoAssociatedMixNodeBond {
owner: Addr::unchecked(sender)
})
);
test_helpers::add_mixnode(
sender,
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
// check the initial profit margin is set to the fixture value
let fixture_profit_margin = tests::fixtures::mix_node_fixture().profit_margin_percent;
assert_eq!(
fixture_profit_margin,
storage::mixnodes()
.idx
.owner
.item(deps.as_ref().storage, Addr::unchecked("bob"))
.unwrap()
.unwrap()
.1
.mix_node
.profit_margin_percent
);
// try updating with an invalid value
let profit_margin_percent = 101;
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!(
ret,
Err(ContractError::InvalidProfitMarginPercent(
profit_margin_percent
))
);
let profit_margin_percent = fixture_profit_margin + 10;
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
profit_margin_percent,
storage::mixnodes()
.idx
.owner
.item(deps.as_ref().storage, Addr::unchecked("bob"))
.unwrap()
.unwrap()
.1
.mix_node
.profit_margin_percent
);
}
#[test]
fn validating_mixnode_bond() {
// you must send SOME funds
@@ -649,6 +757,6 @@ pub mod tests {
assert_eq!(alice_node.mix_node.identity_key, alice_identity);
assert_eq!(alice_node.layer, Layer::One);
assert_eq!(bob_node.mix_node.identity_key, bob_identity);
assert_eq!(bob_node.layer, mixnet_contract::Layer::Two);
assert_eq!(bob_node.layer, mixnet_contract_common::Layer::Two);
}
}
+5 -5
View File
@@ -5,8 +5,8 @@ use super::storage;
use crate::error::ContractError;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{Addr, Storage, Uint128};
use mixnet_contract::mixnode::DelegatorRewardParams;
use mixnet_contract::{
use mixnet_contract_common::mixnode::DelegatorRewardParams;
use mixnet_contract_common::{
IdentityKey, IdentityKeyRef, PendingDelegatorRewarding, RewardingResult, RewardingStatus,
};
@@ -55,7 +55,7 @@ pub(crate) fn update_post_rewarding_storage(
pub(crate) fn update_rewarding_status(
storage: &mut dyn Storage,
rewarding_interval_nonce: u32,
interval_id: u32,
mix_identity: IdentityKey,
rewarding_results: RewardingResult,
next_start: Option<Addr>,
@@ -64,7 +64,7 @@ pub(crate) fn update_rewarding_status(
if let Some(next_start) = next_start {
storage::REWARDING_STATUS.save(
storage,
(rewarding_interval_nonce.into(), mix_identity),
(interval_id, mix_identity),
&RewardingStatus::PendingNextDelegatorPage(PendingDelegatorRewarding {
running_results: rewarding_results,
next_start,
@@ -74,7 +74,7 @@ pub(crate) fn update_rewarding_status(
} else {
storage::REWARDING_STATUS.save(
storage,
(rewarding_interval_nonce.into(), mix_identity),
(interval_id, mix_identity),
&RewardingStatus::Complete(rewarding_results),
)?;
}
+29 -47
View File
@@ -4,7 +4,7 @@
use super::storage;
use cosmwasm_std::Uint128;
use cosmwasm_std::{Deps, StdResult};
use mixnet_contract::{IdentityKey, MixnodeRewardingStatusResponse};
use mixnet_contract_common::{IdentityKey, MixnodeRewardingStatusResponse};
pub(crate) fn query_reward_pool(deps: Deps) -> StdResult<Uint128> {
storage::REWARD_POOL.load(deps.storage)
@@ -17,12 +17,9 @@ pub(crate) fn query_circulating_supply(deps: Deps) -> StdResult<Uint128> {
pub(crate) fn query_rewarding_status(
deps: Deps,
mix_identity: IdentityKey,
rewarding_interval_nonce: u32,
interval_id: u32,
) -> StdResult<MixnodeRewardingStatusResponse> {
let status = storage::REWARDING_STATUS.may_load(
deps.storage,
(rewarding_interval_nonce.into(), mix_identity),
)?;
let status = storage::REWARDING_STATUS.may_load(deps.storage, (interval_id, mix_identity))?;
Ok(MixnodeRewardingStatusResponse { status })
}
@@ -37,16 +34,17 @@ pub(crate) mod tests {
#[cfg(test)]
mod querying_for_rewarding_status {
use super::storage;
use super::*;
use crate::constants;
use crate::delegations::transactions::try_delegate_to_mixnode;
use crate::rewards::transactions::{
try_begin_mixnode_rewarding, try_finish_mixnode_rewarding, try_reward_mixnode,
try_reward_next_mixnode_delegators,
try_reward_mixnode, try_reward_next_mixnode_delegators,
};
use config::defaults::DENOM;
use cosmwasm_std::{coin, Addr};
use mixnet_contract::{RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT};
use mixnet_contract_common::{
RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT,
};
#[test]
fn returns_empty_status_for_unrewarded_nodes() {
@@ -70,19 +68,17 @@ pub(crate) mod tests {
.is_none()
);
// node was rewarded but for different epoch
// node was rewarded but for different interval
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
info.clone(),
env,
info,
node_identity.clone(),
tests::fixtures::node_rewarding_params_fixture(100),
1,
0,
)
.unwrap();
try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap();
assert!(query_rewarding_status(deps.as_ref(), node_identity, 2)
.unwrap()
@@ -107,22 +103,20 @@ pub(crate) mod tests {
deps.as_mut(),
);
env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING;
env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
info.clone(),
info,
node_identity.clone(),
tests::fixtures::node_rewarding_params_fixture(100),
1,
0,
)
.unwrap();
try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap();
let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap();
let res = query_rewarding_status(deps.as_ref(), node_identity, 0).unwrap();
assert!(matches!(res.status, Some(RewardingStatus::Complete(..))));
match res.status.unwrap() {
@@ -151,40 +145,32 @@ pub(crate) mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info(
&*format!("delegator{:04}", i),
&vec![coin(200_000000, DENOM)],
),
mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]),
node_identity.clone(),
)
.unwrap();
}
env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING;
env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage);
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 2).unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
env,
info.clone(),
node_identity.clone(),
tests::fixtures::node_rewarding_params_fixture(100),
2,
1,
)
.unwrap();
// rewards all pending
try_reward_next_mixnode_delegators(
deps.as_mut(),
info.clone(),
node_identity.to_string(),
2,
)
.unwrap();
try_reward_next_mixnode_delegators(deps.as_mut(), info, node_identity.to_string(), 1)
.unwrap();
let res = query_rewarding_status(deps.as_ref(), node_identity, 2).unwrap();
let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap();
assert!(matches!(res.status, Some(RewardingStatus::Complete(..))));
match res.status.unwrap() {
@@ -222,31 +208,27 @@ pub(crate) mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info(
&*format!("delegator{:04}", i),
&vec![coin(200_000000, DENOM)],
),
mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]),
node_identity.clone(),
)
.unwrap();
}
env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING;
env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap();
try_reward_mixnode(
deps.as_mut(),
env.clone(),
env,
info,
node_identity.clone(),
tests::fixtures::node_rewarding_params_fixture(100),
1,
0,
)
.unwrap();
let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap();
let res = query_rewarding_status(deps.as_ref(), node_identity, 0).unwrap();
assert!(matches!(
res.status,
Some(RewardingStatus::PendingNextDelegatorPage(..))
+4 -9
View File
@@ -4,17 +4,12 @@
use crate::error::ContractError;
use config::defaults::TOTAL_SUPPLY;
use cosmwasm_std::{StdResult, Storage, Uint128};
use cw_storage_plus::{Item, Map, U32Key};
use mixnet_contract::{IdentityKey, RewardingStatus};
use cw_storage_plus::{Item, Map};
use mixnet_contract_common::{IdentityKey, RewardingStatus};
pub(crate) const REWARD_POOL: Item<Uint128> = Item::new("pool");
pub(crate) const REWARDING_STATUS: Map<(U32Key, IdentityKey), RewardingStatus> = Map::new("rm");
// approximately 1 day (assuming 5s per block)
pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280;
// approximately 30min (assuming 5s per block)
pub(crate) const MAX_REWARDING_DURATION_IN_BLOCKS: u64 = 360;
// TODO: Do we need a migration for this?
pub(crate) const REWARDING_STATUS: Map<(u32, IdentityKey), RewardingStatus> = Map::new("rm");
#[allow(dead_code)]
pub fn incr_reward_pool(
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::error::ContractError;
use crate::gateways::storage as gateways_storage;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{Addr, Deps, Storage};
use mixnet_contract::IdentityKeyRef;
use mixnet_contract_common::IdentityKeyRef;
pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec<u8> {
if let Some(proxy) = &proxy {
@@ -1,13 +1,11 @@
use crate::contract::{
DEFAULT_SYBIL_RESISTANCE_PERCENT, EPOCH_REWARD_PERCENT, INITIAL_MIXNODE_PLEDGE,
INITIAL_REWARD_POOL,
};
use crate::constants::{INTERVAL_REWARD_PERCENT, SYBIL_RESISTANCE_PERCENT};
use crate::contract::{INITIAL_MIXNODE_PLEDGE, INITIAL_REWARD_POOL};
use crate::mixnodes::storage as mixnodes_storage;
use crate::{mixnodes::storage::StoredMixnodeBond, support::tests};
use config::defaults::{DENOM, TOTAL_SUPPLY};
use cosmwasm_std::{coin, Addr, Coin};
use mixnet_contract::mixnode::NodeRewardParams;
use mixnet_contract::{Gateway, GatewayBond, Layer, MixNode};
use mixnet_contract_common::mixnode::NodeRewardParams;
use mixnet_contract_common::{Gateway, GatewayBond, Layer, MixNode};
pub fn mix_node_fixture() -> MixNode {
MixNode {
@@ -59,7 +57,6 @@ pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::Stor
..super::fixtures::mix_node_fixture()
},
None,
None,
)
}
@@ -80,13 +77,13 @@ pub fn good_gateway_pledge() -> Vec<Coin> {
// when exact values are irrelevant and what matters is the action of rewarding
pub fn node_rewarding_params_fixture(uptime: u128) -> NodeRewardParams {
NodeRewardParams::new(
(INITIAL_REWARD_POOL / 100) * EPOCH_REWARD_PERCENT as u128,
(INITIAL_REWARD_POOL / 100) * INTERVAL_REWARD_PERCENT as u128,
50 as u128,
25 as u128,
0,
TOTAL_SUPPLY - INITIAL_REWARD_POOL,
uptime,
DEFAULT_SYBIL_RESISTANCE_PERCENT,
SYBIL_RESISTANCE_PERCENT,
true,
10,
)
@@ -1,4 +1,4 @@
use mixnet_contract::{ExecuteMsg, Gateway, IdentityKey, MixNode};
use mixnet_contract_common::{ExecuteMsg, Gateway, IdentityKey, MixNode};
use rand::thread_rng;
use crate::support::tests;
+20 -4
View File
@@ -12,11 +12,12 @@ pub mod test_helpers {
use crate::contract::instantiate;
use crate::delegations::storage as delegations_storage;
use crate::gateways::transactions::try_add_gateway;
use crate::interval;
use crate::interval::storage as interval_storage;
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::try_add_mixnode;
use crate::support::tests;
use config::defaults::DENOM;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
@@ -25,10 +26,11 @@ pub mod test_helpers {
use cosmwasm_std::Coin;
use cosmwasm_std::DepsMut;
use cosmwasm_std::OwnedDeps;
use cosmwasm_std::{coin, Env, Timestamp};
use cosmwasm_std::{Addr, StdResult, Storage};
use cosmwasm_std::{Empty, MemoryStorage};
use cw_storage_plus::PrimaryKey;
use mixnet_contract::{Delegation, Gateway, IdentityKeyRef, InstantiateMsg, MixNode};
use mixnet_contract_common::{Delegation, Gateway, IdentityKeyRef, InstantiateMsg, MixNode};
use rand::thread_rng;
pub fn add_mixnode(sender: &str, stake: Vec<Coin>, deps: DepsMut) -> String {
@@ -81,11 +83,11 @@ pub mod test_helpers {
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
rewarding_validator_address: config::defaults::REWARDING_VALIDATOR_ADDRESS.to_string(),
rewarding_validator_address: config::defaults::DEFAULT_REWARDING_VALIDATOR.to_string(),
};
let env = mock_env();
let info = mock_info("creator", &[]);
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
instantiate(deps.as_mut(), env, info, msg).unwrap();
deps
}
@@ -125,4 +127,18 @@ pub mod test_helpers {
.may_load(storage, (mix.into(), owner.into()).joined_key())
.unwrap()
}
pub(crate) fn update_env_and_progress_interval(env: &mut Env, storage: &mut dyn Storage) {
// make sure current block time is within the expected next interval
env.block.time = Timestamp::from_seconds(
(interval_storage::CURRENT_INTERVAL
.load(storage)
.unwrap()
.next_interval()
.start_unix_timestamp()
+ 123) as u64,
);
interval::transactions::try_advance_interval(env.clone(), storage).unwrap();
}
}
@@ -4,7 +4,7 @@ use cosmwasm_std::{
testing::{mock_env, MockApi, MockQuerier, MockStorage},
Addr, Coin, OwnedDeps,
};
use mixnet_contract::{
use mixnet_contract_common::{
GatewayBond, MixNodeBond, PagedGatewayResponse, PagedMixnodeResponse, QueryMsg,
};
+4 -5
View File
@@ -14,14 +14,13 @@ exclude = [
crate-type = ["cdylib", "rlib"]
[dependencies]
mixnet-contract = { path = "../../common/mixnet-contract" }
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
config = { path = "../../common/config" }
cosmwasm-std = { version = "1.0.0-beta3"}
cw-storage-plus = { version = "0.10.3", features = ["iterator"] }
cw-storage-plus = { version = "0.11.1", features = ["iterator"] }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
rand = {version = "0.8.4", features = ["std_rng"]}
getrandom = { version = "0.2.3", features = ["js"]}
thiserror = { version = "1.0.23" }
+39 -16
View File
@@ -7,10 +7,15 @@ use crate::traits::{
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM};
use cosmwasm_std::{
entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Timestamp, Uint128,
};
use mixnet_contract::{Gateway, IdentityKey, MixNode};
use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
use vesting_contract_common::events::{
new_ownership_transfer_event, new_periodic_vesting_account_event,
new_staking_address_update_event, new_track_gateway_unbond_event,
new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event,
};
// We're using a 24 month vesting period with 3 months sub-periods.
// There are 8 three month periods in two years
@@ -96,6 +101,10 @@ pub fn try_withdraw_vested_coins(
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
if amount.denom != DENOM {
return Err(ContractError::WrongDenom(amount.denom, DENOM.to_string()));
}
let address = info.sender.clone();
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
if address != account.owner_address() {
@@ -111,12 +120,16 @@ pub fn try_withdraw_vested_coins(
let send_tokens = BankMsg::Send {
to_address: account.owner_address().as_str().to_string(),
amount: vec![amount],
amount: vec![amount.clone()],
};
Ok(Response::new()
.add_attribute("action", "whitdraw")
.add_message(send_tokens))
.add_message(send_tokens)
.add_event(new_vested_coins_withdraw_event(
&address,
&amount,
&coin(new_balance, &amount.denom),
)))
} else {
Err(ContractError::InsufficientSpendable(
account.owner_address().as_str().to_string(),
@@ -135,7 +148,7 @@ fn try_transfer_ownership(
let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
if address == account.owner_address() {
account.transfer_ownership(&to_address, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_ownership_transfer_event(&address, &to_address)))
} else {
Err(ContractError::NotOwner(account.owner_address().to_string()))
}
@@ -150,8 +163,9 @@ fn try_update_staking_address(
let to_address = to_address.and_then(|x| deps.api.addr_validate(&x).ok());
let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
if address == account.owner_address() {
account.update_staking_address(to_address, deps.storage)?;
Ok(Response::default())
let old = account.staking_address().cloned();
account.update_staking_address(to_address.clone(), deps.storage)?;
Ok(Response::new().add_event(new_staking_address_update_event(&old, &to_address)))
} else {
Err(ContractError::NotOwner(account.owner_address().to_string()))
}
@@ -186,7 +200,7 @@ pub fn try_track_unbond_gateway(
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.try_track_unbond_gateway(amount, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_track_gateway_unbond_event()))
}
pub fn try_bond_mixnode(
@@ -217,7 +231,7 @@ pub fn try_track_unbond_mixnode(
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.try_track_unbond_mixnode(amount, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_track_mixnode_unbond_event()))
}
fn try_track_undelegation(
@@ -232,7 +246,7 @@ fn try_track_undelegation(
}
let account = account_from_address(address, deps.storage, deps.api)?;
account.track_undelegation(mix_identity, amount, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_track_undelegation_event()))
}
fn try_delegate_to_mixnode(
@@ -275,15 +289,24 @@ fn try_create_periodic_vesting_account(
};
let start_time = start_time.unwrap_or_else(|| env.block.time.seconds());
let periods = populate_vesting_periods(start_time, NUM_VESTING_PERIODS);
let start_time = Timestamp::from_seconds(start_time);
Account::new(
owner_address,
staking_address,
coin,
Timestamp::from_seconds(start_time),
owner_address.clone(),
staking_address.clone(),
coin.clone(),
start_time,
periods,
deps.storage,
)?;
Ok(Response::default())
Ok(
Response::new().add_event(new_periodic_vesting_account_event(
&owner_address,
&coin,
&staking_address,
start_time,
)),
)
}
#[entry_point]

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