Compare commits

...

47 Commits

Author SHA1 Message Date
vnpmid d8fed178aa Upgrade to tokio 1.19.1, tokio-util 0.7.3, tokio-stream 0.1.9, tokio-test 0.4.2 (#1305)
* Upgrade to tokio 1.19.1, tokio-util 0.7.3, tokio-stream 0.1.9, tokio-test 0.4.2

* Tokio-util 0.7.3 handle_done_delaying, handle_expired_ack_timer

* Upgrade to tokio 1.19.1, tokio-util 0.7.3, tokio-stream 0.1.9, tokio-test 0.4.2
2022-06-06 14:38:01 +01:00
Drazen Urch 27a4a44717 Claim and compound reward wallet endpoints (#1302)
* Add claim and compound wallet endpoints, proc_macro to generate execute and simulate

* CHANGELOG

* Sort CHANGELOG lines

* PR comments
2022-06-06 14:19:36 +02:00
Drazen Urch 459db5718e Set probability to 1 if less nodes then the limit (#1306)
* Set probability to 1 if less nodes then the limit

* Check both sets independantly
2022-06-06 14:19:24 +02:00
durch 1bdaab6c97 Fix underflow in rewards estimation 2022-06-06 14:17:59 +02:00
Tommy Verrall d3fe80c875 Merge pull request #1303 from nymtech/feature-225-delegation-number
explorer: implement SummedDelegations endpoint and display value on f…
2022-06-06 10:04:32 +01:00
Raphaël Walther ec799cf17c Add sccache to wallet workflow 2022-06-02 18:59:23 +02:00
Bogdan-Ștefan Neacşu 5f7b3db9a4 Feature/spend coconut (#1261)
* Add coconut verifier structure for coconut protocol in gateway

* Add endpoint for validator-api cred verification

* Remove unused signature field

* Register new endpoint

* Improve validator-api config handling

* Aggregate verif result from all apis

* Simplify aggregate functions

* Verify cred on apis correctly

* Introduced coconut bandwidth contract to validator client

* Fix rebase double import

* Fix clippy on non-coconut

* Add multisig contract address to validator client

* Refactor Credential struct

* Do bincode magic in the coconut interface

* Implement serialization for credential and remove bindcode

* Fix clippy and don't remove dkg

* Client release funds proposal

* Add wrapper for blinded serial number

Also compare theta with a blinded serial number (in base 58 form)
for future double spend protection.

* Only post blinded serial number to blockchain

* Validator api propose credential spending

* Fix wallet

* Gateway calls proposal creation

* Query for proposal in verify coconut

* Remove db from git

* Verify against proposal description

* Validator apis vote based on verification of cred

* Fix wallet fmt

* Execute the release of funds

* Fix translation between token and bytes

* Update CHANGELOG
2022-06-02 16:54:59 +03:00
gala1234 6a929af584 Merge branch 'develop' into feature-225-delegation-number 2022-06-01 15:39:57 +02:00
Tommy Verrall a5759ab227 Merge pull request #1300 from nymtech/feature-224-add-stake-saturation
explorer: adding stake saturation o node list
2022-06-01 14:38:24 +01:00
Raphaël Walther 960305b54e Modify runner label 2022-06-01 15:20:40 +02:00
Raphaël Walther 066aded9bb Modify runner label 2022-06-01 14:54:22 +02:00
gala1234 a6a5ffce68 explorer: implement SummedDelegations endpoint and display value on frontend 2022-06-01 14:18:20 +02:00
Jon Häggblad 802334b37e explorer-api: add endpoint for summed delegations (#1299)
* explorer-api: add endpoint for summed delegations

* changelog: add note
2022-06-01 13:36:34 +02:00
Jon Häggblad c79ee5052f validator-api: add detailed mixnode bond endpoints (#1294)
* validator-api: add mixnodes-detailed endpoint

* validator-api: add detailed variants of active and rewarded set

* explorer-api: cache as mixnode bond detailed

* explorer-api: add in stake-saturation in response

* changelog: update

* rustfmt

* validator-api: rename to MixNodeBondResponse

* validator-api: cache MixNodeBondResponse instead

* validator-api: rename to MixNodeBondAnnotated

* validator-client: fix unused warning

* explorer-api: remove unnecessary clone

* rustfmt
2022-06-01 13:02:16 +02:00
Tommy Verrall fc985da2f7 Merge pull request #1301 from nymtech/feature/update-geo-ip
Update to use new geo_locate stats
2022-06-01 11:43:11 +01:00
tommy b7d3333ff8 Update to use new geo_locate stats 2022-06-01 11:19:54 +01:00
gala1234 41f4097628 explorer: remove hardcoded part 2022-06-01 11:29:02 +02:00
gala1234 e21216419d Merge branch 'develop' into feature-224-add-stake-saturation 2022-06-01 11:05:02 +02:00
Tommy Verrall 79e6dc5e77 Merge pull request #1298 from nymtech/fix/dotenv
Clippy warnings on .env file
2022-06-01 08:24:25 +01:00
tommy 266b050c82 merge 2022-05-31 17:30:12 +01:00
tommy 7609e7084c clippy warnings 2022-05-31 17:27:32 +01:00
Tommy Verrall 0faed6085e Merge pull request #1297 from nymtech/fix/dotenv
Allow .env not to be present
2022-05-31 17:05:54 +01:00
tommy c513d59724 allow dotenv not to be present 2022-05-31 17:01:02 +01:00
Jędrzej Stuczyński 9f51c60bac Wallet simulate bandaid (#1296)
* Removed Add/Sub that somehow got brought back in a merge

* Created 'get_old_and_incorrect_hardcoded_fee' to make wallet as it did before

* Brought back all Operation variants just in case
2022-05-31 15:52:26 +01:00
Jędrzej Stuczyński 58b5389ed9 Consolidate validator-client coin (#1295)
* Created nymd internal coin

* Additional From implementations plus a constructor

* try_add

* Changed client API to use the new coin type

* CoinConverter trait

* Made wallet compilable with the recent changes

* Simplified the API by removing the generics in favour of explicit Coin type

* Fixed validator api

* Fixed up tests and clippy

* Refactored  missed coin-generic API methods

* changelog
2022-05-31 14:10:19 +01:00
Drazen Urch 2f4be6dedc Add claim reward to vesting and mixnet contract (#1292)
* Add claim reward to vesting contract

* Add compound and claim methods to nymd client

* Add TrackReward message

* CHANGELOG
2022-05-31 11:06:10 +02:00
Drazen Urch 189b83e769 Fix uptime integer division, change lambda and sigma from ticked to regular (#1284)
* Add more data to reward-estimate response

* Fix uptime integer division error

* typo

* Reify tuple response

* Fix uptime calculation

* Use lambda and sigma instead of ticked versions for delegator and operator rewards calculation

* Changelog
2022-05-31 11:03:28 +02:00
Jon Häggblad 83f80f094c wallet: tweak log text to avoid confusion 2022-05-31 09:06:57 +02:00
Jędrzej Stuczyński 2dfbd2f714 Removed rustfmt.toml file and adjusted wallet formatting (#1293) 2022-05-30 10:57:49 +01:00
gala1234 296ed47ba4 explorer: fixing colour 2022-05-26 12:32:21 +02:00
gala1234 7dd11d4602 explorer: stake saturation colour 2022-05-26 12:25:43 +02:00
gala1234 02a34b2592 Merge branch 'develop' into feature-224-add-stake-saturation 2022-05-26 12:04:26 +02:00
gala1234 6afecbddfa explorer: mook stake saturation response from API 2022-05-26 12:02:44 +02:00
Mark Sinclair 41d5c05a76 Merge pull request #1290 from nymtech/fix/build-dev-wallet-windows
add run-all package to build dev wallet on windows
2022-05-26 09:57:09 +01:00
Bogdan-Ștefan Neacşu a9422a32ed Make stats in network requester opt-in (#1288) 2022-05-26 11:33:25 +03:00
Jędrzej Stuczyński 177f1deefe Feature/gas simulation improvements (#1291)
* Updated cosmrs

* Simpler fmt::Display for Operation

* Adjusted GasPrice parsing test due to changes in Denom FromStr impl

* Removed direct dependency on cosmrs in the wallet

* Removed TryFrom<GasPrice> for Coin as it didn't make much sense

* disgusting workaround for providing serde for fee

* NymdError improvements

* Ability to simulate "send"

* Removed needless conversions

* Changedi nterface to "normal" send to account for new fee

* Removed outdated imports in tests

* Removal of 'Operation' enum

* Implemented simulate endpoints for all other txs
2022-05-26 09:26:49 +01:00
Tommy Verrall a1f633d225 add run-all package to build dev wallet on windows 2022-05-25 17:13:01 +01:00
Jon Häggblad 60526fdb90 wallet: check against importing same mnemonic twice (#1283) 2022-05-24 15:13:45 +02:00
Jędrzej Stuczyński 7d82fe0c0d Feature/various improvements (#1282)
* Added abci::Data field to ExecuteResult

* optional serde support for ed25519 keys

* optional serde support for x25519 keys

* actually calling dotenv at validator API startup

* Added STATE_DENOM network specific constant

* unit test fixes
2022-05-24 09:52:00 +01:00
Bogdan-Ștefan Neacşu d7920a4f50 Use remote source for stats provider address (#1281) 2022-05-23 16:33:47 +03:00
durch 71cfdb4d07 Add vesting glossary 2022-05-23 13:25:20 +02:00
Raphaël Walther db743578a9 Add an extra clean step in nightly build 2022-05-23 11:13:07 +02:00
Bogdan-Ștefan Neacșu ba7f535cb7 Update stats provider mainnet client address 2022-05-20 13:23:58 +03:00
Bogdan-Ștefan Neacşu fd4f5b319c Add serviced client data to stats (#1278)
* Add serviced client data to stats

* Put stats service address in network defaults

* Update CHANGELOG.md
2022-05-19 15:54:51 +03:00
Mark Sinclair 6045f57612 Merge pull request #1279 from nymtech/change-text
change text
2022-05-19 12:50:20 +01:00
Mark Sinclair 878cb3f0e5 Merge pull request #1277 from nymtech/av-uptime-nodes-list
adding percentage symbol and make column wider
2022-05-19 12:50:06 +01:00
gala1234 1362fcdbfa adding percentage symbol and make column wider 2022-05-18 09:41:56 +02:00
195 changed files with 8798 additions and 5772 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs: jobs:
build: build:
runs-on: [ self-hosted, custom-linux-exoscale ] runs-on: [ self-hosted, custom-linux ]
# Enable sccache via environment variable # Enable sccache via environment variable
env: env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
+1 -1
View File
@@ -4,7 +4,7 @@ on: workflow_dispatch
jobs: jobs:
build: build:
runs-on: [ self-hosted, custom-linux-exoscale ] runs-on: [ self-hosted, custom-linux ]
# Enable sccache via environment variable # Enable sccache via environment variable
env: env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
+6
View File
@@ -75,6 +75,12 @@ jobs:
command: clippy command: clippy
args: --workspace --all-targets -- -D warnings args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
# COCONUT stuff # COCONUT stuff
- name: Build all binaries with coconut enabled - name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
+3 -1
View File
@@ -10,7 +10,9 @@ on:
jobs: jobs:
build: build:
runs-on: [ self-hosted, custom-linux-exoscale ] runs-on: [ self-hosted, custom-linux ]
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps: steps:
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
+28 -7
View File
@@ -4,30 +4,51 @@
### Added ### Added
- wallet: require password to switch accounts - all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
- explorer-api: learned how to sum the delegations by owner in a new endpoint.
- gateway: Added gateway coconut verifications and validator-api communication for double spending protection ([#1261])
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
- network-requester: send traffic statistics from all network requesters and receive it in a special network-requester that aggregates the data and exposes it via a rest API ([#1267], [#1278]).
- validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284])
- validator-api: add detailed mixnode bond endpoints, and explorer-api makes use of that data to append stake saturation.
- validator-api: add Swagger to document the REST API ([#1249]).
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
- wallet: add simple CLI tool for decrypting and recovering the wallet file. - wallet: add simple CLI tool for decrypting and recovering the wallet file.
- wallet: added support for multiple accounts ([#1265]) - wallet: added support for multiple accounts ([#1265])
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
- wallet: require password to switch accounts
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint. - wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
- validator-api: add Swagger to document the REST API ([#1249]).
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
- network-requester: send traffic statistics from all network requesters and receive it in a special network-requester that aggregates the data and exposes it via a rest API ([#1267]).
### Fixed ### Fixed
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275]) - mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257]) - mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258]) - mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260]) - mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
### Changed
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
[#1258]: https://github.com/nymtech/nym/pull/1258
[#1249]: https://github.com/nymtech/nym/pull/1249 [#1249]: https://github.com/nymtech/nym/pull/1249
[#1256]: https://github.com/nymtech/nym/pull/1256 [#1256]: https://github.com/nymtech/nym/pull/1256
[#1257]: https://github.com/nymtech/nym/pull/1257 [#1257]: https://github.com/nymtech/nym/pull/1257
[#1258]: https://github.com/nymtech/nym/pull/1258
[#1260]: https://github.com/nymtech/nym/pull/1260 [#1260]: https://github.com/nymtech/nym/pull/1260
[#1261]: https://github.com/nymtech/nym/pull/1261
[#1265]: https://github.com/nymtech/nym/pull/1265 [#1265]: https://github.com/nymtech/nym/pull/1265
[#1267]: https://github.com/nymtech/nym/pull/1267 [#1267]: https://github.com/nymtech/nym/pull/1267
[#1275]: https://github.com/nymtech/nym/pull/1275 [#1275]: https://github.com/nymtech/nym/pull/1275
[#1278]: https://github.com/nymtech/nym/pull/1278
[#1284]: https://github.com/nymtech/nym/pull/1284
[#1292]: https://github.com/nymtech/nym/pull/1292
[#1295]: https://github.com/nymtech/nym/pull/1295
[#1302]: https://github.com/nymtech/nym/pull/1302
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04) ## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
Generated
+158 -87
View File
@@ -664,8 +664,8 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a" checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a"
dependencies = [ dependencies = [
"prost", "prost 0.9.0",
"prost-types", "prost-types 0.9.0",
"tonic", "tonic",
"tonic-build", "tonic-build",
"tracing-core", "tracing-core",
@@ -683,7 +683,7 @@ dependencies = [
"futures", "futures",
"hdrhistogram", "hdrhistogram",
"humantime 2.1.0", "humantime 2.1.0",
"prost-types", "prost-types 0.9.0",
"serde", "serde",
"serde_json", "serde_json",
"thread_local", "thread_local",
@@ -761,60 +761,29 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]] [[package]]
name = "cosmos-sdk-proto" name = "cosmos-sdk-proto"
version = "0.9.0" version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0254ffee603f5301d6a66963d9e1cc5091479c22e2e925e1f7689c8027a0828" checksum = "f109fe191e73898d74b8020c50f86018364ad19bc30318aa074616c382b52856"
dependencies = [ dependencies = [
"prost", "prost 0.10.3",
"prost-types", "prost-types 0.10.1",
"tendermint-proto",
]
[[package]]
name = "cosmos-sdk-proto"
version = "0.9.0"
source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496"
dependencies = [
"prost",
"prost-types",
"tendermint-proto", "tendermint-proto",
] ]
[[package]] [[package]]
name = "cosmrs" name = "cosmrs"
version = "0.4.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d" checksum = "8413275b23cb5a0734d9d1e3e33f0b5b94547c1e94776dbc3149dbf46588a533"
dependencies = [ dependencies = [
"bip32", "bip32",
"cosmos-sdk-proto 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "cosmos-sdk-proto",
"ecdsa 0.13.4", "ecdsa 0.13.4",
"eyre", "eyre",
"getrandom 0.2.6", "getrandom 0.2.6",
"k256 0.10.4", "k256 0.10.4",
"prost", "prost 0.10.3",
"prost-types", "prost-types 0.10.1",
"rand_core 0.6.3",
"serde",
"serde_json",
"subtle-encoding",
"tendermint",
"thiserror",
]
[[package]]
name = "cosmrs"
version = "0.4.1"
source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496"
dependencies = [
"bip32",
"cosmos-sdk-proto 0.9.0 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)",
"ecdsa 0.13.4",
"eyre",
"getrandom 0.2.6",
"k256 0.10.4",
"prost",
"prost-types",
"rand_core 0.6.3", "rand_core 0.6.3",
"serde", "serde",
"serde_json", "serde_json",
@@ -904,7 +873,6 @@ dependencies = [
"bip39", "bip39",
"cfg-if 0.1.10", "cfg-if 0.1.10",
"clap 3.1.8", "clap 3.1.8",
"coconut-bandwidth-contract-common",
"coconut-interface", "coconut-interface",
"credential-storage", "credential-storage",
"credentials", "credentials",
@@ -938,7 +906,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bls12_381 0.5.0", "bls12_381 0.5.0",
"coconut-interface", "coconut-interface",
"cosmrs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "cosmrs",
"crypto", "crypto",
"network-defaults", "network-defaults",
"rand 0.7.3", "rand 0.7.3",
@@ -1063,6 +1031,8 @@ dependencies = [
"pemstore", "pemstore",
"rand 0.7.3", "rand 0.7.3",
"rand_chacha 0.2.2", "rand_chacha 0.2.2",
"serde",
"serde_bytes",
"subtle-encoding", "subtle-encoding",
"x25519-dalek", "x25519-dalek",
] ]
@@ -1179,6 +1149,7 @@ dependencies = [
"byteorder", "byteorder",
"digest 0.9.0", "digest 0.9.0",
"rand_core 0.5.1", "rand_core 0.5.1",
"serde",
"subtle 2.4.1", "subtle 2.4.1",
"zeroize", "zeroize",
] ]
@@ -1194,6 +1165,42 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "cw-utils"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "babd2c090f39d07ce5bf2556962305e795daa048ce20a93709eb591476e4a29e"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cw3"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f871854338a54c7bb094d16ffe17212b93b146d9659dbce4c9402a9b77e240ef"
dependencies = [
"cosmwasm-std",
"cw-utils",
"schemars",
"serde",
]
[[package]]
name = "cw4"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4476d6a7c13c46ed9ff260bd0e1cf648dc37b13f483822e1ff2a431f0f6ee52"
dependencies = [
"cosmwasm-std",
"cw-storage-plus",
"schemars",
"serde",
]
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.13.4" version = "0.13.4"
@@ -1427,6 +1434,7 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4" checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4"
dependencies = [ dependencies = [
"serde",
"signature", "signature",
] ]
@@ -1440,6 +1448,7 @@ dependencies = [
"ed25519", "ed25519",
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
"serde_bytes",
"sha2", "sha2",
"zeroize", "zeroize",
] ]
@@ -1593,6 +1602,14 @@ dependencies = [
"uint", "uint",
] ]
[[package]]
name = "execute"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "explorer-api" name = "explorer-api"
version = "1.0.1" version = "1.0.1"
@@ -1600,6 +1617,7 @@ dependencies = [
"chrono", "chrono",
"humantime-serde", "humantime-serde",
"isocountry", "isocountry",
"itertools",
"log", "log",
"mixnet-contract-common", "mixnet-contract-common",
"network-defaults", "network-defaults",
@@ -1976,7 +1994,6 @@ dependencies = [
name = "gateway-requests" name = "gateway-requests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bincode",
"bs58", "bs58",
"coconut-interface", "coconut-interface",
"credentials", "credentials",
@@ -2132,7 +2149,7 @@ dependencies = [
"indexmap", "indexmap",
"slab", "slab",
"tokio", "tokio",
"tokio-util 0.7.1", "tokio-util 0.7.3",
"tracing", "tracing",
] ]
@@ -2380,11 +2397,12 @@ dependencies = [
"headers", "headers",
"http", "http",
"hyper", "hyper",
"hyper-tls", "hyper-rustls",
"native-tls", "rustls-native-certs",
"tokio", "tokio",
"tokio-native-tls", "tokio-rustls",
"tower-service", "tower-service",
"webpki",
] ]
[[package]] [[package]]
@@ -2861,7 +2879,7 @@ dependencies = [
"log", "log",
"nymsphinx", "nymsphinx",
"tokio", "tokio",
"tokio-util 0.6.9", "tokio-util 0.7.3",
] ]
[[package]] [[package]]
@@ -2902,7 +2920,7 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"tokio", "tokio",
"tokio-util 0.6.9", "tokio-util 0.7.3",
"url", "url",
"validator-client", "validator-client",
"version-checker", "version-checker",
@@ -2934,6 +2952,18 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
name = "multisig-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-utils",
"cw3",
"cw4",
"schemars",
"serde",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.10" version = "0.2.10"
@@ -2980,7 +3010,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util 0.6.9", "tokio-util 0.7.3",
] ]
[[package]] [[package]]
@@ -3115,7 +3145,7 @@ dependencies = [
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-tungstenite", "tokio-tungstenite",
"tokio-util 0.6.9", "tokio-util 0.7.3",
"url", "url",
"validator-client", "validator-client",
"vergen", "vergen",
@@ -3152,7 +3182,7 @@ dependencies = [
"serde", "serde",
"serial_test", "serial_test",
"tokio", "tokio",
"tokio-util 0.6.9", "tokio-util 0.7.3",
"toml", "toml",
"topology", "topology",
"url", "url",
@@ -3178,6 +3208,7 @@ dependencies = [
"proxy-helpers", "proxy-helpers",
"publicsuffix", "publicsuffix",
"rand 0.7.3", "rand 0.7.3",
"reqwest",
"rocket", "rocket",
"serde", "serde",
"socks5-requests", "socks5-requests",
@@ -3237,6 +3268,7 @@ dependencies = [
"coconut-interface", "coconut-interface",
"config", "config",
"console-subscriber", "console-subscriber",
"cosmwasm-std",
"credential-storage", "credential-storage",
"credentials", "credentials",
"crypto", "crypto",
@@ -3248,6 +3280,7 @@ dependencies = [
"humantime-serde", "humantime-serde",
"log", "log",
"mixnet-contract-common", "mixnet-contract-common",
"multisig-contract-common",
"nymcoconut", "nymcoconut",
"nymsphinx", "nymsphinx",
"okapi", "okapi",
@@ -3397,7 +3430,7 @@ dependencies = [
"bytes", "bytes",
"nymsphinx-params", "nymsphinx-params",
"nymsphinx-types", "nymsphinx-types",
"tokio-util 0.6.9", "tokio-util 0.7.3",
] ]
[[package]] [[package]]
@@ -3932,7 +3965,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
dependencies = [ dependencies = [
"bytes", "bytes",
"prost-derive", "prost-derive 0.9.0",
]
[[package]]
name = "prost"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc03e116981ff7d8da8e5c220e374587b98d294af7ba7dd7fda761158f00086f"
dependencies = [
"bytes",
"prost-derive 0.10.1",
] ]
[[package]] [[package]]
@@ -3948,8 +3991,8 @@ dependencies = [
"log", "log",
"multimap", "multimap",
"petgraph", "petgraph",
"prost", "prost 0.9.0",
"prost-types", "prost-types 0.9.0",
"regex", "regex",
"tempfile", "tempfile",
"which", "which",
@@ -3968,6 +4011,19 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "prost-derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "prost-types" name = "prost-types"
version = "0.9.0" version = "0.9.0"
@@ -3975,7 +4031,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a"
dependencies = [ dependencies = [
"bytes", "bytes",
"prost", "prost 0.9.0",
]
[[package]]
name = "prost-types"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68"
dependencies = [
"bytes",
"prost 0.10.3",
] ]
[[package]] [[package]]
@@ -3989,7 +4055,7 @@ dependencies = [
"socks5-requests", "socks5-requests",
"tokio", "tokio",
"tokio-test", "tokio-test",
"tokio-util 0.6.9", "tokio-util 0.7.3",
] ]
[[package]] [[package]]
@@ -4834,9 +4900,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_bytes" name = "serde_bytes"
version = "0.11.5" version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -5459,9 +5525,9 @@ dependencies = [
[[package]] [[package]]
name = "tendermint" name = "tendermint"
version = "0.23.3" version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9ef686b8ecd36550d0581f0989c9d8607090b23005df479d8e6ba348b5800b9" checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@@ -5472,8 +5538,8 @@ dependencies = [
"k256 0.10.4", "k256 0.10.4",
"num-traits", "num-traits",
"once_cell", "once_cell",
"prost", "prost 0.10.3",
"prost-types", "prost-types 0.10.1",
"ripemd160", "ripemd160",
"serde", "serde",
"serde_bytes", "serde_bytes",
@@ -5490,9 +5556,9 @@ dependencies = [
[[package]] [[package]]
name = "tendermint-config" name = "tendermint-config"
version = "0.23.3" version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc127f82e7a8c7337c1f293d65a821380d3407c4c44bc979ef4da0ebc3b31ed" checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd"
dependencies = [ dependencies = [
"flex-error", "flex-error",
"serde", "serde",
@@ -5504,16 +5570,16 @@ dependencies = [
[[package]] [[package]]
name = "tendermint-proto" name = "tendermint-proto"
version = "0.23.3" version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e0a0251fd81bed7420bea0f4d91c2a58f6c9fa34d5b2f70bed0ba8890de5aa" checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874"
dependencies = [ dependencies = [
"bytes", "bytes",
"flex-error", "flex-error",
"num-derive", "num-derive",
"num-traits", "num-traits",
"prost", "prost 0.10.3",
"prost-types", "prost-types 0.10.1",
"serde", "serde",
"serde_bytes", "serde_bytes",
"subtle-encoding", "subtle-encoding",
@@ -5522,9 +5588,9 @@ dependencies = [
[[package]] [[package]]
name = "tendermint-rpc" name = "tendermint-rpc"
version = "0.23.3" version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626c493e9ced3a9de37583bbd929f4b6dbd49aa513ab9b4776aa44a9332ce9b5" checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@@ -5695,9 +5761,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.17.0" version = "1.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" checksum = "95eec79ea28c00a365f539f1961e9278fbcaf81c0ff6aaf0e93c181352446948"
dependencies = [ dependencies = [
"bytes", "bytes",
"libc", "libc",
@@ -5758,9 +5824,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.8" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"pin-project-lite", "pin-project-lite",
@@ -5805,20 +5871,20 @@ dependencies = [
"futures-sink", "futures-sink",
"log", "log",
"pin-project-lite", "pin-project-lite",
"slab",
"tokio", "tokio",
] ]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.1" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"pin-project-lite", "pin-project-lite",
"slab",
"tokio", "tokio",
"tracing", "tracing",
] ]
@@ -5851,8 +5917,8 @@ dependencies = [
"hyper-timeout", "hyper-timeout",
"percent-encoding", "percent-encoding",
"pin-project", "pin-project",
"prost", "prost 0.9.0",
"prost-derive", "prost-derive 0.9.0",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util 0.6.9", "tokio-util 0.6.9",
@@ -5903,7 +5969,7 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"slab", "slab",
"tokio", "tokio",
"tokio-util 0.7.1", "tokio-util 0.7.3",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing", "tracing",
@@ -6192,18 +6258,22 @@ dependencies = [
"async-trait", "async-trait",
"base64", "base64",
"bip39", "bip39",
"coconut-bandwidth-contract-common",
"coconut-interface", "coconut-interface",
"colored", "colored",
"config", "config",
"cosmrs 0.4.1 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)", "cosmrs",
"cosmwasm-std", "cosmwasm-std",
"cw3",
"execute",
"flate2", "flate2",
"futures", "futures",
"itertools", "itertools",
"log", "log",
"mixnet-contract-common", "mixnet-contract-common",
"multisig-contract-common",
"network-defaults", "network-defaults",
"prost", "prost 0.10.3",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
@@ -6623,6 +6693,7 @@ checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"rand_core 0.5.1", "rand_core 0.5.1",
"serde",
"zeroize", "zeroize",
] ]
+2
View File
@@ -31,10 +31,12 @@ members = [
"common/credentials", "common/credentials",
"common/crypto", "common/crypto",
"common/crypto/dkg", "common/crypto/dkg",
"common/execute",
"common/bandwidth-claim-contract", "common/bandwidth-claim-contract",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/vesting-contract", "common/cosmwasm-smart-contracts/vesting-contract",
"common/mixnode-common", "common/mixnode-common",
"common/network-defaults", "common/network-defaults",
+1 -1
View File
@@ -14,7 +14,7 @@ log = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
sled = "0.34" sled = "0.34"
tokio = { version = "1.4", features = ["macros"] } tokio = { version = "1.19.1", features = ["macros"] }
url = { version ="2.2", features = ["serde"] } url = { version ="2.2", features = ["serde"] }
# internal # internal
@@ -6,7 +6,7 @@ use crate::client::real_messages_control::acknowledgement_control::Retransmissio
use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey, TimerError}; use nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey};
use nymsphinx::chunking::fragment::FragmentIdentifier; use nymsphinx::chunking::fragment::FragmentIdentifier;
use nymsphinx::Delay as SphinxDelay; use nymsphinx::Delay as SphinxDelay;
use std::collections::HashMap; use std::collections::HashMap;
@@ -209,16 +209,11 @@ impl ActionController {
} }
// note: when the entry expires it's automatically removed from pending_acks_timers // note: when the entry expires it's automatically removed from pending_acks_timers
fn handle_expired_ack_timer( fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) {
&mut self,
expired_ack: Result<Expired<FragmentIdentifier>, TimerError>,
) {
// I'm honestly not sure how to handle it, because getting it means other things in our // I'm honestly not sure how to handle it, because getting it means other things in our
// system are already misbehaving. If we ever see this panic, then I guess we should worry // system are already misbehaving. If we ever see this panic, then I guess we should worry
// about it. Perhaps just reschedule it at later point? // about it. Perhaps just reschedule it at later point?
let frag_id = expired_ack let frag_id = expired_ack.into_inner();
.expect("Tokio timer returned an error!")
.into_inner();
trace!("{} has expired", frag_id); trace!("{} has expired", frag_id);
+1 -2
View File
@@ -15,9 +15,8 @@ rand = "0.7.3"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0" thiserror = "1.0"
url = "2.2" url = "2.2"
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
coconut-interface = { path = "../../common/coconut-interface" } coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" } credentials = { path = "../../common/credentials" }
credential-storage = { path = "../../common/credential-storage" } credential-storage = { path = "../../common/credential-storage" }
File diff suppressed because one or more lines are too long
+17 -35
View File
@@ -2,66 +2,48 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use bip39::Mnemonic; use bip39::Mnemonic;
use coconut_bandwidth_contract_common::deposit::DepositData;
use std::str::FromStr; use std::str::FromStr;
use url::Url; use url::Url;
use crate::error::Result; use crate::error::Result;
use crate::{CONTRACT_ADDRESS, MNEMONIC, NYMD_URL}; use crate::{MNEMONIC, NYMD_URL};
use coconut_bandwidth_contract_common::msg::ExecuteMsg; use network_defaults::{DEFAULT_NETWORK, DENOM, VOUCHER_INFO};
use network_defaults::DEFAULT_NETWORK; use validator_client::nymd::traits::CoconutBandwidthSigningClient;
use validator_client::nymd::{ use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient};
AccountId, CosmosCoin, Decimal, Denom, NymdClient, SigningNymdClient,
};
pub(crate) struct Client { pub(crate) struct Client {
nymd_client: NymdClient<SigningNymdClient>, nymd_client: NymdClient<SigningNymdClient>,
denom: Denom,
contract_address: AccountId,
} }
impl Client { impl Client {
pub fn new() -> Self { pub fn new() -> Self {
let nymd_url = Url::from_str(NYMD_URL).unwrap(); let nymd_url = Url::from_str(NYMD_URL).unwrap();
let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap(); let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
let nymd_client = NymdClient::connect_with_mnemonic( let nymd_client =
DEFAULT_NETWORK, NymdClient::connect_with_mnemonic(DEFAULT_NETWORK, nymd_url.as_ref(), mnemonic, None)
nymd_url.as_ref(), .unwrap();
None,
None,
None,
mnemonic,
None,
)
.unwrap();
let denom = Denom::from_str(network_defaults::DENOM).unwrap();
let contract_address = AccountId::from_str(CONTRACT_ADDRESS).unwrap();
Client { Client { nymd_client }
nymd_client,
denom,
contract_address,
}
} }
pub async fn deposit( pub async fn deposit(
&self, &self,
amount: u64, amount: u64,
info: &str,
verification_key: String, verification_key: String,
encryption_key: String, encryption_key: String,
fee: Option<Fee>,
) -> Result<String> { ) -> Result<String> {
let req = ExecuteMsg::DepositFunds { let amount = Coin::new(amount as u128, DENOM.to_string());
data: DepositData::new(info.to_string(), verification_key, encryption_key),
};
let funds = vec![CosmosCoin {
denom: self.denom.clone(),
amount: Decimal::from(amount),
}];
Ok(self Ok(self
.nymd_client .nymd_client
.execute(&self.contract_address, &req, Default::default(), "", funds) .deposit(
amount,
String::from(VOUCHER_INFO),
verification_key,
encryption_key,
fee,
)
.await? .await?
.transaction_hash .transaction_hash
.to_string()) .to_string())
+1 -1
View File
@@ -55,9 +55,9 @@ impl Execute for Deposit {
let tx_hash = client let tx_hash = client
.deposit( .deposit(
self.amount, self.amount,
VOUCHER_INFO,
signing_keypair.public_key.clone(), signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(), encryption_keypair.public_key.clone(),
None,
) )
.await?; .await?;
+1 -1
View File
@@ -27,7 +27,7 @@ pretty_env_logger = "0.4" # for formatting log messages
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
sled = "0.34" # for storage of replySURB decryption keys sled = "0.34" # for storage of replySURB decryption keys
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.14" # websocket tokio-tungstenite = "0.14" # websocket
## internal ## internal
+1 -1
View File
@@ -20,7 +20,7 @@ pretty_env_logger = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
snafu = "0.6" snafu = "0.6"
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal"] }
url = "2.2" url = "2.2"
# internal # internal
+1 -1
View File
@@ -35,7 +35,7 @@ default-features = false
# non-wasm-only dependencies # non-wasm-only dependencies
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
version = "1.4" version = "1.19.1"
features = ["macros", "rt", "net", "sync", "time"] features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
@@ -83,7 +83,7 @@ impl GatewayClient {
) -> Self { ) -> Self {
GatewayClient { GatewayClient {
authenticated: false, authenticated: false,
disabled_credentials_mode: true, disabled_credentials_mode: false,
bandwidth_remaining: 0, bandwidth_remaining: 0,
gateway_address, gateway_address,
gateway_identity, gateway_identity,
@@ -100,8 +100,8 @@ impl GatewayClient {
} }
} }
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) { pub fn set_disabled_credentials_mode(&mut self, _disabled_credentials_mode: bool) {
self.disabled_credentials_mode = disabled_credentials_mode self.disabled_credentials_mode = false;
} }
// TODO: later convert into proper builder methods // TODO: later convert into proper builder methods
@@ -496,7 +496,6 @@ impl GatewayClient {
self.shared_key.as_ref().unwrap(), self.shared_key.as_ref().unwrap(),
iv, iv,
) )
.ok_or(GatewayClientError::SerializeCredential)?
.into(); .into();
self.bandwidth_remaining = match self.send_websocket_message(msg).await? { self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total), ServerResponse::Bandwidth { available_total } => Ok(available_total),
+2 -2
View File
@@ -9,8 +9,8 @@ edition = "2021"
[dependencies] [dependencies]
futures = "0.3" futures = "0.3"
log = "0.4.8" log = "0.4.8"
tokio = { version = "1.4", features = ["time", "net", "rt"] } tokio = { version = "1.19.1", features = ["time", "net", "rt"] }
tokio-util = { version = "0.6", features = ["codec"] } tokio-util = { version = "0.7.3", features = ["codec"] }
# internal # internal
nymsphinx = {path = "../../nymsphinx" } nymsphinx = {path = "../../nymsphinx" }
@@ -10,8 +10,11 @@ rust-version = "1.56"
[dependencies] [dependencies]
base64 = "0.13" base64 = "0.13"
colored = "2.0" colored = "2.0"
cw3 = "0.13.1"
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" } mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" } vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
vesting-contract = { path = "../../../contracts/vesting" } vesting-contract = { path = "../../../contracts/vesting" }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
@@ -19,7 +22,7 @@ reqwest = { version = "0.11", features = ["json"] }
thiserror = "1" thiserror = "1"
log = "0.4" log = "0.4"
url = { version = "2.2", features = ["serde"] } url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.10", features = ["sync", "time"] } tokio = { version = "1.19.1", features = ["sync", "time"] }
futures = "0.3" futures = "0.3"
coconut-interface = { path = "../../coconut-interface" } coconut-interface = { path = "../../coconut-interface" }
@@ -32,12 +35,13 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests
async-trait = { version = "0.1.51", optional = true } async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true } bip39 = { version = "1", features = ["rand"], optional = true }
config = { path = "../../config", optional = true } config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"], optional = true} cosmrs = { version = "0.7.0", features = ["rpc", "bip32", "cosmwasm"], optional = true}
prost = { version = "0.9", default-features = false, optional = true } prost = { version = "0.10", default-features = false, optional = true }
flate2 = { version = "1.0.20", optional = true } flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true } sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true } itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0-beta8", optional = true } cosmwasm-std = { version = "1.0.0-beta8", optional = true }
execute = { path = "../../execute" }
[dev-dependencies] [dev-dependencies]
ts-rs = "6.1.2" ts-rs = "6.1.2"
+108 -60
View File
@@ -2,16 +2,20 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::{validator_api, ValidatorClientError}; use crate::{validator_api, ValidatorClientError};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; use coconut_interface::{
BlindSignRequestBody, BlindedSignatureResponse, ExecuteReleaseFundsRequestBody,
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
VerifyCredentialBody, VerifyCredentialResponse,
};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use url::Url; use url::Url;
#[cfg(feature = "nymd-client")]
use validator_api_requests::models::UptimeResponse;
use validator_api_requests::models::{ use validator_api_requests::models::{
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
StakeSaturationResponse, StakeSaturationResponse,
}; };
#[cfg(feature = "nymd-client")]
use validator_api_requests::models::{MixNodeBondAnnotated, UptimeResponse};
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
use network_defaults::DEFAULT_NETWORK; use network_defaults::DEFAULT_NETWORK;
@@ -29,8 +33,6 @@ use mixnet_contract_common::{
}; };
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[cfg(feature = "nymd-client")]
use std::str::FromStr;
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
#[must_use] #[must_use]
@@ -39,9 +41,9 @@ pub struct Config {
network: network_defaults::all::Network, network: network_defaults::all::Network,
api_url: Url, api_url: Url,
nymd_url: Url, nymd_url: Url,
mixnet_contract_address: Option<cosmrs::AccountId>, mixnet_contract_address: cosmrs::AccountId,
vesting_contract_address: Option<cosmrs::AccountId>, vesting_contract_address: cosmrs::AccountId,
erc20_bridge_contract_address: Option<cosmrs::AccountId>, bandwidth_claim_contract_address: cosmrs::AccountId,
mixnode_page_limit: Option<u32>, mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>, gateway_page_limit: Option<u32>,
@@ -51,20 +53,22 @@ pub struct Config {
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
impl Config { impl Config {
pub fn new( pub fn new(network: network_defaults::all::Network, nymd_url: Url, api_url: Url) -> Self {
network: network_defaults::all::Network,
nymd_url: Url,
api_url: Url,
mixnet_contract_address: Option<cosmrs::AccountId>,
vesting_contract_address: Option<cosmrs::AccountId>,
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
) -> Self {
Config { Config {
network, network,
nymd_url, nymd_url,
mixnet_contract_address, mixnet_contract_address: DEFAULT_NETWORK
vesting_contract_address, .mixnet_contract_address()
erc20_bridge_contract_address, .parse()
.expect("Error parsing mixnet contract address"),
vesting_contract_address: DEFAULT_NETWORK
.vesting_contract_address()
.parse()
.expect("Error parsing vesting contract address"),
bandwidth_claim_contract_address: DEFAULT_NETWORK
.bandwidth_claim_contract_address()
.parse()
.expect("Error parsing bandwidth claim contract address"),
api_url, api_url,
mixnode_page_limit: None, mixnode_page_limit: None,
gateway_page_limit: None, gateway_page_limit: None,
@@ -73,6 +77,21 @@ impl Config {
} }
} }
pub fn with_mixnode_contract_address(mut self, address: cosmrs::AccountId) -> Self {
self.mixnet_contract_address = address;
self
}
pub fn with_vesting_contract_address(mut self, address: cosmrs::AccountId) -> Self {
self.vesting_contract_address = address;
self
}
pub fn with_bandwidth_claim_contract_address(mut self, address: cosmrs::AccountId) -> Self {
self.bandwidth_claim_contract_address = address;
self
}
pub fn with_mixnode_page_limit(mut self, limit: Option<u32>) -> Config { pub fn with_mixnode_page_limit(mut self, limit: Option<u32>) -> Config {
self.mixnode_page_limit = limit; self.mixnode_page_limit = limit;
self self
@@ -97,9 +116,9 @@ impl Config {
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
pub struct Client<C> { pub struct Client<C> {
pub network: network_defaults::all::Network, pub network: network_defaults::all::Network,
mixnet_contract_address: Option<cosmrs::AccountId>, mixnet_contract_address: cosmrs::AccountId,
vesting_contract_address: Option<cosmrs::AccountId>, vesting_contract_address: cosmrs::AccountId,
erc20_bridge_contract_address: Option<cosmrs::AccountId>, bandwidth_claim_contract_address: cosmrs::AccountId,
mnemonic: Option<bip39::Mnemonic>, mnemonic: Option<bip39::Mnemonic>,
mixnode_page_limit: Option<u32>, mixnode_page_limit: Option<u32>,
@@ -122,18 +141,18 @@ impl Client<SigningNymdClient> {
let nymd_client = NymdClient::connect_with_mnemonic( let nymd_client = NymdClient::connect_with_mnemonic(
config.network, config.network,
config.nymd_url.as_str(), config.nymd_url.as_str(),
config.mixnet_contract_address.clone(),
config.vesting_contract_address.clone(),
config.erc20_bridge_contract_address.clone(),
mnemonic.clone(), mnemonic.clone(),
None, None,
)?; )?
.with_mixnet_contract_address(config.mixnet_contract_address.clone())
.with_vesting_contract_address(config.vesting_contract_address.clone())
.with_bandwidth_claim_contract_address(config.bandwidth_claim_contract_address.clone());
Ok(Client { Ok(Client {
network: config.network, network: config.network,
mixnet_contract_address: config.mixnet_contract_address, mixnet_contract_address: config.mixnet_contract_address,
vesting_contract_address: config.vesting_contract_address, vesting_contract_address: config.vesting_contract_address,
erc20_bridge_contract_address: config.erc20_bridge_contract_address, bandwidth_claim_contract_address: config.bandwidth_claim_contract_address,
mnemonic: Some(mnemonic), mnemonic: Some(mnemonic),
mixnode_page_limit: config.mixnode_page_limit, mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit, gateway_page_limit: config.gateway_page_limit,
@@ -148,12 +167,12 @@ impl Client<SigningNymdClient> {
self.nymd = NymdClient::connect_with_mnemonic( self.nymd = NymdClient::connect_with_mnemonic(
self.network, self.network,
new_endpoint.as_ref(), new_endpoint.as_ref(),
self.mixnet_contract_address.clone(),
self.vesting_contract_address.clone(),
self.erc20_bridge_contract_address.clone(),
self.mnemonic.clone().unwrap(), self.mnemonic.clone().unwrap(),
None, None,
)?; )?
.with_mixnet_contract_address(self.mixnet_contract_address.clone())
.with_vesting_contract_address(self.vesting_contract_address.clone())
.with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone());
Ok(()) Ok(())
} }
@@ -166,32 +185,15 @@ impl Client<SigningNymdClient> {
impl Client<QueryNymdClient> { impl Client<QueryNymdClient> {
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> { pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
let validator_api_client = validator_api::Client::new(config.api_url.clone()); let validator_api_client = validator_api::Client::new(config.api_url.clone());
let nymd_client = NymdClient::connect( let nymd_client = NymdClient::connect(config.nymd_url.as_str())?
config.nymd_url.as_str(), .with_mixnet_contract_address(config.mixnet_contract_address.clone())
Some(config.mixnet_contract_address.clone().unwrap_or_else(|| { .with_vesting_contract_address(config.vesting_contract_address.clone());
cosmrs::AccountId::from_str(DEFAULT_NETWORK.mixnet_contract_address()).unwrap()
})),
Some(config.vesting_contract_address.clone().unwrap_or_else(|| {
cosmrs::AccountId::from_str(DEFAULT_NETWORK.vesting_contract_address()).unwrap()
})),
Some(
config
.erc20_bridge_contract_address
.clone()
.unwrap_or_else(|| {
cosmrs::AccountId::from_str(
DEFAULT_NETWORK.bandwidth_claim_contract_address(),
)
.unwrap()
}),
),
)?;
Ok(Client { Ok(Client {
network: config.network, network: config.network,
mixnet_contract_address: config.mixnet_contract_address, mixnet_contract_address: config.mixnet_contract_address,
vesting_contract_address: config.vesting_contract_address, vesting_contract_address: config.vesting_contract_address,
erc20_bridge_contract_address: config.erc20_bridge_contract_address, bandwidth_claim_contract_address: config.bandwidth_claim_contract_address,
mnemonic: None, mnemonic: None,
mixnode_page_limit: config.mixnode_page_limit, mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit, gateway_page_limit: config.gateway_page_limit,
@@ -203,12 +205,10 @@ impl Client<QueryNymdClient> {
} }
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
self.nymd = NymdClient::connect( self.nymd = NymdClient::connect(new_endpoint.as_ref())?
new_endpoint.as_ref(), .with_mixnet_contract_address(self.mixnet_contract_address.clone())
self.mixnet_contract_address.clone(), .with_vesting_contract_address(self.vesting_contract_address.clone())
self.vesting_contract_address.clone(), .with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone());
self.erc20_bridge_contract_address.clone(),
)?;
Ok(()) Ok(())
} }
} }
@@ -222,10 +222,10 @@ impl<C> Client<C> {
// use case: somebody initialised client without a contract in order to upload and initialise one // use case: somebody initialised client without a contract in order to upload and initialise one
// and now they want to actually use it without making new client // and now they want to actually use it without making new client
pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) { pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
self.mixnet_contract_address = Some(mixnet_contract_address) self.mixnet_contract_address = mixnet_contract_address
} }
pub fn get_mixnet_contract_address(&self) -> Option<cosmrs::AccountId> { pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId {
self.mixnet_contract_address.clone() self.mixnet_contract_address.clone()
} }
@@ -233,18 +233,36 @@ impl<C> Client<C> {
Ok(self.validator_api.get_mixnodes().await?) Ok(self.validator_api.get_mixnodes().await?)
} }
pub async fn get_cached_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes_detailed().await?)
}
pub async fn get_cached_rewarded_mixnodes( pub async fn get_cached_rewarded_mixnodes(
&self, &self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> { ) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes().await?) Ok(self.validator_api.get_rewarded_mixnodes().await?)
} }
pub async fn get_cached_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes_detailed().await?)
}
pub async fn get_cached_active_mixnodes( pub async fn get_cached_active_mixnodes(
&self, &self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> { ) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes().await?) Ok(self.validator_api.get_active_mixnodes().await?)
} }
pub async fn get_cached_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes_detailed().await?)
}
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> { pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.validator_api.get_gateways().await?) Ok(self.validator_api.get_gateways().await?)
} }
@@ -715,4 +733,34 @@ impl ApiClient {
) -> Result<VerificationKeyResponse, ValidatorClientError> { ) -> Result<VerificationKeyResponse, ValidatorClientError> {
Ok(self.validator_api.get_coconut_verification_key().await?) Ok(self.validator_api.get_coconut_verification_key().await?)
} }
pub async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
Ok(self
.validator_api
.verify_bandwidth_credential(request_body)
.await?)
}
pub async fn propose_release_funds(
&self,
request_body: &ProposeReleaseFundsRequestBody,
) -> Result<ProposeReleaseFundsResponse, ValidatorClientError> {
Ok(self
.validator_api
.propose_release_funds(request_body)
.await?)
}
pub async fn execute_release_funds(
&self,
request_body: &ExecuteReleaseFundsRequestBody,
) -> Result<(), ValidatorClientError> {
Ok(self
.validator_api
.execute_release_funds(request_body)
.await?)
}
} }
@@ -19,7 +19,7 @@ const CONNECTION_TEST_TIMEOUT_SEC: u64 = 2;
pub async fn run_validator_connection_test<H: BuildHasher + 'static>( pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
nymd_urls: impl Iterator<Item = (Network, Url)>, nymd_urls: impl Iterator<Item = (Network, Url)>,
api_urls: impl Iterator<Item = (Network, Url)>, api_urls: impl Iterator<Item = (Network, Url)>,
mixnet_contract_address: HashMap<Network, Option<cosmrs::AccountId>, H>, mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
) -> ( ) -> (
HashMap<Network, Vec<(Url, bool)>>, HashMap<Network, Vec<(Url, bool)>>,
HashMap<Network, Vec<(Url, bool)>>, HashMap<Network, Vec<(Url, bool)>>,
@@ -47,14 +47,15 @@ pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
fn setup_connection_tests<H: BuildHasher + 'static>( fn setup_connection_tests<H: BuildHasher + 'static>(
nymd_urls: impl Iterator<Item = (Network, Url)>, nymd_urls: impl Iterator<Item = (Network, Url)>,
api_urls: impl Iterator<Item = (Network, Url)>, api_urls: impl Iterator<Item = (Network, Url)>,
mixnet_contract_address: HashMap<Network, Option<cosmrs::AccountId>, H>, mixnet_contract_address: HashMap<Network, cosmrs::AccountId, H>,
) -> impl Iterator<Item = ClientForConnectionTest> { ) -> impl Iterator<Item = ClientForConnectionTest> {
let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| { let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| {
let address = mixnet_contract_address let address = mixnet_contract_address
.get(&network) .get(&network)
.expect("No configured contract address") .expect("No configured contract address")
.clone(); .clone();
NymdClient::<QueryNymdClient>::connect(url.as_str(), address, None, None) NymdClient::<QueryNymdClient>::connect(url.as_str())
.map(|client| client.with_mixnet_contract_address(address))
.map(move |client| ClientForConnectionTest::Nymd(network, url, Box::new(client))) .map(move |client| ClientForConnectionTest::Nymd(network, url, Box::new(client)))
.ok() .ok()
}); });
@@ -0,0 +1,130 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::fmt;
pub use cosmrs::Coin as CosmosCoin;
pub use cosmwasm_std::Coin as CosmWasmCoin;
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq)]
pub struct MismatchedDenoms;
// the reason the coin is created here as opposed to different place in the codebase is that
// eventually we want to either publish the cosmwasm client separately or commit it to
// some other project, like cosmrs. Either way, in that case we can't really have
// a dependency on an internal type
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq)]
pub struct Coin {
pub amount: u128,
pub denom: String,
}
impl Coin {
pub fn new<S: Into<String>>(amount: u128, denom: S) -> Self {
Coin {
amount,
denom: denom.into(),
}
}
pub fn try_add(&self, other: &Self) -> Result<Self, MismatchedDenoms> {
if self.denom != other.denom {
Err(MismatchedDenoms)
} else {
Ok(Coin {
amount: self.amount + other.amount,
denom: self.denom.clone(),
})
}
}
}
impl fmt::Display for Coin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.amount, self.denom)
}
}
impl From<Coin> for CosmosCoin {
fn from(coin: Coin) -> Self {
assert!(
coin.amount <= u64::MAX as u128,
"the coin amount is higher than the maximum supported by cosmrs"
);
CosmosCoin {
denom: coin
.denom
.parse()
.expect("the coin should have had a valid denom!"),
amount: (coin.amount as u64).into(),
}
}
}
impl From<CosmosCoin> for Coin {
fn from(coin: CosmosCoin) -> Self {
Coin {
amount: coin
.amount
.to_string()
.parse()
.expect("somehow failed to parse string representation of u64"),
denom: coin.denom.to_string(),
}
}
}
impl From<Coin> for CosmWasmCoin {
fn from(coin: Coin) -> Self {
CosmWasmCoin::new(coin.amount, coin.denom)
}
}
impl From<CosmWasmCoin> for Coin {
fn from(coin: CosmWasmCoin) -> Self {
Coin {
amount: coin.amount.u128(),
denom: coin.denom,
}
}
}
pub trait CoinConverter {
type Target;
fn convert_coin(&self) -> Self::Target;
}
impl CoinConverter for CosmosCoin {
type Target = CosmWasmCoin;
fn convert_coin(&self) -> Self::Target {
CosmWasmCoin::new(
self.amount
.to_string()
.parse()
.expect("cosmos coin had an invalid amount assigned"),
self.denom.to_string(),
)
}
}
impl CoinConverter for CosmWasmCoin {
type Target = CosmosCoin;
fn convert_coin(&self) -> Self::Target {
assert!(
self.amount.u128() <= u64::MAX as u128,
"the coin amount is higher than the maximum supported by cosmrs"
);
CosmosCoin {
denom: self
.denom
.parse()
.expect("cosmwasm coin had an invalid amount assigned"),
amount: (self.amount.u128() as u64).into(),
}
}
}
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nymd::coin::Coin;
use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key}; use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key};
use crate::nymd::cosmwasm_client::types::{ use crate::nymd::cosmwasm_client::types::{
Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId, Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId,
@@ -25,8 +26,7 @@ use cosmrs::rpc::{self, HttpClient, Order};
use cosmrs::tendermint::abci::Code as AbciCode; use cosmrs::tendermint::abci::Code as AbciCode;
use cosmrs::tendermint::abci::Transaction; use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{abci, block, chain}; use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{tx, AccountId, Coin, Denom, Tx}; use cosmrs::{tx, AccountId, Coin as CosmosCoin, Denom, Tx};
use cosmwasm_std::Coin as CosmWasmCoin;
use prost::Message; use prost::Message;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
@@ -135,7 +135,7 @@ pub trait CosmWasmClient: rpc::Client {
.await?; .await?;
res.balance res.balance
.map(TryFrom::try_from) .map(|proto| CosmosCoin::try_from(proto).map(Into::into))
.transpose() .transpose()
.map_err(|_| NymdError::SerializationError("Coin".to_owned())) .map_err(|_| NymdError::SerializationError("Coin".to_owned()))
} }
@@ -166,16 +166,12 @@ pub trait CosmWasmClient: rpc::Client {
raw_balances raw_balances
.into_iter() .into_iter()
.map(TryFrom::try_from) .map(|proto| CosmosCoin::try_from(proto).map(Into::into))
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
.map_err(|_| NymdError::SerializationError("Coins".to_owned())) .map_err(|_| NymdError::SerializationError("Coins".to_owned()))
} }
// this is annoyingly and inconsistently returning `Vec<CosmWasmCoin>` rather than async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError> {
// 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 path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap());
let mut supply = Vec::new(); let mut supply = Vec::new();
@@ -198,12 +194,7 @@ pub trait CosmWasmClient: rpc::Client {
supply supply
.into_iter() .into_iter()
.map(|coin| { .map(|proto| CosmosCoin::try_from(proto).map(Into::into))
coin.amount.parse().map(|amount| CosmWasmCoin {
denom: coin.denom,
amount,
})
})
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
.map_err(|_| NymdError::SerializationError("Coins".to_owned())) .map_err(|_| NymdError::SerializationError("Coins".to_owned()))
} }
@@ -483,6 +474,9 @@ pub trait CosmWasmClient: rpc::Client {
// deprecation warning is due to the fact the protobuf files built were based on cosmos-sdk 0.44, // deprecation warning is due to the fact the protobuf files built were based on cosmos-sdk 0.44,
// where they prefer using tx_bytes directly. However, in 0.42, which we are using at the time // where they prefer using tx_bytes directly. However, in 0.42, which we are using at the time
// of writing this, the option does not work // of writing this, the option does not work
// TODO: we should really stop using the `tx` argument here and use `tx_bytes` exlusively,
// however, at the time of writing this update, while our QA and mainnet networks do support it,
// sandbox is still running old version of wasmd that lacks support for `tx_bytes`
#[allow(deprecated)] #[allow(deprecated)]
async fn query_simulate( async fn query_simulate(
&self, &self,
@@ -68,6 +68,7 @@ pub(crate) fn create_pagination(key: Vec<u8>) -> PageRequest {
offset: 0, offset: 0,
limit: 0, limit: 0,
count_total: false, count_total: false,
reverse: false,
} }
} }
@@ -22,7 +22,7 @@ pub struct Log {
/// Searches in logs for the first event of the given event type and in that event /// Searches in logs for the first event of the given event type and in that event
/// for the first attribute with the given attribute key. /// for the first attribute with the given attribute key.
pub(crate) fn find_attribute<'a>( pub fn find_attribute<'a>(
logs: &'a [Log], logs: &'a [Log],
event_type: &str, event_type: &str,
attribute_key: &str, attribute_key: &str,
@@ -1,23 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;
use std::time::Duration;
use async_trait::async_trait;
use cosmrs::bank::MsgSend;
use cosmrs::distribution::MsgWithdrawDelegatorReward;
use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
use cosmrs::staking::{MsgDelegate, MsgUndelegate};
use cosmrs::tx::{self, Msg, SignDoc, SignerInfo};
use cosmrs::{cosmwasm, rpc, AccountId, Any, Coin, Tx};
use log::debug;
use serde::Serialize;
use sha2::Digest;
use sha2::Sha256;
use crate::nymd::cosmwasm_client::client::CosmWasmClient; use crate::nymd::cosmwasm_client::client::CosmWasmClient;
use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; use crate::nymd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse};
use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs}; use crate::nymd::cosmwasm_client::logs::{self, parse_raw_logs};
@@ -25,20 +8,49 @@ use crate::nymd::cosmwasm_client::types::*;
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
use crate::nymd::wallet::DirectSecp256k1HdWallet; use crate::nymd::wallet::DirectSecp256k1HdWallet;
use crate::nymd::{CosmosCoin, GasPrice, TxResponse}; use crate::nymd::{Coin, GasPrice, TxResponse};
use async_trait::async_trait;
// we need to have **a** valid secp256k1 signature for simulation purposes. use cosmrs::bank::MsgSend;
// it doesn't matter what it is as long as it parses correctly use cosmrs::distribution::MsgWithdrawDelegatorReward;
const DUMMY_SECP256K1_SIGNATURE: &[u8] = &[ use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
54, 167, 169, 61, 100, 173, 231, 87, 1, 113, 179, 49, 102, 141, 67, 22, 170, 153, 52, 88, 178, use cosmrs::rpc::endpoint::broadcast;
159, 200, 11, 37, 138, 76, 221, 187, 70, 104, 123, 98, 216, 190, 249, 149, 81, 1, 158, 0, 220, use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
32, 147, 101, 60, 64, 77, 44, 83, 221, 119, 170, 124, 109, 177, 73, 116, 46, 57, 102, 181, 98, use cosmrs::staking::{MsgDelegate, MsgUndelegate};
91, use cosmrs::tx::{self, Msg, SignDoc, SignerInfo};
]; use cosmrs::{cosmwasm, rpc, AccountId, Any, Tx};
use log::debug;
use serde::Serialize;
use sha2::Digest;
use sha2::Sha256;
use std::convert::TryInto;
use std::time::Duration;
const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
fn empty_fee() -> tx::Fee {
tx::Fee {
amount: vec![],
gas_limit: Default::default(),
payer: None,
granter: None,
}
}
fn single_unspecified_signer_auth(
public_key: Option<tx::SignerPublicKey>,
sequence_number: tx::SequenceNumber,
) -> tx::AuthInfo {
tx::SignerInfo {
public_key,
mode_info: tx::ModeInfo::Single(tx::mode_info::Single {
mode: SignMode::Unspecified,
}),
sequence: sequence_number,
}
.auth_info(empty_fee())
}
#[async_trait] #[async_trait]
pub trait SigningCosmWasmClient: CosmWasmClient { pub trait SigningCosmWasmClient: CosmWasmClient {
fn signer(&self) -> &DirectSecp256k1HdWallet; fn signer(&self) -> &DirectSecp256k1HdWallet;
@@ -64,33 +76,21 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
let sequence_response = self.get_sequence(signer_address).await?; let sequence_response = self.get_sequence(signer_address).await?;
let partial_tx = Tx { let partial_tx = Tx {
body: tx::Body { body: tx::Body::new(messages, memo, 0u32),
messages, auth_info: single_unspecified_signer_auth(public_key, sequence_response.sequence),
memo: memo.into(), signatures: vec![Vec::new()],
timeout_height: 0u32.into(),
extension_options: vec![],
non_critical_extension_options: vec![],
},
auth_info: tx::AuthInfo {
signer_infos: vec![tx::SignerInfo {
public_key,
mode_info: tx::ModeInfo::Single(tx::mode_info::Single {
mode: SignMode::Unspecified,
}),
sequence: sequence_response.sequence,
}],
fee: tx::Fee::from_amount_and_gas(
CosmosCoin {
denom: "".parse().unwrap(),
amount: 0u64.into(),
},
0,
),
},
signatures: vec![DUMMY_SECP256K1_SIGNATURE.try_into().unwrap()],
}; };
self.query_simulate(Some(partial_tx), Vec::new()).await self.query_simulate(Some(partial_tx), Vec::new()).await
// for completion sake, once we're able to transition into using `tx_bytes`,
// we might want to use something like this instead:
// let tx_raw: tx::Raw = cosmrs::proto::cosmos::tx::v1beta1::TxRaw {
// body_bytes: partial_tx.body.into_bytes().unwrap(),
// auth_info_bytes: partial_tx.auth_info.into_bytes().unwrap(),
// signatures: partial_tx.signatures,
// }
// .into();
// self.query_simulate(None, tx_raw.to_bytes().unwrap()).await
} }
async fn upload( async fn upload(
@@ -310,7 +310,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
sender: sender_address.clone(), sender: sender_address.clone(),
contract: contract_address.clone(), contract: contract_address.clone(),
msg: serde_json::to_vec(msg)?, msg: serde_json::to_vec(msg)?,
funds, funds: funds.into_iter().map(Into::into).collect(),
} }
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
@@ -324,6 +324,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
Ok(ExecuteResult { Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
data: tx_res.tx_result.data,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -348,7 +349,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
sender: sender_address.clone(), sender: sender_address.clone(),
contract: contract_address.clone(), contract: contract_address.clone(),
msg: serde_json::to_vec(&msg)?, msg: serde_json::to_vec(&msg)?,
funds, funds: funds.into_iter().map(Into::into).collect(),
} }
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned())) .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
@@ -364,6 +365,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
Ok(ExecuteResult { Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
data: tx_res.tx_result.data,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -380,7 +382,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
let send_msg = MsgSend { let send_msg = MsgSend {
from_address: sender_address.clone(), from_address: sender_address.clone(),
to_address: recipient_address.clone(), to_address: recipient_address.clone(),
amount, amount: amount.into_iter().map(Into::into).collect(),
} }
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
@@ -406,7 +408,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
MsgSend { MsgSend {
from_address: sender_address.clone(), from_address: sender_address.clone(),
to_address, to_address,
amount, amount: amount.into_iter().map(Into::into).collect(),
} }
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned())) .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
@@ -429,7 +431,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
let delegate_msg = MsgDelegate { let delegate_msg = MsgDelegate {
delegator_address: delegator_address.to_owned(), delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
amount, amount: amount.into(),
} }
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
@@ -450,7 +452,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
let undelegate_msg = MsgUndelegate { let undelegate_msg = MsgUndelegate {
delegator_address: delegator_address.to_owned(), delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
amount: Some(amount), amount: amount.into(),
} }
.to_any() .to_any()
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
@@ -25,9 +25,10 @@ use cosmrs::proto::cosmwasm::wasm::v1::{
CodeInfoResponse, ContractCodeHistoryEntry as ProtoContractCodeHistoryEntry, CodeInfoResponse, ContractCodeHistoryEntry as ProtoContractCodeHistoryEntry,
ContractCodeHistoryOperationType, ContractInfo as ProtoContractInfo, ContractCodeHistoryOperationType, ContractInfo as ProtoContractInfo,
}; };
use cosmrs::tendermint::abci::Data;
use cosmrs::tendermint::{abci, chain}; use cosmrs::tendermint::{abci, chain};
use cosmrs::tx::{AccountNumber, Gas, SequenceNumber}; use cosmrs::tx::{AccountNumber, Gas, SequenceNumber};
use cosmrs::{tx, AccountId, Any, Coin}; use cosmrs::{tx, AccountId, Any, Coin as CosmosCoin};
use prost::Message; use prost::Message;
use serde::Serialize; use serde::Serialize;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
@@ -106,9 +107,9 @@ impl TryFrom<ProtoModuleAccount> for ModuleAccount {
#[derive(Debug)] #[derive(Debug)]
pub struct BaseVestingAccount { pub struct BaseVestingAccount {
pub base_account: Option<BaseAccount>, pub base_account: Option<BaseAccount>,
pub original_vesting: Vec<Coin>, pub original_vesting: Vec<CosmosCoin>,
pub delegated_free: Vec<Coin>, pub delegated_free: Vec<CosmosCoin>,
pub delegated_vesting: Vec<Coin>, pub delegated_vesting: Vec<CosmosCoin>,
pub end_time: i64, pub end_time: i64,
} }
@@ -183,7 +184,7 @@ impl TryFrom<ProtoDelayedVestingAccount> for DelayedVestingAccount {
#[derive(Debug)] #[derive(Debug)]
pub struct Period { pub struct Period {
pub length: i64, pub length: i64,
pub amount: Vec<Coin>, pub amount: Vec<CosmosCoin>,
} }
impl TryFrom<ProtoPeriod> for Period { impl TryFrom<ProtoPeriod> for Period {
@@ -488,7 +489,7 @@ impl TryFrom<ProtoContractCodeHistoryEntry> for ContractCodeHistoryEntry {
} }
} }
#[derive(Debug)] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct GasInfo { pub struct GasInfo {
/// GasWanted is the maximum units of work we allow this tx to perform. /// GasWanted is the maximum units of work we allow this tx to perform.
pub gas_wanted: Gas, pub gas_wanted: Gas,
@@ -627,7 +628,7 @@ pub struct InstantiateOptions {
/// created and before the instantiation message is executed by the contract. /// created and before the instantiation message is executed by the contract.
/// ///
/// Only native tokens are supported. /// Only native tokens are supported.
pub funds: Vec<Coin>, pub funds: Vec<CosmosCoin>,
/// A bech32 encoded address of an admin account. /// A bech32 encoded address of an admin account.
/// Caution: an admin has the privilege to upgrade a contract. /// Caution: an admin has the privilege to upgrade a contract.
@@ -635,6 +636,15 @@ pub struct InstantiateOptions {
pub admin: Option<AccountId>, pub admin: Option<AccountId>,
} }
impl InstantiateOptions {
pub fn new<T: Into<CosmosCoin>>(funds: Vec<T>, admin: Option<AccountId>) -> Self {
InstantiateOptions {
funds: funds.into_iter().map(Into::into).collect(),
admin,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct InstantiateResult { pub struct InstantiateResult {
/// The address of the newly instantiated contract /// The address of the newly instantiated contract
@@ -672,6 +682,8 @@ pub struct MigrateResult {
pub struct ExecuteResult { pub struct ExecuteResult {
pub logs: Vec<Log>, pub logs: Vec<Log>,
pub data: Data,
/// Transaction hash (might be used as transaction ID) /// Transaction hash (might be used as transaction ID)
pub transaction_hash: tx::Hash, pub transaction_hash: tx::Hash,
@@ -21,9 +21,12 @@ pub enum NymdError {
#[error("There was an issue with bip32 - {0}")] #[error("There was an issue with bip32 - {0}")]
Bip32Error(#[from] bip32::Error), Bip32Error(#[from] bip32::Error),
#[error("There was an issue with bip32 - {0}")] #[error("There was an issue with bip39 - {0}")]
Bip39Error(#[from] bip39::Error), Bip39Error(#[from] bip39::Error),
#[error("There was an issue on the cosmrs side - {0}")]
CosmrsError(#[from] cosmrs::Error),
#[error("Failed to derive account address")] #[error("Failed to derive account address")]
AccountDerivationError, AccountDerivationError,
@@ -39,10 +42,10 @@ pub enum NymdError {
#[error("There was an issue with a tendermint RPC request - {0}")] #[error("There was an issue with a tendermint RPC request - {0}")]
TendermintError(#[from] TendermintRpcError), TendermintError(#[from] TendermintRpcError),
#[error("There was an issue when attempting to serialize data")] #[error("There was an issue when attempting to serialize data ({0})")]
SerializationError(String), SerializationError(String),
#[error("There was an issue when attempting to deserialize data")] #[error("There was an issue when attempting to deserialize data ({0})")]
DeserializationError(String), DeserializationError(String),
#[error("There was an issue when attempting to encode our protobuf data - {0}")] #[error("There was an issue when attempting to encode our protobuf data - {0}")]
@@ -121,6 +124,12 @@ pub enum NymdError {
#[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())] #[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())]
BroadcastTimeout { hash: tx::Hash, timeout: Duration }, BroadcastTimeout { hash: tx::Hash, timeout: Duration },
#[error("Cosmwasm std error: {0}")]
CosmwasmStdError(#[from] cosmwasm_std::StdError),
#[error("Coconut interface error: {0}")]
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
} }
impl NymdError { impl NymdError {
@@ -4,7 +4,7 @@
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use config::defaults; use config::defaults;
use cosmrs::tx::Gas; use cosmrs::tx::Gas;
use cosmrs::{Coin, Denom}; use cosmrs::Coin;
use cosmwasm_std::{Decimal, Fraction, Uint128}; use cosmwasm_std::{Decimal, Fraction, Uint128};
use std::ops::Mul; use std::ops::Mul;
use std::str::FromStr; use std::str::FromStr;
@@ -13,11 +13,12 @@ use std::str::FromStr;
/// the smallest fee token unit, such as 0.012utoken. /// the smallest fee token unit, such as 0.012utoken.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct GasPrice { pub struct GasPrice {
// I really hate the combination of cosmwasm Decimal with cosmos-sdk Denom, // I really dislike the usage of cosmwasm Decimal, but I didn't feel like implementing
// but cosmos-sdk's Decimal is too basic for our needs // our own maths subcrate just for the purposes of calculating gas requirements
// this should definitely be rectified later on
pub amount: Decimal, pub amount: Decimal,
pub denom: Denom, pub denom: String,
} }
impl<'a> Mul<Gas> for &'a GasPrice { impl<'a> Mul<Gas> for &'a GasPrice {
@@ -44,7 +45,10 @@ impl<'a> Mul<Gas> for &'a GasPrice {
assert!(amount.u128() <= u64::MAX as u128); assert!(amount.u128() <= u64::MAX as u128);
Coin { Coin {
denom: self.denom.clone(), denom: self
.denom
.parse()
.expect("the gas price has been created with invalid denom"),
amount: (amount.u128() as u64).into(), amount: (amount.u128() as u64).into(),
} }
} }
@@ -63,9 +67,7 @@ impl FromStr for GasPrice {
.parse() .parse()
.map_err(|_| NymdError::MalformedGasPrice)?; .map_err(|_| NymdError::MalformedGasPrice)?;
let possible_denom = s.chars().skip(amount_len).collect::<String>(); let possible_denom = s.chars().skip(amount_len).collect::<String>();
let denom = possible_denom let denom = possible_denom.trim().to_string();
.parse()
.map_err(|_| NymdError::MalformedGasPrice)?;
Ok(GasPrice { amount, denom }) Ok(GasPrice { amount, denom })
} }
@@ -106,8 +108,14 @@ mod tests {
); );
assert!(".25upunk".parse::<GasPrice>().is_err()); assert!(".25upunk".parse::<GasPrice>().is_err());
assert!("0.025 upunk".parse::<GasPrice>().is_err());
assert!("0.025UPUNK".parse::<GasPrice>().is_err()); assert_eq!(
"0.025upunk".parse::<GasPrice>().unwrap(),
"0.025 upunk".parse().unwrap()
);
let gas: GasPrice = "0.025 upunk ".parse().unwrap();
assert_eq!("upunk", gas.denom);
} }
#[test] #[test]
@@ -1,195 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::GasPrice;
use cosmrs::tx::{Fee, Gas};
use cosmrs::Coin;
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../../../nym-wallet/src/types/rust/operation.ts")
)]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum Operation {
Upload,
Init,
Migrate,
ChangeAdmin,
Send,
BondMixnode,
BondMixnodeOnBehalf,
UnbondMixnode,
UnbondMixnodeOnBehalf,
UpdateMixnodeConfig,
DelegateToMixnode,
DelegateToMixnodeOnBehalf,
UndelegateFromMixnode,
UndelegateFromMixnodeOnBehalf,
BondGateway,
BondGatewayOnBehalf,
UnbondGateway,
UnbondGatewayOnBehalf,
UpdateContractSettings,
BeginMixnodeRewarding,
FinishMixnodeRewarding,
TrackUnbondGateway,
TrackUnbondMixnode,
WithdrawVestedCoins,
TrackUndelegation,
CreatePeriodicVestingAccount,
AdvanceCurrentInterval,
AdvanceCurrentEpoch,
WriteRewardedSet,
ClearRewardedSet,
UpdateMixnetAddress,
CheckpointMixnodes,
ReconcileDelegations,
}
pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin {
gas_price * gas_limit
}
impl fmt::Display for Operation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Operation::Upload => f.write_str("Upload"),
Operation::Init => f.write_str("Init"),
Operation::Migrate => f.write_str("Migrate"),
Operation::ChangeAdmin => f.write_str("ChangeAdmin"),
Operation::Send => f.write_str("Send"),
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"),
Operation::UnbondGateway => f.write_str("UnbondGateway"),
Operation::UnbondGatewayOnBehalf => f.write_str("UnbondGatewayOnBehalf"),
Operation::DelegateToMixnode => f.write_str("DelegateToMixnode"),
Operation::DelegateToMixnodeOnBehalf => f.write_str("DelegateToMixnodeOnBehalf"),
Operation::UndelegateFromMixnode => f.write_str("UndelegateFromMixnode"),
Operation::UndelegateFromMixnodeOnBehalf => {
f.write_str("UndelegateFromMixnodeOnBehalf")
}
Operation::UpdateContractSettings => f.write_str("UpdateContractSettings"),
Operation::BeginMixnodeRewarding => f.write_str("BeginMixnodeRewarding"),
Operation::FinishMixnodeRewarding => f.write_str("FinishMixnodeRewarding"),
Operation::TrackUnbondGateway => f.write_str("TrackUnbondGateway"),
Operation::TrackUnbondMixnode => f.write_str("TrackUnbondMixnode"),
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"),
Operation::UpdateMixnetAddress => f.write_str("UpdateMixnetAddress"),
Operation::CheckpointMixnodes => f.write_str("CheckpointMixnodes"),
Operation::ReconcileDelegations => f.write_str("ReconcileDelegations"),
Operation::AdvanceCurrentEpoch => f.write_str("AdvanceCurrentEpoch"),
}
}
}
impl Operation {
// TODO: some value tweaking
pub fn default_gas_limit(&self) -> Gas {
match self {
Operation::Upload => 3_000_000u64.into(),
Operation::Init => 500_000u64.into(),
Operation::Migrate => 200_000u64.into(),
Operation::ChangeAdmin => 80_000u64.into(),
Operation::Send => 80_000u64.into(),
Operation::BondMixnode => 175_000u64.into(),
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(),
Operation::UndelegateFromMixnodeOnBehalf => 175_000u64.into(),
Operation::BondGateway => 175_000u64.into(),
Operation::BondGatewayOnBehalf => 200_000u64.into(),
Operation::UnbondGateway => 175_000u64.into(),
Operation::UnbondGatewayOnBehalf => 200_000u64.into(),
Operation::UpdateContractSettings => 175_000u64.into(),
Operation::BeginMixnodeRewarding => 175_000u64.into(),
Operation::FinishMixnodeRewarding => 175_000u64.into(),
Operation::TrackUnbondGateway => 175_000u64.into(),
Operation::TrackUnbondMixnode => 175_000u64.into(),
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(),
Operation::UpdateMixnetAddress => 80_000u64.into(),
Operation::CheckpointMixnodes => 175_000u64.into(),
Operation::ReconcileDelegations => 500_000u64.into(),
Operation::AdvanceCurrentEpoch => 175_000u64.into(),
}
}
pub(crate) fn determine_custom_fee(gas_price: &GasPrice, gas_limit: Gas) -> Fee {
// we need to know 2 of the following 3 parameters (the third one is being implicit) in order to construct Fee:
// (source: https://docs.cosmos.network/v0.42/basics/gas-fees.html)
// - gas price
// - gas limit
// - fees
let fee = calculate_fee(gas_price, gas_limit);
Fee::from_amount_and_gas(fee, gas_limit)
}
pub fn default_fee(&self, gas_price: &GasPrice) -> Fee {
Self::determine_custom_fee(gas_price, self.default_gas_limit())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn calculating_fee() {
let expected = Coin {
denom: "upunk".parse().unwrap(),
amount: 1000u64.into(),
};
let gas_price = "1upunk".parse().unwrap();
let gas_limit = 1000u64.into();
assert_eq!(expected, calculate_fee(&gas_price, gas_limit));
let expected = Coin {
denom: "upunk".parse().unwrap(),
amount: 50u64.into(),
};
let gas_price = "0.05upunk".parse().unwrap();
let gas_limit = 1000u64.into();
assert_eq!(expected, calculate_fee(&gas_price, gas_limit));
let expected = Coin {
denom: "upunk".parse().unwrap(),
amount: 100000u64.into(),
};
let gas_price = "100upunk".parse().unwrap();
let gas_limit = 1000u64.into();
assert_eq!(expected, calculate_fee(&gas_price, gas_limit))
}
}
@@ -2,15 +2,15 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use cosmrs::tx; use cosmrs::tx;
use serde::{Deserialize, Serialize};
pub mod gas_price; pub mod gas_price;
pub mod helpers;
pub const DEFAULT_SIMULATED_GAS_MULTIPLIER: f32 = 1.3; pub const DEFAULT_SIMULATED_GAS_MULTIPLIER: f32 = 1.3;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Fee { pub enum Fee {
Manual(tx::Fee), Manual(#[serde(with = "sealed::TxFee")] tx::Fee),
Auto(Option<f32>), Auto(Option<f32>),
} }
@@ -31,3 +31,91 @@ impl Default for Fee {
Fee::Auto(Some(DEFAULT_SIMULATED_GAS_MULTIPLIER)) Fee::Auto(Some(DEFAULT_SIMULATED_GAS_MULTIPLIER))
} }
} }
// a workaround to provide serde implementation for tx::Fee. We don't want to ever expose any of those
// types to the public and ideally they will get replaced by proper implementation inside comrs
mod sealed {
use cosmrs::tx::{self, Gas};
use cosmrs::Coin as CosmosCoin;
use cosmrs::{AccountId, Decimal as CosmosDecimal, Denom as CosmosDenom};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String {
val.as_ref().to_string()
}
#[derive(Serialize, Deserialize)]
#[serde(remote = "CosmosDenom")]
struct Denom(#[serde(getter = "cosmos_denom_inner_getter")] String);
impl From<Denom> for CosmosDenom {
fn from(val: Denom) -> Self {
val.0.parse().unwrap()
}
}
fn cosmos_decimal_inner_getter(val: &CosmosDecimal) -> u64 {
// haha, this code is so disgusting. I'll make a PR on cosmrs to slightly alleviate those issues...
// note: unwrap here is fine as the to_string is just returning a stringified u64 which, well, is a valid u64
val.to_string().parse().unwrap()
}
// at the time of writing it the current cosmrs' Decimal is extremely limited...
#[derive(Serialize, Deserialize)]
#[serde(remote = "CosmosDecimal")]
struct Decimal(#[serde(getter = "cosmos_decimal_inner_getter")] u64);
impl From<Decimal> for CosmosDecimal {
fn from(val: Decimal) -> Self {
val.0.into()
}
}
#[derive(Serialize, Deserialize, Clone)]
struct Coin {
#[serde(with = "Denom")]
denom: CosmosDenom,
#[serde(with = "Decimal")]
amount: CosmosDecimal,
}
impl From<Coin> for CosmosCoin {
fn from(val: Coin) -> Self {
CosmosCoin {
denom: val.denom,
amount: val.amount,
}
}
}
impl From<CosmosCoin> for Coin {
fn from(val: CosmosCoin) -> Self {
Coin {
denom: val.denom,
amount: val.amount,
}
}
}
fn coin_vec_ser<S: Serializer>(val: &[CosmosCoin], serializer: S) -> Result<S::Ok, S::Error> {
let vec: Vec<Coin> = val.iter().cloned().map(Into::into).collect();
vec.serialize(serializer)
}
fn coin_vec_deser<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<CosmosCoin>, D::Error> {
let vec: Vec<Coin> = Deserialize::deserialize(deserializer)?;
Ok(vec.iter().cloned().map(Into::into).collect())
}
#[derive(Serialize, Deserialize)]
#[serde(remote = "tx::Fee")]
pub(super) struct TxFee {
#[serde(serialize_with = "coin_vec_ser")]
#[serde(deserialize_with = "coin_vec_deser")]
pub amount: Vec<CosmosCoin>,
pub gas_limit: Gas,
pub payer: Option<AccountId>,
pub granter: Option<AccountId>,
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
use crate::nymd::cosmwasm_client::types::ExecuteResult;
use crate::nymd::error::NymdError;
use crate::nymd::{Coin, Fee, NymdClient};
use coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg};
use async_trait::async_trait;
#[async_trait]
pub trait CoconutBandwidthSigningClient {
async fn deposit(
&self,
amount: Coin,
info: String,
verification_key: String,
encryption_key: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
}
#[async_trait]
impl<C: SigningCosmWasmClient + Sync + Send> CoconutBandwidthSigningClient for NymdClient<C> {
async fn deposit(
&self,
amount: Coin,
info: String,
verification_key: String,
encryption_key: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = ExecuteMsg::DepositFunds {
data: DepositData::new(info.to_string(), verification_key, encryption_key),
};
self.client
.execute(
self.address(),
self.coconut_bandwidth_contract_address(),
&req,
fee,
"CoconutBandwidth::Deposit",
vec![amount],
)
.await
}
}
@@ -1,8 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
mod coconut_bandwidth_signing_client;
mod multisig_query_client;
mod multisig_signing_client;
mod vesting_query_client; mod vesting_query_client;
mod vesting_signing_client; mod vesting_signing_client;
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
pub use multisig_query_client::QueryClient;
pub use multisig_signing_client::MultisigSigningClient;
pub use vesting_query_client::VestingQueryClient; pub use vesting_query_client::VestingQueryClient;
pub use vesting_signing_client::VestingSigningClient; pub use vesting_signing_client::VestingSigningClient;
@@ -0,0 +1,24 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::error::NymdError;
use crate::nymd::{CosmWasmClient, NymdClient};
use multisig_contract_common::msg::{ProposalResponse, QueryMsg};
use async_trait::async_trait;
#[async_trait]
pub trait QueryClient {
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError>;
}
#[async_trait]
impl<C: CosmWasmClient + Sync + Send> QueryClient for NymdClient<C> {
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError> {
let request = QueryMsg::Proposal { proposal_id };
self.client
.query_contract_smart(self.multisig_contract_address(), &request)
.await
}
}
@@ -0,0 +1,116 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
use crate::nymd::cosmwasm_client::types::ExecuteResult;
use crate::nymd::error::NymdError;
use crate::nymd::{Fee, NymdClient};
use coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
use multisig_contract_common::msg::ExecuteMsg;
use async_trait::async_trait;
use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg};
use cw3::Vote;
use network_defaults::DEFAULT_NETWORK;
#[async_trait]
pub trait MultisigSigningClient {
async fn propose_release_funds(
&self,
title: String,
blinded_serial_number: String,
voucher_value: u128,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn vote_proposal(
&self,
proposal_id: u64,
yes: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn execute_proposal(
&self,
proposal_id: u64,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
}
#[async_trait]
impl<C: SigningCosmWasmClient + Sync + Send> MultisigSigningClient for NymdClient<C> {
async fn propose_release_funds(
&self,
title: String,
blinded_serial_number: String,
voucher_value: u128,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds {
funds: Coin::new(voucher_value, DEFAULT_NETWORK.denom()),
};
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: self.coconut_bandwidth_contract_address().to_string(),
msg: to_binary(&release_funds_req)?,
funds: vec![],
});
let req = ExecuteMsg::Propose {
title,
description: blinded_serial_number,
msgs: vec![release_funds_msg],
latest: None,
};
self.client
.execute(
self.address(),
self.multisig_contract_address(),
&req,
fee,
"Multisig::Propose::Execute::ReleaseFunds",
vec![],
)
.await
}
async fn vote_proposal(
&self,
proposal_id: u64,
vote_yes: bool,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let vote = if vote_yes { Vote::Yes } else { Vote::No };
let req = ExecuteMsg::Vote { proposal_id, vote };
self.client
.execute(
self.address(),
self.multisig_contract_address(),
&req,
fee,
"Multisig::Vote",
vec![],
)
.await
}
async fn execute_proposal(
&self,
proposal_id: u64,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = ExecuteMsg::Execute { proposal_id };
self.client
.execute(
self.address(),
self.multisig_contract_address(),
&req,
fee,
"Multisig::Execute",
vec![],
)
.await
}
}
@@ -1,11 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nymd::coin::Coin;
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient; pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use crate::nymd::NymdClient; use crate::nymd::NymdClient;
use async_trait::async_trait; use async_trait::async_trait;
use cosmwasm_std::{Coin, Timestamp}; use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
use vesting_contract::vesting::Account; use vesting_contract::vesting::Account;
use vesting_contract_common::{ use vesting_contract_common::{
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData, messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
@@ -83,8 +84,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
block_time, block_time,
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
.await .await
.map(Into::into)
} }
async fn spendable_coins( async fn spendable_coins(
@@ -97,8 +99,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
block_time, block_time,
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
.await .await
.map(Into::into)
} }
async fn vested_coins( async fn vested_coins(
&self, &self,
@@ -110,8 +113,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
block_time, block_time,
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
.await .await
.map(Into::into)
} }
async fn vesting_coins( async fn vesting_coins(
&self, &self,
@@ -123,8 +127,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
block_time, block_time,
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
.await .await
.map(Into::into)
} }
async fn vesting_start_time( async fn vesting_start_time(
@@ -135,7 +140,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
vesting_account_address: vesting_account_address.to_string(), vesting_account_address: vesting_account_address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
@@ -147,7 +152,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
vesting_account_address: vesting_account_address.to_string(), vesting_account_address: vesting_account_address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
@@ -159,7 +164,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
vesting_account_address: vesting_account_address.to_string(), vesting_account_address: vesting_account_address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
@@ -173,8 +178,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
block_time, block_time,
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
.await .await
.map(Into::into)
} }
async fn delegated_vesting( async fn delegated_vesting(
@@ -187,8 +193,9 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
block_time, block_time,
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
.await .await
.map(Into::into)
} }
async fn get_account(&self, address: &str) -> Result<Account, NymdError> { async fn get_account(&self, address: &str) -> Result<Account, NymdError> {
@@ -196,7 +203,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
address: address.to_string(), address: address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError> { async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError> {
@@ -204,7 +211,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
address: address.to_string(), address: address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError> { async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError> {
@@ -212,7 +219,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
address: address.to_string(), address: address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
@@ -221,7 +228,7 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
address: address.to_string(), address: address.to_string(),
}; };
self.client self.client
.query_contract_smart(self.vesting_contract_address()?, &request) .query_contract_smart(self.vesting_contract_address(), &request)
.await .await
} }
} }
@@ -4,10 +4,8 @@
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
use crate::nymd::cosmwasm_client::types::ExecuteResult; use crate::nymd::cosmwasm_client::types::ExecuteResult;
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use crate::nymd::fee::helpers::Operation; use crate::nymd::{Coin, Fee, NymdClient};
use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient};
use async_trait::async_trait; use async_trait::async_trait;
use cosmwasm_std::Coin;
use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode}; use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification}; use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, VestingSpecification};
@@ -16,23 +14,30 @@ pub trait VestingSigningClient {
async fn vesting_update_mixnode_config( async fn vesting_update_mixnode_config(
&self, &self,
profix_margin_percent: u8, profix_margin_percent: u8,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn update_mixnet_address(&self, address: &str) -> Result<ExecuteResult, NymdError>; async fn update_mixnet_address(
&self,
address: &str,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_bond_gateway( async fn vesting_bond_gateway(
&self, &self,
gateway: Gateway, gateway: Gateway,
owner_signature: &str, owner_signature: &str,
pledge: Coin, pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn vesting_unbond_gateway(&self) -> Result<ExecuteResult, NymdError>; async fn vesting_unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
async fn vesting_track_unbond_gateway( async fn vesting_track_unbond_gateway(
&self, &self,
owner: &str, owner: &str,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn vesting_bond_mixnode( async fn vesting_bond_mixnode(
@@ -40,33 +45,42 @@ pub trait VestingSigningClient {
mix_node: MixNode, mix_node: MixNode,
owner_signature: &str, owner_signature: &str,
pledge: Coin, pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn vesting_unbond_mixnode(&self) -> Result<ExecuteResult, NymdError>; async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
async fn vesting_track_unbond_mixnode( async fn vesting_track_unbond_mixnode(
&self, &self,
owner: &str, owner: &str,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn withdraw_vested_coins(&self, amount: Coin) -> Result<ExecuteResult, NymdError>; async fn withdraw_vested_coins(
&self,
amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_track_undelegation( async fn vesting_track_undelegation(
&self, &self,
address: &str, address: &str,
mix_identity: IdentityKey, mix_identity: IdentityKey,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn vesting_delegate_to_mixnode<'a>( async fn vesting_delegate_to_mixnode<'a>(
&self, &self,
mix_identity: IdentityKeyRef<'a>, mix_identity: IdentityKeyRef<'a>,
amount: &Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn vesting_undelegate_from_mixnode<'a>( async fn vesting_undelegate_from_mixnode<'a>(
&self, &self,
mix_identity: IdentityKeyRef<'a>, mix_identity: IdentityKeyRef<'a>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
async fn create_periodic_vesting_account( async fn create_periodic_vesting_account(
@@ -75,27 +89,71 @@ pub trait VestingSigningClient {
staking_address: Option<String>, staking_address: Option<String>,
vesting_spec: Option<VestingSpecification>, vesting_spec: Option<VestingSpecification>,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>; ) -> Result<ExecuteResult, NymdError>;
} }
#[async_trait] #[async_trait]
impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient<C> { impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient<C> {
async fn vesting_update_mixnode_config(
&self,
profit_margin_percent: u8,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
self.client
.execute(
self.address(),
self.vesting_contract_address(),
&req,
fee,
"VestingContract::UpdateMixnetConfig",
vec![],
)
.await
}
async fn update_mixnet_address(
&self,
address: &str,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::UpdateMixnetAddress {
address: address.to_string(),
};
self.client
.execute(
self.address(),
self.vesting_contract_address(),
&req,
fee,
"VestingContract::UpdateMixnetAddress",
vec![],
)
.await
}
async fn vesting_bond_gateway( async fn vesting_bond_gateway(
&self, &self,
gateway: Gateway, gateway: Gateway,
owner_signature: &str, owner_signature: &str,
pledge: Coin, pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::BondGateway); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::BondGateway { let req = VestingExecuteMsg::BondGateway {
gateway, gateway,
owner_signature: owner_signature.to_string(), owner_signature: owner_signature.to_string(),
amount: pledge, amount: pledge.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::BondGateway", "VestingContract::BondGateway",
@@ -104,13 +162,13 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
.await .await
} }
async fn vesting_unbond_gateway(&self) -> Result<ExecuteResult, NymdError> { async fn vesting_unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UnbondGateway); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::UnbondGateway {}; let req = VestingExecuteMsg::UnbondGateway {};
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::UnbondGateway", "VestingContract::UnbondGateway",
@@ -123,16 +181,17 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
&self, &self,
owner: &str, owner: &str,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::TrackUnbondGateway); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::TrackUnbondGateway { let req = VestingExecuteMsg::TrackUnbondGateway {
owner: owner.to_string(), owner: owner.to_string(),
amount, amount: amount.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::TrackUnbondGateway", "VestingContract::TrackUnbondGateway",
@@ -146,17 +205,18 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
mix_node: MixNode, mix_node: MixNode,
owner_signature: &str, owner_signature: &str,
pledge: Coin, pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::BondMixnode); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::BondMixnode { let req = VestingExecuteMsg::BondMixnode {
mix_node, mix_node,
owner_signature: owner_signature.to_string(), owner_signature: owner_signature.to_string(),
amount: pledge, amount: pledge.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::BondMixnode", "VestingContract::BondMixnode",
@@ -165,13 +225,13 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
.await .await
} }
async fn vesting_unbond_mixnode(&self) -> Result<ExecuteResult, NymdError> { async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UnbondMixnode); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::UnbondMixnode {}; let req = VestingExecuteMsg::UnbondMixnode {};
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::UnbondMixnode", "VestingContract::UnbondMixnode",
@@ -184,16 +244,17 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
&self, &self,
owner: &str, owner: &str,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::TrackUnbondMixnode); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::TrackUnbondMixnode { let req = VestingExecuteMsg::TrackUnbondMixnode {
owner: owner.to_string(), owner: owner.to_string(),
amount, amount: amount.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::TrackUnbondMixnode", "VestingContract::TrackUnbondMixnode",
@@ -201,14 +262,19 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
) )
.await .await
} }
async fn withdraw_vested_coins(
async fn withdraw_vested_coins(&self, amount: Coin) -> Result<ExecuteResult, NymdError> { &self,
let fee = self.operation_fee(Operation::WithdrawVestedCoins); amount: Coin,
let req = VestingExecuteMsg::WithdrawVestedCoins { amount }; fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::WithdrawVestedCoins {
amount: amount.into(),
};
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::WithdrawVested", "VestingContract::WithdrawVested",
@@ -216,23 +282,23 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
) )
.await .await
} }
async fn vesting_track_undelegation( async fn vesting_track_undelegation(
&self, &self,
address: &str, address: &str,
mix_identity: IdentityKey, mix_identity: IdentityKey,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::TrackUndelegation); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::TrackUndelegation { let req = VestingExecuteMsg::TrackUndelegation {
owner: address.to_string(), owner: address.to_string(),
mix_identity, mix_identity,
amount, amount: amount.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::TrackUndelegation", "VestingContract::TrackUndelegation",
@@ -243,17 +309,18 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
async fn vesting_delegate_to_mixnode<'a>( async fn vesting_delegate_to_mixnode<'a>(
&self, &self,
mix_identity: IdentityKeyRef<'a>, mix_identity: IdentityKeyRef<'a>,
amount: &Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::DelegateToMixnode); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::DelegateToMixnode { let req = VestingExecuteMsg::DelegateToMixnode {
mix_identity: mix_identity.into(), mix_identity: mix_identity.into(),
amount: amount.clone(), amount: amount.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::DelegateToMixnode", "VestingContract::DelegateToMixnode",
@@ -261,18 +328,20 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
) )
.await .await
} }
async fn vesting_undelegate_from_mixnode<'a>( async fn vesting_undelegate_from_mixnode<'a>(
&self, &self,
mix_identity: IdentityKeyRef<'a>, mix_identity: IdentityKeyRef<'a>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UndelegateFromMixnode); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::UndelegateFromMixnode { let req = VestingExecuteMsg::UndelegateFromMixnode {
mix_identity: mix_identity.into(), mix_identity: mix_identity.into(),
}; };
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::UndelegateFromMixnode", "VestingContract::UndelegateFromMixnode",
@@ -280,14 +349,16 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
) )
.await .await
} }
async fn create_periodic_vesting_account( async fn create_periodic_vesting_account(
&self, &self,
owner_address: &str, owner_address: &str,
staking_address: Option<String>, staking_address: Option<String>,
vesting_spec: Option<VestingSpecification>, vesting_spec: Option<VestingSpecification>,
amount: Coin, amount: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> { ) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::CreatePeriodicVestingAccount); let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = VestingExecuteMsg::CreateAccount { let req = VestingExecuteMsg::CreateAccount {
owner_address: owner_address.to_string(), owner_address: owner_address.to_string(),
staking_address, staking_address,
@@ -296,48 +367,11 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
self.client self.client
.execute( .execute(
self.address(), self.address(),
self.vesting_contract_address()?, self.vesting_contract_address(),
&req, &req,
fee, fee,
"VestingContract::CreatePeriodicVestingAccount", "VestingContract::CreatePeriodicVestingAccount",
vec![cosmwasm_coin_to_cosmos_coin(amount)], vec![amount],
)
.await
}
async fn update_mixnet_address(&self, address: &str) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UpdateMixnetAddress);
let req = VestingExecuteMsg::UpdateMixnetAddress {
address: address.to_string(),
};
self.client
.execute(
self.address(),
self.vesting_contract_address()?,
&req,
fee,
"VestingContract::UpdateMixnetAddress",
vec![],
)
.await
}
async fn vesting_update_mixnode_config(
&self,
profit_margin_percent: u8,
) -> Result<ExecuteResult, NymdError> {
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
let req = VestingExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
self.client
.execute(
self.address(),
self.vesting_contract_address()?,
&req,
fee,
"VestingContract::UpdateMixnetConfig",
vec![],
) )
.await .await
} }
@@ -3,14 +3,18 @@
use crate::validator_api::error::ValidatorAPIError; use crate::validator_api::error::ValidatorAPIError;
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG}; use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; use coconut_interface::{
BlindSignRequestBody, BlindedSignatureResponse, ExecuteReleaseFundsRequestBody,
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
VerifyCredentialBody, VerifyCredentialResponse,
};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use url::Url; use url::Url;
use validator_api_requests::models::{ use validator_api_requests::models::{
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse, CoreNodeStatusResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
}; };
pub mod error; pub mod error;
@@ -85,6 +89,16 @@ impl Client {
.await .await
} }
pub async fn get_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
self.query_validator_api(
&[routes::API_VERSION, routes::MIXNODES, routes::DETAILED],
NO_PARAMS,
)
.await
}
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> { pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS) self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await .await
@@ -98,6 +112,21 @@ impl Client {
.await .await
} }
pub async fn get_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::MIXNODES,
routes::ACTIVE,
routes::DETAILED,
],
NO_PARAMS,
)
.await
}
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> { pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api( self.query_validator_api(
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED], &[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
@@ -106,6 +135,21 @@ impl Client {
.await .await
} }
pub async fn get_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::MIXNODES,
routes::REWARDED,
routes::DETAILED,
],
NO_PARAMS,
)
.await
}
pub async fn get_probs_mixnode_rewarded( pub async fn get_probs_mixnode_rewarded(
&self, &self,
mixnode_id: &str, mixnode_id: &str,
@@ -331,6 +375,57 @@ impl Client {
) )
.await .await
} }
pub async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, ValidatorAPIError> {
self.post_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_VERIFY_BANDWIDTH_CREDENTIAL,
],
NO_PARAMS,
request_body,
)
.await
}
pub async fn propose_release_funds(
&self,
request_body: &ProposeReleaseFundsRequestBody,
) -> Result<ProposeReleaseFundsResponse, ValidatorAPIError> {
self.post_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_PROPOSE_RELEASE_FUNDS,
],
NO_PARAMS,
request_body,
)
.await
}
pub async fn execute_release_funds(
&self,
request_body: &ExecuteReleaseFundsRequestBody,
) -> Result<(), ValidatorAPIError> {
self.post_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_EXECUTE_RELEASE_FUNDS,
],
NO_PARAMS,
request_body,
)
.await
}
} }
// utility function that should solve the double slash problem in validator API forever. // utility function that should solve the double slash problem in validator API forever.
@@ -7,6 +7,7 @@ pub const API_VERSION: &str = VALIDATOR_API_VERSION;
pub const MIXNODES: &str = "mixnodes"; pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways"; pub const GATEWAYS: &str = "gateways";
pub const DETAILED: &str = "detailed";
pub const ACTIVE: &str = "active"; pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded"; pub const REWARDED: &str = "rewarded";
@@ -16,6 +17,9 @@ pub const BANDWIDTH: &str = "bandwidth";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign"; pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential"; pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key"; pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
pub const COCONUT_PROPOSE_RELEASE_FUNDS: &str = "propose-release-funds";
pub const COCONUT_EXECUTE_RELEASE_FUNDS: &str = "execute-release-funds";
pub const STATUS_ROUTES: &str = "status"; pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode"; pub const MIXNODE: &str = "mixnode";
+3 -5
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nymcoconut::CoconutError;
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -11,9 +12,6 @@ pub enum CoconutInterfaceError {
#[error("Could not decode base 58 string - {0}")] #[error("Could not decode base 58 string - {0}")]
MalformedString(#[from] bs58::decode::Error), MalformedString(#[from] bs58::decode::Error),
#[error("Not enough public attributes were specified")] #[error("Coconut error - {0}")]
NotEnoughPublicAttributes, CoconutError(#[from] CoconutError),
#[error("Could not recover bandwidth value")]
InvalidBandwidth,
} }
+191 -46
View File
@@ -5,96 +5,158 @@ pub mod error;
use getset::{CopyGetters, Getters}; use getset::{CopyGetters, Getters};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr;
use error::CoconutInterfaceError; use error::CoconutInterfaceError;
pub use nymcoconut::*; pub use nymcoconut::*;
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)] #[derive(Debug, Serialize, Deserialize, Getters, CopyGetters, Clone, PartialEq)]
pub struct Credential { pub struct Credential {
#[getset(get = "pub")] #[getset(get = "pub")]
n_params: u32, n_params: u32,
#[getset(get = "pub")] #[getset(get = "pub")]
theta: Theta, theta: Theta,
public_attributes: Vec<Vec<u8>>, voucher_value: u64,
#[getset(get = "pub")] voucher_info: String,
signature: Signature,
} }
impl Credential { impl Credential {
pub fn new( pub fn new(
n_params: u32, n_params: u32,
theta: Theta, theta: Theta,
voucher_value: String, voucher_value: u64,
voucher_info: String, voucher_info: String,
signature: &Signature,
) -> Credential { ) -> Credential {
let public_attributes = vec![voucher_value.into_bytes(), voucher_info.into_bytes()];
Credential { Credential {
n_params, n_params,
theta, theta,
public_attributes, voucher_value,
signature: *signature, voucher_info,
} }
} }
pub fn voucher_value(&self) -> Result<u64, CoconutInterfaceError> { pub fn blinded_serial_number(&self) -> String {
let bandwidth_vec = self self.theta.blinded_serial_number_bs58()
.public_attributes }
.get(0)
.ok_or(CoconutInterfaceError::NotEnoughPublicAttributes)?
.to_owned();
let bandwidth_str = String::from_utf8(bandwidth_vec)
.map_err(|_| CoconutInterfaceError::InvalidBandwidth)?;
let value =
u64::from_str(&bandwidth_str).map_err(|_| CoconutInterfaceError::InvalidBandwidth)?;
Ok(value) pub fn has_blinded_serial_number(
&self,
blinded_serial_number_bs58: &str,
) -> Result<bool, CoconutInterfaceError> {
Ok(self
.theta
.has_blinded_serial_number(blinded_serial_number_bs58)?)
}
pub fn voucher_value(&self) -> u64 {
self.voucher_value
} }
pub fn verify(&self, verification_key: &VerificationKey) -> bool { pub fn verify(&self, verification_key: &VerificationKey) -> bool {
let params = Parameters::new(self.n_params).unwrap(); let params = Parameters::new(self.n_params).unwrap();
let public_attributes = self let public_attributes = vec![
.public_attributes self.voucher_value.to_string().as_bytes(),
.iter() self.voucher_info.as_bytes(),
.map(hash_to_scalar) ]
.collect::<Vec<Attribute>>(); .iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
nymcoconut::verify_credential(&params, verification_key, &self.theta, &public_attributes) nymcoconut::verify_credential(&params, verification_key, &self.theta, &public_attributes)
} }
pub fn as_bytes(&self) -> Vec<u8> {
let n_params_bytes = self.n_params.to_be_bytes();
let theta_bytes = self.theta.to_bytes();
let theta_bytes_len = theta_bytes.len();
let voucher_value_bytes = self.voucher_value.to_be_bytes();
let voucher_info_bytes = self.voucher_info.as_bytes();
let voucher_info_len = voucher_info_bytes.len();
let mut bytes = Vec::with_capacity(28 + theta_bytes_len + voucher_info_len);
bytes.extend_from_slice(&n_params_bytes);
bytes.extend_from_slice(&(theta_bytes_len as u64).to_be_bytes());
bytes.extend_from_slice(&theta_bytes);
bytes.extend_from_slice(&voucher_value_bytes);
bytes.extend_from_slice(voucher_info_bytes);
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CoconutError> {
if bytes.len() < 28 {
return Err(CoconutError::Deserialization(String::from(
"To few bytes in credential",
)));
}
let mut four_byte = [0u8; 4];
let mut eight_byte = [0u8; 8];
four_byte.copy_from_slice(&bytes[..4]);
let n_params = u32::from_be_bytes(four_byte);
eight_byte.copy_from_slice(&bytes[4..12]);
let theta_len = u64::from_be_bytes(eight_byte);
if bytes.len() < 28 + theta_len as usize {
return Err(CoconutError::Deserialization(String::from(
"To few bytes in credential",
)));
}
let theta = Theta::from_bytes(&bytes[12..12 + theta_len as usize])
.map_err(|e| CoconutError::Deserialization(e.to_string()))?;
eight_byte.copy_from_slice(&bytes[12 + theta_len as usize..20 + theta_len as usize]);
let voucher_value = u64::from_be_bytes(eight_byte);
let voucher_info = String::from_utf8(bytes[20 + theta_len as usize..].to_vec())
.map_err(|e| CoconutError::Deserialization(e.to_string()))?;
Ok(Credential {
n_params,
theta,
voucher_value,
voucher_info,
})
}
} }
#[derive(Serialize, Deserialize, Debug, Getters, CopyGetters)] impl Bytable for Credential {
fn to_byte_vec(&self) -> Vec<u8> {
self.as_bytes()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
Credential::from_bytes(slice)
}
}
impl Base58 for Credential {}
#[derive(Serialize, Deserialize, Getters, CopyGetters)]
pub struct VerifyCredentialBody { pub struct VerifyCredentialBody {
#[getset(get = "pub")] #[getset(get = "pub")]
n_params: u32, credential: Credential,
#[getset(get = "pub")] #[getset(get = "pub")]
theta: Theta, proposal_id: u64,
public_attributes: Vec<String>,
} }
impl VerifyCredentialBody { impl VerifyCredentialBody {
pub fn new( pub fn new(credential: Credential, proposal_id: u64) -> VerifyCredentialBody {
n_params: u32,
theta: &Theta,
public_attributes: &[Attribute],
) -> VerifyCredentialBody {
VerifyCredentialBody { VerifyCredentialBody {
n_params, credential,
theta: theta.clone(), proposal_id,
public_attributes: public_attributes
.iter()
.map(|attr| attr.to_bs58())
.collect(),
} }
} }
}
pub fn public_attributes(&self) -> Vec<Attribute> { #[derive(Debug, Serialize, Deserialize)]
self.public_attributes pub struct VerifyCredentialResponse {
.iter() pub verification_result: bool,
.map(|x| Attribute::try_from_bs58(x).unwrap()) }
.collect()
impl VerifyCredentialResponse {
pub fn new(verification_result: bool) -> Self {
VerifyCredentialResponse {
verification_result,
}
} }
} }
// All strings are base58 encoded representations of structs // All strings are base58 encoded representations of structs
#[derive(Clone, Serialize, Deserialize, Debug, Getters, CopyGetters)] #[derive(Clone, Serialize, Deserialize, Debug, Getters, CopyGetters)]
pub struct BlindSignRequestBody { pub struct BlindSignRequestBody {
@@ -194,3 +256,86 @@ impl VerificationKeyResponse {
VerificationKeyResponse { key } VerificationKeyResponse { key }
} }
} }
#[derive(Serialize, Deserialize, Getters, CopyGetters)]
pub struct ProposeReleaseFundsRequestBody {
#[getset(get = "pub")]
credential: Credential,
}
impl ProposeReleaseFundsRequestBody {
pub fn new(credential: Credential) -> Self {
ProposeReleaseFundsRequestBody { credential }
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ProposeReleaseFundsResponse {
pub proposal_id: u64,
}
impl ProposeReleaseFundsResponse {
pub fn new(proposal_id: u64) -> Self {
ProposeReleaseFundsResponse { proposal_id }
}
}
#[derive(Debug, Serialize, Deserialize, Getters, CopyGetters)]
pub struct ExecuteReleaseFundsRequestBody {
#[getset(get = "pub")]
proposal_id: u64,
}
impl ExecuteReleaseFundsRequestBody {
pub fn new(proposal_id: u64) -> Self {
ExecuteReleaseFundsRequestBody { proposal_id }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde_coconut_credential() {
let voucher_value = 1000000u64;
let voucher_info = String::from("BandwidthVoucher");
let serial_number =
Attribute::try_from_bs58("7Rp3imcuNX3w9se9wm5th8gSvc2czsnMrGsdt5HsrycA").unwrap();
let binding_number =
Attribute::try_from_bs58("Auf8yVEgyEAWNHaXUZmimS4n9g5YiYnNYqp6F9BtBe9E").unwrap();
let signature = Signature::try_from_bs58(
"ta3pM9ffj5T6YGbwjSBp2W118rcwyP9PXStc\
7ssb91g5GQYMQHhuTNajbdZcjxUFBFL5rhED8EHpRzE8r432ss3qbPBfpNev4CdkfMkQ3wepyM7hy7q1W6Rn9WmFoZL\
ZR9j",
)
.unwrap();
let params = Parameters::new(4).unwrap();
let verification_key = VerificationKey::try_from_bs58("8CFtVVXdwLy4WHMQPE4\
woe89q3DRHoNxBSchftrEjSBPWA4r4xZv4Y9qSvS5x5bMmFtp7BX6ikECAnuXr5EjXWSsgjirZJmpS5XDUynVfht1cD\
FWGDvy2XFrRCuoCMotNXi3PoF6wYqdTR9Rqcfoj3i2H5Nid422WBaLtVoC9QNobvpvaqq6vX5PbsSyPayvU8HCXFxM6\
JjScYpbRTxQtdwefWLrk3LmXyJQBWi7c2VAhSxu9msp7VTBycqdwQNgxHETStZuwXsozxaGQ2KssVUCaaoYPR4g2RqK\
UAvtWwA7pMiAQNcbkXcbsjCgVjWaCpMWC37XA31cLcFf3zbjHD9e5tXjAcqa4M89fbFhuvvSXxowSAZ5NoWrN32kd5d\
wxJm1JW3Tt2h6yDDBe84oMy71462dZn7N78DVk2mFNGwBCibrZWA7oUzRBMfYxiQrksoFcou7QfLLd58zoNYmPQPt84\
1VpQopEBfdQ7Nf9zoXxBt3zMy7g5NsFGvzh7KTbDUyeeXrdkKJPQBs6dqaizr9sS8CPPmR4uk96vDTRh8CJ5FbSsmb8\
nP71dRvvwRZJHGzwYirMo6SXS3ZYxFuiA3mkxYuqDHCwkTWDuRCcAaztrDYRZg7VCMo4Q446AaEso5eqpeWpHZQt53E\
ZRpqmNYKASGwMhTeEHPSLgSmtoAAUcaRWpGRzYfd6kzEma8tdGLwyP4rLXgvSvtDLP37dU7YgF3LEXbGAz57U9ATy46\
6sroLpHPdaCWB8RF11wvB6Tu196JnJd2KyQBP1iUWP3rtZs3GhAF1QVcxquh8BqDZzAcpQ6wCS1P9c5GxKgww77FVF5\
Kp83XtoxSrw3GaYVyKTGxNh3vcKPR31txCjTxPaN2fg7TaPLhoQJX4YaAroFSXqrqbbRsisuHhhCeUP2YwDjHedes9y")
.unwrap();
let theta = prove_bandwidth_credential(
&params,
&verification_key,
&signature,
serial_number,
binding_number,
)
.unwrap();
let credential = Credential::new(4, theta, voucher_value, voucher_info);
let serialized_credential = credential.as_bytes();
let deserialized_credential = Credential::from_bytes(&serialized_credential).unwrap();
assert_eq!(credential, deserialized_credential);
}
}
@@ -23,7 +23,9 @@ pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set";
pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval"; pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval";
pub const ADVANCE_EPOCH_EVENT_TYPE: &str = "advance_epoch"; pub const ADVANCE_EPOCH_EVENT_TYPE: &str = "advance_epoch";
pub const COMPOUND_DELEGATOR_REWARD_EVENT_TYPE: &str = "compound_delegator_reward"; pub const COMPOUND_DELEGATOR_REWARD_EVENT_TYPE: &str = "compound_delegator_reward";
pub const CLAIM_DELEGATOR_REWARD_EVENT_TYPE: &str = "claim_delegator_reward";
pub const COMPOUND_OPERATOR_REWARD_EVENT_TYPE: &str = "compound_operator_reward"; pub const COMPOUND_OPERATOR_REWARD_EVENT_TYPE: &str = "compound_operator_reward";
pub const CLAIM_OPERATOR_REWARD_EVENT_TYPE: &str = "claim_operator_reward";
pub const SNAPSHOT_MIXNODES_EVENT: &str = "snapshot_mixnodes"; pub const SNAPSHOT_MIXNODES_EVENT: &str = "snapshot_mixnodes";
// attributes that are used in multiple places // attributes that are used in multiple places
@@ -151,6 +153,11 @@ pub fn new_compound_operator_reward_event(owner: &Addr, amount: Uint128) -> Even
event.add_attribute(AMOUNT_KEY, amount.to_string()) event.add_attribute(AMOUNT_KEY, amount.to_string())
} }
pub fn new_claim_operator_reward_event(owner: &Addr, amount: Uint128) -> Event {
let event = Event::new(CLAIM_OPERATOR_REWARD_EVENT_TYPE).add_attribute(OWNER_KEY, owner);
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_compound_delegator_reward_event( pub fn new_compound_delegator_reward_event(
delegator: &Addr, delegator: &Addr,
proxy: &Option<Addr>, proxy: &Option<Addr>,
@@ -171,6 +178,26 @@ pub fn new_compound_delegator_reward_event(
.add_attribute(DELEGATOR_KEY, delegator) .add_attribute(DELEGATOR_KEY, delegator)
} }
pub fn new_claim_delegator_reward_event(
delegator: &Addr,
proxy: &Option<Addr>,
amount: Uint128,
mix_identity: IdentityKeyRef<'_>,
) -> Event {
let mut event =
Event::new(CLAIM_DELEGATOR_REWARD_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)
.add_attribute(DELEGATOR_KEY, delegator)
}
pub fn new_undelegation_event( pub fn new_undelegation_event(
delegator: &Addr, delegator: &Addr,
proxy: &Option<Addr>, proxy: &Option<Addr>,
@@ -318,6 +318,14 @@ impl NodeRewardResult {
} }
} }
pub struct RewardEstimate {
pub total_node_reward: u64,
pub operator_reward: u64,
pub delegators_reward: u64,
pub node_profit: u64,
pub operator_cost: u64,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNodeBond { pub struct MixNodeBond {
pub pledge_amount: Coin, pub pledge_amount: Coin,
@@ -410,63 +418,80 @@ impl MixNodeBond {
/ U128::from_num(circulating_supply) / U128::from_num(circulating_supply)
} }
pub fn lambda_ticked(&self, params: &RewardParams) -> U128 {
// Ratio of a bond to the token circulating supply
self.lambda(params).min(params.one_over_k())
}
pub fn lambda(&self, params: &RewardParams) -> U128 { pub fn lambda(&self, params: &RewardParams) -> U128 {
// Ratio of a bond to the token circulating supply // Ratio of a bond to the token circulating supply
let pledge_to_circulating_supply_ratio = self.pledge_to_circulating_supply(params.circulating_supply())
self.pledge_to_circulating_supply(params.circulating_supply()); }
pledge_to_circulating_supply_ratio.min(params.one_over_k())
pub fn sigma_ticked(&self, params: &RewardParams) -> U128 {
// Ratio of a delegation to the the token circulating supply
self.sigma(params).min(params.one_over_k())
} }
pub fn sigma(&self, params: &RewardParams) -> U128 { pub fn sigma(&self, params: &RewardParams) -> U128 {
// Ratio of a delegation to the the token circulating supply // Ratio of a delegation to the the token circulating supply
let total_bond_to_circulating_supply_ratio = self.total_bond_to_circulating_supply(params.circulating_supply())
self.total_bond_to_circulating_supply(params.circulating_supply());
total_bond_to_circulating_supply_ratio.min(params.one_over_k())
} }
pub fn estimate_reward( pub fn estimate_reward(
&self, &self,
params: &RewardParams, params: &RewardParams,
) -> Result<(u64, u64, u64), MixnetContractError> { ) -> Result<RewardEstimate, MixnetContractError> {
let total_node_reward = self let total_node_reward = self
.reward(params) .reward(params)
.reward() .reward()
.checked_to_num::<u128>() .checked_to_num::<u128>()
.unwrap_or_default(); .unwrap_or_default();
let node_profit = self
.node_profit(params)
.checked_to_num::<u128>()
.unwrap_or_default();
let operator_cost = params
.node
.operator_cost()
.checked_to_num::<u128>()
.unwrap_or_default();
let operator_reward = self.operator_reward(params); let operator_reward = self.operator_reward(params);
// Total reward has to be the sum of operator and delegator rewards // Total reward has to be the sum of operator and delegator rewards
let delegators_reward = total_node_reward - operator_reward; let delegators_reward = node_profit.saturating_sub(operator_reward);
Ok(( Ok(RewardEstimate {
total_node_reward.try_into()?, total_node_reward: total_node_reward.try_into()?,
operator_reward.try_into()?, operator_reward: operator_reward.try_into()?,
delegators_reward.try_into()?, delegators_reward: delegators_reward.try_into()?,
)) node_profit: node_profit.try_into()?,
operator_cost: operator_cost.try_into()?,
})
} }
// keybase://chat/nymtech#dev-core/14473
pub fn reward(&self, params: &RewardParams) -> NodeRewardResult { pub fn reward(&self, params: &RewardParams) -> NodeRewardResult {
let lambda = self.lambda(params); let lambda_ticked = self.lambda_ticked(params);
let sigma = self.sigma(params); let sigma_ticked = self.sigma_ticked(params);
let reward = params.performance() let reward = params.performance()
* params.epoch_reward_pool() * params.epoch_reward_pool()
* (sigma * params.omega() * (sigma_ticked * params.omega()
+ params.alpha() * lambda * sigma * params.rewarded_set_size()) + params.alpha() * lambda_ticked * sigma_ticked * params.rewarded_set_size())
/ (ONE + params.alpha()); / (ONE + params.alpha());
// we only need regular lambda and sigma to calculate operator and delegator rewards
NodeRewardResult { NodeRewardResult {
reward, reward,
lambda, lambda: self.lambda(params),
sigma, sigma: self.sigma(params),
} }
} }
pub fn node_profit(&self, params: &RewardParams) -> U128 { pub fn node_profit(&self, params: &RewardParams) -> U128 {
if self.reward(params).reward() < params.node.operator_cost() { self.reward(params)
U128::from_num(0u128) .reward()
} else { .saturating_sub(params.node.operator_cost())
self.reward(params).reward() - params.node.operator_cost()
}
} }
pub fn operator_reward(&self, params: &RewardParams) -> u128 { pub fn operator_reward(&self, params: &RewardParams) -> u128 {
@@ -474,11 +499,9 @@ impl MixNodeBond {
if reward.sigma == 0 { if reward.sigma == 0 {
return 0; return 0;
} }
let profit = if reward.reward < params.node.operator_cost() {
U128::from_num(0u128) let profit = reward.reward.saturating_sub(params.node.operator_cost());
} else {
reward.reward - params.node.operator_cost()
};
let operator_base_reward = reward.reward.min(params.node.operator_cost()); let operator_base_reward = reward.reward.min(params.node.operator_cost());
// Div by zero checked above // Div by zero checked above
let operator_reward = (self.profit_margin() let operator_reward = (self.profit_margin()
@@ -99,6 +99,17 @@ pub enum ExecuteMsg {
}, },
// AdvanceCurrentInterval {}, // AdvanceCurrentInterval {},
AdvanceCurrentEpoch {}, AdvanceCurrentEpoch {},
ClaimOperatorReward {},
ClaimOperatorRewardOnBehalf {
owner: String,
},
ClaimDelegatorReward {
mix_identity: IdentityKey,
},
ClaimDelegatorRewardOnBehalf {
mix_identity: IdentityKey,
owner: String,
},
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -42,7 +42,7 @@ impl NodeEpochRewards {
} }
pub fn operator_cost(&self) -> U128 { pub fn operator_cost(&self) -> U128 {
U128::from_num(self.params.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128) self.params.operator_cost()
} }
pub fn node_profit(&self) -> U128 { pub fn node_profit(&self) -> U128 {
@@ -178,11 +178,15 @@ impl NodeRewardParams {
} }
pub fn operator_cost(&self) -> U128 { pub fn operator_cost(&self) -> U128 {
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128) self.performance() * U128::from_num(DEFAULT_OPERATOR_INTERVAL_COST)
} }
pub fn uptime(&self) -> u128 { pub fn uptime(&self) -> Uint128 {
self.uptime.u128() self.uptime
}
pub fn performance(&self) -> U128 {
U128::from_num(self.uptime.u128()) / U128::from_num(100)
} }
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) { pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
@@ -233,7 +237,7 @@ impl RewardParams {
} }
pub fn performance(&self) -> U128 { pub fn performance(&self) -> U128 {
U128::from_num(self.node.uptime.u128()) / U128::from_num(100) self.node.performance()
} }
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) { pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
@@ -256,8 +260,8 @@ impl RewardParams {
self.node.reward_blockstamp self.node.reward_blockstamp
} }
pub fn uptime(&self) -> u128 { pub fn uptime(&self) -> Uint128 {
self.node.uptime.u128() self.node.uptime()
} }
pub fn one_over_k(&self) -> U128 { pub fn one_over_k(&self) -> U128 {
@@ -0,0 +1,14 @@
[package]
name = "multisig-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]
cw-utils = { version = "0.13.1" }
cw3 = { version = "0.13.1" }
cw4 = { version = "0.13.1" }
cosmwasm-std = "1.0.0-beta6"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -0,0 +1 @@
pub mod msg;
@@ -1,7 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use cosmwasm_std::{CosmosMsg, Empty}; use cosmwasm_std::{CosmosMsg, Empty};
pub use cw3::ProposalResponse;
use cw3::Vote; use cw3::Vote;
use cw4::MemberChangedHookMsg; use cw4::MemberChangedHookMsg;
use cw_utils::{Duration, Expiration, Threshold}; use cw_utils::{Duration, Expiration, Threshold};
@@ -20,6 +20,7 @@ pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixno
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond"; 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_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation"; pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation";
pub const TRACK_REWARD_EVENT_TYPE: &str = "track_reaward";
// attributes that are used in multiple places // attributes that are used in multiple places
pub const OWNER_KEY: &str = "owner"; pub const OWNER_KEY: &str = "owner";
@@ -136,3 +137,7 @@ pub fn new_track_gateway_unbond_event() -> Event {
pub fn new_track_undelegation_event() -> Event { pub fn new_track_undelegation_event() -> Event {
Event::new(TRACK_UNDELEGATION_EVENT_TYPE) Event::new(TRACK_UNDELEGATION_EVENT_TYPE)
} }
pub fn new_track_reward_event() -> Event {
Event::new(TRACK_REWARD_EVENT_TYPE)
}
@@ -5,6 +5,8 @@ use cosmwasm_std::{Coin, Timestamp};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
pub mod events; pub mod events;
pub mod messages; pub mod messages;
@@ -49,6 +49,14 @@ impl VestingSpecification {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ExecuteMsg { pub enum ExecuteMsg {
TrackReward {
amount: Coin,
address: String,
},
ClaimOperatorReward {},
ClaimDelegatorReward {
mix_identity: String,
},
CompoundDelegatorReward { CompoundDelegatorReward {
mix_identity: String, mix_identity: String,
}, },
+2 -2
View File
@@ -12,9 +12,9 @@ nymcoconut = { path = "../nymcoconut" }
log = "0.4" log = "0.4"
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]} sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
thiserror = "1.0" thiserror = "1.0"
tokio = { version = "1.4", features = [ "rt-multi-thread", "net", "signal", "fs" ] } tokio = { version = "1.19.1", features = [ "rt-multi-thread", "net", "signal", "fs" ] }
[build-dependencies] [build-dependencies]
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
tokio = { version = "1.4", features = ["rt-multi-thread", "macros"] } tokio = { version = "1.19.1", features = ["rt-multi-thread", "macros"] }
+1 -1
View File
@@ -7,7 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] } bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
cosmrs = { version = "0.4.1", optional = true } cosmrs = { version = "0.7.0", optional = true }
thiserror = "1.0" thiserror = "1.0"
url = "2.2" url = "2.2"
+4 -1
View File
@@ -204,7 +204,10 @@ mod test {
.to_base58_string(), .to_base58_string(),
) )
.unwrap(), .unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(), encryption::PrivateKey::from_bytes(
&encryption::KeyPair::new(&mut rng).private_key().to_bytes(),
)
.unwrap(),
); );
assert!(!BandwidthVoucher::verify_against_plain( assert!(!BandwidthVoucher::verify_against_plain(
&[], &[],
+3 -16
View File
@@ -51,12 +51,7 @@ pub async fn obtain_aggregate_verification_key(
let mut shares = Vec::with_capacity(validators.len()); let mut shares = Vec::with_capacity(validators.len());
let mut client = validator_client::ApiClient::new(validators[0].clone()); let mut client = validator_client::ApiClient::new(validators[0].clone());
let response = client.get_coconut_verification_key().await?; for (id, validator_url) in validators.iter().enumerate() {
indices.push(1);
shares.push(response.key);
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone()); client.change_validator_api(validator_url.clone());
let response = client.get_coconut_verification_key().await?; let response = client.get_coconut_verification_key().await?;
indices.push((id + 1) as u64); indices.push((id + 1) as u64);
@@ -135,14 +130,7 @@ pub async fn obtain_aggregate_signature(
let mut validators_partial_vks: Vec<VerificationKey> = Vec::with_capacity(validators.len()); let mut validators_partial_vks: Vec<VerificationKey> = Vec::with_capacity(validators.len());
let mut client = validator_client::ApiClient::new(validators[0].clone()); let mut client = validator_client::ApiClient::new(validators[0].clone());
let validator_partial_vk = client.get_coconut_verification_key().await?; for (id, validator_url) in validators.iter().enumerate() {
validators_partial_vks.push(validator_partial_vk.key.clone());
let first =
obtain_partial_credential(params, attributes, &client, &validator_partial_vk.key).await?;
shares.push(SignatureShare::new(first, 1));
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone()); client.change_validator_api(validator_url.clone());
let validator_partial_vk = client.get_coconut_verification_key().await?; let validator_partial_vk = client.get_coconut_verification_key().await?;
validators_partial_vks.push(validator_partial_vk.key.clone()); validators_partial_vks.push(validator_partial_vk.key.clone());
@@ -193,8 +181,7 @@ pub fn prepare_credential_for_spending(
Ok(Credential::new( Ok(Credential::new(
PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES,
theta, theta,
voucher_value.to_string(), voucher_value,
voucher_info, voucher_info,
signature,
)) ))
} }
+1 -5
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "coconut")] #[cfg(feature = "coconut")]
use coconut_interface::{error::CoconutInterfaceError, CoconutError}; use coconut_interface::CoconutError;
use crypto::asymmetric::encryption::KeyRecoveryError; use crypto::asymmetric::encryption::KeyRecoveryError;
use validator_client::ValidatorClientError; use validator_client::ValidatorClientError;
@@ -20,10 +20,6 @@ pub enum Error {
#[error("Ran into a coconut error - {0}")] #[error("Ran into a coconut error - {0}")]
CoconutError(#[from] CoconutError), CoconutError(#[from] CoconutError),
#[cfg(feature = "coconut")]
#[error("Ran into a coconut interface error - {0}")]
CoconutInterfaceError(#[from] CoconutInterfaceError),
#[error("Ran into a validator client error - {0}")] #[error("Ran into a validator client error - {0}")]
ValidatorClientError(#[from] ValidatorClientError), ValidatorClientError(#[from] ValidatorClientError),
+3
View File
@@ -19,6 +19,8 @@ cipher = { version = "0.4.3", optional = true }
x25519-dalek = { version = "1.1", optional = true } x25519-dalek = { version = "1.1", optional = true }
ed25519-dalek = { version = "1.0", optional = true } ed25519-dalek = { version = "1.0", optional = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true } rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
serde_bytes = { version = "0.11.6", optional = true }
serde_crate = { version = "1.0", optional = true, default_features = false, package = "serde" }
subtle-encoding = { version = "0.5", features = ["bech32-preview"]} subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
# internal # internal
@@ -30,6 +32,7 @@ config = { path="../../common/config" }
rand_chacha = "0.2" rand_chacha = "0.2"
[features] [features]
serde = ["serde_crate", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
asymmetric = ["x25519-dalek", "ed25519-dalek"] asymmetric = ["x25519-dalek", "ed25519-dalek"]
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"] hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"]
symmetric = ["aes", "ctr", "cipher", "generic-array"] symmetric = ["aes", "ctr", "cipher", "generic-array"]
+46 -1
View File
@@ -4,6 +4,8 @@
use pemstore::traits::{PemStorableKey, PemStorableKeyPair}; use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
#[cfg(feature = "rand")] #[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
/// Size of a X25519 private key /// Size of a X25519 private key
@@ -127,6 +129,28 @@ impl PublicKey {
} }
} }
#[cfg(feature = "serde")]
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
Ok(PublicKey(x25519_dalek::PublicKey::deserialize(
deserializer,
)?))
}
}
impl PemStorableKey for PublicKey { impl PemStorableKey for PublicKey {
type Error = KeyRecoveryError; type Error = KeyRecoveryError;
@@ -143,7 +167,6 @@ impl PemStorableKey for PublicKey {
} }
} }
#[derive(Clone)]
pub struct PrivateKey(x25519_dalek::StaticSecret); pub struct PrivateKey(x25519_dalek::StaticSecret);
impl Display for PrivateKey { impl Display for PrivateKey {
@@ -187,6 +210,28 @@ impl PrivateKey {
} }
} }
#[cfg(feature = "serde")]
impl Serialize for PrivateKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for PrivateKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
Ok(PrivateKey(x25519_dalek::StaticSecret::deserialize(
deserializer,
)?))
}
}
impl PemStorableKey for PrivateKey { impl PemStorableKey for PrivateKey {
type Error = KeyRecoveryError; type Error = KeyRecoveryError;
+74 -1
View File
@@ -10,6 +10,13 @@ use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
#[cfg(feature = "serde")]
use serde::de::Error as SerdeError;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")]
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
#[derive(Debug)] #[derive(Debug)]
pub enum Ed25519RecoveryError { pub enum Ed25519RecoveryError {
MalformedBytes(SignatureError), MalformedBytes(SignatureError),
@@ -40,6 +47,7 @@ impl fmt::Display for Ed25519RecoveryError {
impl std::error::Error for Ed25519RecoveryError {} impl std::error::Error for Ed25519RecoveryError {}
/// Keypair for usage in ed25519 EdDSA. /// Keypair for usage in ed25519 EdDSA.
#[derive(Debug)]
pub struct KeyPair { pub struct KeyPair {
private_key: PrivateKey, private_key: PrivateKey,
public_key: PublicKey, public_key: PublicKey,
@@ -135,6 +143,28 @@ impl PublicKey {
} }
} }
#[cfg(feature = "serde")]
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
Ok(PublicKey(ed25519_dalek::PublicKey::deserialize(
deserializer,
)?))
}
}
impl PemStorableKey for PublicKey { impl PemStorableKey for PublicKey {
type Error = Ed25519RecoveryError; type Error = Ed25519RecoveryError;
@@ -200,6 +230,28 @@ impl PrivateKey {
} }
} }
#[cfg(feature = "serde")]
impl Serialize for PrivateKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for PrivateKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
Ok(PrivateKey(ed25519_dalek::SecretKey::deserialize(
deserializer,
)?))
}
}
impl PemStorableKey for PrivateKey { impl PemStorableKey for PrivateKey {
type Error = Ed25519RecoveryError; type Error = Ed25519RecoveryError;
@@ -216,7 +268,7 @@ impl PemStorableKey for PrivateKey {
} }
} }
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
pub struct Signature(ed25519_dalek::Signature); pub struct Signature(ed25519_dalek::Signature);
impl Signature { impl Signature {
@@ -237,3 +289,24 @@ impl Signature {
Ok(Signature(ed25519_dalek::Signature::from_bytes(bytes)?)) Ok(Signature(ed25519_dalek::Signature::from_bytes(bytes)?))
} }
} }
#[cfg(feature = "serde")]
impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SerdeBytes::new(&self.to_bytes()).serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
Signature::from_bytes(bytes.as_ref()).map_err(SerdeError::custom)
}
}
+2 -2
View File
@@ -29,5 +29,5 @@ pub use blake3;
#[cfg(feature = "symmetric")] #[cfg(feature = "symmetric")]
pub use ctr; pub use ctr;
// TODO: this function uses all three modules: asymmetric crypto, symmetric crypto and derives key..., #[cfg(feature = "serde")]
// so I don't know where to put it... extern crate serde_crate as serde;
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "execute"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
syn = { version = "1", features = ["full"] }
quote = "1"
+110
View File
@@ -0,0 +1,110 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse_macro_input, Block, ExprMethodCall, FnArg, Ident, ItemFn, LitStr, ReturnType, Token,
VisPublic, Visibility,
};
#[proc_macro_attribute]
pub fn execute(attr: TokenStream, item: TokenStream) -> TokenStream {
let f = parse_macro_input!(item as ItemFn);
let target = parse_macro_input!(attr as LitStr).value();
let cl = if target == "mixnet" {
quote! {self.mixnet_contract_address()}
} else if target == "vesting" {
quote! {self.vesting_contract_address()}
} else {
panic!("Only `mixnet` and `vesting` targets are supported!")
};
let cl = proc_macro::TokenStream::from(cl);
let cl = parse_macro_input!(cl as ExprMethodCall);
let orig_f = f.clone();
let mut execute_f = f.clone();
let mut simulate_f = f.clone();
let name = f.sig.ident;
let name_str = name.to_string();
let call_args = f.sig.inputs.into_iter().filter_map(|arg| match arg {
FnArg::Receiver(_) => None,
FnArg::Typed(arg) => Some(arg.pat),
});
let execute_args = call_args.clone();
let simulate_args = call_args;
execute_f.sig.asyncness = Some(Token![async](execute_f.sig.ident.span()));
simulate_f.sig.asyncness = Some(Token![async](simulate_f.sig.ident.span()));
execute_f.vis = Visibility::Public(VisPublic {
pub_token: Token![pub](execute_f.sig.ident.span()),
});
simulate_f.vis = Visibility::Public(VisPublic {
pub_token: Token![pub](simulate_f.sig.ident.span()),
});
execute_f.sig.ident = Ident::new(
&format!("execute{}", execute_f.sig.ident),
execute_f.sig.ident.span(),
);
simulate_f.sig.ident = Ident::new(
&format!("simulate{}", simulate_f.sig.ident),
simulate_f.sig.ident.span(),
);
let execute_output = quote! {
-> Result<ExecuteResult, NymdError>
};
let o_ts = proc_macro::TokenStream::from(execute_output);
execute_f.sig.output = parse_macro_input!(o_ts as ReturnType);
let simulate_output = quote! {
-> Result<SimulateResponse, NymdError>
};
let o_ts = proc_macro::TokenStream::from(simulate_output);
simulate_f.sig.output = parse_macro_input!(o_ts as ReturnType);
let simulate_block = quote! {
{
let (msg, _fee) = self.#name(#(#simulate_args),*);
let msg = self.wrap_contract_execute_message(
#cl,
&msg,
vec![],
)?;
self.simulate(vec![msg]).await
}
};
let ts = proc_macro::TokenStream::from(simulate_block);
simulate_f.block = Box::new(parse_macro_input!(ts as Block));
let execute_block = quote! {
{
let (req, fee) = self.#name(#(#execute_args),*);
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
self.client
.execute(
self.address(),
#cl,
&req,
fee,
#name_str,
vec![],
)
.await
}
};
let ts = proc_macro::TokenStream::from(execute_block);
execute_f.block = Box::new(parse_macro_input!(ts as Block));
let out = quote! {
#orig_f
#execute_f
#simulate_f
};
out.into()
}
+2 -2
View File
@@ -14,8 +14,8 @@ humantime-serde = "1.0"
log = "0.4" log = "0.4"
rand = "0.8" rand = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.4", features = ["time", "macros", "rt", "net", "io-util"] } tokio = { version = "1.19.1", features = ["time", "macros", "rt", "net", "io-util"] }
tokio-util = { version = "0.6", features = ["codec"] } tokio-util = { version = "0.7.3", features = ["codec"] }
url = "2.2" url = "2.2"
crypto = { path = "../crypto" } crypto = { path = "../crypto" }
+8 -8
View File
@@ -52,6 +52,14 @@ impl Network {
self.details().bandwidth_claim_contract_address self.details().bandwidth_claim_contract_address
} }
pub fn coconut_bandwidth_contract_address(&self) -> &str {
self.details().coconut_bandwidth_contract_address
}
pub fn multisig_contract_address(&self) -> &str {
self.details().multisig_contract_address
}
pub fn rewarding_validator_address(&self) -> &str { pub fn rewarding_validator_address(&self) -> &str {
self.details().rewarding_validator_address self.details().rewarding_validator_address
} }
@@ -93,7 +101,6 @@ pub struct NetworkDetails {
mixnet_contract_address: String, mixnet_contract_address: String,
vesting_contract_address: String, vesting_contract_address: String,
bandwidth_claim_contract_address: String, bandwidth_claim_contract_address: String,
rewarding_validator_address: String,
validators: Vec<ValidatorDetails>, validators: Vec<ValidatorDetails>,
} }
@@ -105,7 +112,6 @@ impl From<&DefaultNetworkDetails<'_>> for NetworkDetails {
mixnet_contract_address: details.mixnet_contract_address.into(), mixnet_contract_address: details.mixnet_contract_address.into(),
vesting_contract_address: details.vesting_contract_address.into(), vesting_contract_address: details.vesting_contract_address.into(),
bandwidth_claim_contract_address: details.bandwidth_claim_contract_address.into(), bandwidth_claim_contract_address: details.bandwidth_claim_contract_address.into(),
rewarding_validator_address: details.rewarding_validator_address.into(),
validators: details.validators.clone(), validators: details.validators.clone(),
} }
} }
@@ -156,12 +162,6 @@ impl SupportedNetworks {
.map(|network_details| network_details.bandwidth_claim_contract_address.as_str()) .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) -> impl Iterator<Item = &ValidatorDetails> { pub fn validators(&self, network: Network) -> impl Iterator<Item = &ValidatorDetails> {
self.networks self.networks
.get(&network) .get(&network)
+13 -2
View File
@@ -18,6 +18,7 @@ cfg_if::cfg_if! {
if #[cfg(network = "mainnet")] { if #[cfg(network = "mainnet")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::MAINNET; pub const DEFAULT_NETWORK: all::Network = all::Network::MAINNET;
pub const DENOM: &str = mainnet::DENOM; pub const DENOM: &str = mainnet::DENOM;
pub const STAKE_DENOM: &str = mainnet::STAKE_DENOM;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS; pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS; pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
@@ -25,6 +26,7 @@ cfg_if::cfg_if! {
} else if #[cfg(network = "qa")] { } else if #[cfg(network = "qa")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::QA; pub const DEFAULT_NETWORK: all::Network = all::Network::QA;
pub const DENOM: &str = qa::DENOM; pub const DENOM: &str = qa::DENOM;
pub const STAKE_DENOM: &str = qa::STAKE_DENOM;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS; pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS; pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS;
@@ -32,6 +34,7 @@ cfg_if::cfg_if! {
} else if #[cfg(network = "sandbox")] { } else if #[cfg(network = "sandbox")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::SANDBOX; pub const DEFAULT_NETWORK: all::Network = all::Network::SANDBOX;
pub const DENOM: &str = sandbox::DENOM; pub const DENOM: &str = sandbox::DENOM;
pub const STAKE_DENOM: &str = sandbox::STAKE_DENOM;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS; pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS; pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS;
@@ -48,6 +51,8 @@ pub struct DefaultNetworkDetails<'a> {
mixnet_contract_address: &'a str, mixnet_contract_address: &'a str,
vesting_contract_address: &'a str, vesting_contract_address: &'a str,
bandwidth_claim_contract_address: &'a str, bandwidth_claim_contract_address: &'a str,
coconut_bandwidth_contract_address: &'a str,
multisig_contract_address: &'a str,
rewarding_validator_address: &'a str, rewarding_validator_address: &'a str,
validators: Vec<ValidatorDetails>, validators: Vec<ValidatorDetails>,
} }
@@ -59,6 +64,8 @@ static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS, mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS, vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: mainnet::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS, rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
validators: mainnet::validators(), validators: mainnet::validators(),
}); });
@@ -70,6 +77,8 @@ static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS, mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS, vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: sandbox::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: sandbox::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS, rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
validators: sandbox::validators(), validators: sandbox::validators(),
}); });
@@ -80,6 +89,8 @@ static QA_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> = Lazy::new(|| DefaultN
mixnet_contract_address: qa::MIXNET_CONTRACT_ADDRESS, mixnet_contract_address: qa::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: qa::VESTING_CONTRACT_ADDRESS, vesting_contract_address: qa::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, bandwidth_claim_contract_address: qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
coconut_bandwidth_contract_address: qa::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
multisig_contract_address: qa::MULTISIG_CONTRACT_ADDRESS,
rewarding_validator_address: qa::REWARDING_VALIDATOR_ADDRESS, rewarding_validator_address: qa::REWARDING_VALIDATOR_ADDRESS,
validators: qa::validators(), validators: qa::validators(),
}); });
@@ -143,7 +154,7 @@ pub const ETH_ERC20_APPROVE_FUNCTION_NAME: &str = "approve";
// Ethereum constants used for token bridge // Ethereum constants used for token bridge
/// How much bandwidth (in bytes) one token can buy /// How much bandwidth (in bytes) one token can buy
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024; pub const BYTES_PER_UTOKEN: u64 = 1024;
/// Threshold for claiming more bandwidth: 1 MB /// Threshold for claiming more bandwidth: 1 MB
pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024; pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024;
@@ -152,7 +163,7 @@ pub const TOKENS_TO_BURN: u64 = 1;
/// How many ERC20 utokens should be burned to buy bandwidth /// How many ERC20 utokens should be burned to buy bandwidth
pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000; pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000;
/// Default bandwidth (in bytes) that we try to buy /// Default bandwidth (in bytes) that we try to buy
pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN; pub const BANDWIDTH_VALUE: u64 = UTOKENS_TO_BURN * BYTES_PER_UTOKEN;
pub const VOUCHER_INFO: &str = "BandwidthVoucher"; pub const VOUCHER_INFO: &str = "BandwidthVoucher";
+4
View File
@@ -5,6 +5,7 @@ use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "n"; pub(crate) const BECH32_PREFIX: &str = "n";
pub const DENOM: &str = "unym"; pub const DENOM: &str = "unym";
pub const STAKE_DENOM: &str = "unyx";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g"; "n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
@@ -12,6 +13,9 @@ pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw"; "n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000"); hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
+4
View File
@@ -5,6 +5,7 @@ use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "n"; pub(crate) const BECH32_PREFIX: &str = "n";
pub const DENOM: &str = "unym"; pub const DENOM: &str = "unym";
pub const STAKE_DENOM: &str = "unyx";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep"; "n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep";
@@ -12,6 +13,9 @@ pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
"n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav"; "n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw";
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000"); hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
+4
View File
@@ -5,11 +5,15 @@ use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "nymt"; pub(crate) const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt"; pub const DENOM: &str = "unymt";
pub const STAKE_DENOM: &str = "unyxt";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j"; pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh"; pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv"; "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"nymt1nz0r0au8aj6dc00wmm3ufy4g4k86rjzlgq608r";
pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("8e0DcFF7F3085235C32E845f3667aEB3f1e83133"); hex_literal::hex!("8e0DcFF7F3085235C32E845f3667aEB3f1e83133");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
+3 -3
View File
@@ -7,6 +7,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tokio = { version = "1.4", features = [] } tokio = { version = "1.19.1", features = [] }
tokio-stream = "0.1" # this one seems to be a thing until `Stream` trait is stabilised in stdlib tokio-stream = "0.1.9" # this one seems to be a thing until `Stream` trait is stabilised in stdlib
tokio-util = { version = "0.6", features = ["time"] } tokio-util = { version = "0.7.3", features = ["time"] }
+1 -2
View File
@@ -327,8 +327,7 @@ impl ProofCmCs {
} }
} }
#[derive(Debug)] #[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(PartialEq))]
pub struct ProofKappaZeta { pub struct ProofKappaZeta {
// c // c
challenge: Scalar, challenge: Scalar,
@@ -0,0 +1,49 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bls12_381::G2Projective;
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::error::{CoconutError, Result};
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
pub struct BlindedSerialNumber {
pub(crate) inner: G2Projective,
}
impl TryFrom<&[u8]> for BlindedSerialNumber {
type Error = CoconutError;
fn try_from(bytes: &[u8]) -> Result<Self> {
if bytes.len() != 96 {
return Err(
CoconutError::Deserialization(
format!("Tried to deserialize blinded serial number with incorrect number of bytes, expected 96, got {}", bytes.len()),
));
}
let inner = try_deserialize_g2_projective(
&bytes.try_into().unwrap(),
CoconutError::Deserialization(
"failed to deserialize the blinded serial number (zeta)".to_string(),
),
)?;
Ok(BlindedSerialNumber { inner })
}
}
impl Bytable for BlindedSerialNumber {
fn to_byte_vec(&self) -> Vec<u8> {
self.inner.to_affine().to_compressed().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
Self::try_from(slice)
}
}
impl Base58 for BlindedSerialNumber {}
+2 -2
View File
@@ -19,6 +19,7 @@ use crate::utils::try_deserialize_g1_projective;
use crate::Attribute; use crate::Attribute;
pub mod aggregation; pub mod aggregation;
pub mod double_use;
pub mod issuance; pub mod issuance;
pub mod keygen; pub mod keygen;
pub mod setup; pub mod setup;
@@ -27,8 +28,7 @@ pub mod verification;
pub type SignerIndex = u64; pub type SignerIndex = u64;
// (h, s) // (h, s)
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Signature(pub(crate) G1Projective, pub(crate) G1Projective); pub struct Signature(pub(crate) G1Projective, pub(crate) G1Projective);
pub type PartialSignature = Signature; pub type PartialSignature = Signature;
+15 -2
View File
@@ -10,6 +10,7 @@ use group::{Curve, Group};
use crate::error::{CoconutError, Result}; use crate::error::{CoconutError, Result};
use crate::proofs::ProofKappaZeta; use crate::proofs::ProofKappaZeta;
use crate::scheme::double_use::BlindedSerialNumber;
use crate::scheme::setup::Parameters; use crate::scheme::setup::Parameters;
use crate::scheme::Signature; use crate::scheme::Signature;
use crate::scheme::VerificationKey; use crate::scheme::VerificationKey;
@@ -19,8 +20,7 @@ use crate::Attribute;
// TODO NAMING: this whole thing // TODO NAMING: this whole thing
// Theta // Theta
#[derive(Debug)] #[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Theta { pub struct Theta {
// blinded_message (kappa) // blinded_message (kappa)
pub blinded_message: G2Projective, pub blinded_message: G2Projective,
@@ -81,6 +81,12 @@ impl Theta {
) )
} }
pub fn has_blinded_serial_number(&self, blinded_serial_number_bs58: &str) -> Result<bool> {
let blinded_serial_number = BlindedSerialNumber::try_from_bs58(blinded_serial_number_bs58)?;
let ret = self.blinded_serial_number.eq(&blinded_serial_number.inner);
Ok(ret)
}
// blinded message (kappa) || blinded serial number (zeta) || credential || pi_v // blinded message (kappa) || blinded serial number (zeta) || credential || pi_v
pub fn to_bytes(&self) -> Vec<u8> { pub fn to_bytes(&self) -> Vec<u8> {
let blinded_message_bytes = self.blinded_message.to_affine().to_compressed(); let blinded_message_bytes = self.blinded_message.to_affine().to_compressed();
@@ -100,6 +106,13 @@ impl Theta {
pub fn from_bytes(bytes: &[u8]) -> Result<Theta> { pub fn from_bytes(bytes: &[u8]) -> Result<Theta> {
Theta::try_from(bytes) Theta::try_from(bytes)
} }
pub fn blinded_serial_number_bs58(&self) -> String {
let blinded_serial_nuumber = BlindedSerialNumber {
inner: self.blinded_serial_number,
};
blinded_serial_nuumber.to_bs58()
}
} }
impl Bytable for Theta { impl Bytable for Theta {
+1 -1
View File
@@ -33,5 +33,5 @@ mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract"
path = "framing" path = "framing"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
version = "1.4" version = "1.19.1"
features = ["sync"] features = ["sync"]
+1 -1
View File
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
bytes = "1.0" bytes = "1.0"
tokio-util = { version = "0.6", features = ["codec"] } tokio-util = { version = "0.7.3", features = ["codec"] }
nymsphinx-types = { path = "../types" } nymsphinx-types = { path = "../types" }
nymsphinx-params = { path = "../params" } nymsphinx-params = { path = "../params" }
+3 -3
View File
@@ -8,8 +8,8 @@ edition = "2021"
[dependencies] [dependencies]
bytes = "1.0" bytes = "1.0"
tokio = { version = "1.4", features = [ "net", "io-util", "sync", "macros", "time", "rt-multi-thread" ] } tokio = { version = "1.19.1", features = [ "net", "io-util", "sync", "macros", "time", "rt-multi-thread" ] }
tokio-util = { version = "0.6", features = [ "io" ] } # reason for getting this guy is to to able to port to tokio 1.X more quickly by being able to use tokio-util = { version = "0.7.3", features = [ "io" ] } # reason for getting this guy is to to able to port to tokio 1.X more quickly by being able to use
# their `read_buf` [from the util crate] replacement rather than having to rethink/reimplement `AvailableReader` with the new AsyncRead trait definition. # their `read_buf` [from the util crate] replacement rather than having to rethink/reimplement `AvailableReader` with the new AsyncRead trait definition.
# In the long run, the dependency should probably get removed in favour of pure-tokio implementation, but for time being it's fine. # In the long run, the dependency should probably get removed in favour of pure-tokio implementation, but for time being it's fine.
futures = "0.3" futures = "0.3"
@@ -18,4 +18,4 @@ socks5-requests = { path = "../requests" }
ordered-buffer = { path = "../ordered-buffer" } ordered-buffer = { path = "../ordered-buffer" }
[dev-dependencies] [dev-dependencies]
tokio-test = "0.4" tokio-test = "0.4.2"
+13
View File
@@ -502,6 +502,7 @@ dependencies = [
"cw3-fixed-multisig", "cw3-fixed-multisig",
"cw4", "cw4",
"cw4-group", "cw4-group",
"multisig-contract-common",
"schemars", "schemars",
"serde", "serde",
"thiserror", "thiserror",
@@ -1042,6 +1043,18 @@ dependencies = [
"time 0.3.6", "time 0.3.6",
] ]
[[package]]
name = "multisig-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"cw-utils",
"cw3",
"cw4",
"schemars",
"serde",
]
[[package]] [[package]]
name = "network-defaults" name = "network-defaults"
version = "0.1.0" version = "0.1.0"
+26
View File
@@ -283,6 +283,32 @@ pub fn execute(
info, info,
) )
} }
ExecuteMsg::ClaimOperatorReward {} => {
crate::rewards::transactions::try_claim_operator_reward(deps, &env, &info)
}
ExecuteMsg::ClaimOperatorRewardOnBehalf { owner } => {
crate::rewards::transactions::try_claim_operator_reward_on_behalf(
deps, &env, &info, owner,
)
}
ExecuteMsg::ClaimDelegatorReward { mix_identity } => {
crate::rewards::transactions::try_claim_delegator_reward(
deps,
&env,
&info,
&mix_identity,
)
}
ExecuteMsg::ClaimDelegatorRewardOnBehalf {
mix_identity,
owner,
} => crate::rewards::transactions::try_claim_delegator_reward_on_behalf(
deps,
&env,
&info,
owner,
&mix_identity,
),
} }
} }
+2 -2
View File
@@ -153,8 +153,8 @@ pub enum ContractError {
#[from] #[from]
source: MixnetContractError, source: MixnetContractError,
}, },
#[error("No rewards to claim for mixnode {identity} for delegate {delegate}")] #[error("No rewards to claim for mixnode {identity} for {address}")]
NoRewardsToClaim { identity: String, delegate: String }, NoRewardsToClaim { identity: String, address: String },
#[error("Epoch not initialized yet!")] #[error("Epoch not initialized yet!")]
EpochNotInitialized, EpochNotInitialized,
+198 -9
View File
@@ -15,9 +15,13 @@ use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond};
use crate::rewards::helpers; use crate::rewards::helpers;
use crate::support::helpers::is_authorized; use crate::support::helpers::is_authorized;
use config::defaults::DENOM; use config::defaults::DENOM;
use cosmwasm_std::{Addr, Api, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128}; use cosmwasm_std::{
coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response,
Storage, Uint128,
};
use cw_storage_plus::Bound; use cw_storage_plus::Bound;
use mixnet_contract_common::events::{ use mixnet_contract_common::events::{
new_claim_delegator_reward_event, new_claim_operator_reward_event,
new_compound_delegator_reward_event, new_compound_operator_reward_event, new_compound_delegator_reward_event, new_compound_operator_reward_event,
new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event, new_mix_operator_rewarding_event, new_not_found_mix_operator_rewarding_event,
new_too_fresh_bond_mix_operator_rewarding_event, new_zero_uptime_mix_operator_rewarding_event, new_too_fresh_bond_mix_operator_rewarding_event, new_zero_uptime_mix_operator_rewarding_event,
@@ -27,6 +31,186 @@ use mixnet_contract_common::reward_params::{NodeEpochRewards, NodeRewardParams,
use mixnet_contract_common::{Delegation, IdentityKey, RewardingStatus}; use mixnet_contract_common::{Delegation, IdentityKey, RewardingStatus};
use mixnet_contract_common::RewardingResult; use mixnet_contract_common::RewardingResult;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use vesting_contract_common::one_ucoin;
// All four of the below methods need to do the following things:
// 1. Calculate currently available rewards
// 2. Send the rewards back to whoever claimed them
// 3. Set the LAST_CLAIMED_HEIGHT to the current height
pub fn try_claim_operator_reward(
deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
) -> Result<Response, ContractError> {
_try_claim_operator_reward(deps.storage, deps.api, env, &info.sender.to_string(), None)
}
pub fn try_claim_operator_reward_on_behalf(
deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
owner: String,
) -> Result<Response, ContractError> {
_try_claim_operator_reward(
deps.storage,
deps.api,
env,
&owner,
Some(info.sender.clone()),
)
}
fn _try_claim_operator_reward(
storage: &mut dyn Storage,
api: &dyn Api,
env: &Env,
owner: &str,
proxy: Option<Addr>,
) -> Result<Response, ContractError> {
let owner = api.addr_validate(owner)?;
let bond = match crate::mixnodes::storage::mixnodes()
.idx
.owner
.item(storage, owner.clone())?
{
Some(record) => record.1,
None => return Err(ContractError::NoAssociatedMixNodeBond { owner }),
};
if proxy != bond.proxy {
return Err(ContractError::ProxyMismatch {
existing: bond
.proxy
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
let reward = calculate_operator_reward(storage, api, &owner, &bond)?;
OPERATOR_REWARD_CLAIMED_HEIGHT.save(
storage,
(owner.to_string(), bond.identity().to_string()),
&env.block.height,
)?;
if reward.is_zero() {
return Err(ContractError::NoRewardsToClaim {
identity: bond.identity().to_string(),
address: owner.to_string(),
});
}
let return_tokens = BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
amount: coins(reward.u128(), DENOM),
};
let mut response = Response::default()
.add_message(return_tokens)
.add_event(new_claim_operator_reward_event(&owner, reward));
if let Some(proxy) = proxy {
let msg = Some(VestingContractExecuteMsg::TrackReward {
address: owner.to_string(),
amount: Coin::new(reward.u128(), DENOM),
});
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(wasm_msg);
}
Ok(response)
}
pub fn _try_claim_delegator_reward(
storage: &mut dyn Storage,
api: &dyn Api,
env: &Env,
owner: &str,
mix_identity: &str,
proxy: Option<Addr>,
) -> Result<Response, ContractError> {
let owner = api.addr_validate(owner)?;
let key = mixnet_contract_common::delegation::generate_storage_key(&owner, proxy.as_ref());
let reward = calculate_delegator_reward(storage, api, key.clone(), mix_identity)?;
DELEGATOR_REWARD_CLAIMED_HEIGHT.save(
storage,
(key, mix_identity.to_string()),
&env.block.height,
)?;
if reward.is_zero() {
return Err(ContractError::NoRewardsToClaim {
identity: mix_identity.to_string(),
address: owner.to_string(),
});
}
let return_tokens = BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
amount: coins(reward.u128(), DENOM),
};
let mut response =
Response::default()
.add_message(return_tokens)
.add_event(new_claim_delegator_reward_event(
&owner,
&proxy,
reward,
mix_identity,
));
if let Some(proxy) = proxy {
let msg = Some(VestingContractExecuteMsg::TrackReward {
address: owner.to_string(),
amount: Coin::new(reward.u128(), DENOM),
});
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(wasm_msg);
}
Ok(response)
}
pub fn try_claim_delegator_reward_on_behalf(
deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
owner: String,
mix_identity: &str,
) -> Result<Response, ContractError> {
_try_claim_delegator_reward(
deps.storage,
deps.api,
env,
&owner,
mix_identity,
Some(info.sender.clone()),
)
}
pub fn try_claim_delegator_reward(
deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
mix_identity: &str,
) -> Result<Response, ContractError> {
_try_claim_delegator_reward(
deps.storage,
deps.api,
env,
&info.sender.to_string(),
mix_identity,
None,
)
}
pub fn try_compound_operator_reward_on_behalf( pub fn try_compound_operator_reward_on_behalf(
deps: DepsMut, deps: DepsMut,
@@ -449,7 +633,7 @@ pub(crate) fn try_reward_mixnode(
let node_delegation = current_bond.total_delegation.amount; let node_delegation = current_bond.total_delegation.amount;
// check if it has non-zero uptime // check if it has non-zero uptime
if params.uptime() == 0 { if params.uptime() == Uint128::zero() {
storage::REWARDING_STATUS.save( storage::REWARDING_STATUS.save(
deps.storage, deps.storage,
(epoch.id(), mix_identity.clone()), (epoch.id(), mix_identity.clone()),
@@ -1381,7 +1565,7 @@ pub mod tests {
let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity) let mix_1 = mixnodes_storage::read_full_mixnode_bond(&deps.storage, &node_identity)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let mix_1_uptime = 100; let mix_1_uptime = 90;
let epoch = Interval::init_epoch(env.clone()); let epoch = Interval::init_epoch(env.clone());
save_epoch(&mut deps.storage, &epoch).unwrap(); save_epoch(&mut deps.storage, &epoch).unwrap();
@@ -1395,7 +1579,7 @@ pub mod tests {
params.set_reward_blockstamp(env.block.height); params.set_reward_blockstamp(env.block.height);
assert_eq!(params.performance(), U128::from_num(1u32)); assert_eq!(params.performance(), U128::from_num(0.8999999999999999));
let mix_1_reward_result = mix_1.reward(&params); let mix_1_reward_result = mix_1.reward(&params);
@@ -1407,9 +1591,14 @@ pub mod tests {
mix_1_reward_result.lambda(), mix_1_reward_result.lambda(),
U128::from_num(0.0000133333333333f64) U128::from_num(0.0000133333333333f64)
); );
assert_eq!(mix_1_reward_result.reward().int(), 259114u128); assert_eq!(mix_1_reward_result.reward().int(), 233202u128);
assert_eq!(mix_1.node_profit(&params).int(), 203558u128); assert_eq!(mix_1.node_profit(&params).int(), 183202u128);
assert_ne!(
mix_1_reward_result.reward(),
mix_1.node_profit(&params).int()
);
let mix1_operator_reward = mix_1.operator_reward(&params); let mix1_operator_reward = mix_1.operator_reward(&params);
@@ -1417,9 +1606,9 @@ pub mod tests {
let mix1_delegator2_reward = mix_1.reward_delegation(Uint128::new(2000_000000), &params); let mix1_delegator2_reward = mix_1.reward_delegation(Uint128::new(2000_000000), &params);
assert_eq!(mix1_operator_reward, 167513); assert_eq!(mix1_operator_reward, 150761);
assert_eq!(mix1_delegator1_reward, 73280); assert_eq!(mix1_delegator1_reward, 65952);
assert_eq!(mix1_delegator2_reward, 18320); assert_eq!(mix1_delegator2_reward, 16488);
assert_eq!( assert_eq!(
mix_1_reward_result.reward().int(), mix_1_reward_result.reward().int(),
@@ -28,6 +28,8 @@ schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] } serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" } thiserror = { version = "1.0.23" }
multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" }
[dev-dependencies] [dev-dependencies]
cosmwasm-schema = { version = "1.0.0-beta6" } cosmwasm-schema = { version = "1.0.0-beta6" }
cw4-group = { path = "../cw4-group", version = "0.13.1" } cw4-group = { path = "../cw4-group", version = "0.13.1" }
@@ -18,8 +18,8 @@ use cw_storage_plus::Bound;
use cw_utils::{maybe_addr, Expiration, ThresholdResponse}; use cw_utils::{maybe_addr, Expiration, ThresholdResponse};
use crate::error::ContractError; use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{Config, CONFIG}; use crate::state::{Config, CONFIG};
use multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
// version info for migration info // version info for migration info
const CONTRACT_NAME: &str = "crates.io:cw3-flex-multisig"; const CONTRACT_NAME: &str = "crates.io:cw3-flex-multisig";
@@ -499,7 +499,7 @@ mod tests {
max_voting_period: Duration, max_voting_period: Duration,
) -> Addr { ) -> Addr {
let flex_id = app.store_code(contract_flex()); let flex_id = app.store_code(contract_flex());
let msg = crate::msg::InstantiateMsg { let msg = InstantiateMsg {
group_addr: group.to_string(), group_addr: group.to_string(),
threshold, threshold,
max_voting_period, max_voting_period,
@@ -1,6 +1,5 @@
pub mod contract; pub mod contract;
pub mod error; pub mod error;
pub mod msg;
pub mod state; pub mod state;
pub use crate::error::ContractError; pub use crate::error::ContractError;
+15
View File
@@ -4,6 +4,21 @@
2. Admin account can then delegate vested and unvested tokens to mixnodes on behalf of vesting accounts 2. Admin account can then delegate vested and unvested tokens to mixnodes on behalf of vesting accounts
3. Vesting accounts can withdraw vested and undelegated (spendable) coins to their addresses 3. Vesting accounts can withdraw vested and undelegated (spendable) coins to their addresses
### Glossary
| Term | Definition |
| ----------------------- | --------------------------------------------------------------------------------------------- |
| original vesting coins | vesting amount specified at account creation |
| vested coins | coins vested up to the current vesting period, current period excluded |
| vesting coins | *original vesting* amount minus *vested coins*, claimed rewards are not part of vesting coins |
| delegated free coins | delegated coins that have vested |
| delegated vesting coins | total delegation amount minus *delegated free* coins |
| pledged free coins | pledged coins that have vested |
| pledged vesting coins | total pledge amount minus *pledged free* coins |
| locked coins | *vesting coins* minus *delegated vesting* and *pledged vesting* coins |
| spendable coins | current vesting account balance minus *locked coins* |
### Vesting coin delegation flow ### Vesting coin delegation flow
![vesting-coin-delegation](images/vesting-coin-delegation.png) ![vesting-coin-delegation](images/vesting-coin-delegation.png)
+40 -1
View File
@@ -13,7 +13,8 @@ use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
use vesting_contract_common::events::{ use vesting_contract_common::events::{
new_ownership_transfer_event, new_periodic_vesting_account_event, new_ownership_transfer_event, new_periodic_vesting_account_event,
new_staking_address_update_event, new_track_gateway_unbond_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, new_track_mixnode_unbond_event, new_track_reward_event, new_track_undelegation_event,
new_vested_coins_withdraw_event,
}; };
use vesting_contract_common::messages::{ use vesting_contract_common::messages::{
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification, ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
@@ -46,6 +47,13 @@ pub fn execute(
msg: ExecuteMsg, msg: ExecuteMsg,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
match msg { match msg {
ExecuteMsg::TrackReward { amount, address } => {
try_track_reward(deps, info, amount, &address)
}
ExecuteMsg::ClaimOperatorReward {} => try_claim_operator_reward(deps, info),
ExecuteMsg::ClaimDelegatorReward { mix_identity } => {
try_claim_delegator_reward(deps, info, mix_identity)
}
ExecuteMsg::CompoundDelegatorReward { mix_identity } => { ExecuteMsg::CompoundDelegatorReward { mix_identity } => {
try_compound_delegator_reward(mix_identity, info, deps) try_compound_delegator_reward(mix_identity, info, deps)
} }
@@ -278,6 +286,20 @@ pub fn try_track_unbond_mixnode(
Ok(Response::new().add_event(new_track_mixnode_unbond_event())) Ok(Response::new().add_event(new_track_mixnode_unbond_event()))
} }
fn try_track_reward(
deps: DepsMut<'_>,
info: MessageInfo,
amount: Coin,
address: &str,
) -> Result<Response, ContractError> {
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
return Err(ContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(address, deps.storage, deps.api)?;
account.track_reward(amount, deps.storage)?;
Ok(Response::new().add_event(new_track_reward_event()))
}
fn try_track_undelegation( fn try_track_undelegation(
address: &str, address: &str,
mix_identity: IdentityKey, mix_identity: IdentityKey,
@@ -314,6 +336,23 @@ fn try_compound_delegator_reward(
account.try_compound_delegator_reward(mix_identity, deps.storage) account.try_compound_delegator_reward(mix_identity, deps.storage)
} }
fn try_claim_operator_reward(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_claim_operator_reward(deps.storage)
}
fn try_claim_delegator_reward(
deps: DepsMut<'_>,
info: MessageInfo,
mix_identity: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_claim_delegator_reward(mix_identity, deps.storage)
}
fn try_undelegate_from_mixnode( fn try_undelegate_from_mixnode(
mix_identity: IdentityKey, mix_identity: IdentityKey,
info: MessageInfo, info: MessageInfo,
@@ -8,6 +8,8 @@ pub trait MixnodeBondingAccount {
storage: &dyn Storage, storage: &dyn Storage,
) -> Result<Response, ContractError>; ) -> Result<Response, ContractError>;
fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result<Response, ContractError>;
fn try_bond_mixnode( fn try_bond_mixnode(
&self, &self,
mix_node: MixNode, mix_node: MixNode,
@@ -3,6 +3,12 @@ use cosmwasm_std::{Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::IdentityKey; use mixnet_contract_common::IdentityKey;
pub trait DelegatingAccount { pub trait DelegatingAccount {
fn try_claim_delegator_reward(
&self,
mix_identity: IdentityKey,
storage: &dyn Storage,
) -> Result<Response, ContractError>;
fn try_compound_delegator_reward( fn try_compound_delegator_reward(
&self, &self,
mix_identity: IdentityKey, mix_identity: IdentityKey,
@@ -73,4 +73,5 @@ pub trait VestingAccount {
to_address: Option<Addr>, to_address: Option<Addr>,
storage: &mut dyn Storage, storage: &mut dyn Storage,
) -> Result<(), ContractError>; ) -> Result<(), ContractError>;
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError>;
} }
@@ -13,6 +13,25 @@ use vesting_contract_common::one_ucoin;
use super::Account; use super::Account;
impl DelegatingAccount for Account { impl DelegatingAccount for Account {
fn try_claim_delegator_reward(
&self,
mix_identity: IdentityKey,
storage: &dyn Storage,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::ClaimDelegatorRewardOnBehalf {
owner: self.owner_address().to_string(),
mix_identity,
};
let compound_delegator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_delegator_reward_msg))
}
fn try_compound_delegator_reward( fn try_compound_delegator_reward(
&self, &self,
mix_identity: IdentityKey, mix_identity: IdentityKey,
@@ -14,6 +14,20 @@ use vesting_contract_common::PledgeData;
use super::Account; use super::Account;
impl MixnodeBondingAccount for Account { impl MixnodeBondingAccount for Account {
fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::ClaimOperatorRewardOnBehalf {
owner: self.owner_address().into_string(),
};
let compound_operator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_operator_reward_msg))
}
fn try_compound_operator_reward( fn try_compound_operator_reward(
&self, &self,
storage: &dyn Storage, storage: &dyn Storage,
@@ -8,6 +8,13 @@ use vesting_contract_common::{OriginalVestingResponse, Period};
use super::Account; use super::Account;
impl VestingAccount for Account { impl VestingAccount for Account {
fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
let current_balance = self.load_balance(storage)?;
let new_balance = current_balance + amount.amount;
self.save_balance(new_balance, storage)?;
Ok(())
}
fn locked_coins( fn locked_coins(
&self, &self,
block_time: Option<Timestamp>, block_time: Option<Timestamp>,
+10 -9
View File
@@ -6,21 +6,22 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
chrono = { version = "0.4.19", features = ["serde"] }
humantime-serde = "1.0"
isocountry = "0.3.2" isocountry = "0.3.2"
itertools = "0.10.3"
log = "0.4.0"
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
pretty_env_logger = "0.4.0"
reqwest = "0.11.4" reqwest = "0.11.4"
rocket = {version = "0.5.0-rc.1", features=["json"] } rocket = {version = "0.5.0-rc.1", features=["json"] }
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" } rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
serde = "1.0.126"
humantime-serde = "1.0"
serde_json = "1.0.66"
tokio = {version = "1.9.0", features = ["full"] }
chrono = { version = "0.4.19", features = ["serde"] }
schemars = { version = "0.8", features = ["preserve_order"] }
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] } rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] }
log = "0.4.0" schemars = { version = "0.8", features = ["preserve_order"] }
pretty_env_logger = "0.4.0" serde = "1.0.126"
serde_json = "1.0.66"
thiserror = "1.0.29" thiserror = "1.0.29"
tokio = {version = "1.19.1", features = ["full"] }
mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" } mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" }
network-defaults = { path = "../common/network-defaults" } network-defaults = { path = "../common/network-defaults" }
+2 -2
View File
@@ -5,13 +5,13 @@ An API that provides data for the [Network Explorer](../explorer).
Features: Features:
- geolocates mixnodes using https://freegeoip.app/ - geolocates mixnodes using https://app.ipbase.com/
- calculates how many nodes are in each country - calculates how many nodes are in each country
- proxies mixnode API requests to add HTTPS - proxies mixnode API requests to add HTTPS
## Running ## Running
Supply the environment variable `GEO_IP_SERVICE_API_KEY` with a key from https://freegeoip.app/. Supply the environment variable `GEO_IP_SERVICE_API_KEY` with a key from https://app.ipbase.com/.
Run as a service and reverse proxy with `nginx` to add `https` with Lets Encrypt. Run as a service and reverse proxy with `nginx` to add `https` with Lets Encrypt.
+1 -10
View File
@@ -23,19 +23,10 @@ impl ThreadsafeValidatorClient {
} }
pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient { pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient {
let network = DEFAULT_NETWORK;
let mixnet_contract = network.mixnet_contract_address().to_string();
let nymd_url = default_nymd_endpoints()[0].clone(); let nymd_url = default_nymd_endpoints()[0].clone();
let api_url = default_api_endpoints()[0].clone(); let api_url = default_api_endpoints()[0].clone();
let client_config = validator_client::Config::new( let client_config = validator_client::Config::new(DEFAULT_NETWORK, nymd_url, api_url);
network,
nymd_url,
api_url,
Some(mixnet_contract.parse().unwrap()),
None,
None,
);
ThreadsafeValidatorClient(Arc::new( ThreadsafeValidatorClient(Arc::new(
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"), validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"),
@@ -51,7 +51,7 @@ impl GeoLocateTask {
.state .state
.inner .inner
.mixnodes .mixnodes
.is_location_valid(&cache_item.mix_node.identity_key) .is_location_valid(&cache_item.mix_node().identity_key)
.await .await
{ {
// when the cached location is valid, don't locate and continue to next mix node // when the cached location is valid, don't locate and continue to next mix node
@@ -59,14 +59,14 @@ impl GeoLocateTask {
} }
// the mix node has not been located or is the cache time has expired // the mix node has not been located or is the cache time has expired
match locate(&cache_item.mix_node.host).await { match locate(&cache_item.mix_node().host).await {
Ok(geo_location) => { Ok(geo_location) => {
let location = Location::new(geo_location); let location = Location::new(geo_location);
trace!( trace!(
"{} mix nodes already located. Ip {} is located in {:#?}", "{} mix nodes already located. Ip {} is located in {:#?}",
i, i,
cache_item.mix_node.host, cache_item.mix_node().host,
location.three_letter_iso_country_code, location.three_letter_iso_country_code,
); );
@@ -80,7 +80,7 @@ impl GeoLocateTask {
self.state self.state
.inner .inner
.mixnodes .mixnodes
.set_location(&cache_item.mix_node.identity_key, Some(location)) .set_location(&cache_item.mix_node().identity_key, Some(location))
.await; .await;
// one node has been located, so return out of the loop // one node has been located, so return out of the loop
@@ -89,22 +89,22 @@ impl GeoLocateTask {
Err(e) => match e { Err(e) => match e {
LocateError::ReqwestError(e) => warn!( LocateError::ReqwestError(e) => warn!(
"❌ Oh no! Location for {} failed {}", "❌ Oh no! Location for {} failed {}",
cache_item.mix_node.host, e cache_item.mix_node().host, e
), ),
LocateError::NotFound(e) => { LocateError::NotFound(e) => {
warn!( warn!(
"❌ Location for {} not found. Response body: {}", "❌ Location for {} not found. Response body: {}",
cache_item.mix_node.host, e cache_item.mix_node().host, e
); );
self.state self.state
.inner .inner
.mixnodes .mixnodes
.set_location(&cache_item.mix_node.identity_key, None) .set_location(&cache_item.mix_node().identity_key, None)
.await; .await;
}, },
LocateError::RateLimited(e) => warn!( LocateError::RateLimited(e) => warn!(
"❌ Oh no, we've been rate limited! Location for {} failed. Response body: {}", "❌ Oh no, we've been rate limited! Location for {} failed. Response body: {}",
cache_item.mix_node.host, e cache_item.mix_node().host, e
), ),
}, },
} }
@@ -129,7 +129,7 @@ enum LocateError {
async fn locate(ip: &str) -> Result<GeoLocation, LocateError> { async fn locate(ip: &str) -> Result<GeoLocation, LocateError> {
let api_key = ::std::env::var("GEO_IP_SERVICE_API_KEY") let api_key = ::std::env::var("GEO_IP_SERVICE_API_KEY")
.expect("Env var GEO_IP_SERVICE_API_KEY is not set"); .expect("Env var GEO_IP_SERVICE_API_KEY is not set");
let uri = format!("{}/{}?apikey={}", crate::GEO_IP_SERVICE, ip, api_key); let uri = format!("{}/?apikey={}&ip={}", crate::GEO_IP_SERVICE, api_key, ip);
match reqwest::get(uri.clone()).await { match reqwest::get(uri.clone()).await {
Ok(response) => { Ok(response) => {
if response.status() == 429 { if response.status() == 429 {
+1 -1
View File
@@ -18,7 +18,7 @@ mod state;
mod tasks; mod tasks;
mod validators; mod validators;
const GEO_IP_SERVICE: &str = "https://api.freegeoip.app/json"; const GEO_IP_SERVICE: &str = "https://api.ipbase.com/json";
const COUNTRY_DATA_REFRESH_INTERVAL: u64 = 60 * 15; // every 15 minutes const COUNTRY_DATA_REFRESH_INTERVAL: u64 = 60 * 15; // every 15 minutes
#[tokio::main] #[tokio::main]
+19
View File
@@ -1,9 +1,13 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net> // Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use itertools::Itertools;
use crate::client::ThreadsafeValidatorClient; use crate::client::ThreadsafeValidatorClient;
use mixnet_contract_common::Delegation; use mixnet_contract_common::Delegation;
use super::models::SummedDelegations;
pub(crate) async fn get_single_mixnode_delegations( pub(crate) async fn get_single_mixnode_delegations(
client: &ThreadsafeValidatorClient, client: &ThreadsafeValidatorClient,
pubkey: &str, pubkey: &str,
@@ -21,3 +25,18 @@ pub(crate) async fn get_single_mixnode_delegations(
}; };
delegates delegates
} }
pub(crate) async fn get_single_mixnode_delegations_summed(
client: &ThreadsafeValidatorClient,
pubkey: &str,
) -> Vec<SummedDelegations> {
let delegations_by_owner = get_single_mixnode_delegations(client, pubkey)
.await
.into_iter()
.into_group_map_by(|delegation| delegation.owner.clone());
delegations_by_owner
.iter()
.filter_map(|(_, delegations)| SummedDelegations::from(delegations))
.collect()
}
+1 -1
View File
@@ -36,6 +36,6 @@ pub(crate) async fn retrieve_mixnode_econ_stats(
estimated_total_node_reward: reward_estimation.estimated_total_node_reward, estimated_total_node_reward: reward_estimation.estimated_total_node_reward,
estimated_operator_reward: reward_estimation.estimated_operator_reward, estimated_operator_reward: reward_estimation.estimated_operator_reward,
estimated_delegators_reward: reward_estimation.estimated_delegators_reward, estimated_delegators_reward: reward_estimation.estimated_delegators_reward,
current_interval_uptime: reward_estimation.reward_params.node.uptime() as u8, current_interval_uptime: reward_estimation.reward_params.node.uptime().u128() as u8,
}) })
} }
+25 -7
View File
@@ -11,16 +11,19 @@ use rocket_okapi::settings::OpenApiSettings;
use mixnet_contract_common::Delegation; use mixnet_contract_common::Delegation;
use crate::mix_node::delegations::get_single_mixnode_delegations; use crate::mix_node::delegations::{
get_single_mixnode_delegations, get_single_mixnode_delegations_summed,
};
use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats; use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats;
use crate::mix_node::models::{ use crate::mix_node::models::{
EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond, EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond, SummedDelegations,
}; };
use crate::state::ExplorerApiStateContext; use crate::state::ExplorerApiStateContext;
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) { pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![ openapi_get_routes_spec![
settings: get_delegations, settings: get_delegations,
get_delegations_summed,
get_by_id, get_by_id,
get_description, get_description,
get_stats, get_stats,
@@ -54,6 +57,15 @@ pub(crate) async fn get_delegations(
Json(get_single_mixnode_delegations(&state.inner.validator_client, pubkey).await) Json(get_single_mixnode_delegations(&state.inner.validator_client, pubkey).await)
} }
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/delegations/summed")]
pub(crate) async fn get_delegations_summed(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Json<Vec<SummedDelegations>> {
Json(get_single_mixnode_delegations_summed(&state.inner.validator_client, pubkey).await)
}
#[openapi(tag = "mix_node")] #[openapi(tag = "mix_node")]
#[get("/<pubkey>/description")] #[get("/<pubkey>/description")]
pub(crate) async fn get_description( pub(crate) async fn get_description(
@@ -70,8 +82,8 @@ pub(crate) async fn get_description(
match state.inner.get_mix_node(pubkey).await { match state.inner.get_mix_node(pubkey).await {
Some(bond) => { Some(bond) => {
match get_mix_node_description( match get_mix_node_description(
&bond.mix_node.host, &bond.mix_node().host,
&bond.mix_node.http_api_port, &bond.mix_node().http_api_port,
) )
.await .await
{ {
@@ -87,7 +99,10 @@ pub(crate) async fn get_description(
Err(e) => { Err(e) => {
error!( error!(
"Unable to get description for {} on {}:{} -> {}", "Unable to get description for {} on {}:{} -> {}",
pubkey, bond.mix_node.host, bond.mix_node.http_api_port, e pubkey,
bond.mix_node().host,
bond.mix_node().http_api_port,
e
); );
Option::None Option::None
} }
@@ -114,7 +129,7 @@ pub(crate) async fn get_stats(
trace!("No valid cache value for {}", pubkey); trace!("No valid cache value for {}", pubkey);
match state.inner.get_mix_node(pubkey).await { match state.inner.get_mix_node(pubkey).await {
Some(bond) => { Some(bond) => {
match get_mix_node_stats(&bond.mix_node.host, &bond.mix_node.http_api_port) match get_mix_node_stats(&bond.mix_node().host, &bond.mix_node().http_api_port)
.await .await
{ {
Ok(response) => { Ok(response) => {
@@ -129,7 +144,10 @@ pub(crate) async fn get_stats(
Err(e) => { Err(e) => {
error!( error!(
"Unable to get description for {} on {}:{} -> {}", "Unable to get description for {} on {}:{} -> {}",
pubkey, bond.mix_node.host, bond.mix_node.http_api_port, e pubkey,
bond.mix_node().host,
bond.mix_node().http_api_port,
e
); );
Option::None Option::None
} }

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