diff --git a/Cargo.lock b/Cargo.lock index 6824e1e4e9..c3af75358c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,6 +260,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874f8444adcb4952a8bc51305c8be95c8ec8237bb0d2e78d2e039f771f8828a0" + [[package]] name = "binascii" version = "0.1.4" @@ -277,14 +283,16 @@ dependencies = [ [[package]] name = "bip32" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d0f0fc59c7ba0333eed9dcc1b6980baa7b7a4dc7c6c5885994d0674f7adf34" +checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" dependencies = [ "bs58", - "hkd32", "hmac", - "k256", + "k256 0.10.1", + "once_cell", + "pbkdf2", + "rand_core 0.6.3", "ripemd160", "sha2", "subtle 2.4.1", @@ -342,9 +350,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd555c66291d5f836dbb6883b48660ece810fe25a31f3bdfb911945dff2691f" +checksum = "526c210b4520e416420759af363083471656e819a75e831b8d2c9d5a584f2413" dependencies = [ "arrayref", "arrayvec 0.7.1", @@ -406,8 +414,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397" dependencies = [ "digest 0.9.0", - "ff", - "group", + "ff 0.10.1", + "group 0.10.0", "pairing", "rand_core 0.6.3", "subtle 2.4.1", @@ -762,6 +770,12 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdab415d6744056100f40250a66bc430c1a46f7a02e20bc11c94c79a0f0464df" +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + [[package]] name = "const_fn" version = "0.4.8" @@ -774,6 +788,13 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "contracts-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -875,8 +896,9 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" -version = "0.8.0" -source = "git+https://github.com/cosmos/cosmos-rust?rev=e5a1872083abb3d88fa62dda966e7f5408deba58#e5a1872083abb3d88fa62dda966e7f5408deba58" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0254ffee603f5301d6a66963d9e1cc5091479c22e2e925e1f7689c8027a0828" dependencies = [ "prost", "prost-types", @@ -885,15 +907,16 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.3.0" -source = "git+https://github.com/cosmos/cosmos-rust?rev=e5a1872083abb3d88fa62dda966e7f5408deba58#e5a1872083abb3d88fa62dda966e7f5408deba58" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d" dependencies = [ "bip32", "cosmos-sdk-proto", - "ecdsa", + "ecdsa 0.13.4", "eyre", "getrandom 0.2.3", - "k256", + "k256 0.10.1", "prost", "prost-types", "rand_core 0.6.3", @@ -913,7 +936,7 @@ checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a" dependencies = [ "digest 0.9.0", "ed25519-zebra", - "k256", + "k256 0.9.6", "rand_core 0.5.1", "thiserror", ] @@ -1118,6 +1141,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.4", + "rand_core 0.6.3", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "crypto-mac" version = "0.7.0" @@ -1335,7 +1370,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2adca118c71ecd9ae094d4b68257b3fdfcb711a612b9eec7b5a0d27a5a70a5b4" dependencies = [ - "const-oid", + "const-oid 0.6.1", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", ] [[package]] @@ -1544,17 +1588,29 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" dependencies = [ - "der", - "elliptic-curve", + "der 0.4.3", + "elliptic-curve 0.10.6", "hmac", "signature", ] [[package]] -name = "ed25519" -version = "1.2.0" +name = "ecdsa" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +dependencies = [ + "der 0.5.1", + "elliptic-curve 0.11.6", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" dependencies = [ "signature", ] @@ -1599,16 +1655,33 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b" dependencies = [ - "crypto-bigint", - "ff", + "crypto-bigint 0.2.10", + "ff 0.10.1", "generic-array 0.14.4", - "group", - "pkcs8", + "group 0.10.0", + "pkcs8 0.7.6", "rand_core 0.6.3", "subtle 2.4.1", "zeroize", ] +[[package]] +name = "elliptic-curve" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decb3a27ea454a5f23f96eb182af0671c12694d64ecc33dada74edd1301f6cfc" +dependencies = [ + "crypto-bigint 0.3.2", + "der 0.5.1", + "ff 0.11.0", + "generic-array 0.14.4", + "group 0.11.0", + "rand_core 0.6.3", + "sec1", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "embed_plist" version = "1.2.0" @@ -1729,7 +1802,7 @@ dependencies = [ "humantime-serde", "isocountry", "log", - "mixnet-contract", + "mixnet-contract-common", "network-defaults", "okapi", "pretty_env_logger", @@ -1780,6 +1853,16 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" +dependencies = [ + "rand_core 0.6.3", + "subtle 2.4.1", +] + [[package]] name = "field-offset" version = "0.3.4" @@ -2422,7 +2505,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" dependencies = [ "byteorder", - "ff", + "ff 0.10.1", + "rand_core 0.6.3", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +dependencies = [ + "ff 0.11.0", "rand_core 0.6.3", "subtle 2.4.1", ] @@ -2595,20 +2689,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e4590e13640f19f249fe3e4eca5113bc4289f2497710378190e7f4bd96f45b" -[[package]] -name = "hkd32" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f2a5541afe0725f0b95619d6af614f48c1b176385b8aa30918cfb8c4bfafc8" -dependencies = [ - "hmac", - "once_cell", - "pbkdf2", - "rand_core 0.6.3", - "sha2", - "zeroize", -] - [[package]] name = "hkdf" version = "0.11.0" @@ -3050,8 +3130,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.12.4", + "elliptic-curve 0.10.6", + "sha2", +] + +[[package]] +name = "k256" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7511aa19fa182a8a4885760c4e5675b17173b02ae86ec5d376d34f5278c874b9" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.13.4", + "elliptic-curve 0.11.6", + "sec1", "sha2", "sha3", ] @@ -3309,10 +3402,11 @@ dependencies = [ ] [[package]] -name = "mixnet-contract" +name = "mixnet-contract-common" version = "0.1.0" dependencies = [ "az", + "contracts-common", "cosmwasm-std", "fixed", "log", @@ -3779,7 +3873,7 @@ dependencies = [ "getset", "humantime-serde", "log", - "mixnet-contract", + "mixnet-contract-common", "nymsphinx", "pin-project", "pretty_env_logger", @@ -3798,6 +3892,7 @@ dependencies = [ "tokio", "topology", "url", + "validator-api-requests", "validator-client", "vergen", "version-checker", @@ -3813,9 +3908,9 @@ dependencies = [ "criterion", "digest 0.9.0", "doc-comment", - "ff", + "ff 0.10.1", "getrandom 0.2.3", - "group", + "group 0.10.0", "itertools", "rand 0.8.4", "serde", @@ -3829,7 +3924,7 @@ name = "nymsphinx" version = "0.1.0" dependencies = [ "crypto", - "mixnet-contract", + "mixnet-contract-common", "nymsphinx-acknowledgements", "nymsphinx-addressing", "nymsphinx-anonymous-replies", @@ -4062,7 +4157,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3" dependencies = [ - "group", + "group 0.10.0", ] [[package]] @@ -4155,9 +4250,9 @@ checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ "crypto-mac 0.11.1", ] @@ -4415,8 +4510,19 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" dependencies = [ - "der", - "spki", + "der 0.4.3", + "spki 0.4.1", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", + "spki 0.5.4", + "zeroize", ] [[package]] @@ -5055,6 +5161,17 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint 0.3.2", + "hmac", + "zeroize", +] + [[package]] name = "rfd" version = "0.4.4" @@ -5409,6 +5526,19 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der 0.5.1", + "generic-array 0.14.4", + "pkcs8 0.8.0", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.20.3" @@ -5874,7 +6004,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" dependencies = [ - "der", + "der 0.4.3", +] + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der 0.5.1", ] [[package]] @@ -6590,18 +6730,17 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.0" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d1cdb0236becb17ab35a2ed1566503e412fd910944dc940239857bb7663652" +checksum = "a9ef686b8ecd36550d0581f0989c9d8607090b23005df479d8e6ba348b5800b9" dependencies = [ "async-trait", "bytes", - "chrono", "ed25519", "ed25519-dalek", "flex-error", "futures", - "k256", + "k256 0.10.1", "num-traits", "once_cell", "prost", @@ -6616,14 +6755,15 @@ dependencies = [ "subtle 2.4.1", "subtle-encoding", "tendermint-proto", + "time 0.3.3", "zeroize", ] [[package]] name = "tendermint-config" -version = "0.23.0" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1f94250d30e3011130a09756b05985d8dbfbd562cf261b5a17e36d89a37992" +checksum = "ecc127f82e7a8c7337c1f293d65a821380d3407c4c44bc979ef4da0ebc3b31ed" dependencies = [ "flex-error", "serde", @@ -6635,12 +6775,11 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.0" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff16a7b42bdbcf31c8cd10a4cffc7631f2a301360ba3a3f71dde48eabfa5bced" +checksum = "a9e0a0251fd81bed7420bea0f4d91c2a58f6c9fa34d5b2f70bed0ba8890de5aa" dependencies = [ "bytes", - "chrono", "flex-error", "num-derive", "num-traits", @@ -6649,20 +6788,20 @@ dependencies = [ "serde", "serde_bytes", "subtle-encoding", + "time 0.3.3", ] [[package]] name = "tendermint-rpc" -version = "0.23.0" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7842dcd5edb60b077572aa92ff8b29fc810b9b463310f9810a2607474130db4" +checksum = "626c493e9ced3a9de37583bbd929f4b6dbd49aa513ab9b4776aa44a9332ce9b5" dependencies = [ "async-trait", "bytes", - "chrono", "flex-error", "futures", - "getrandom 0.1.16", + "getrandom 0.2.3", "http", "hyper", "hyper-proxy", @@ -6677,6 +6816,7 @@ dependencies = [ "tendermint-config", "tendermint-proto", "thiserror", + "time 0.3.3", "tokio", "tracing", "url", @@ -6955,7 +7095,7 @@ dependencies = [ "bs58", "crypto", "log", - "mixnet-contract", + "mixnet-contract-common", "nymsphinx-addressing", "nymsphinx-types", "rand 0.7.3", @@ -7187,6 +7327,14 @@ dependencies = [ "getrandom 0.2.3", ] +[[package]] +name = "validator-api-requests" +version = "0.1.0" +dependencies = [ + "serde", + "ts-rs", +] + [[package]] name = "validator-client" version = "0.1.0" @@ -7201,7 +7349,7 @@ dependencies = [ "flate2", "itertools", "log", - "mixnet-contract", + "mixnet-contract-common", "network-defaults", "prost", "reqwest", @@ -7211,6 +7359,7 @@ dependencies = [ "thiserror", "ts-rs", "url", + "validator-api-requests", "vesting-contract", ] @@ -7275,10 +7424,18 @@ dependencies = [ "config", "cosmwasm-std", "cw-storage-plus", - "mixnet-contract", + "mixnet-contract-common", "schemars", "serde", "thiserror", + "vesting-contract-common", +] + +[[package]] +name = "vesting-contract-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5537a3d5a5..90a214fe8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,9 @@ members = [ "common/credentials", "common/crypto", "common/bandwidth-claim-contract", - "common/mixnet-contract", + "common/cosmwasm-smart-contracts/contracts-common", + "common/cosmwasm-smart-contracts/mixnet-contract", + "common/cosmwasm-smart-contracts/vesting-contract", "common/mixnode-common", "common/network-defaults", "common/nonexhaustive-delayqueue", @@ -52,6 +54,7 @@ members = [ "mixnode", "service-providers/network-requester", "validator-api", + "validator-api/validator-api-requests", ] default-members = [ diff --git a/README.md b/README.md index 14531cf0a5..4a10ea119a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr ### Building -Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/). +Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym). +Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers). ### Developing diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 236bd71754..e5f831efbe 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -9,7 +9,7 @@ rust-version = "1.56" [dependencies] base64 = "0.13" -mixnet-contract = { path="../../../common/mixnet-contract" } +mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" } vesting-contract = { path="../../../contracts/vesting" } serde = { version="1", features=["derive"] } serde_json = "1" @@ -20,6 +20,7 @@ url = { version = "2.2", features = ["serde"] } coconut-interface = { path = "../../coconut-interface" } network-defaults = { path = "../../network-defaults" } +validator-api-requests = { path = "../../../validator-api/validator-api-requests" } # required for nymd-client # at some point it might be possible to make it wasm-compatible @@ -27,8 +28,7 @@ network-defaults = { path = "../../network-defaults" } async-trait = { version = "0.1.51", optional = true } bip39 = { version = "1", features = ["rand"], optional = true } config = { path = "../../config", optional = true } -#cosmrs = { version = "0.3", features = ["rpc", "bip32", "cosmwasm"], optional = true } -cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev="e5a1872083abb3d88fa62dda966e7f5408deba58", features = ["rpc", "bip32", "cosmwasm"], optional = true } +cosmrs = { version = "0.4.1", features = ["rpc", "bip32", "cosmwasm"], optional = true } prost = { version = "0.9", default-features = false, optional = true } flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } @@ -38,3 +38,4 @@ ts-rs = {version = "5.1", optional = true} [features] nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"] +typescript-types = ["ts-rs", "validator-api-requests/ts-rs"] diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index afacd3260c..4a3653fb21 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -6,19 +6,23 @@ use crate::nymd::{ error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient, }; #[cfg(feature = "nymd-client")] -use mixnet_contract::ContractStateParams; +use mixnet_contract_common::ContractStateParams; use crate::{validator_api, ValidatorClientError}; use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; #[cfg(feature = "nymd-client")] -use mixnet_contract::{ +use mixnet_contract_common::{ Delegation, MixnetContractVersion, MixnodeRewardingStatusResponse, RewardingIntervalResponse, }; -use mixnet_contract::{GatewayBond, MixNodeBond}; +use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; #[cfg(feature = "nymd-client")] use std::str::FromStr; use url::Url; +use validator_api_requests::models::{ + CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, + StakeSaturationResponse, +}; #[cfg(feature = "nymd-client")] pub struct Config { @@ -208,7 +212,7 @@ impl Client { pub async fn get_rewarding_status( &self, - mix_identity: mixnet_contract::IdentityKey, + mix_identity: mixnet_contract_common::IdentityKey, rewarding_interval_nonce: u32, ) -> Result where @@ -297,8 +301,8 @@ impl Client { pub async fn get_all_nymd_single_mixnode_delegations( &self, - identity: mixnet_contract::IdentityKey, - ) -> Result, ValidatorClientError> + identity: mixnet_contract_common::IdentityKey, + ) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync, { @@ -434,6 +438,55 @@ impl ApiClient { Ok(self.validator_api.get_gateways().await?) } + pub async fn get_gateway_core_status_count( + &self, + identity: IdentityKeyRef<'_>, + since: Option, + ) -> Result { + Ok(self + .validator_api + .get_gateway_core_status_count(identity, since) + .await?) + } + + pub async fn get_mixnode_core_status_count( + &self, + identity: IdentityKeyRef<'_>, + since: Option, + ) -> Result { + Ok(self + .validator_api + .get_mixnode_core_status_count(identity, since) + .await?) + } + + pub async fn get_mixnode_status( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + Ok(self.validator_api.get_mixnode_status(identity).await?) + } + + pub async fn get_mixnode_reward_estimation( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + Ok(self + .validator_api + .get_mixnode_reward_estimation(identity) + .await?) + } + + pub async fn get_mixnode_stake_saturation( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + Ok(self + .validator_api + .get_mixnode_stake_saturation(identity) + .await?) + } + pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index 9ef577d97f..251501114d 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -9,6 +9,7 @@ pub mod validator_api; pub use crate::client::ApiClient; pub use crate::error::ValidatorClientError; +pub use validator_api_requests::*; #[cfg(feature = "nymd-client")] pub use client::{Client, Config}; diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index 71c8766cf7..dfaa1abbb8 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -13,7 +13,7 @@ use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl}; use cosmwasm_std::{Coin, Uint128}; pub use fee::gas_price::GasPrice; use fee::helpers::Operation; -use mixnet_contract::{ +use mixnet_contract_common::{ ContractStateParams, Delegation, ExecuteMsg, Gateway, GatewayBond, GatewayOwnershipResponse, IdentityKey, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse, MixnetContractVersion, MixnodeRewardingStatusResponse, PagedAllDelegationsResponse, @@ -289,7 +289,7 @@ impl NymdClient { pub async fn get_rewarding_status( &self, - mix_identity: mixnet_contract::IdentityKey, + mix_identity: mixnet_contract_common::IdentityKey, rewarding_interval_nonce: u32, ) -> Result where diff --git a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs index 558a5af2ba..8c862f0851 100644 --- a/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/vesting_signing_client.rs @@ -8,7 +8,7 @@ use crate::nymd::fee::helpers::Operation; use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient}; use async_trait::async_trait; use cosmwasm_std::Coin; -use mixnet_contract::{Gateway, IdentityKey, IdentityKeyRef, MixNode}; +use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode}; use vesting_contract::messages::ExecuteMsg as VestingExecuteMsg; #[async_trait] diff --git a/common/client-libs/validator-client/src/validator_api/mod.rs b/common/client-libs/validator-client/src/validator_api/mod.rs index 841e59b69e..6511b1157c 100644 --- a/common/client-libs/validator-client/src/validator_api/mod.rs +++ b/common/client-libs/validator-client/src/validator_api/mod.rs @@ -1,16 +1,24 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::validator_api::error::ValidatorAPIError; +use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG}; use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; -use mixnet_contract::{GatewayBond, MixNodeBond}; +use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond}; use serde::{Deserialize, Serialize}; use url::Url; +use validator_api_requests::models::{ + CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, + StakeSaturationResponse, +}; pub mod error; pub(crate) mod routes; type PathSegments<'a> = &'a [&'a str]; +type Params<'a, K, V> = &'a [(K, V)]; + +const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[]; pub struct Client { url: Url, @@ -30,24 +38,33 @@ impl Client { self.url = new_url } - async fn query_validator_api(&self, path: PathSegments<'_>) -> Result + async fn query_validator_api( + &self, + path: PathSegments<'_>, + params: Params<'_, K, V>, + ) -> Result where for<'a> T: Deserialize<'a>, + K: AsRef, + V: AsRef, { - let url = create_api_url(&self.url, path); + let url = create_api_url(&self.url, path, params); Ok(self.reqwest_client.get(url).send().await?.json().await?) } - async fn post_validator_api( + async fn post_validator_api( &self, path: PathSegments<'_>, + params: Params<'_, K, V>, json_body: &B, ) -> Result where B: Serialize + ?Sized, for<'a> T: Deserialize<'a>, + K: AsRef, + V: AsRef, { - let url = create_api_url(&self.url, path); + let url = create_api_url(&self.url, path, params); Ok(self .reqwest_client .post(url) @@ -59,23 +76,142 @@ impl Client { } pub async fn get_mixnodes(&self) -> Result, ValidatorAPIError> { - self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES]) + self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS) .await } pub async fn get_gateways(&self) -> Result, ValidatorAPIError> { - self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS]) + self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS) .await } pub async fn get_active_mixnodes(&self) -> Result, ValidatorAPIError> { - self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE]) - .await + self.query_validator_api( + &[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE], + NO_PARAMS, + ) + .await } pub async fn get_rewarded_mixnodes(&self) -> Result, ValidatorAPIError> { - self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED]) + self.query_validator_api( + &[routes::API_VERSION, routes::MIXNODES, routes::REWARDED], + NO_PARAMS, + ) + .await + } + + pub async fn get_gateway_core_status_count( + &self, + identity: IdentityKeyRef<'_>, + since: Option, + ) -> Result { + if let Some(since) = since { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::GATEWAY, + identity, + CORE_STATUS_COUNT, + ], + &[(SINCE_ARG, since.to_string())], + ) .await + } else { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::GATEWAY, + identity, + ], + NO_PARAMS, + ) + .await + } + } + + pub async fn get_mixnode_core_status_count( + &self, + identity: IdentityKeyRef<'_>, + since: Option, + ) -> Result { + if let Some(since) = since { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + identity, + CORE_STATUS_COUNT, + ], + &[(SINCE_ARG, since.to_string())], + ) + .await + } else { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + identity, + ], + NO_PARAMS, + ) + .await + } + } + + pub async fn get_mixnode_status( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + identity, + routes::STATUS, + ], + NO_PARAMS, + ) + .await + } + + pub async fn get_mixnode_reward_estimation( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + identity, + routes::REWARD_ESTIMATION, + ], + NO_PARAMS, + ) + .await + } + + pub async fn get_mixnode_stake_saturation( + &self, + identity: IdentityKeyRef<'_>, + ) -> Result { + self.query_validator_api( + &[ + routes::API_VERSION, + routes::STATUS_ROUTES, + routes::MIXNODE, + identity, + routes::STAKE_SATURATION, + ], + NO_PARAMS, + ) + .await } pub async fn blind_sign( @@ -84,6 +220,7 @@ impl Client { ) -> Result { self.post_validator_api( &[routes::API_VERSION, routes::COCONUT_BLIND_SIGN], + NO_PARAMS, request_body, ) .await @@ -92,13 +229,20 @@ impl Client { pub async fn get_coconut_verification_key( &self, ) -> Result { - self.query_validator_api(&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY]) - .await + self.query_validator_api( + &[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY], + NO_PARAMS, + ) + .await } } // utility function that should solve the double slash problem in validator API forever. -fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url { +fn create_api_url, V: AsRef>( + base: &Url, + segments: PathSegments<'_>, + params: Params<'_, K, V>, +) -> Url { let mut url = base.clone(); let mut path_segments = url .path_segments_mut() @@ -113,6 +257,10 @@ fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url { // and can be dropped drop(path_segments); + if !params.is_empty() { + url.query_pairs_mut().extend_pairs(params); + } + url } @@ -127,51 +275,66 @@ mod tests { // works with 1 segment assert_eq!( "http://foomp.com/foo", - create_api_url(&base_url, &["foo"]).as_str() + create_api_url(&base_url, &["foo"], NO_PARAMS).as_str() ); // works with 2 segments assert_eq!( "http://foomp.com/foo/bar", - create_api_url(&base_url, &["foo", "bar"]).as_str() + create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str() ); // works with leading slash assert_eq!( "http://foomp.com/foo", - create_api_url(&base_url, &["/foo"]).as_str() + create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str() ); assert_eq!( "http://foomp.com/foo/bar", - create_api_url(&base_url, &["/foo", "bar"]).as_str() + create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str() ); assert_eq!( "http://foomp.com/foo/bar", - create_api_url(&base_url, &["foo", "/bar"]).as_str() + create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str() ); // works with trailing slash assert_eq!( "http://foomp.com/foo", - create_api_url(&base_url, &["foo/"]).as_str() + create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str() ); assert_eq!( "http://foomp.com/foo/bar", - create_api_url(&base_url, &["foo/", "bar"]).as_str() + create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str() ); assert_eq!( "http://foomp.com/foo/bar", - create_api_url(&base_url, &["foo", "bar/"]).as_str() + create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str() ); // works with both leading and trailing slash assert_eq!( "http://foomp.com/foo", - create_api_url(&base_url, &["/foo/"]).as_str() + create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str() ); assert_eq!( "http://foomp.com/foo/bar", - create_api_url(&base_url, &["/foo/", "/bar/"]).as_str() + create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str() + ); + + // adds params + assert_eq!( + "http://foomp.com/foo/bar?foomp=baz", + create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str() + ); + assert_eq!( + "http://foomp.com/foo/bar?arg1=val1&arg2=val2", + create_api_url( + &base_url, + &["/foo/", "/bar/"], + &[("arg1", "val1"), ("arg2", "val2")] + ) + .as_str() ); } } diff --git a/common/client-libs/validator-client/src/validator_api/routes.rs b/common/client-libs/validator-client/src/validator_api/routes.rs index ac171b01e6..7259ed6610 100644 --- a/common/client-libs/validator-client/src/validator_api/routes.rs +++ b/common/client-libs/validator-client/src/validator_api/routes.rs @@ -10,5 +10,16 @@ pub const GATEWAYS: &str = "gateways"; pub const ACTIVE: &str = "active"; pub const REWARDED: &str = "rewarded"; -pub const COCONUT_BLIND_SIGN: &str = "blind_sign"; -pub const COCONUT_VERIFICATION_KEY: &str = "verification_key"; +pub const COCONUT_BLIND_SIGN: &str = "blind-sign"; +pub const COCONUT_VERIFICATION_KEY: &str = "verification-key"; + +pub const STATUS_ROUTES: &str = "status"; +pub const MIXNODE: &str = "mixnode"; +pub const GATEWAY: &str = "gateway"; + +pub const CORE_STATUS_COUNT: &str = "core-status-count"; +pub const SINCE_ARG: &str = "since"; + +pub const STATUS: &str = "status"; +pub const REWARD_ESTIMATION: &str = "reward-estimation"; +pub const STAKE_SATURATION: &str = "stake-saturation"; diff --git a/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml b/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml new file mode 100644 index 0000000000..26c0b3d4af --- /dev/null +++ b/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "contracts-common" +version = "0.1.0" +authors = ["Jędrzej Stuczyński "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cosmwasm-std = "1.0.0-beta3" diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/events.rs b/common/cosmwasm-smart-contracts/contracts-common/src/events.rs new file mode 100644 index 0000000000..d9b5b363ca --- /dev/null +++ b/common/cosmwasm-smart-contracts/contracts-common/src/events.rs @@ -0,0 +1,30 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::Event; + +/// Looks up value of particular attribute in the provided event. If it fails to find it, +/// the function panics. +/// +/// # Arguments +/// +/// * `event`: event to search through. +/// * `key`: key associated with the particular attribute +pub fn must_find_attribute(event: &Event, key: &str) -> String { + may_find_attribute(event, key).unwrap() +} + +/// Looks up value of particular attribute in the provided event. Returns None if it does not exist. +/// +/// # Arguments +/// +/// * `event`: event to search through. +/// * `key`: key associated with the particular attribute +pub fn may_find_attribute(event: &Event, key: &str) -> Option { + for attr in &event.attributes { + if attr.key == key { + return Some(attr.value.clone()); + } + } + None +} diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/lib.rs b/common/cosmwasm-smart-contracts/contracts-common/src/lib.rs new file mode 100644 index 0000000000..58d367a2f5 --- /dev/null +++ b/common/cosmwasm-smart-contracts/contracts-common/src/lib.rs @@ -0,0 +1,4 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod events; diff --git a/common/mixnet-contract/Cargo.toml b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml similarity index 77% rename from common/mixnet-contract/Cargo.toml rename to common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml index 6f17d0e879..ba0edd57b1 100644 --- a/common/mixnet-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "mixnet-contract" +name = "mixnet-contract-common" version = "0.1.0" authors = ["Jędrzej Stuczyński "] edition = "2018" @@ -14,10 +14,12 @@ serde_repr = "0.1" schemars = "0.8" ts-rs = { version = "5.1", optional = true } thiserror = "1.0" -network-defaults = { path = "../network-defaults" } +network-defaults = { path = "../../network-defaults" } fixed = { version = "1.1", features = ["serde"] } az = "1.1" log = "0.4.14" +contracts-common = { path = "../contracts-common" } + [features] default = [] diff --git a/common/mixnet-contract/src/delegation.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs similarity index 100% rename from common/mixnet-contract/src/delegation.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs diff --git a/common/mixnet-contract/src/error.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs similarity index 100% rename from common/mixnet-contract/src/error.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs new file mode 100644 index 0000000000..d464e96acf --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs @@ -0,0 +1,353 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::mixnode::NodeRewardResult; +use crate::{ContractStateParams, Delegation, IdentityKeyRef, Layer}; +use cosmwasm_std::{Addr, Coin, Event, Uint128}; + +pub use contracts_common::events::*; + +// event types +pub const DELEGATION_EVENT_TYPE: &str = "delegation"; +pub const UNDELEGATION_EVENT_TYPE: &str = "undelegation"; +pub const GATEWAY_BONDING_EVENT_TYPE: &str = "gateway_bonding"; +pub const GATEWAY_UNBONDING_EVENT_TYPE: &str = "gateway_unbonding"; +pub const MIXNODE_BONDING_EVENT_TYPE: &str = "mixnode_bonding"; +pub const MIXNODE_UNBONDING_EVENT_TYPE: &str = "mixnode_unbonding"; +pub const SETTINGS_UPDATE_EVENT_TYPE: &str = "settings_update"; +pub const BEGIN_REWARDING_EVENT_TYPE: &str = "begin_rewarding"; +pub const OPERATOR_REWARDING_EVENT_TYPE: &str = "mix_rewarding"; +pub const MIX_DELEGATORS_REWARDING_EVENT_TYPE: &str = "mix_delegators_rewarding"; +pub const FINISH_REWARDING_EVENT_TYPE: &str = "finish_rewarding"; + +// attributes that are used in multiple places +pub const OWNER_KEY: &str = "owner"; +pub const AMOUNT_KEY: &str = "amount"; +pub const PROXY_KEY: &str = "proxy"; + +// event-specific attributes + +// delegation/undelegation +pub const DELEGATOR_KEY: &str = "delegator"; +pub const DELEGATION_TARGET_KEY: &str = "delegation_target"; +pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height"; + +// bonding/unbonding +pub const NODE_IDENTITY_KEY: &str = "identity"; +pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer"; + +// settings change +pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge"; +pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge"; +pub const OLD_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "old_mixnode_rewarded_set_size"; +pub const OLD_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "old_mixnode_active_set_size"; +pub const OLD_ACTIVE_SET_WORK_FACTOR_KEY: &str = "old_active_set_work_factor"; + +pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge"; +pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge"; +pub const NEW_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "new_mixnode_rewarded_set_size"; +pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size"; +pub const NEW_ACTIVE_SET_WORK_FACTOR_KEY: &str = "new_active_set_work_factor"; + +// rewarding +pub const REWARDING_INTERVAL_NONCE_KEY: &str = "rewarding_interval_nonce"; +pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward"; +pub const OPERATOR_REWARD_KEY: &str = "operator_reward"; +pub const LAMBDA_KEY: &str = "lambda"; +pub const SIGMA_KEY: &str = "sigma"; +pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards"; +pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations"; +pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason"; +pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found"; +pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh"; +pub const ZERO_UPTIME_VALUE: &str = "zero_uptime"; + +pub fn new_delegation_event( + delegator: &Addr, + proxy: &Option, + amount: &Coin, + mix_identity: IdentityKeyRef, +) -> Event { + let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event + .add_attribute(AMOUNT_KEY, amount.to_string()) + .add_attribute(DELEGATION_TARGET_KEY, mix_identity) +} + +pub fn new_undelegation_event( + delegator: &Addr, + proxy: &Option, + old_delegation: &Delegation, + mix_identity: IdentityKeyRef, +) -> Event { + let mut event = Event::new(UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event + .add_attribute(AMOUNT_KEY, old_delegation.amount.to_string()) + .add_attribute( + DELEGATION_HEIGHT_KEY, + old_delegation.block_height.to_string(), + ) + .add_attribute(DELEGATION_TARGET_KEY, mix_identity) +} + +pub fn new_gateway_bonding_event( + owner: &Addr, + proxy: &Option, + amount: &Coin, + identity: IdentityKeyRef, +) -> Event { + let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE) + .add_attribute(OWNER_KEY, owner) + .add_attribute(NODE_IDENTITY_KEY, identity); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event.add_attribute(AMOUNT_KEY, amount.to_string()) +} + +pub fn new_gateway_unbonding_event( + owner: &Addr, + proxy: &Option, + amount: &Coin, + identity: IdentityKeyRef, +) -> Event { + let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE) + .add_attribute(OWNER_KEY, owner) + .add_attribute(NODE_IDENTITY_KEY, identity); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event.add_attribute(AMOUNT_KEY, amount.to_string()) +} + +pub fn new_mixnode_bonding_event( + owner: &Addr, + proxy: &Option, + amount: &Coin, + identity: IdentityKeyRef, + assigned_layer: Layer, +) -> Event { + let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE) + .add_attribute(OWNER_KEY, owner) + .add_attribute(NODE_IDENTITY_KEY, identity); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event + .add_attribute(ASSIGNED_LAYER_KEY, assigned_layer) + .add_attribute(AMOUNT_KEY, amount.to_string()) +} + +pub fn new_mixnode_unbonding_event( + owner: &Addr, + proxy: &Option, + amount: &Coin, + identity: IdentityKeyRef, +) -> Event { + let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE) + .add_attribute(OWNER_KEY, owner) + .add_attribute(NODE_IDENTITY_KEY, identity); + + if let Some(proxy) = proxy { + event = event.add_attribute(PROXY_KEY, proxy) + } + + // coin implements Display trait and we use that implementation here + event.add_attribute(AMOUNT_KEY, amount.to_string()) +} + +pub fn new_settings_update_event( + old_params: &ContractStateParams, + new_params: &ContractStateParams, +) -> Event { + let mut event = Event::new(SETTINGS_UPDATE_EVENT_TYPE); + + if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge { + event = event + .add_attribute( + OLD_MINIMUM_MIXNODE_PLEDGE_KEY, + old_params.minimum_mixnode_pledge, + ) + .add_attribute( + NEW_MINIMUM_MIXNODE_PLEDGE_KEY, + new_params.minimum_mixnode_pledge, + ) + } + + if old_params.minimum_gateway_pledge != new_params.minimum_gateway_pledge { + event = event + .add_attribute( + OLD_MINIMUM_GATEWAY_PLEDGE_KEY, + old_params.minimum_gateway_pledge, + ) + .add_attribute( + NEW_MINIMUM_GATEWAY_PLEDGE_KEY, + new_params.minimum_gateway_pledge, + ) + } + + if old_params.mixnode_rewarded_set_size != new_params.mixnode_rewarded_set_size { + event = event + .add_attribute( + OLD_MIXNODE_REWARDED_SET_SIZE_KEY, + old_params.mixnode_rewarded_set_size.to_string(), + ) + .add_attribute( + NEW_MIXNODE_REWARDED_SET_SIZE_KEY, + new_params.mixnode_rewarded_set_size.to_string(), + ) + } + + if old_params.mixnode_active_set_size != new_params.mixnode_active_set_size { + event = event + .add_attribute( + OLD_MIXNODE_ACTIVE_SET_SIZE_KEY, + old_params.mixnode_active_set_size.to_string(), + ) + .add_attribute( + NEW_MIXNODE_ACTIVE_SET_SIZE_KEY, + new_params.mixnode_active_set_size.to_string(), + ) + } + + if old_params.active_set_work_factor != new_params.active_set_work_factor { + event = event + .add_attribute( + OLD_ACTIVE_SET_WORK_FACTOR_KEY, + old_params.active_set_work_factor.to_string(), + ) + .add_attribute( + NEW_ACTIVE_SET_WORK_FACTOR_KEY, + new_params.active_set_work_factor.to_string(), + ) + } + + event +} + +pub fn new_begin_rewarding_event(rewarding_interval_nonce: u32) -> Event { + Event::new(BEGIN_REWARDING_EVENT_TYPE).add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) +} + +pub fn new_finish_rewarding_event(rewarding_interval_nonce: u32) -> Event { + Event::new(FINISH_REWARDING_EVENT_TYPE).add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) +} + +pub fn new_not_found_mix_operator_rewarding_event( + rewarding_interval_nonce: u32, + identity: IdentityKeyRef, +) -> Event { + Event::new(OPERATOR_REWARDING_EVENT_TYPE) + .add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) + .add_attribute(NODE_IDENTITY_KEY, identity) + .add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE) +} + +pub fn new_too_fresh_bond_mix_operator_rewarding_event( + rewarding_interval_nonce: u32, + identity: IdentityKeyRef, +) -> Event { + Event::new(OPERATOR_REWARDING_EVENT_TYPE) + .add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) + .add_attribute(NODE_IDENTITY_KEY, identity) + .add_attribute(NO_REWARD_REASON_KEY, BOND_TOO_FRESH_VALUE) +} + +pub fn new_zero_uptime_mix_operator_rewarding_event( + rewarding_interval_nonce: u32, + identity: IdentityKeyRef, +) -> Event { + Event::new(OPERATOR_REWARDING_EVENT_TYPE) + .add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) + .add_attribute(NODE_IDENTITY_KEY, identity) + .add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE) +} + +pub fn new_mix_operator_rewarding_event( + rewarding_interval_nonce: u32, + identity: IdentityKeyRef, + node_reward_result: NodeRewardResult, + operator_reward: Uint128, + delegation_rewards_distributed: Uint128, + further_delegations: bool, +) -> Event { + Event::new(OPERATOR_REWARDING_EVENT_TYPE) + .add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) + .add_attribute(NODE_IDENTITY_KEY, identity) + .add_attribute( + TOTAL_MIXNODE_REWARD_KEY, + node_reward_result.reward().to_string(), + ) + .add_attribute(LAMBDA_KEY, node_reward_result.lambda().to_string()) + .add_attribute(SIGMA_KEY, node_reward_result.sigma().to_string()) + .add_attribute(OPERATOR_REWARD_KEY, operator_reward) + .add_attribute( + DISTRIBUTED_DELEGATION_REWARDS_KEY, + delegation_rewards_distributed, + ) + .add_attribute( + FURTHER_DELEGATIONS_TO_REWARD_KEY, + further_delegations.to_string(), + ) +} + +pub fn new_mix_delegators_rewarding_event( + rewarding_interval_nonce: u32, + identity: IdentityKeyRef, + delegation_rewards_distributed: Uint128, + further_delegations: bool, +) -> Event { + Event::new(MIX_DELEGATORS_REWARDING_EVENT_TYPE) + .add_attribute( + REWARDING_INTERVAL_NONCE_KEY, + rewarding_interval_nonce.to_string(), + ) + .add_attribute(NODE_IDENTITY_KEY, identity) + .add_attribute( + DISTRIBUTED_DELEGATION_REWARDS_KEY, + delegation_rewards_distributed, + ) + .add_attribute( + FURTHER_DELEGATIONS_TO_REWARD_KEY, + further_delegations.to_string(), + ) +} diff --git a/common/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs similarity index 100% rename from common/mixnet-contract/src/gateway.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs diff --git a/common/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs similarity index 97% rename from common/mixnet-contract/src/lib.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index 813fa3b894..6f1854cc12 100644 --- a/common/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -3,6 +3,7 @@ mod delegation; pub mod error; +pub mod events; mod gateway; pub mod mixnode; mod msg; diff --git a/common/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs similarity index 97% rename from common/mixnet-contract/src/mixnode.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index e53af230ab..ff77ec628a 100644 --- a/common/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -53,6 +53,16 @@ pub enum Layer { Three = 3, } +impl From for String { + fn from(layer: Layer) -> Self { + if layer == Layer::Gateway { + "gateway".to_string() + } else { + (layer as u8).to_string() + } + } +} + #[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)] pub struct NodeRewardParams { period_reward_pool: Uint128, @@ -243,7 +253,7 @@ impl DelegatorRewardParams { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct NodeRewardResult { reward: U128, lambda: U128, @@ -327,6 +337,11 @@ impl MixNodeBond { self.total_delegation.clone() } + pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 { + self.total_bond_to_circulating_supply(circulating_supply) + * U128::from_num(rewarded_set_size) + } + pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 { U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply) } diff --git a/common/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs similarity index 100% rename from common/mixnet-contract/src/msg.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs diff --git a/common/mixnet-contract/src/types.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs similarity index 100% rename from common/mixnet-contract/src/types.rs rename to common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs diff --git a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml new file mode 100644 index 0000000000..278244b8f0 --- /dev/null +++ b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "vesting-contract-common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cosmwasm-std = "1.0.0-beta3" diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs new file mode 100644 index 0000000000..946383b7e4 --- /dev/null +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs @@ -0,0 +1,133 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::{Addr, Coin, Event, Timestamp}; + +// event types +pub const WITHDRAW_EVENT_TYPE: &str = "vested_coins_withdraw"; +pub const OWNERSHIP_TRANSFER_EVENT_TYPE: &str = "ownership_transfer"; +pub const STAKING_ADDRESS_UPDATE_EVENT_TYPE: &str = "staking_address_update"; +pub const NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE: &str = "new_periodic_vesting_account"; + +pub const VESTING_DELEGATION_EVENT_TYPE: &str = "vesting_delegation"; +pub const VESTING_UNDELEGATION_EVENT_TYPE: &str = "vesting_undelegation"; +pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding"; +pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding"; +pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding"; +pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding"; + +pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond"; +pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond"; +pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation"; + +// attributes that are used in multiple places +pub const OWNER_KEY: &str = "owner"; +pub const AMOUNT_KEY: &str = "amount"; + +// event-specific attributes + +// withdraw +pub const REMAINING_SPENDABLE_KEY: &str = "remaining_spendable"; + +// ownership transfer +pub const FROM_ACCOUNT_KEY: &str = "from"; +pub const TO_ACCOUNT_KEY: &str = "to"; +pub const NO_VALUE_VALUE: &str = "none"; + +// new vesting account +pub const START_TIME_KEY: &str = "start_time"; +pub const STAKING_ADDRESS_KEY: &str = "staking_address"; + +// OPEN QUESTION: would it make sense to also emit amount of vesting/locked coins here? +// however, then it would require additional storage reads. +pub fn new_vested_coins_withdraw_event( + address: &Addr, + amount: &Coin, + remaining_spendable: &Coin, +) -> Event { + Event::new(WITHDRAW_EVENT_TYPE) + .add_attribute(OWNER_KEY, address) + .add_attribute(AMOUNT_KEY, amount.to_string()) + .add_attribute(REMAINING_SPENDABLE_KEY, remaining_spendable.to_string()) +} + +pub fn new_ownership_transfer_event(from: &Addr, to: &Addr) -> Event { + Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE) + .add_attribute(FROM_ACCOUNT_KEY, from) + .add_attribute(TO_ACCOUNT_KEY, to) +} + +pub fn new_staking_address_update_event(from: &Option, to: &Option) -> Event { + let mut event = Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE); + + if let Some(from) = from { + event = event.add_attribute(FROM_ACCOUNT_KEY, from) + } else { + event = event.add_attribute(FROM_ACCOUNT_KEY, NO_VALUE_VALUE); + } + + if let Some(to) = to { + event = event.add_attribute(TO_ACCOUNT_KEY, to) + } else { + event = event.add_attribute(TO_ACCOUNT_KEY, NO_VALUE_VALUE); + } + + event +} + +pub fn new_periodic_vesting_account_event( + owner_address: &Addr, + amount: &Coin, + staking_address: &Option, + start_time: Timestamp, +) -> Event { + let mut event = Event::new(NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE) + .add_attribute(OWNER_KEY, owner_address) + .add_attribute(AMOUNT_KEY, amount.to_string()); + + if let Some(staking_address) = staking_address { + event = event.add_attribute(STAKING_ADDRESS_KEY, staking_address); + } + + event.add_attribute(START_TIME_KEY, start_time.to_string()) +} + +// In most cases the events are rather barebone as there's no point in attaching +// bunch of data to them as it would be redundant. It is because in most cases when the event is emitted +// a call to the mixnet contract is made that throws another event with relevant attributes already attached. + +pub fn new_vesting_gateway_bonding_event() -> Event { + Event::new(VESTING_GATEWAY_BONDING_EVENT_TYPE) +} + +pub fn new_vesting_gateway_unbonding_event() -> Event { + Event::new(VESTING_GATEWAY_UNBONDING_EVENT_TYPE) +} + +pub fn new_vesting_mixnode_bonding_event() -> Event { + Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE) +} + +pub fn new_vesting_mixnode_unbonding_event() -> Event { + Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE) +} + +pub fn new_vesting_delegation_event() -> Event { + Event::new(VESTING_DELEGATION_EVENT_TYPE) +} + +pub fn new_vesting_undelegation_event() -> Event { + Event::new(VESTING_UNDELEGATION_EVENT_TYPE) +} + +pub fn new_track_mixnode_unbond_event() -> Event { + Event::new(TRACK_MIXNODE_UNBOND_EVENT_TYPE) +} + +pub fn new_track_gateway_unbond_event() -> Event { + Event::new(TRACK_GATEWAY_UNBOND_EVENT_TYPE) +} + +pub fn new_track_undelegation_event() -> Event { + Event::new(TRACK_UNDELEGATION_EVENT_TYPE) +} diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs new file mode 100644 index 0000000000..002130e5fd --- /dev/null +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -0,0 +1,4 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod events; diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index cf0032de13..f55a364a68 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] aes = { version = "0.7.4", features = ["ctr"] } bs58 = "0.4.0" -blake3 = { version = "1.0.0", features = ["traits-preview"] } +blake3 = { version = "~1.2.0", features = ["traits-preview"] } digest = "0.9.0" generic-array = "0.14" hkdf = "0.11.0" diff --git a/common/nymsphinx/Cargo.toml b/common/nymsphinx/Cargo.toml index 4c2feb78a3..ce94c101b0 100644 --- a/common/nymsphinx/Cargo.toml +++ b/common/nymsphinx/Cargo.toml @@ -25,7 +25,7 @@ crypto = { path = "../crypto" } topology = { path = "../topology" } [dev-dependencies] -mixnet-contract = { path = "../mixnet-contract" } +mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } # do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require # net2 via tokio-util -> tokio -> mio -> net2 diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index c3e9aebd9d..d06afa9a91 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -186,7 +186,7 @@ impl Default for MessageReceiver { mod message_receiver { use super::*; use crypto::asymmetric::identity; - use mixnet_contract::Layer; + use mixnet_contract_common::Layer; use nymsphinx_addressing::clients::Recipient; use rand::rngs::OsRng; use std::collections::HashMap; diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 199505b83f..7d7bb7ecc3 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -13,7 +13,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } ## internal crypto = { path = "../crypto" } -mixnet-contract = { path = "../mixnet-contract" } +mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nymsphinx-addressing = { path = "../nymsphinx/addressing" } nymsphinx-types = { path = "../nymsphinx/types" } version-checker = { path = "../version-checker" } diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index a0c068bfd6..70284f1c3e 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -3,7 +3,7 @@ use crate::{filter, NetworkAddress}; use crypto::asymmetric::{encryption, identity}; -use mixnet_contract::GatewayBond; +use mixnet_contract_common::GatewayBond; use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress}; use nymsphinx_types::Node as SphinxNode; use std::convert::{TryFrom, TryInto}; diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index eb32901f18..c14256512a 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -3,7 +3,7 @@ use crate::filter::VersionFilterable; use log::warn; -use mixnet_contract::{GatewayBond, MixNodeBond}; +use mixnet_contract_common::{GatewayBond, MixNodeBond}; use nymsphinx_addressing::nodes::NodeIdentity; use nymsphinx_types::Node as SphinxNode; use rand::Rng; @@ -272,7 +272,7 @@ mod converting_mixes_to_vec { use crypto::asymmetric::{encryption, identity}; use super::*; - use mixnet_contract::Layer; + use mixnet_contract_common::Layer; #[test] fn returns_a_vec_with_hashmap_values() { diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index 2f2faf5a4d..f28c9175b1 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -3,7 +3,7 @@ use crate::{filter, NetworkAddress}; use crypto::asymmetric::{encryption, identity}; -use mixnet_contract::{Layer, MixNodeBond}; +use mixnet_contract_common::{Layer, MixNodeBond}; use nymsphinx_addressing::nodes::NymNodeRoutingAddress; use nymsphinx_types::Node as SphinxNode; use std::convert::{TryFrom, TryInto}; diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index d1e0a14153..f374cb6199 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -236,6 +236,13 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "contracts-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", +] + [[package]] name = "cosmwasm-crypto" version = "1.0.0-beta3" @@ -831,21 +838,6 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mixnet-contract" version = "0.1.0" -dependencies = [ - "az", - "cosmwasm-std", - "fixed", - "log", - "network-defaults", - "schemars", - "serde", - "serde_repr", - "thiserror", -] - -[[package]] -name = "mixnet-contracts" -version = "0.1.0" dependencies = [ "bs58", "config", @@ -855,7 +847,7 @@ dependencies = [ "crypto", "cw-storage-plus", "fixed", - "mixnet-contract", + "mixnet-contract-common", "rand", "rand_chacha", "schemars", @@ -865,6 +857,22 @@ dependencies = [ "vesting-contract", ] +[[package]] +name = "mixnet-contract-common" +version = "0.1.0" +dependencies = [ + "az", + "contracts-common", + "cosmwasm-std", + "fixed", + "log", + "network-defaults", + "schemars", + "serde", + "serde_repr", + "thiserror", +] + [[package]] name = "network-defaults" version = "0.1.0" @@ -1525,10 +1533,18 @@ dependencies = [ "config", "cosmwasm-std", "cw-storage-plus", - "mixnet-contract", + "mixnet-contract-common", "schemars", "serde", "thiserror", + "vesting-contract-common", +] + +[[package]] +name = "vesting-contract-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", ] [[package]] diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index 25de04f9db..5fc7aba1f0 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "mixnet-contracts" +name = "mixnet-contract" version = "0.1.0" authors = ["Dave Hrycyszyn "] edition = "2018" @@ -16,7 +16,7 @@ exclude = [ crate-type = ["cdylib", "rlib"] [dependencies] -mixnet-contract = { path = "../../common/mixnet-contract" } +mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } config = { path = "../../common/config"} vesting-contract = { path = "../vesting" } diff --git a/contracts/mixnet/examples/schema.rs b/contracts/mixnet/examples/schema.rs index 0059dfb5a4..8f390777ca 100644 --- a/contracts/mixnet/examples/schema.rs +++ b/contracts/mixnet/examples/schema.rs @@ -4,7 +4,7 @@ extern crate mixnet_contract; use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; -use mixnet_contract::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg}; +use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg}; use std::env::current_dir; use std::fs::create_dir_all; diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 2cf4d65686..badd8d12c6 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -23,7 +23,9 @@ use crate::rewards::storage as rewards_storage; use cosmwasm_std::{ entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128, }; -use mixnet_contract::{ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use mixnet_contract_common::{ + ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, +}; /// Constant specifying minimum of coin required to bond a gateway pub const INITIAL_GATEWAY_PLEDGE: Uint128 = Uint128::new(100_000_000); @@ -289,7 +291,61 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result Result { +pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { + #[derive(serde::Serialize, serde::Deserialize, Clone)] + pub struct StoredMixnodeBondOld { + pub pledge_amount: mixnet_contract_common::Coin, + pub owner: Addr, + pub layer: mixnet_contract_common::Layer, + pub block_height: u64, + pub mix_node: mixnet_contract_common::MixNode, + pub profit_margin_percent: Option, + pub proxy: Option, + } + pub struct MixnodeBondIndexOld<'a> { + pub owner: cw_storage_plus::UniqueIndex<'a, Addr, StoredMixnodeBondOld>, + } + impl<'a> cw_storage_plus::IndexList for MixnodeBondIndexOld<'a> { + fn get_indexes( + &'_ self, + ) -> Box> + '_> + { + let v: Vec<&dyn cw_storage_plus::Index> = vec![&self.owner]; + Box::new(v.into_iter()) + } + } + pub(crate) fn mixnodes_old<'a>() -> cw_storage_plus::IndexedMap< + 'a, + mixnet_contract_common::IdentityKeyRef<'a>, + StoredMixnodeBondOld, + MixnodeBondIndexOld<'a>, + > { + let indexes = MixnodeBondIndexOld { + owner: cw_storage_plus::UniqueIndex::new(|d| d.owner.clone(), "mno"), + }; + cw_storage_plus::IndexedMap::new("mno", indexes) + } + + let stored_mixnode_bonds: Vec<_> = mixnodes_old() + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .flatten() + .map(|record| crate::mixnodes::storage::StoredMixnodeBond { + pledge_amount: record.1.pledge_amount, + owner: record.1.owner, + layer: record.1.layer, + block_height: record.1.block_height, + mix_node: record.1.mix_node, + proxy: record.1.proxy, + }) + .collect(); + for stored_mixnode_bond in stored_mixnode_bonds { + crate::mixnodes::storage::mixnodes().save( + deps.storage, + stored_mixnode_bond.identity(), + &stored_mixnode_bond, + )?; + } + Ok(Default::default()) } @@ -300,7 +356,7 @@ pub mod tests { use config::defaults::DENOM; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coins, from_binary}; - use mixnet_contract::PagedMixnodeResponse; + use mixnet_contract_common::PagedMixnodeResponse; #[test] fn initialize_contract() { diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index 4bc9ceed71..258f1d548d 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -7,10 +7,10 @@ use cosmwasm_std::Deps; use cosmwasm_std::Order; use cosmwasm_std::StdResult; use cw_storage_plus::{Bound, PrimaryKey}; -use mixnet_contract::PagedAllDelegationsResponse; -use mixnet_contract::PagedDelegatorDelegationsResponse; -use mixnet_contract::PagedMixDelegationsResponse; -use mixnet_contract::{Delegation, IdentityKey}; +use mixnet_contract_common::{ + Delegation, IdentityKey, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, + PagedMixDelegationsResponse, +}; pub(crate) fn query_all_network_delegations_paged( deps: Deps, @@ -140,7 +140,7 @@ pub(crate) mod tests { #[cfg(test)] mod querying_for_mixnode_delegations_paged { use super::*; - use mixnet_contract::IdentityKey; + use mixnet_contract_common::IdentityKey; #[test] fn retrieval_obeys_limits() { @@ -281,7 +281,7 @@ pub(crate) mod tests { mod querying_for_all_mixnode_delegations_paged { use super::*; use crate::support::tests::test_helpers; - use mixnet_contract::IdentityKey; + use mixnet_contract_common::IdentityKey; #[test] fn retrieval_obeys_limits() { @@ -444,7 +444,7 @@ pub(crate) mod tests { // add delegation from a different address let delegation = Delegation::new( - delegation_owner2.clone(), + delegation_owner2, node_identity1.clone(), coin(1234, DENOM), 1234, @@ -474,7 +474,7 @@ pub(crate) mod tests { // add delegation for a different node let delegation = Delegation::new( delegation_owner1.clone(), - node_identity2.clone(), + node_identity2, coin(1234, DENOM), 1234, None, @@ -493,11 +493,7 @@ pub(crate) mod tests { identity: node_identity1.clone(), address: Addr::unchecked(delegation_owner1.clone()) }), - query_mixnode_delegation( - deps.as_ref(), - node_identity1.clone(), - delegation_owner1.to_string() - ) + query_mixnode_delegation(deps.as_ref(), node_identity1, delegation_owner1.to_string()) ) } diff --git a/contracts/mixnet/src/delegations/storage.rs b/contracts/mixnet/src/delegations/storage.rs index 46fd0cc7fd..8e4f3cedcb 100644 --- a/contracts/mixnet/src/delegations/storage.rs +++ b/contracts/mixnet/src/delegations/storage.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex}; -use mixnet_contract::{Addr, Delegation, IdentityKey}; +use mixnet_contract_common::{Addr, Delegation, IdentityKey}; // storage prefixes const DELEGATION_PK_NAMESPACE: &str = "dl"; @@ -64,7 +64,7 @@ pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, Delega mod tests { use crate::delegations::storage; use cosmwasm_std::Addr; - use mixnet_contract::IdentityKey; + use mixnet_contract_common::IdentityKey; #[cfg(test)] mod reverse_mix_delegations { @@ -74,7 +74,7 @@ mod tests { use cosmwasm_std::testing::mock_env; use cosmwasm_std::{coin, Order}; use cw_storage_plus::PrimaryKey; - use mixnet_contract::Delegation; + use mixnet_contract_common::Delegation; #[test] fn reverse_mix_delegation_exists() { @@ -94,7 +94,7 @@ mod tests { storage::delegations() .save( &mut deps.storage, - (node_identity.clone(), delegation_owner.clone()).joined_key(), + (node_identity, delegation_owner.clone()).joined_key(), &dummy_data, ) .unwrap(); @@ -131,7 +131,7 @@ mod tests { // add delegation for a different node let dummy_data = Delegation::new( delegation_owner1.clone(), - node_identity2.clone(), + node_identity2, delegation.clone(), mock_env().block.height, None, @@ -156,14 +156,14 @@ mod tests { let dummy_data = Delegation::new( delegation_owner2.clone(), node_identity1.clone(), - delegation.clone(), + delegation, mock_env().block.height, None, ); storage::delegations() .save( &mut deps.storage, - (node_identity1.clone(), delegation_owner2.clone()).joined_key(), + (node_identity1.clone(), delegation_owner2).joined_key(), &dummy_data, ) .unwrap(); diff --git a/contracts/mixnet/src/delegations/transactions.rs b/contracts/mixnet/src/delegations/transactions.rs index 5de8764e93..9d060e891c 100644 --- a/contracts/mixnet/src/delegations/transactions.rs +++ b/contracts/mixnet/src/delegations/transactions.rs @@ -7,8 +7,8 @@ use crate::support::helpers::generate_storage_key; use config::defaults::DENOM; use cosmwasm_std::{coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response}; use cw_storage_plus::PrimaryKey; -use mixnet_contract::Delegation; -use mixnet_contract::IdentityKey; +use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event}; +use mixnet_contract_common::{Delegation, IdentityKey}; use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; fn validate_delegation_stake(mut delegation: Vec) -> Result { @@ -113,16 +113,21 @@ pub(crate) fn _try_delegate_to_mixnode( } None => Delegation::new( delegate.to_owned(), - mix_identity, - amount, + mix_identity.clone(), + amount.clone(), env.block.height, - proxy, + proxy.clone(), ), }) }, )?; - Ok(Response::default()) + Ok(Response::new().add_event(new_delegation_event( + &delegate, + &proxy, + &amount, + &mix_identity, + ))) } pub(crate) fn try_remove_delegation_from_mixnode( @@ -204,14 +209,19 @@ pub(crate) fn _try_remove_delegation_from_mixnode( let msg = Some(VestingContractExecuteMsg::TrackUndelegation { owner: delegate.as_str().to_string(), mix_identity: mix_identity.clone(), - amount: old_delegation.amount, + amount: old_delegation.amount.clone(), }); let track_undelegation_msg = wasm_execute(proxy, &msg, coins(0, DENOM))?; response = response.add_message(track_undelegation_msg); } - Ok(response) + Ok(response.add_event(new_undelegation_event( + &delegate, + &proxy, + &old_delegation, + &mix_identity, + ))) } } } @@ -530,7 +540,7 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), env1, - mock_info(delegation_owner1.as_str(), &[delegation1.clone()]), + mock_info(delegation_owner1.as_str(), &[delegation1]), identity.clone(), ) .unwrap(); @@ -544,7 +554,7 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), env2, - mock_info(delegation_owner2.as_str(), &[delegation2.clone()]), + mock_info(delegation_owner2.as_str(), &[delegation2]), identity.clone(), ) .unwrap(); @@ -699,7 +709,7 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner.as_str(), &vec![delegation_amount.clone()]), + mock_info(delegation_owner.as_str(), &[delegation_amount.clone()]), identity.clone(), ) .unwrap(); @@ -722,6 +732,7 @@ mod tests { #[cfg(test)] mod removing_mix_stake_delegation { + use crate::delegations::queries::query_mixnode_delegation; use cosmwasm_std::coin; use cosmwasm_std::testing::mock_env; use cosmwasm_std::testing::mock_info; @@ -774,11 +785,27 @@ mod tests { identity.clone(), ) .unwrap(); - assert_eq!( - Ok(Response::new().add_message(BankMsg::Send { + let delegation = query_mixnode_delegation( + deps.as_ref(), + identity.clone(), + delegation_owner.clone().into_string(), + ) + .unwrap(); + + let expected_response = Response::new() + .add_message(BankMsg::Send { to_address: delegation_owner.clone().into(), amount: coins(100, DENOM), - })), + }) + .add_event(new_undelegation_event( + &delegation_owner, + &None, + &delegation, + &identity, + )); + + assert_eq!( + Ok(expected_response), try_remove_delegation_from_mixnode( deps.as_mut(), mock_info(delegation_owner.as_str(), &[]), @@ -819,12 +846,27 @@ mod tests { identity.clone(), ) .unwrap(); - try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); - assert_eq!( - Ok(Response::new().add_message(BankMsg::Send { + let delegation = query_mixnode_delegation( + deps.as_ref(), + identity.clone(), + delegation_owner.clone().into_string(), + ) + .unwrap(); + let expected_response = Response::new() + .add_message(BankMsg::Send { to_address: delegation_owner.clone().into(), amount: coins(100, DENOM), - })), + }) + .add_event(new_undelegation_event( + &delegation_owner, + &None, + &delegation, + &identity, + )); + + try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); + assert_eq!( + Ok(expected_response), try_remove_delegation_from_mixnode( deps.as_mut(), mock_info(delegation_owner.as_str(), &[]), @@ -853,7 +895,7 @@ mod tests { assert!(try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner1.as_str(), &[delegation1.clone()]), + mock_info(delegation_owner1.as_str(), &[delegation1]), identity.clone(), ) .is_ok()); diff --git a/contracts/mixnet/src/error.rs b/contracts/mixnet/src/error.rs index 3cdccc1f13..46e85e9de0 100644 --- a/contracts/mixnet/src/error.rs +++ b/contracts/mixnet/src/error.rs @@ -3,7 +3,7 @@ use config::defaults::DENOM; use cosmwasm_std::{Addr, StdError}; -use mixnet_contract::IdentityKey; +use mixnet_contract_common::IdentityKey; use thiserror::Error; /// Custom errors for contract failure conditions. diff --git a/contracts/mixnet/src/gateways/queries.rs b/contracts/mixnet/src/gateways/queries.rs index 2305f470c6..4125ca2143 100644 --- a/contracts/mixnet/src/gateways/queries.rs +++ b/contracts/mixnet/src/gateways/queries.rs @@ -5,7 +5,9 @@ use super::storage; use crate::mixnodes::storage::{BOND_PAGE_DEFAULT_LIMIT, BOND_PAGE_MAX_LIMIT}; // Keeps gateway and mixnode retrieval in sync by re-using the constant. Could be split into its own constant. use cosmwasm_std::{Deps, Order, StdResult}; use cw_storage_plus::Bound; -use mixnet_contract::{GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse}; +use mixnet_contract_common::{ + GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse, +}; pub(crate) fn query_gateways_paged( deps: Deps, diff --git a/contracts/mixnet/src/gateways/storage.rs b/contracts/mixnet/src/gateways/storage.rs index 33b5ef9ab0..723a8c6ea0 100644 --- a/contracts/mixnet/src/gateways/storage.rs +++ b/contracts/mixnet/src/gateways/storage.rs @@ -3,7 +3,7 @@ use cosmwasm_std::Addr; use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex}; -use mixnet_contract::{GatewayBond, IdentityKeyRef}; +use mixnet_contract_common::{GatewayBond, IdentityKeyRef}; // storage prefixes const GATEWAYS_PK_NAMESPACE: &str = "gt"; @@ -41,9 +41,7 @@ mod tests { use cosmwasm_std::StdResult; use cosmwasm_std::Storage; use cosmwasm_std::{coin, Addr, Uint128}; - use mixnet_contract::GatewayBond; - use mixnet_contract::IdentityKey; - use mixnet_contract::{Gateway, IdentityKeyRef}; + use mixnet_contract_common::{Gateway, GatewayBond, IdentityKey, IdentityKeyRef}; // currently this is only used in tests but may become useful later on pub(crate) fn read_gateway_pledge_amount( @@ -87,7 +85,7 @@ mod tests { let gateway_bond = GatewayBond { pledge_amount: coin(pledge_amount, DENOM), - owner: node_owner.clone(), + owner: node_owner, block_height: 12_345, gateway: Gateway { identity_key: node_identity.clone(), diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index 14cd6881c6..6426b44357 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -9,7 +9,8 @@ use config::defaults::DENOM; use cosmwasm_std::{ coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; -use mixnet_contract::{Gateway, GatewayBond, Layer}; +use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event}; +use mixnet_contract_common::{Gateway, GatewayBond, Layer}; use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; pub fn try_add_gateway( @@ -97,12 +98,24 @@ pub(crate) fn _try_add_gateway( &gateway.identity_key, )?; - let bond = GatewayBond::new(pledge, owner, env.block.height, gateway, proxy); + let gateway_identity = gateway.identity_key.clone(); + let bond = GatewayBond::new( + pledge.clone(), + owner.clone(), + env.block.height, + gateway, + proxy.clone(), + ); storage::gateways().save(deps.storage, bond.identity(), &bond)?; mixnet_params_storage::increment_layer_count(deps.storage, Layer::Gateway)?; - Ok(Response::new()) + Ok(Response::new().add_event(new_gateway_bonding_event( + &owner, + &proxy, + &pledge, + &gateway_identity, + ))) } pub fn try_remove_gateway_on_behalf( @@ -155,23 +168,24 @@ pub(crate) fn _try_remove_gateway( // decrement layer count mixnet_params_storage::decrement_layer_count(deps.storage, Layer::Gateway)?; - let mut response = Response::new() - .add_message(return_tokens) - .add_attribute("action", "unbond") - .add_attribute("address", owner.clone()) - .add_attribute("gateway_bond", gateway_bond.to_string()); + let mut response = Response::new().add_message(return_tokens); if let Some(proxy) = &proxy { let msg = VestingContractExecuteMsg::TrackUnbondGateway { owner: owner.as_str().to_string(), - amount: gateway_bond.pledge_amount, + amount: gateway_bond.pledge_amount(), }; let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?; response = response.add_message(track_unbond_message); } - Ok(response) + Ok(response.add_event(new_gateway_unbonding_event( + &owner, + &proxy, + &gateway_bond.pledge_amount, + gateway_bond.identity(), + ))) } fn validate_gateway_pledge( @@ -212,12 +226,10 @@ pub mod tests { use crate::support::tests; use crate::support::tests::test_helpers; use config::defaults::DENOM; - use cosmwasm_std::attr; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, BankMsg, Response}; use cosmwasm_std::{from_binary, Addr, Uint128}; - use mixnet_contract::Gateway; - use mixnet_contract::{ExecuteMsg, PagedGatewayResponse, QueryMsg}; + use mixnet_contract_common::{ExecuteMsg, Gateway, PagedGatewayResponse, QueryMsg}; #[test] fn gateway_add() { @@ -454,19 +466,6 @@ pub mod tests { let msg = ExecuteMsg::UnbondGateway {}; let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap(); - // we should see log messages come back showing an unbond message - let expected_attributes = vec![ - attr("action", "unbond"), - attr("address", "fred"), - attr( - "gateway_bond", - format!( - "amount: {} {}, owner: fred, identity: {}", - INITIAL_GATEWAY_PLEDGE, DENOM, fred_identity - ), - ), - ]; - // we should see a funds transfer from the contract back to fred let expected_message = BankMsg::Send { to_address: String::from(info.sender), @@ -474,11 +473,17 @@ pub mod tests { }; // run the executor and check that we got back the correct results - let expected = Response::new() - .add_attributes(expected_attributes) - .add_message(expected_message); + let expected_response = + Response::new() + .add_message(expected_message) + .add_event(new_gateway_unbonding_event( + &Addr::unchecked("fred"), + &None, + &tests::fixtures::good_gateway_pledge()[0], + &fred_identity, + )); - assert_eq!(remove_fred, expected); + assert_eq!(expected_response, remove_fred); // only 1 node now exists, owned by bob: let gateway_bonds = tests::queries::get_gateways(&mut deps); diff --git a/contracts/mixnet/src/mixnet_contract_settings/models.rs b/contracts/mixnet/src/mixnet_contract_settings/models.rs index 0e5dcd8cb4..6b9d5d73f9 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/models.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/models.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use cosmwasm_std::Addr; -use mixnet_contract::ContractStateParams; +use mixnet_contract_common::ContractStateParams; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/contracts/mixnet/src/mixnet_contract_settings/queries.rs b/contracts/mixnet/src/mixnet_contract_settings/queries.rs index 2178ed8930..68427a8173 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/queries.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/queries.rs @@ -3,7 +3,9 @@ use super::storage; use cosmwasm_std::{Deps, StdResult}; -use mixnet_contract::{ContractStateParams, MixnetContractVersion, RewardingIntervalResponse}; +use mixnet_contract_common::{ + ContractStateParams, MixnetContractVersion, RewardingIntervalResponse, +}; pub(crate) fn query_contract_settings_params(deps: Deps) -> StdResult { storage::CONTRACT_STATE diff --git a/contracts/mixnet/src/mixnet_contract_settings/storage.rs b/contracts/mixnet/src/mixnet_contract_settings/storage.rs index 3ec217c352..2f2bd48664 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/storage.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/storage.rs @@ -5,8 +5,7 @@ use crate::mixnet_contract_settings::models::ContractState; use cosmwasm_std::StdResult; use cosmwasm_std::Storage; use cw_storage_plus::Item; -use mixnet_contract::Layer; -use mixnet_contract::LayerDistribution; +use mixnet_contract_common::{Layer, LayerDistribution}; pub(crate) const CONTRACT_STATE: Item = Item::new("config"); pub(crate) const LAYERS: Item = Item::new("layers"); diff --git a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs index 21bd496be6..e087584c4d 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs @@ -6,7 +6,8 @@ use crate::error::ContractError; use cosmwasm_std::DepsMut; use cosmwasm_std::MessageInfo; use cosmwasm_std::Response; -use mixnet_contract::ContractStateParams; +use mixnet_contract_common::events::new_settings_update_event; +use mixnet_contract_common::ContractStateParams; pub(crate) fn try_update_contract_settings( deps: DepsMut, @@ -34,10 +35,12 @@ pub(crate) fn try_update_contract_settings( return Err(ContractError::InvalidActiveSetSize); } + let response = Response::new().add_event(new_settings_update_event(&state.params, ¶ms)); + state.params = params; storage::CONTRACT_STATE.save(deps.storage, &state)?; - Ok(Response::default()) + Ok(response) } #[cfg(test)] @@ -49,7 +52,7 @@ pub mod tests { use crate::support::tests::test_helpers; use cosmwasm_std::testing::mock_info; use cosmwasm_std::Response; - use mixnet_contract::ContractStateParams; + use mixnet_contract_common::ContractStateParams; #[test] fn updating_contract_settings() { @@ -63,14 +66,13 @@ pub mod tests { active_set_work_factor: 10, }; + let initial_params = storage::CONTRACT_STATE + .load(deps.as_ref().storage) + .unwrap() + .params; + // sanity check to ensure new_params are different than the default ones - assert_ne!( - new_params, - storage::CONTRACT_STATE - .load(deps.as_ref().storage) - .unwrap() - .params - ); + assert_ne!(new_params, initial_params); // cannot be updated from non-owner account let info = mock_info("not-the-creator", &[]); @@ -80,7 +82,10 @@ pub mod tests { // but works fine from the creator account let info = mock_info("creator", &[]); let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone()); - assert_eq!(res, Ok(Response::default())); + assert_eq!( + res, + Ok(Response::new().add_event(new_settings_update_event(&initial_params, &new_params))) + ); // and the state is actually updated let current_state = storage::CONTRACT_STATE.load(deps.as_ref().storage).unwrap(); @@ -90,21 +95,21 @@ pub mod tests { let info = mock_info("creator", &[]); let mut new_params = current_state.params.clone(); new_params.mixnode_rewarded_set_size = new_params.mixnode_active_set_size - 1; - let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone()); + let res = try_update_contract_settings(deps.as_mut(), info, new_params); assert_eq!(Err(ContractError::InvalidActiveSetSize), res); // error is thrown for 0 size rewarded set let info = mock_info("creator", &[]); let mut new_params = current_state.params.clone(); new_params.mixnode_rewarded_set_size = 0; - let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone()); + let res = try_update_contract_settings(deps.as_mut(), info, new_params); assert_eq!(Err(ContractError::ZeroRewardedSet), res); // error is thrown for 0 size active set let info = mock_info("creator", &[]); - let mut new_params = current_state.params.clone(); + let mut new_params = current_state.params; new_params.mixnode_active_set_size = 0; - let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone()); + let res = try_update_contract_settings(deps.as_mut(), info, new_params); assert_eq!(Err(ContractError::ZeroActiveSet), res); } } diff --git a/contracts/mixnet/src/mixnodes/bonding_queries.rs b/contracts/mixnet/src/mixnodes/bonding_queries.rs index 4e485f1b77..e916e7263e 100644 --- a/contracts/mixnet/src/mixnodes/bonding_queries.rs +++ b/contracts/mixnet/src/mixnodes/bonding_queries.rs @@ -4,7 +4,9 @@ use super::storage; use cosmwasm_std::{Deps, Order, StdResult}; use cw_storage_plus::Bound; -use mixnet_contract::{IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse}; +use mixnet_contract_common::{ + IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse, +}; pub fn query_mixnodes_paged( deps: Deps, diff --git a/contracts/mixnet/src/mixnodes/layer_queries.rs b/contracts/mixnet/src/mixnodes/layer_queries.rs index 2ce21648f3..7a45c58167 100644 --- a/contracts/mixnet/src/mixnodes/layer_queries.rs +++ b/contracts/mixnet/src/mixnodes/layer_queries.rs @@ -3,7 +3,7 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage; use cosmwasm_std::{Deps, StdResult}; -use mixnet_contract::LayerDistribution; +use mixnet_contract_common::LayerDistribution; pub(crate) fn query_layer_distribution(deps: Deps) -> StdResult { mixnet_params_storage::LAYERS.load(deps.storage) diff --git a/contracts/mixnet/src/mixnodes/storage.rs b/contracts/mixnet/src/mixnodes/storage.rs index cbbcf749b4..15d365f0d9 100644 --- a/contracts/mixnet/src/mixnodes/storage.rs +++ b/contracts/mixnet/src/mixnodes/storage.rs @@ -4,7 +4,7 @@ use config::defaults::DENOM; use cosmwasm_std::{StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex}; -use mixnet_contract::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond}; +use mixnet_contract_common::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond}; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; @@ -49,7 +49,6 @@ pub(crate) struct StoredMixnodeBond { pub layer: Layer, pub block_height: u64, pub mix_node: MixNode, - pub profit_margin_percent: Option, pub proxy: Option, } @@ -60,7 +59,6 @@ impl StoredMixnodeBond { layer: Layer, block_height: u64, mix_node: MixNode, - profit_margin_percent: Option, proxy: Option, ) -> Self { StoredMixnodeBond { @@ -69,7 +67,6 @@ impl StoredMixnodeBond { layer, block_height, mix_node, - profit_margin_percent, proxy, } } @@ -141,8 +138,7 @@ mod tests { use config::defaults::DENOM; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{coin, Addr, Uint128}; - use mixnet_contract::IdentityKey; - use mixnet_contract::MixNode; + use mixnet_contract_common::{IdentityKey, MixNode}; #[test] fn mixnode_single_read_retrieval() { @@ -173,14 +169,13 @@ mod tests { let mixnode_bond = StoredMixnodeBond { pledge_amount: coin(pledge_value, DENOM), - owner: node_owner.clone(), + owner: node_owner, layer: Layer::One, block_height: 12_345, mix_node: MixNode { identity_key: node_identity.clone(), ..tests::fixtures::mix_node_fixture() }, - profit_margin_percent: None, proxy: None, }; diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 5ec7cdc33a..252128324a 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -11,7 +11,8 @@ use config::defaults::DENOM; use cosmwasm_std::{ coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; -use mixnet_contract::MixNode; +use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event}; +use mixnet_contract_common::MixNode; use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg; pub fn try_add_mixnode( @@ -109,13 +110,12 @@ fn _try_add_mixnode( let layer = layer_distribution.choose_with_fewest(); let stored_bond = StoredMixnodeBond::new( - pledge_amount, - owner, + pledge_amount.clone(), + owner.clone(), layer, env.block.height, mix_node, - None, - proxy, + proxy.clone(), ); // technically we don't have to set the total_delegation bucket, but it makes things easier @@ -133,7 +133,13 @@ fn _try_add_mixnode( mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?; - Ok(Response::new()) + Ok(Response::new().add_event(new_mixnode_bonding_event( + &owner, + &proxy, + &pledge_amount, + identity, + stored_bond.layer, + ))) } pub fn try_remove_mixnode_on_behalf( @@ -186,22 +192,24 @@ pub(crate) fn _try_remove_mixnode( // decrement layer count mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?; - let mut response = Response::new() - .add_message(return_tokens) - .add_attribute("action", "unbond") - .add_attribute("mixnode_bond", mixnode_bond.to_string()); + let mut response = Response::new().add_message(return_tokens); if let Some(proxy) = &proxy { let msg = VestingContractExecuteMsg::TrackUnbondMixnode { owner: owner.as_str().to_string(), - amount: mixnode_bond.pledge_amount, + amount: mixnode_bond.pledge_amount(), }; let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?; response = response.add_message(track_unbond_message); } - Ok(response) + Ok(response.add_event(new_mixnode_unbonding_event( + &owner, + &proxy, + &mixnode_bond.pledge_amount, + mixnode_bond.identity(), + ))) } pub(crate) fn try_update_mixnode_config( @@ -278,13 +286,12 @@ pub mod tests { use crate::support::tests; use crate::support::tests::test_helpers; use config::defaults::DENOM; - use cosmwasm_std::attr; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, BankMsg, Response}; use cosmwasm_std::{from_binary, Addr, Uint128}; - use mixnet_contract::Layer; - use mixnet_contract::MixNode; - use mixnet_contract::{ExecuteMsg, LayerDistribution, PagedMixnodeResponse, QueryMsg}; + use mixnet_contract_common::{ + ExecuteMsg, Layer, LayerDistribution, MixNode, PagedMixnodeResponse, QueryMsg, + }; #[test] fn mixnode_add() { @@ -543,18 +550,6 @@ pub mod tests { let msg = ExecuteMsg::UnbondMixnode {}; let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap(); - // we should see log messages come back showing an unbond message - let expected_attributes = vec![ - attr("action", "unbond"), - attr( - "mixnode_bond", - format!( - "amount: {}{}, owner: fred, identity: {}", - INITIAL_MIXNODE_PLEDGE, DENOM, fred_identity - ), - ), - ]; - // we should see a funds transfer from the contract back to fred let expected_message = BankMsg::Send { to_address: String::from(info.sender), @@ -562,11 +557,17 @@ pub mod tests { }; // run the executor and check that we got back the correct results - let expected = Response::new() - .add_attributes(expected_attributes) - .add_message(expected_message); + let expected_response = + Response::new() + .add_message(expected_message) + .add_event(new_mixnode_unbonding_event( + &Addr::unchecked("fred"), + &None, + &tests::fixtures::good_gateway_pledge()[0], + &fred_identity, + )); - assert_eq!(remove_fred, expected); + assert_eq!(expected_response, remove_fred); // only 1 node now exists, owned by bob: let mix_node_bonds = tests::queries::get_mix_nodes(&mut deps); @@ -756,6 +757,6 @@ pub mod tests { assert_eq!(alice_node.mix_node.identity_key, alice_identity); assert_eq!(alice_node.layer, Layer::One); assert_eq!(bob_node.mix_node.identity_key, bob_identity); - assert_eq!(bob_node.layer, mixnet_contract::Layer::Two); + assert_eq!(bob_node.layer, mixnet_contract_common::Layer::Two); } } diff --git a/contracts/mixnet/src/rewards/helpers.rs b/contracts/mixnet/src/rewards/helpers.rs index 593e2eb7f9..c363cb5e39 100644 --- a/contracts/mixnet/src/rewards/helpers.rs +++ b/contracts/mixnet/src/rewards/helpers.rs @@ -5,8 +5,8 @@ use super::storage; use crate::error::ContractError; use crate::mixnodes::storage as mixnodes_storage; use cosmwasm_std::{Addr, Storage, Uint128}; -use mixnet_contract::mixnode::DelegatorRewardParams; -use mixnet_contract::{ +use mixnet_contract_common::mixnode::DelegatorRewardParams; +use mixnet_contract_common::{ IdentityKey, IdentityKeyRef, PendingDelegatorRewarding, RewardingResult, RewardingStatus, }; diff --git a/contracts/mixnet/src/rewards/queries.rs b/contracts/mixnet/src/rewards/queries.rs index 7d643c073d..a52f74dcf0 100644 --- a/contracts/mixnet/src/rewards/queries.rs +++ b/contracts/mixnet/src/rewards/queries.rs @@ -4,7 +4,7 @@ use super::storage; use cosmwasm_std::Uint128; use cosmwasm_std::{Deps, StdResult}; -use mixnet_contract::{IdentityKey, MixnodeRewardingStatusResponse}; +use mixnet_contract_common::{IdentityKey, MixnodeRewardingStatusResponse}; pub(crate) fn query_reward_pool(deps: Deps) -> StdResult { storage::REWARD_POOL.load(deps.storage) @@ -46,7 +46,9 @@ pub(crate) mod tests { }; use config::defaults::DENOM; use cosmwasm_std::{coin, Addr}; - use mixnet_contract::{RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT}; + use mixnet_contract_common::{ + RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT, + }; #[test] fn returns_empty_status_for_unrewarded_nodes() { @@ -75,14 +77,14 @@ pub(crate) mod tests { try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap(); try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), 1, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap(); + try_finish_mixnode_rewarding(deps.as_mut(), info, 1).unwrap(); assert!(query_rewarding_status(deps.as_ref(), node_identity, 2) .unwrap() @@ -120,7 +122,7 @@ pub(crate) mod tests { 1, ) .unwrap(); - try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap(); + try_finish_mixnode_rewarding(deps.as_mut(), info, 1).unwrap(); let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap(); assert!(matches!(res.status, Some(RewardingStatus::Complete(..)))); @@ -151,10 +153,7 @@ pub(crate) mod tests { try_delegate_to_mixnode( deps.as_mut(), env.clone(), - mock_info( - &*format!("delegator{:04}", i), - &vec![coin(200_000000, DENOM)], - ), + mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]), node_identity.clone(), ) .unwrap(); @@ -167,7 +166,7 @@ pub(crate) mod tests { try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), @@ -176,13 +175,8 @@ pub(crate) mod tests { .unwrap(); // rewards all pending - try_reward_next_mixnode_delegators( - deps.as_mut(), - info.clone(), - node_identity.to_string(), - 2, - ) - .unwrap(); + try_reward_next_mixnode_delegators(deps.as_mut(), info, node_identity.to_string(), 2) + .unwrap(); let res = query_rewarding_status(deps.as_ref(), node_identity, 2).unwrap(); assert!(matches!(res.status, Some(RewardingStatus::Complete(..)))); @@ -222,10 +216,7 @@ pub(crate) mod tests { try_delegate_to_mixnode( deps.as_mut(), env.clone(), - mock_info( - &*format!("delegator{:04}", i), - &vec![coin(200_000000, DENOM)], - ), + mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]), node_identity.clone(), ) .unwrap(); @@ -238,7 +229,7 @@ pub(crate) mod tests { try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), diff --git a/contracts/mixnet/src/rewards/storage.rs b/contracts/mixnet/src/rewards/storage.rs index 56ce6a4964..7f8e1765c9 100644 --- a/contracts/mixnet/src/rewards/storage.rs +++ b/contracts/mixnet/src/rewards/storage.rs @@ -5,7 +5,7 @@ use crate::error::ContractError; use config::defaults::TOTAL_SUPPLY; use cosmwasm_std::{StdResult, Storage, Uint128}; use cw_storage_plus::{Item, Map, U32Key}; -use mixnet_contract::{IdentityKey, RewardingStatus}; +use mixnet_contract_common::{IdentityKey, RewardingStatus}; pub(crate) const REWARD_POOL: Item = Item::new("pool"); pub(crate) const REWARDING_STATUS: Map<(U32Key, IdentityKey), RewardingStatus> = Map::new("rm"); diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index 7457f0e3ed..438ad9504d 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -9,8 +9,13 @@ use crate::mixnodes::storage as mixnodes_storage; use crate::rewards::helpers; use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, StdResult, Storage, Uint128}; use cw_storage_plus::{Bound, PrimaryKey}; -use mixnet_contract::mixnode::{DelegatorRewardParams, NodeRewardParams}; -use mixnet_contract::{ +use mixnet_contract_common::events::{ + new_begin_rewarding_event, new_finish_rewarding_event, new_mix_delegators_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, +}; +use mixnet_contract_common::mixnode::{DelegatorRewardParams, NodeRewardParams}; +use mixnet_contract_common::{ IdentityKey, RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT, }; @@ -106,10 +111,7 @@ pub(crate) fn try_begin_mixnode_rewarding( mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?; - Ok(Response::new().add_attribute( - "rewarding interval nonce", - rewarding_interval_nonce.to_string(), - )) + Ok(Response::new().add_event(new_begin_rewarding_event(rewarding_interval_nonce))) } fn reward_mix_delegators( @@ -232,21 +234,26 @@ pub(crate) fn try_reward_next_mixnode_delegators( let mut rewarding_results = next_page_info.running_results; rewarding_results.total_delegator_reward += delegation_rewarding_result.total_rewarded; - let round_increase = delegation_rewarding_result.total_rewarded.to_string(); + let round_increase = delegation_rewarding_result.total_rewarded; let more_delegators = delegation_rewarding_result.start_next.is_some(); helpers::update_rewarding_status( deps.storage, rewarding_interval_nonce, - mix_identity, + mix_identity.clone(), rewarding_results, delegation_rewarding_result.start_next, next_page_info.rewarding_params, )?; - Ok(Response::new() - .add_attribute("current round delegation increase", round_increase) - .add_attribute("more delegators to reward", more_delegators.to_string())) + Ok( + Response::new().add_event(new_mix_delegators_rewarding_event( + rewarding_interval_nonce, + &mix_identity, + round_increase, + more_delegators, + )), + ) } } } @@ -283,68 +290,91 @@ pub(crate) fn try_reward_mixnode( let current_bond = match mixnodes_storage::read_full_mixnode_bond(deps.storage, &mix_identity)? { Some(bond) => bond, - None => return Ok(Response::new().add_attribute("result", "bond not found")), + None => { + return Ok( + Response::new().add_event(new_not_found_mix_operator_rewarding_event( + rewarding_interval_nonce, + &mix_identity, + )), + ) + } }; - let mut node_reward = "0".to_string(); - let mut operator_reward = Uint128::zero(); - let mut total_delegation_increase = Uint128::zero(); - let mut more_delegators = false; - - // check if node is old enough for rewarding and if its uptime is non-zero - if current_bond.block_height + storage::MINIMUM_BLOCK_AGE_FOR_REWARDING <= env.block.height - && params.uptime() > 0 - { - let mut node_reward_params = params; - node_reward_params.set_reward_blockstamp(env.block.height); - - let operator_reward_result = current_bond.reward(&node_reward_params); - node_reward = operator_reward_result.reward().to_string(); - - // Omitting the price per packet function now, it follows that base operator reward is the node_reward - operator_reward = Uint128::new(current_bond.operator_reward(&node_reward_params)); - - let delegator_params = DelegatorRewardParams::new(¤t_bond, node_reward_params); - let delegation_rewarding_result = - reward_mix_delegators(deps.storage, mix_identity.clone(), None, delegator_params)?; - - helpers::update_post_rewarding_storage( - deps.storage, - &mix_identity, - operator_reward, - delegation_rewarding_result.total_rewarded, - )?; - - let rewarding_results = RewardingResult { - operator_reward, - total_delegator_reward: delegation_rewarding_result.total_rewarded, - }; - - total_delegation_increase = rewarding_results.total_delegator_reward; - more_delegators = delegation_rewarding_result.start_next.is_some(); - - helpers::update_rewarding_status( - deps.storage, - rewarding_interval_nonce, - mix_identity, - rewarding_results, - delegation_rewarding_result.start_next, - delegator_params, - )?; - } else { - // node is not eligible for rewarding, so we're done immediately + // check if node is old enough for rewarding + if current_bond.block_height + storage::MINIMUM_BLOCK_AGE_FOR_REWARDING > env.block.height { storage::REWARDING_STATUS.save( deps.storage, - (rewarding_interval_nonce.into(), mix_identity), + (rewarding_interval_nonce.into(), mix_identity.clone()), &RewardingStatus::Complete(Default::default()), )?; + + return Ok( + Response::new().add_event(new_too_fresh_bond_mix_operator_rewarding_event( + rewarding_interval_nonce, + &mix_identity, + )), + ); } - Ok(Response::new() - .add_attribute("node reward", node_reward) - .add_attribute("operator reward", operator_reward) - .add_attribute("total delegation increase", total_delegation_increase) - .add_attribute("more delegators to reward", more_delegators.to_string())) + // check if it has non-zero uptime + if params.uptime() == 0 { + storage::REWARDING_STATUS.save( + deps.storage, + (rewarding_interval_nonce.into(), mix_identity.clone()), + &RewardingStatus::Complete(Default::default()), + )?; + + return Ok( + Response::new().add_event(new_zero_uptime_mix_operator_rewarding_event( + rewarding_interval_nonce, + &mix_identity, + )), + ); + } + + let mut node_reward_params = params; + node_reward_params.set_reward_blockstamp(env.block.height); + + let node_reward_result = current_bond.reward(&node_reward_params); + + // Omitting the price per packet function now, it follows that base operator reward is the node_reward + let operator_reward = Uint128::new(current_bond.operator_reward(&node_reward_params)); + + let delegator_params = DelegatorRewardParams::new(¤t_bond, node_reward_params); + let delegation_rewarding_result = + reward_mix_delegators(deps.storage, mix_identity.clone(), None, delegator_params)?; + + helpers::update_post_rewarding_storage( + deps.storage, + &mix_identity, + operator_reward, + delegation_rewarding_result.total_rewarded, + )?; + + let rewarding_results = RewardingResult { + operator_reward, + total_delegator_reward: delegation_rewarding_result.total_rewarded, + }; + let total_delegator_reward = rewarding_results.total_delegator_reward; + let further_delegations = delegation_rewarding_result.start_next.is_some(); + + helpers::update_rewarding_status( + deps.storage, + rewarding_interval_nonce, + mix_identity.clone(), + rewarding_results, + delegation_rewarding_result.start_next, + delegator_params, + )?; + + Ok(Response::new().add_event(new_mix_operator_rewarding_event( + rewarding_interval_nonce, + &mix_identity, + node_reward_result, + operator_reward, + total_delegator_reward, + further_delegations, + ))) } pub(crate) fn try_finish_mixnode_rewarding( @@ -374,7 +404,7 @@ pub(crate) fn try_finish_mixnode_rewarding( state.rewarding_in_progress = false; mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?; - Ok(Response::new()) + Ok(Response::new().add_event(new_finish_rewarding_event(rewarding_interval_nonce))) } #[cfg(test)] @@ -394,11 +424,15 @@ pub mod tests { use config::defaults::DENOM; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::Coin; - use cosmwasm_std::{attr, Order}; + use cosmwasm_std::Order; use cosmwasm_std::{coin, coins, Addr, Uint128}; - use mixnet_contract::mixnode::NodeRewardParams; - use mixnet_contract::{Delegation, MixNode}; - use mixnet_contract::{IdentityKey, Layer}; + use mixnet_contract_common::events::{ + must_find_attribute, BOND_TOO_FRESH_VALUE, DISTRIBUTED_DELEGATION_REWARDS_KEY, + FURTHER_DELEGATIONS_TO_REWARD_KEY, NO_REWARD_REASON_KEY, OPERATOR_REWARDING_EVENT_TYPE, + OPERATOR_REWARD_KEY, TOTAL_MIXNODE_REWARD_KEY, + }; + use mixnet_contract_common::mixnode::NodeRewardParams; + use mixnet_contract_common::{Delegation, IdentityKey, Layer, MixNode}; #[cfg(test)] mod beginning_mixnode_rewarding { @@ -426,7 +460,7 @@ pub mod tests { let res = try_begin_mixnode_rewarding( deps.as_mut(), - env.clone(), + env, mock_info(rewarding_validator_address.as_ref(), &[]), 1, ); @@ -452,7 +486,7 @@ pub mod tests { let res = try_begin_mixnode_rewarding( deps.as_mut(), - env.clone(), + env, mock_info(rewarding_validator_address.as_ref(), &[]), 2, ); @@ -547,7 +581,7 @@ pub mod tests { let res = try_begin_mixnode_rewarding( deps.as_mut(), - env.clone(), + env, mock_info(rewarding_validator_address.as_ref(), &[]), 43, ); @@ -774,9 +808,9 @@ pub mod tests { let res = try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info, - node_identity.clone(), + node_identity, tests::fixtures::node_rewarding_params_fixture(100), 1, ); @@ -836,9 +870,9 @@ pub mod tests { let res = try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info, - node_identity.clone(), + node_identity, tests::fixtures::node_rewarding_params_fixture(100), 1, ); @@ -900,7 +934,7 @@ pub mod tests { deps.as_mut(), env, info, - node_identity.clone(), + node_identity, tests::fixtures::node_rewarding_params_fixture(100), 2, ); @@ -923,14 +957,13 @@ pub mod tests { let initial_delegation = 20000_000000; let mixnode_bond = StoredMixnodeBond { pledge_amount: coin(initial_bond, DENOM), - owner: node_owner.clone(), + owner: node_owner, layer: Layer::One, block_height: env.block.height, mix_node: MixNode { identity_key: node_identity.clone(), ..tests::fixtures::mix_node_fixture() }, - profit_margin_percent: Some(10), proxy: None, }; @@ -988,13 +1021,11 @@ pub mod tests { .unwrap() .u128() ); - - assert_eq!(res.attributes[0], attr("node reward", "0")); - assert_eq!(res.attributes[1], attr("operator reward", "0")); - assert_eq!(res.attributes[2], attr("total delegation increase", "0")); + assert_eq!(1, res.events.len()); + assert_eq!(OPERATOR_REWARDING_EVENT_TYPE, res.events[0].ty); assert_eq!( - res.attributes[3], - attr("more delegators to reward", false.to_string()) + BOND_TOO_FRESH_VALUE, + must_find_attribute(&res.events[0], NO_REWARD_REASON_KEY) ); // reward can happen now, but only for bonded node @@ -1027,12 +1058,18 @@ pub mod tests { .u128() ); - assert_ne!(res.attributes[0], attr("node reward", "0")); - assert_ne!(res.attributes[1], attr("operator reward", "0")); - assert_eq!(res.attributes[2], attr("total delegation increase", "0")); + assert_eq!(1, res.events.len()); + let event = &res.events[0]; + assert_eq!(OPERATOR_REWARDING_EVENT_TYPE, event.ty); + assert_ne!("0", must_find_attribute(event, TOTAL_MIXNODE_REWARD_KEY)); + assert_ne!("0", must_find_attribute(event, OPERATOR_REWARD_KEY)); assert_eq!( - res.attributes[3], - attr("more delegators to reward", false.to_string()) + "0", + must_find_attribute(event, DISTRIBUTED_DELEGATION_REWARDS_KEY) + ); + assert_eq!( + false.to_string(), + must_find_attribute(event, FURTHER_DELEGATIONS_TO_REWARD_KEY) ); // reward happens now, both for node owner and delegators @@ -1047,7 +1084,7 @@ pub mod tests { try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 3).unwrap(); let res = try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info.clone(), node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), @@ -1070,12 +1107,18 @@ pub mod tests { > initial_delegation ); - assert_ne!(res.attributes[0], attr("node reward", "0")); - assert_ne!(res.attributes[1], attr("operator reward", "0")); - assert_ne!(res.attributes[2], attr("total delegation increase", "0")); + assert_eq!(1, res.events.len()); + let event = &res.events[0]; + assert_eq!(OPERATOR_REWARDING_EVENT_TYPE, event.ty); + assert_ne!("0", must_find_attribute(event, TOTAL_MIXNODE_REWARD_KEY)); + assert_ne!("0", must_find_attribute(event, OPERATOR_REWARD_KEY)); + assert_ne!( + "0", + must_find_attribute(event, DISTRIBUTED_DELEGATION_REWARDS_KEY) + ); assert_eq!( - res.attributes[3], - attr("more delegators to reward", false.to_string()) + false.to_string(), + must_find_attribute(event, FURTHER_DELEGATIONS_TO_REWARD_KEY) ); } @@ -1198,7 +1241,7 @@ pub mod tests { ); assert_eq!( mixnodes_storage::TOTAL_DELEGATION - .load(&deps.storage, &node_identity.clone()) + .load(&deps.storage, &node_identity) .unwrap() .u128(), pre_reward_delegation + mix1_delegator1_reward + mix1_delegator2_reward @@ -1214,7 +1257,7 @@ pub mod tests { // it's all correctly saved match storage::REWARDING_STATUS - .load(deps.as_ref().storage, (1.into(), node_identity.clone())) + .load(deps.as_ref().storage, (1.into(), node_identity)) .unwrap() { RewardingStatus::Complete(result) => assert_eq!( @@ -1244,6 +1287,7 @@ pub mod tests { .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; + #[allow(clippy::inconsistent_digit_grouping)] let mix_bond = Uint128::new(10000_000_000); let delegation_value = 2000_000000; @@ -1263,7 +1307,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{}", i), - &vec![coin(delegation_value, DENOM)], + &[coin(delegation_value, DENOM)], ), node_identity.clone(), ) @@ -1291,8 +1335,8 @@ pub mod tests { ) .unwrap(); assert_eq!( - res.attributes[3], - attr("more delegators to reward", false.to_string()) + false.to_string(), + must_find_attribute(&res.events[0], FURTHER_DELEGATIONS_TO_REWARD_KEY) ); try_finish_mixnode_rewarding( @@ -1325,6 +1369,7 @@ pub mod tests { .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; + #[allow(clippy::inconsistent_digit_grouping)] let mix_bond = Uint128::new(10000_000_000); let delegation_value = 2000_000000; @@ -1344,7 +1389,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{}", i), - &vec![coin(delegation_value, DENOM)], + &[coin(delegation_value, DENOM)], ), node_identity.clone(), ) @@ -1372,8 +1417,8 @@ pub mod tests { ) .unwrap(); assert_eq!( - res.attributes[3], - attr("more delegators to reward", false.to_string()) + false.to_string(), + must_find_attribute(&res.events[0], FURTHER_DELEGATIONS_TO_REWARD_KEY) ); try_finish_mixnode_rewarding( @@ -1406,6 +1451,7 @@ pub mod tests { .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; + #[allow(clippy::inconsistent_digit_grouping)] let mix_bond = Uint128::new(10000_000_000); let delegation_value = 2000_000000; @@ -1425,7 +1471,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &vec![coin(delegation_value, DENOM)], + &[coin(delegation_value, DENOM)], ), node_identity.clone(), ) @@ -1453,8 +1499,8 @@ pub mod tests { ) .unwrap(); assert_eq!( - res.attributes[3], - attr("more delegators to reward", true.to_string()) + true.to_string(), + must_find_attribute(&res.events[0], FURTHER_DELEGATIONS_TO_REWARD_KEY) ); try_finish_mixnode_rewarding( @@ -1477,7 +1523,7 @@ pub mod tests { let delegation = test_helpers::read_delegation( &deps.storage, - node_identity.clone(), + node_identity, format!("delegator{:04}", MIXNODE_DELEGATORS_PAGE_LIMIT), ) .unwrap(); @@ -1493,6 +1539,8 @@ pub mod tests { let mut env = mock_env(); let node_owner: Addr = Addr::unchecked("bob"); + + #[allow(clippy::inconsistent_digit_grouping)] let node_identity = test_helpers::add_mixnode( node_owner.as_str(), coins(10000_000_000, DENOM), @@ -1512,7 +1560,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &vec![coin(base_delegation, DENOM)], + &[coin(base_delegation, DENOM)], ), node_identity.clone(), ) @@ -1531,7 +1579,7 @@ pub mod tests { for delegation in delegations_storage::delegations() .idx .mixnode - .prefix(node_identity.clone()) + .prefix(node_identity) .range(deps.as_ref().storage, None, None, Order::Ascending) { actual_reward += @@ -1546,6 +1594,8 @@ pub mod tests { // with paging let node_owner: Addr = Addr::unchecked("alice"); + + #[allow(clippy::inconsistent_digit_grouping)] let node_identity = test_helpers::add_mixnode( node_owner.as_str(), coins(10000_000_000, DENOM), @@ -1565,7 +1615,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &vec![coin(base_delegation, DENOM)], + &[coin(base_delegation, DENOM)], ), node_identity.clone(), ) @@ -1629,7 +1679,7 @@ pub mod tests { for delegation in delegations_storage::delegations() .idx .mixnode - .prefix(node_identity.clone()) + .prefix(node_identity) .range(deps.as_ref().storage, Some(start), None, Order::Ascending) { actual_reward += @@ -1686,7 +1736,7 @@ pub mod tests { try_begin_mixnode_rewarding( deps.as_mut(), - env.clone(), + env, mock_info(rewarding_validator_address.as_ref(), &[]), 1, ) @@ -1718,6 +1768,8 @@ pub mod tests { let rewarding_validator_address = current_state.rewarding_validator_address; let node_owner: Addr = Addr::unchecked("alice"); + + #[allow(clippy::inconsistent_digit_grouping)] let node_identity = test_helpers::add_mixnode( node_owner.as_str(), coins(10000_000_000, DENOM), @@ -1753,7 +1805,7 @@ pub mod tests { assert_eq!( Err(ContractError::MixnodeAlreadyRewarded { - identity: node_identity.clone() + identity: node_identity }), res ); @@ -1767,6 +1819,8 @@ pub mod tests { // there was another page of delegators, but they were already dealt with let node_owner: Addr = Addr::unchecked("bob"); + + #[allow(clippy::inconsistent_digit_grouping)] let node_identity = test_helpers::add_mixnode( node_owner.as_str(), coins(10000_000_000, DENOM), @@ -1777,10 +1831,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), env.clone(), - mock_info( - &*format!("delegator{:04}", i), - &vec![coin(2000_000000, DENOM)], - ), + mock_info(&*format!("delegator{:04}", i), &[coin(2000_000000, DENOM)]), node_identity.clone(), ) .unwrap(); @@ -1799,7 +1850,7 @@ pub mod tests { try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), @@ -1825,7 +1876,7 @@ pub mod tests { assert_eq!( Err(ContractError::MixnodeAlreadyRewarded { - identity: node_identity.clone() + identity: node_identity }), res ); @@ -1848,6 +1899,7 @@ pub mod tests { .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; + #[allow(clippy::inconsistent_digit_grouping)] let mix_bond = Uint128::new(10000_000_000); let delegation_value = 2000_000000; @@ -1869,7 +1921,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &vec![coin(delegation_value, DENOM)], + &[coin(delegation_value, DENOM)], ), node_identity.clone(), ) @@ -1889,7 +1941,7 @@ pub mod tests { try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), @@ -1945,6 +1997,7 @@ pub mod tests { .unwrap(); let rewarding_validator_address = current_state.rewarding_validator_address; + #[allow(clippy::inconsistent_digit_grouping)] let mix_bond = Uint128::new(10000_000_000); let delegation_value = 2000_000000; @@ -1966,7 +2019,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &vec![coin(delegation_value, DENOM)], + &[coin(delegation_value, DENOM)], ), node_identity.clone(), ) @@ -1979,7 +2032,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), env.clone(), - mock_info("delegator0123", &vec![coin(delegation_value, DENOM)]), + mock_info("delegator0123", &[coin(delegation_value, DENOM)]), node_identity.clone(), ) .unwrap(); @@ -1989,7 +2042,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", 123 + MIXNODE_DELEGATORS_PAGE_LIMIT), - &vec![coin(delegation_value, DENOM)], + &[coin(delegation_value, DENOM)], ), node_identity.clone(), ) @@ -2008,7 +2061,7 @@ pub mod tests { try_reward_mixnode( deps.as_mut(), - env.clone(), + env, info, node_identity.clone(), tests::fixtures::node_rewarding_params_fixture(100), diff --git a/contracts/mixnet/src/support/helpers.rs b/contracts/mixnet/src/support/helpers.rs index a5994b42d2..94c273879e 100644 --- a/contracts/mixnet/src/support/helpers.rs +++ b/contracts/mixnet/src/support/helpers.rs @@ -5,7 +5,7 @@ use crate::error::ContractError; use crate::gateways::storage as gateways_storage; use crate::mixnodes::storage as mixnodes_storage; use cosmwasm_std::{Addr, Deps, Storage}; -use mixnet_contract::IdentityKeyRef; +use mixnet_contract_common::IdentityKeyRef; pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec { if let Some(proxy) = &proxy { diff --git a/contracts/mixnet/src/support/tests/fixtures.rs b/contracts/mixnet/src/support/tests/fixtures.rs index af4f6ab14f..cd1861e344 100644 --- a/contracts/mixnet/src/support/tests/fixtures.rs +++ b/contracts/mixnet/src/support/tests/fixtures.rs @@ -6,8 +6,8 @@ use crate::mixnodes::storage as mixnodes_storage; use crate::{mixnodes::storage::StoredMixnodeBond, support::tests}; use config::defaults::{DENOM, TOTAL_SUPPLY}; use cosmwasm_std::{coin, Addr, Coin}; -use mixnet_contract::mixnode::NodeRewardParams; -use mixnet_contract::{Gateway, GatewayBond, Layer, MixNode}; +use mixnet_contract_common::mixnode::NodeRewardParams; +use mixnet_contract_common::{Gateway, GatewayBond, Layer, MixNode}; pub fn mix_node_fixture() -> MixNode { MixNode { @@ -59,7 +59,6 @@ pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::Stor ..super::fixtures::mix_node_fixture() }, None, - None, ) } diff --git a/contracts/mixnet/src/support/tests/messages.rs b/contracts/mixnet/src/support/tests/messages.rs index e7f8af7a91..fabea69ad4 100644 --- a/contracts/mixnet/src/support/tests/messages.rs +++ b/contracts/mixnet/src/support/tests/messages.rs @@ -1,4 +1,4 @@ -use mixnet_contract::{ExecuteMsg, Gateway, IdentityKey, MixNode}; +use mixnet_contract_common::{ExecuteMsg, Gateway, IdentityKey, MixNode}; use rand::thread_rng; use crate::support::tests; diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 7b0b99fff8..3218b72162 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -28,7 +28,7 @@ pub mod test_helpers { use cosmwasm_std::{Addr, StdResult, Storage}; use cosmwasm_std::{Empty, MemoryStorage}; use cw_storage_plus::PrimaryKey; - use mixnet_contract::{Delegation, Gateway, IdentityKeyRef, InstantiateMsg, MixNode}; + use mixnet_contract_common::{Delegation, Gateway, IdentityKeyRef, InstantiateMsg, MixNode}; use rand::thread_rng; pub fn add_mixnode(sender: &str, stake: Vec, deps: DepsMut) -> String { diff --git a/contracts/mixnet/src/support/tests/queries.rs b/contracts/mixnet/src/support/tests/queries.rs index 4c8d00195e..9a9900501b 100644 --- a/contracts/mixnet/src/support/tests/queries.rs +++ b/contracts/mixnet/src/support/tests/queries.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ testing::{mock_env, MockApi, MockQuerier, MockStorage}, Addr, Coin, OwnedDeps, }; -use mixnet_contract::{ +use mixnet_contract_common::{ GatewayBond, MixNodeBond, PagedGatewayResponse, PagedMixnodeResponse, QueryMsg, }; diff --git a/contracts/vesting/Cargo.toml b/contracts/vesting/Cargo.toml index 55e731443c..069d31ae3e 100644 --- a/contracts/vesting/Cargo.toml +++ b/contracts/vesting/Cargo.toml @@ -14,7 +14,8 @@ exclude = [ crate-type = ["cdylib", "rlib"] [dependencies] -mixnet-contract = { path = "../../common/mixnet-contract" } +mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } +vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } config = { path = "../../common/config" } cosmwasm-std = { version = "1.0.0-beta3"} diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index 44a68ae853..89f469f1b5 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -7,10 +7,15 @@ use crate::traits::{ use crate::vesting::{populate_vesting_periods, Account}; use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM}; use cosmwasm_std::{ - entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, + coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Timestamp, Uint128, }; -use mixnet_contract::{Gateway, IdentityKey, MixNode}; +use mixnet_contract_common::{Gateway, IdentityKey, MixNode}; +use vesting_contract_common::events::{ + new_ownership_transfer_event, new_periodic_vesting_account_event, + new_staking_address_update_event, new_track_gateway_unbond_event, + new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event, +}; // We're using a 24 month vesting period with 3 months sub-periods. // There are 8 three month periods in two years @@ -96,6 +101,10 @@ pub fn try_withdraw_vested_coins( info: MessageInfo, deps: DepsMut, ) -> Result { + if amount.denom != DENOM { + return Err(ContractError::WrongDenom(amount.denom, DENOM.to_string())); + } + let address = info.sender.clone(); let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; if address != account.owner_address() { @@ -111,12 +120,16 @@ pub fn try_withdraw_vested_coins( let send_tokens = BankMsg::Send { to_address: account.owner_address().as_str().to_string(), - amount: vec![amount], + amount: vec![amount.clone()], }; Ok(Response::new() - .add_attribute("action", "whitdraw") - .add_message(send_tokens)) + .add_message(send_tokens) + .add_event(new_vested_coins_withdraw_event( + &address, + &amount, + &coin(new_balance, &amount.denom), + ))) } else { Err(ContractError::InsufficientSpendable( account.owner_address().as_str().to_string(), @@ -135,7 +148,7 @@ fn try_transfer_ownership( let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; if address == account.owner_address() { account.transfer_ownership(&to_address, deps.storage)?; - Ok(Response::default()) + Ok(Response::new().add_event(new_ownership_transfer_event(&address, &to_address))) } else { Err(ContractError::NotOwner(account.owner_address().to_string())) } @@ -150,8 +163,9 @@ fn try_update_staking_address( let to_address = to_address.and_then(|x| deps.api.addr_validate(&x).ok()); let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; if address == account.owner_address() { - account.update_staking_address(to_address, deps.storage)?; - Ok(Response::default()) + let old = account.staking_address().cloned(); + account.update_staking_address(to_address.clone(), deps.storage)?; + Ok(Response::new().add_event(new_staking_address_update_event(&old, &to_address))) } else { Err(ContractError::NotOwner(account.owner_address().to_string())) } @@ -186,7 +200,7 @@ pub fn try_track_unbond_gateway( } let account = account_from_address(owner, deps.storage, deps.api)?; account.try_track_unbond_gateway(amount, deps.storage)?; - Ok(Response::default()) + Ok(Response::new().add_event(new_track_gateway_unbond_event())) } pub fn try_bond_mixnode( @@ -217,7 +231,7 @@ pub fn try_track_unbond_mixnode( } let account = account_from_address(owner, deps.storage, deps.api)?; account.try_track_unbond_mixnode(amount, deps.storage)?; - Ok(Response::default()) + Ok(Response::new().add_event(new_track_mixnode_unbond_event())) } fn try_track_undelegation( @@ -232,7 +246,7 @@ fn try_track_undelegation( } let account = account_from_address(address, deps.storage, deps.api)?; account.track_undelegation(mix_identity, amount, deps.storage)?; - Ok(Response::default()) + Ok(Response::new().add_event(new_track_undelegation_event())) } fn try_delegate_to_mixnode( @@ -275,15 +289,24 @@ fn try_create_periodic_vesting_account( }; let start_time = start_time.unwrap_or_else(|| env.block.time.seconds()); let periods = populate_vesting_periods(start_time, NUM_VESTING_PERIODS); + + let start_time = Timestamp::from_seconds(start_time); Account::new( - owner_address, - staking_address, - coin, - Timestamp::from_seconds(start_time), + owner_address.clone(), + staking_address.clone(), + coin.clone(), + start_time, periods, deps.storage, )?; - Ok(Response::default()) + Ok( + Response::new().add_event(new_periodic_vesting_account_event( + &owner_address, + &coin, + &staking_address, + start_time, + )), + ) } #[entry_point] diff --git a/contracts/vesting/src/errors.rs b/contracts/vesting/src/errors.rs index 3651ac48b5..fa1b8fc5f7 100644 --- a/contracts/vesting/src/errors.rs +++ b/contracts/vesting/src/errors.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{Addr, StdError}; -use mixnet_contract::IdentityKey; +use mixnet_contract_common::IdentityKey; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -24,11 +24,11 @@ pub enum ContractError { ImprobableVestingAmount(u128), #[error("Address {0} has already bonded a node")] AlreadyBonded(String), - #[error("Recieved empty funds vector")] + #[error("Received empty funds vector")] EmptyFunds, - #[error("Recieved wrong denom: {0}, expected {1}")] + #[error("Received wrong denom: {0}, expected {1}")] WrongDenom(String, String), - #[error("Recieved multiple denoms, expected 1")] + #[error("Received multiple denoms, expected 1")] MultipleDenoms, #[error("No delegations found for account {0}, mix_identity {1}")] NoSuchDelegation(Addr, IdentityKey), diff --git a/contracts/vesting/src/messages.rs b/contracts/vesting/src/messages.rs index fffec2ca15..f32661818a 100644 --- a/contracts/vesting/src/messages.rs +++ b/contracts/vesting/src/messages.rs @@ -1,6 +1,5 @@ use cosmwasm_std::{Coin, Timestamp}; -use mixnet_contract::IdentityKey; -use mixnet_contract::{Gateway, MixNode}; +use mixnet_contract_common::{Gateway, IdentityKey, MixNode}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/contracts/vesting/src/traits/bonding.rs b/contracts/vesting/src/traits/bonding.rs index 73ec75024b..9ab5066d72 100644 --- a/contracts/vesting/src/traits/bonding.rs +++ b/contracts/vesting/src/traits/bonding.rs @@ -1,6 +1,6 @@ use crate::errors::ContractError; use cosmwasm_std::{Coin, Env, Response, Storage}; -use mixnet_contract::{Gateway, MixNode}; +use mixnet_contract_common::{Gateway, MixNode}; pub trait MixnodeBondingAccount { fn try_bond_mixnode( diff --git a/contracts/vesting/src/traits/delegating_account.rs b/contracts/vesting/src/traits/delegating_account.rs index bc133911d9..c2d7104893 100644 --- a/contracts/vesting/src/traits/delegating_account.rs +++ b/contracts/vesting/src/traits/delegating_account.rs @@ -1,6 +1,6 @@ use crate::errors::ContractError; use cosmwasm_std::{Coin, Env, Response, Storage, Timestamp, Uint128}; -use mixnet_contract::IdentityKey; +use mixnet_contract_common::IdentityKey; pub trait DelegatingAccount { fn try_delegate_to_mixnode( diff --git a/contracts/vesting/src/vesting/account/delegating_account.rs b/contracts/vesting/src/vesting/account/delegating_account.rs index 20b67c971e..c39ae4a870 100644 --- a/contracts/vesting/src/vesting/account/delegating_account.rs +++ b/contracts/vesting/src/vesting/account/delegating_account.rs @@ -3,8 +3,11 @@ use crate::traits::DelegatingAccount; use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM}; use cosmwasm_std::{wasm_execute, Coin, Env, Order, Response, Storage, Timestamp, Uint128}; use cw_storage_plus::Map; -use mixnet_contract::ExecuteMsg as MixnetExecuteMsg; -use mixnet_contract::IdentityKey; +use mixnet_contract_common::ExecuteMsg as MixnetExecuteMsg; +use mixnet_contract_common::IdentityKey; +use vesting_contract_common::events::{ + new_vesting_delegation_event, new_vesting_undelegation_event, +}; use super::Account; @@ -34,8 +37,8 @@ impl DelegatingAccount for Account { self.track_delegation(env.block.time, mix_identity, current_balance, coin, storage)?; Ok(Response::new() - .add_attribute("action", "delegate to mixnode on behalf") - .add_message(delegate_to_mixnode)) + .add_message(delegate_to_mixnode) + .add_event(new_vesting_delegation_event())) } fn try_undelegate_from_mixnode( @@ -64,8 +67,8 @@ impl DelegatingAccount for Account { )?; Ok(Response::new() - .add_attribute("action", "undelegate to mixnode on behalf") - .add_message(undelegate_from_mixnode)) + .add_message(undelegate_from_mixnode) + .add_event(new_vesting_undelegation_event())) } fn track_delegation( diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index b4100557b2..ce54d8ab60 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -3,7 +3,10 @@ use crate::errors::ContractError; use crate::traits::GatewayBondingAccount; use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; -use mixnet_contract::{ExecuteMsg as MixnetExecuteMsg, Gateway}; +use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway}; +use vesting_contract_common::events::{ + new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event, +}; use super::Account; @@ -50,8 +53,8 @@ impl GatewayBondingAccount for Account { self.save_gateway_pledge(pledge_data, storage)?; Ok(Response::new() - .add_attribute("action", "bond gateway on behalf") - .add_message(bond_gateway_msg)) + .add_message(bond_gateway_msg) + .add_event(new_vesting_gateway_bonding_event())) } fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result { @@ -63,8 +66,8 @@ impl GatewayBondingAccount for Account { let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?; Ok(Response::new() - .add_attribute("action", "unbond gateway on behalf") - .add_message(unbond_msg)) + .add_message(unbond_msg) + .add_event(new_vesting_gateway_unbonding_event())) } else { Err(ContractError::NoBondFound( self.owner_address().as_str().to_string(), diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index e62f9fea00..ea78f580e1 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -3,7 +3,10 @@ use crate::errors::ContractError; use crate::traits::MixnodeBondingAccount; use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; -use mixnet_contract::{ExecuteMsg as MixnetExecuteMsg, MixNode}; +use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode}; +use vesting_contract_common::events::{ + new_vesting_mixnode_bonding_event, new_vesting_mixnode_unbonding_event, +}; use super::Account; @@ -50,8 +53,8 @@ impl MixnodeBondingAccount for Account { self.save_mixnode_pledge(pledge_data, storage)?; Ok(Response::new() - .add_attribute("action", "bond mixnode on behalf") - .add_message(bond_mixnode_mag)) + .add_message(bond_mixnode_mag) + .add_event(new_vesting_mixnode_bonding_event())) } fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result { @@ -63,8 +66,8 @@ impl MixnodeBondingAccount for Account { let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?; Ok(Response::new() - .add_attribute("action", "unbond mixnode on behalf") - .add_message(unbond_msg)) + .add_message(unbond_msg) + .add_event(new_vesting_mixnode_unbonding_event())) } else { Err(ContractError::NoBondFound( self.owner_address().as_str().to_string(), diff --git a/contracts/vesting/src/vesting/account/mod.rs b/contracts/vesting/src/vesting/account/mod.rs index 4156630147..2ce20122ee 100644 --- a/contracts/vesting/src/vesting/account/mod.rs +++ b/contracts/vesting/src/vesting/account/mod.rs @@ -4,7 +4,7 @@ use crate::errors::ContractError; use crate::storage::{save_account, KEY}; use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128}; use cw_storage_plus::{Item, Map}; -use mixnet_contract::IdentityKey; +use mixnet_contract_common::IdentityKey; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/contracts/vesting/src/vesting/mod.rs b/contracts/vesting/src/vesting/mod.rs index ef4551c11f..b0a0f6e71a 100644 --- a/contracts/vesting/src/vesting/mod.rs +++ b/contracts/vesting/src/vesting/mod.rs @@ -46,7 +46,7 @@ mod tests { use config::defaults::DENOM; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128}; - use mixnet_contract::{Gateway, MixNode}; + use mixnet_contract_common::{Gateway, MixNode}; #[test] fn test_account_creation() { @@ -124,7 +124,7 @@ mod tests { let msg = ExecuteMsg::WithdrawVestedCoins { amount: Coin { amount: Uint128::new(1), - denom: "nym".to_string(), + denom: DENOM.to_string(), }, }; let info = mock_info("new_owner", &[]); diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index 3719d89d7a..24a9357530 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -22,6 +22,6 @@ log = "0.4.0" pretty_env_logger = "0.4.0" thiserror = "1.0.29" -mixnet-contract = { path = "../common/mixnet-contract" } +mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" } network-defaults = { path = "../common/network-defaults" } validator-client = { path = "../common/client-libs/validator-client", features=["nymd-client"] } diff --git a/explorer-api/src/mix_node/http.rs b/explorer-api/src/mix_node/http.rs index a663f594c5..c3888f393e 100644 --- a/explorer-api/src/mix_node/http.rs +++ b/explorer-api/src/mix_node/http.rs @@ -4,7 +4,7 @@ use crate::mix_node::models::{NodeDescription, NodeStats}; use crate::mix_nodes::delegations::{get_mixnode_delegations, get_single_mixnode_delegations}; use crate::state::ExplorerApiStateContext; -use mixnet_contract::Delegation; +use mixnet_contract_common::Delegation; use reqwest::Error as ReqwestError; use rocket::serde::json::Json; use rocket::{Route, State}; diff --git a/explorer-api/src/mix_node/models.rs b/explorer-api/src/mix_node/models.rs index 323946a47f..835bfe3991 100644 --- a/explorer-api/src/mix_node/models.rs +++ b/explorer-api/src/mix_node/models.rs @@ -3,7 +3,7 @@ use crate::mix_node::cache::Cache; use crate::mix_nodes::location::Location; -use mixnet_contract::{Addr, Coin, Layer, MixNode}; +use mixnet_contract_common::{Addr, Coin, Layer, MixNode}; use serde::Deserialize; use serde::Serialize; use std::sync::Arc; diff --git a/explorer-api/src/mix_nodes/delegations.rs b/explorer-api/src/mix_nodes/delegations.rs index 17f15989a7..a6f0ee1c05 100644 --- a/explorer-api/src/mix_nodes/delegations.rs +++ b/explorer-api/src/mix_nodes/delegations.rs @@ -1,7 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use mixnet_contract::Delegation; +use mixnet_contract_common::Delegation; pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec { let client = super::utils::new_nymd_client(); diff --git a/explorer-api/src/mix_nodes/models.rs b/explorer-api/src/mix_nodes/models.rs index 7ccd179f86..7a07131fc1 100644 --- a/explorer-api/src/mix_nodes/models.rs +++ b/explorer-api/src/mix_nodes/models.rs @@ -4,7 +4,7 @@ use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem}; use crate::mix_nodes::MIXNODES_CACHE_ENTRY_TTL; -use mixnet_contract::MixNodeBond; +use mixnet_contract_common::MixNodeBond; use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::{Duration, SystemTime}; diff --git a/explorer-api/src/mix_nodes/tasks.rs b/explorer-api/src/mix_nodes/tasks.rs index d1580a832c..b6257c1660 100644 --- a/explorer-api/src/mix_nodes/tasks.rs +++ b/explorer-api/src/mix_nodes/tasks.rs @@ -3,7 +3,7 @@ use crate::mix_nodes::MIXNODES_CACHE_REFRESH_RATE; use crate::state::ExplorerApiStateContext; -use mixnet_contract::MixNodeBond; +use mixnet_contract_common::MixNodeBond; use reqwest::Url; use std::future::Future; use validator_client::ValidatorClientError; diff --git a/explorer-api/src/ping/http.rs b/explorer-api/src/ping/http.rs index 006938bec6..67a2730bb7 100644 --- a/explorer-api/src/ping/http.rs +++ b/explorer-api/src/ping/http.rs @@ -8,7 +8,7 @@ use rocket_okapi::okapi::openapi3::OpenApi; use rocket_okapi::openapi_get_routes_spec; use rocket_okapi::settings::OpenApiSettings; -use mixnet_contract::MixNodeBond; +use mixnet_contract_common::MixNodeBond; use crate::ping::models::PingResponse; use crate::state::ExplorerApiStateContext; diff --git a/explorer-api/src/state.rs b/explorer-api/src/state.rs index d6be0e3f70..676c4f9423 100644 --- a/explorer-api/src/state.rs +++ b/explorer-api/src/state.rs @@ -5,7 +5,7 @@ use chrono::{DateTime, Utc}; use log::info; use serde::{Deserialize, Serialize}; -use mixnet_contract::MixNodeBond; +use mixnet_contract_common::MixNodeBond; use crate::country_statistics::country_nodes_distribution::{ ConcurrentCountryNodesDistribution, CountryNodesDistribution, diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 1ef0c5f80d..3be5369708 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -164,6 +164,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874f8444adcb4952a8bc51305c8be95c8ec8237bb0d2e78d2e039f771f8828a0" + [[package]] name = "bincode" version = "1.3.3" @@ -175,14 +181,16 @@ dependencies = [ [[package]] name = "bip32" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d0f0fc59c7ba0333eed9dcc1b6980baa7b7a4dc7c6c5885994d0674f7adf34" +checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" dependencies = [ "bs58", - "hkd32", "hmac", - "k256", + "k256 0.10.1", + "once_cell", + "pbkdf2", + "rand_core 0.6.3", "ripemd160", "sha2", "subtle 2.4.1", @@ -292,8 +300,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397" dependencies = [ "digest 0.9.0", - "ff", - "group", + "ff 0.10.1", + "group 0.10.0", "pairing", "rand_core 0.6.3", "subtle 2.4.1", @@ -555,12 +563,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "contracts-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -651,8 +672,9 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" -version = "0.8.0" -source = "git+https://github.com/cosmos/cosmos-rust?rev=e5a1872083abb3d88fa62dda966e7f5408deba58#e5a1872083abb3d88fa62dda966e7f5408deba58" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0254ffee603f5301d6a66963d9e1cc5091479c22e2e925e1f7689c8027a0828" dependencies = [ "prost", "prost-types", @@ -661,15 +683,16 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.3.0" -source = "git+https://github.com/cosmos/cosmos-rust?rev=e5a1872083abb3d88fa62dda966e7f5408deba58#e5a1872083abb3d88fa62dda966e7f5408deba58" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d" dependencies = [ "bip32", "cosmos-sdk-proto", - "ecdsa", + "ecdsa 0.13.4", "eyre", "getrandom 0.2.3", - "k256", + "k256 0.10.1", "prost", "prost-types", "rand_core 0.6.3", @@ -689,7 +712,7 @@ checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a" dependencies = [ "digest 0.9.0", "ed25519-zebra", - "k256", + "k256 0.9.6", "rand_core 0.5.1", "thiserror", ] @@ -833,6 +856,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.4", + "rand_core 0.6.3", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "crypto-mac" version = "0.7.0" @@ -989,7 +1024,16 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" dependencies = [ - "const-oid", + "const-oid 0.6.2", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", ] [[package]] @@ -1147,12 +1191,24 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" dependencies = [ - "der", - "elliptic-curve", + "der 0.4.5", + "elliptic-curve 0.10.6", "hmac", "signature", ] +[[package]] +name = "ecdsa" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +dependencies = [ + "der 0.5.1", + "elliptic-curve 0.11.6", + "rfc6979", + "signature", +] + [[package]] name = "ed25519" version = "1.3.0" @@ -1202,16 +1258,33 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b" dependencies = [ - "crypto-bigint", - "ff", + "crypto-bigint 0.2.11", + "ff 0.10.1", "generic-array 0.14.4", - "group", - "pkcs8", + "group 0.10.0", + "pkcs8 0.7.6", "rand_core 0.6.3", "subtle 2.4.1", "zeroize", ] +[[package]] +name = "elliptic-curve" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decb3a27ea454a5f23f96eb182af0671c12694d64ecc33dada74edd1301f6cfc" +dependencies = [ + "crypto-bigint 0.3.2", + "der 0.5.1", + "ff 0.11.0", + "generic-array 0.14.4", + "group 0.11.0", + "rand_core 0.6.3", + "sec1", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "embed_plist" version = "1.2.0" @@ -1274,6 +1347,16 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" +dependencies = [ + "rand_core 0.6.3", + "subtle 2.4.1", +] + [[package]] name = "field-offset" version = "0.3.4" @@ -1775,7 +1858,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" dependencies = [ "byteorder", - "ff", + "ff 0.10.1", + "rand_core 0.6.3", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +dependencies = [ + "ff 0.11.0", "rand_core 0.6.3", "subtle 2.4.1", ] @@ -1936,20 +2030,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" -[[package]] -name = "hkd32" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f2a5541afe0725f0b95619d6af614f48c1b176385b8aa30918cfb8c4bfafc8" -dependencies = [ - "hmac", - "once_cell", - "pbkdf2", - "rand_core 0.6.3", - "sha2", - "zeroize", -] - [[package]] name = "hkdf" version = "0.11.0" @@ -2291,8 +2371,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.12.4", + "elliptic-curve 0.10.6", + "sha2", +] + +[[package]] +name = "k256" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7511aa19fa182a8a4885760c4e5675b17173b02ae86ec5d376d34f5278c874b9" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.13.4", + "elliptic-curve 0.11.6", + "sec1", "sha2", "sha3", ] @@ -2503,10 +2596,11 @@ dependencies = [ ] [[package]] -name = "mixnet-contract" +name = "mixnet-contract-common" version = "0.1.0" dependencies = [ "az", + "contracts-common", "cosmwasm-std", "fixed", "log", @@ -2711,7 +2805,7 @@ dependencies = [ "credentials", "dirs", "eyre", - "mixnet-contract", + "mixnet-contract-common", "rand 0.6.5", "serde", "serde_json", @@ -2732,9 +2826,9 @@ dependencies = [ "bls12_381", "bs58", "digest 0.9.0", - "ff", + "ff 0.10.1", "getrandom 0.2.3", - "group", + "group 0.10.0", "itertools", "rand 0.8.4", "serde", @@ -2855,7 +2949,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3" dependencies = [ - "group", + "group 0.10.0", ] [[package]] @@ -2928,9 +3022,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ "crypto-mac 0.11.1", ] @@ -3165,8 +3259,19 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" dependencies = [ - "der", - "spki", + "der 0.4.5", + "spki 0.4.1", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", + "spki 0.5.4", + "zeroize", ] [[package]] @@ -3683,6 +3788,17 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint 0.3.2", + "hmac", + "zeroize", +] + [[package]] name = "rfd" version = "0.4.4" @@ -3852,6 +3968,19 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der 0.5.1", + "generic-array 0.14.4", + "pkcs8 0.8.0", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "security-framework" version = "2.4.2" @@ -4180,7 +4309,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" dependencies = [ - "der", + "der 0.4.5", +] + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der 0.5.1", ] [[package]] @@ -4707,9 +4846,9 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9015fdeab074f9b8f97dcb89c2bb2ec8537c89423e95551e8d7ecdfbab58a329" +checksum = "a9ef686b8ecd36550d0581f0989c9d8607090b23005df479d8e6ba348b5800b9" dependencies = [ "async-trait", "bytes", @@ -4717,7 +4856,7 @@ dependencies = [ "ed25519-dalek", "flex-error", "futures", - "k256", + "k256 0.10.1", "num-traits", "once_cell", "prost", @@ -4738,9 +4877,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b2e6d4442bab49319dbacdfd79c5929bc6e0b35d1e0d959ff5b79fddf3f018" +checksum = "ecc127f82e7a8c7337c1f293d65a821380d3407c4c44bc979ef4da0ebc3b31ed" dependencies = [ "flex-error", "serde", @@ -4752,9 +4891,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da86f6e52ced9c2f24c4ae57662ce8a44dd90ee7bc47bae27a710b02d48e193c" +checksum = "a9e0a0251fd81bed7420bea0f4d91c2a58f6c9fa34d5b2f70bed0ba8890de5aa" dependencies = [ "bytes", "flex-error", @@ -4770,15 +4909,15 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f5f4875c36798e5590894a5176cf8d75e4bc81ec34ced1b9a87609e7d56861" +checksum = "626c493e9ced3a9de37583bbd929f4b6dbd49aa513ab9b4776aa44a9332ce9b5" dependencies = [ "async-trait", "bytes", "flex-error", "futures", - "getrandom 0.1.16", + "getrandom 0.2.3", "http", "hyper", "hyper-proxy", @@ -5130,6 +5269,14 @@ dependencies = [ "getrandom 0.2.3", ] +[[package]] +name = "validator-api-requests" +version = "0.1.0" +dependencies = [ + "serde", + "ts-rs", +] + [[package]] name = "validator-client" version = "0.1.0" @@ -5144,7 +5291,7 @@ dependencies = [ "flate2", "itertools", "log", - "mixnet-contract", + "mixnet-contract-common", "network-defaults", "prost", "reqwest", @@ -5154,6 +5301,7 @@ dependencies = [ "thiserror", "ts-rs", "url", + "validator-api-requests", "vesting-contract", ] @@ -5188,10 +5336,18 @@ dependencies = [ "config", "cosmwasm-std", "cw-storage-plus", - "mixnet-contract", + "mixnet-contract-common", "schemars", "serde", "thiserror", + "vesting-contract-common", +] + +[[package]] +name = "vesting-contract-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-std", ] [[package]] diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 7d122b7f27..457444a2c1 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -29,13 +29,13 @@ rand = "0.6.5" eyre = "0.6.5" -cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev="e5a1872083abb3d88fa62dda966e7f5408deba58", features = ["rpc", "bip32", "cosmwasm"] } +cosmrs = { version = "0.4.1", features = ["rpc", "bip32", "cosmwasm"] } cosmwasm-std = "1.0.0-beta3" validator-client = { path = "../../common/client-libs/validator-client", features = [ "nymd-client", ] } -mixnet-contract = { path = "../../common/mixnet-contract" } +mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } config = { path = "../../common/config" } coconut-interface = { path = "../../common/coconut-interface" } credentials = { path = "../../common/credentials" } @@ -43,13 +43,13 @@ credentials = { path = "../../common/credentials" } [dev-dependencies] ts-rs = "5.1" -[dev-dependencies.mixnet-contract] -path = "../../common/mixnet-contract" +[dev-dependencies.mixnet-contract-common] +path = "../../common/cosmwasm-smart-contracts/mixnet-contract" features = ["ts-rs"] [dev-dependencies.validator-client] path = "../../common/client-libs/validator-client" -features = ["ts-rs"] +features = ["typescript-types"] [features] default = ["custom-protocol"] diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 605cc78047..a99090ba56 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -1,6 +1,7 @@ use serde::{Serialize, Serializer}; use thiserror::Error; use validator_client::nymd::error::NymdError; +use validator_client::validator_api::error::ValidatorAPIError; #[derive(Error, Debug)] pub enum BackendError { @@ -29,6 +30,11 @@ pub enum BackendError { #[from] source: eyre::Report, }, + #[error("{source}")] + ValidatorApiError { + #[from] + source: ValidatorAPIError, + }, #[error("Client has not been initialized yet, connect with mnemonic to initialize")] ClientNotInitialized, #[error("No balance available for address {0}")] diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index c0c89b1a21..d2e873a297 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -3,7 +3,7 @@ windows_subsystem = "windows" )] -use mixnet_contract::{Gateway, MixNode}; +use mixnet_contract_common::{Gateway, MixNode}; use std::sync::Arc; use tauri::Menu; use tokio::sync::RwLock; @@ -19,6 +19,7 @@ mod utils; use crate::menu::AddDefaultSubmenus; use crate::operations::mixnet; +use crate::operations::validator_api; use crate::operations::vesting; use crate::state::State; @@ -63,6 +64,11 @@ fn main() { vesting::queries::original_vesting, vesting::queries::delegated_free, vesting::queries::delegated_vesting, + validator_api::status::mixnode_core_node_status, + validator_api::status::gateway_core_node_status, + validator_api::status::mixnode_status, + validator_api::status::mixnode_reward_estimation, + validator_api::status::mixnode_stake_saturation, ]) .menu(Menu::new().add_default_app_submenu_if_macos()) .run(tauri::generate_context!()) @@ -72,16 +78,21 @@ fn main() { #[cfg(test)] mod test { ts_rs::export! { - mixnet_contract::MixNode => "../src/types/rust/mixnode.ts", + mixnet_contract_common::MixNode => "../src/types/rust/mixnode.ts", crate::coin::Coin => "../src/types/rust/coin.ts", crate::mixnet::account::Balance => "../src/types/rust/balance.ts", - mixnet_contract::Gateway => "../src/types/rust/gateway.ts", + mixnet_contract_common::Gateway => "../src/types/rust/gateway.ts", crate::mixnet::send::TauriTxResult => "../src/types/rust/tauritxresult.ts", crate::mixnet::send::TransactionDetails => "../src/types/rust/transactiondetails.ts", validator_client::nymd::fee::helpers::Operation => "../src/types/rust/operation.ts", crate::coin::Denom => "../src/types/rust/denom.ts", crate::utils::DelegationResult => "../src/types/rust/delegationresult.ts", crate::mixnet::account::Account => "../src/types/rust/account.ts", - crate::mixnet::admin::TauriContractStateParams => "../src/types/rust/stateparams.ts" + crate::mixnet::admin::TauriContractStateParams => "../src/types/rust/stateparams.ts", + validator_client::models::CoreNodeStatusResponse => "../src/types/corenodestatusresponse.ts", + validator_client::models::MixnodeStatus => "../src/types/rust/mixnodestatus.ts", + validator_client::models::MixnodeStatusResponse => "../src/types/rust/mixnodestatusresponse.ts", + validator_client::models::RewardEstimationResponse => "../src/types/rust/rewardestimationresponse.ts", + validator_client::models::StakeSaturationResponse => "../src/types/rust/stakesaturaionresponse.ts", } } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs index 4413ea1efc..14bf378378 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs @@ -2,7 +2,7 @@ use crate::error::BackendError; use crate::nymd_client; use crate::state::State; use cosmwasm_std::Uint128; -use mixnet_contract::ContractStateParams; +use mixnet_contract_common::ContractStateParams; use serde::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; use std::sync::Arc; diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index c7da0fa4d5..9a2cf0cb30 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -3,7 +3,7 @@ use crate::error::BackendError; use crate::nymd_client; use crate::state::State; use crate::{Gateway, MixNode}; -use mixnet_contract::{GatewayBond, MixNodeBond}; +use mixnet_contract_common::{GatewayBond, MixNodeBond}; use std::convert::TryInto; use std::sync::Arc; use tokio::sync::RwLock; diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 842ed3fb59..dbf56cc0ad 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -4,7 +4,7 @@ use crate::nymd_client; use crate::state::State; use crate::utils::DelegationResult; use cosmwasm_std::Coin as CosmWasmCoin; -use mixnet_contract::PagedDelegatorDelegationsResponse; +use mixnet_contract_common::PagedDelegatorDelegationsResponse; use std::convert::TryInto; use std::sync::Arc; use tokio::sync::RwLock; diff --git a/nym-wallet/src-tauri/src/operations/mod.rs b/nym-wallet/src-tauri/src/operations/mod.rs index 9bfd654e73..c49e27d4d2 100644 --- a/nym-wallet/src-tauri/src/operations/mod.rs +++ b/nym-wallet/src-tauri/src/operations/mod.rs @@ -1,2 +1,3 @@ pub mod mixnet; +pub mod validator_api; pub mod vesting; diff --git a/nym-wallet/src-tauri/src/operations/validator_api/mod.rs b/nym-wallet/src-tauri/src/operations/validator_api/mod.rs new file mode 100644 index 0000000000..3529bb0b4a --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/validator_api/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod status; diff --git a/nym-wallet/src-tauri/src/operations/validator_api/status.rs b/nym-wallet/src-tauri/src/operations/validator_api/status.rs new file mode 100644 index 0000000000..cb2fd9d17a --- /dev/null +++ b/nym-wallet/src-tauri/src/operations/validator_api/status.rs @@ -0,0 +1,69 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::api_client; +use crate::error::BackendError; +use crate::state::State; +use std::sync::Arc; +use tokio::sync::RwLock; +use validator_client::models::{ + CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, +}; + +#[tauri::command] +pub async fn mixnode_core_node_status( + identity: &str, + since: Option, + state: tauri::State<'_, Arc>>, +) -> Result { + Ok( + api_client!(state) + .get_mixnode_core_status_count(identity, since) + .await?, + ) +} + +#[tauri::command] +pub async fn gateway_core_node_status( + identity: &str, + since: Option, + state: tauri::State<'_, Arc>>, +) -> Result { + Ok( + api_client!(state) + .get_gateway_core_status_count(identity, since) + .await?, + ) +} + +#[tauri::command] +pub async fn mixnode_status( + identity: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + Ok(api_client!(state).get_mixnode_status(identity).await?) +} + +#[tauri::command] +pub async fn mixnode_reward_estimation( + identity: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + Ok( + api_client!(state) + .get_mixnode_reward_estimation(identity) + .await?, + ) +} + +#[tauri::command] +pub async fn mixnode_stake_saturation( + identity: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + Ok( + api_client!(state) + .get_mixnode_stake_saturation(identity) + .await?, + ) +} diff --git a/nym-wallet/src/types/corenodestatusresponse.ts b/nym-wallet/src/types/corenodestatusresponse.ts new file mode 100644 index 0000000000..2a743b37a9 --- /dev/null +++ b/nym-wallet/src/types/corenodestatusresponse.ts @@ -0,0 +1,4 @@ +export interface CoreNodeStatusResponse { + identity: string; + count: number; +} \ No newline at end of file diff --git a/nym-wallet/src/types/rust/mixnodestatus.ts b/nym-wallet/src/types/rust/mixnodestatus.ts new file mode 100644 index 0000000000..75849a8ce9 --- /dev/null +++ b/nym-wallet/src/types/rust/mixnodestatus.ts @@ -0,0 +1 @@ +export type MixnodeStatus = "Active" | "Standby" | "Inactive" | "NotFound"; \ No newline at end of file diff --git a/nym-wallet/src/types/rust/mixnodestatusresponse.ts b/nym-wallet/src/types/rust/mixnodestatusresponse.ts new file mode 100644 index 0000000000..6015807b4f --- /dev/null +++ b/nym-wallet/src/types/rust/mixnodestatusresponse.ts @@ -0,0 +1,5 @@ +import { MixnodeStatus } from "./mixnodestatus"; + +export interface MixnodeStatusResponse { + status: MixnodeStatus; +} \ No newline at end of file diff --git a/nym-wallet/src/types/rust/rewardestimationresponse.ts b/nym-wallet/src/types/rust/rewardestimationresponse.ts new file mode 100644 index 0000000000..973641f97c --- /dev/null +++ b/nym-wallet/src/types/rust/rewardestimationresponse.ts @@ -0,0 +1,9 @@ +export interface RewardEstimationResponse { + estimated_total_node_reward: bigint; + estimated_operator_reward: bigint; + estimated_delegators_reward: bigint; + current_epoch_start: bigint; + current_epoch_end: bigint; + current_epoch_uptime: number; + as_at: bigint; +} \ No newline at end of file diff --git a/nym-wallet/src/types/rust/stakesaturaionresponse.ts b/nym-wallet/src/types/rust/stakesaturaionresponse.ts new file mode 100644 index 0000000000..78044fb241 --- /dev/null +++ b/nym-wallet/src/types/rust/stakesaturaionresponse.ts @@ -0,0 +1,4 @@ +export interface StakeSaturationResponse { + saturation: number; + as_at: bigint; +} \ No newline at end of file diff --git a/validator-api/Cargo.toml b/validator-api/Cargo.toml index 5f52d05c1d..2e12d321ab 100644 --- a/validator-api/Cargo.toml +++ b/validator-api/Cargo.toml @@ -39,7 +39,7 @@ time = { version = "0.3", features = ["serde-human-readable", "parsing"]} anyhow = "1" getset = "0.1.1" -rocket_sync_db_pools = {version = "0.1.0-rc.1", default-features = false} +rocket_sync_db_pools = { version = "0.1.0-rc.1", default-features = false } sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]} @@ -47,9 +47,10 @@ sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros" config = { path = "../common/config" } crypto = { path="../common/crypto" } gateway-client = { path="../common/client-libs/gateway-client" } -mixnet-contract = { path="../common/mixnet-contract" } +mixnet-contract-common = { path= "../common/cosmwasm-smart-contracts/mixnet-contract" } nymsphinx = { path="../common/nymsphinx" } topology = { path="../common/topology" } +validator-api-requests = { path = "validator-api-requests" } validator-client = { path="../common/client-libs/validator-client", features = ["nymd-client"] } version-checker = { path="../common/version-checker" } coconut-interface = { path = "../common/coconut-interface", optional = true } diff --git a/validator-api/src/cache/mod.rs b/validator-api/src/cache/mod.rs index b13dd53ae2..865af46966 100644 --- a/validator-api/src/cache/mod.rs +++ b/validator-api/src/cache/mod.rs @@ -2,10 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nymd_client::Client; +use crate::rewarding::EpochRewardParams; +use ::time::OffsetDateTime; use anyhow::Result; use config::defaults::VALIDATOR_API_VERSION; -use mixnet_contract::{ - ContractStateParams, GatewayBond, IdentityKey, MixNodeBond, RewardingIntervalResponse, +use mixnet_contract_common::{ + ContractStateParams, GatewayBond, IdentityKey, IdentityKeyRef, MixNodeBond, + RewardingIntervalResponse, }; use rand::prelude::SliceRandom; use rand_chacha::rand_core::SeedableRng; @@ -14,23 +17,15 @@ use rocket::fairing::AdHoc; use serde::Serialize; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; use std::sync::Arc; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::Duration; use tokio::sync::RwLock; use tokio::time; +use validator_api_requests::models::MixnodeStatus; use validator_client::nymd::hash::SHA256_HASH_SIZE; use validator_client::nymd::CosmWasmClient; pub(crate) mod routes; -#[derive(Clone, Copy, Debug, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum MixnodeStatus { - Active, // in both the active set and the rewarded set - Standby, // only in the rewarded set - Inactive, // in neither the rewarded set nor the active set, but is bonded - NotFound, // doesn't even exist in the bonded set -} - pub struct ValidatorCacheRefresher { nymd_client: Client, cache: ValidatorCache, @@ -53,28 +48,40 @@ struct ValidatorCacheInner { current_mixnode_rewarded_set_size: AtomicU32, current_mixnode_active_set_size: AtomicU32, + + current_reward_params: RwLock>, +} + +fn current_unix_timestamp() -> i64 { + let now = OffsetDateTime::now_utc(); + now.unix_timestamp() } #[derive(Default, Serialize, Clone)] pub struct Cache { value: T, - as_at: u64, + as_at: i64, } impl Cache { + fn new(value: T) -> Self { + Cache { + value, + as_at: current_unix_timestamp(), + } + } + fn set(&mut self, value: T) { self.value = value; - self.as_at = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() + self.as_at = current_unix_timestamp() } fn renew(&mut self) { - self.as_at = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() + self.as_at = current_unix_timestamp() + } + + pub fn timestamp(&self) -> i64 { + self.as_at } pub fn into_inner(self) -> T { @@ -113,6 +120,8 @@ impl ValidatorCacheRefresher { ) .await?; + let epoch_rewarding_params = self.nymd_client.get_current_epoch_reward_params().await?; + info!( "Updating validator cache. There are {} mixnodes and {} gateways", mixnodes.len(), @@ -126,6 +135,7 @@ impl ValidatorCacheRefresher { contract_settings, current_rewarding_interval, rewarding_block_hash, + epoch_rewarding_params, ) .await; @@ -168,7 +178,6 @@ impl ValidatorCache { routes::get_gateways, routes::get_active_mixnodes, routes::get_rewarded_mixnodes, - routes::get_mixnode_status, ], ) }) @@ -237,6 +246,7 @@ impl ValidatorCache { state: ContractStateParams, rewarding_interval: RewardingIntervalResponse, rewarding_block_hash: Option<[u8; SHA256_HASH_SIZE]>, + epoch_rewarding_params: EpochRewardParams, ) { // if the rewarding is currently in progress, don't mess with the rewarded/active sets // as most likely will be changed next time this function is called @@ -282,6 +292,11 @@ impl ValidatorCache { self.inner.mixnodes.write().await.set(mixnodes); self.inner.gateways.write().await.set(gateways); + self.inner + .current_reward_params + .write() + .await + .set(epoch_rewarding_params); } pub async fn mixnodes(&self) -> Cache> { @@ -317,7 +332,14 @@ impl ValidatorCache { } } - pub async fn mixnode_status(&self, identity: IdentityKey) -> MixnodeStatus { + pub(crate) async fn epoch_reward_params(&self) -> Cache { + self.inner.current_reward_params.read().await.clone() + } + + pub async fn mixnode_details( + &self, + identity: IdentityKeyRef<'_>, + ) -> (Option, MixnodeStatus) { // it might not be the most optimal to possibly iterate the entire vector to find (or not) // the relevant value. However, the vectors are relatively small (< 10_000 elements) and // the implementation for active/rewarded sets might change soon so there's no point in premature optimisation @@ -329,35 +351,39 @@ impl ValidatorCache { .load(Ordering::SeqCst) as usize; // see if node is in the top active_set_size of rewarded nodes, i.e. it's active - if rewarded_mixnodes + if let Some(bond) = rewarded_mixnodes .iter() .take(active_set_size) - .any(|mix| mix.mix_node.identity_key == identity) + .find(|mix| mix.mix_node.identity_key == identity) { - MixnodeStatus::Active + (Some(bond.clone()), MixnodeStatus::Active) // see if it's in the bottom part of the rewarded set, i.e. it's in standby - } else if rewarded_mixnodes + } else if let Some(bond) = rewarded_mixnodes .iter() .skip(active_set_size) - .any(|mix| mix.mix_node.identity_key == identity) + .find(|mix| mix.mix_node.identity_key == identity) { - MixnodeStatus::Standby + (Some(bond.clone()), MixnodeStatus::Standby) // if it's not in the rewarded set see if its bonded at all - } else if self + } else if let Some(bond) = self .inner .mixnodes .read() .await .value .iter() - .any(|mix| mix.mix_node.identity_key == identity) + .find(|mix| mix.mix_node.identity_key == identity) { - MixnodeStatus::Inactive + (Some(bond.clone()), MixnodeStatus::Inactive) } else { - MixnodeStatus::NotFound + (None, MixnodeStatus::NotFound) } } + pub async fn mixnode_status(&self, identity: IdentityKey) -> MixnodeStatus { + self.mixnode_details(&identity).await.1 + } + pub fn initialised(&self) -> bool { self.inner.initialised.load(Ordering::Relaxed) } @@ -385,6 +411,7 @@ impl ValidatorCacheInner { rewarded_mixnodes: RwLock::new(Cache::default()), current_mixnode_rewarded_set_size: Default::default(), current_mixnode_active_set_size: Default::default(), + current_reward_params: RwLock::new(Cache::new(EpochRewardParams::new_empty())), } } } diff --git a/validator-api/src/cache/routes.rs b/validator-api/src/cache/routes.rs index 41ebc3c2d9..be902cd311 100644 --- a/validator-api/src/cache/routes.rs +++ b/validator-api/src/cache/routes.rs @@ -1,11 +1,10 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::cache::{MixnodeStatus, ValidatorCache}; -use mixnet_contract::{GatewayBond, MixNodeBond}; +use crate::cache::ValidatorCache; +use mixnet_contract_common::{GatewayBond, MixNodeBond}; use rocket::serde::json::Json; use rocket::State; -use serde::Serialize; #[get("/mixnodes")] pub(crate) async fn get_mixnodes(cache: &State) -> Json> { @@ -26,18 +25,3 @@ pub(crate) async fn get_rewarded_mixnodes(cache: &State) -> Json pub(crate) async fn get_active_mixnodes(cache: &State) -> Json> { Json(cache.active_mixnodes().await.value) } - -#[derive(Serialize)] -pub(crate) struct MixnodeStatusResponse { - status: MixnodeStatus, -} - -#[get("/mixnode//status")] -pub(crate) async fn get_mixnode_status( - cache: &State, - identity: String, -) -> Json { - Json(MixnodeStatusResponse { - status: cache.mixnode_status(identity).await, - }) -} diff --git a/validator-api/src/coconut/mod.rs b/validator-api/src/coconut/mod.rs index ace0c62851..cd50c7cdf8 100644 --- a/validator-api/src/coconut/mod.rs +++ b/validator-api/src/coconut/mod.rs @@ -62,7 +62,7 @@ fn blind_sign(request: InternalSignRequest, key_pair: &KeyPair) -> BlindedSignat .unwrap() } -#[post("/blind_sign", data = "")] +#[post("/blind-sign", data = "")] // Until we have serialization and deserialization traits we'll be using a crutch pub async fn post_blind_sign( blind_sign_request_body: Json, @@ -79,7 +79,7 @@ pub async fn post_blind_sign( Json(BlindedSignatureResponse::new(blinded_signature)) } -#[get("/verification_key")] +#[get("/verification-key")] pub async fn get_verification_key(key_pair: &State) -> Json { Json(VerificationKeyResponse::new(key_pair.verification_key())) } diff --git a/validator-api/src/main.rs b/validator-api/src/main.rs index a74ef82f28..589b06d25f 100644 --- a/validator-api/src/main.rs +++ b/validator-api/src/main.rs @@ -399,6 +399,7 @@ fn expected_monitor_test_runs(config: &Config) -> usize { fn setup_rewarder( config: &Config, + first_epoch: Epoch, rocket: &Rocket, nymd_client: &Client, ) -> Option { @@ -407,10 +408,6 @@ fn setup_rewarder( let node_status_storage = rocket.state::().unwrap().clone(); let validator_cache = rocket.state::().unwrap().clone(); - let first_epoch = Epoch::new( - config.get_first_rewarding_epoch(), - config.get_epoch_length(), - ); Some(Rewarder::new( nymd_client.clone(), validator_cache, @@ -427,11 +424,16 @@ fn setup_rewarder( } } -async fn setup_rocket(config: &Config, liftoff_notify: Arc) -> Result> { +async fn setup_rocket( + config: &Config, + first_epoch: Epoch, + liftoff_notify: Arc, +) -> Result> { // let's build our rocket! let rocket = rocket::build() .attach(setup_cors()?) .attach(setup_liftoff_notify(liftoff_notify)) + .manage(first_epoch) .attach(ValidatorCache::stage()); #[cfg(feature = "coconut")] @@ -440,13 +442,17 @@ async fn setup_rocket(config: &Config, liftoff_notify: Arc) -> Result) -> Result<()> { .map_err(|err| err.into()); } + let first_epoch = Epoch::new( + config.get_first_rewarding_epoch(), + config.get_epoch_length(), + ); + let liftoff_notify = Arc::new(Notify::new()); // let's build our rocket! - let rocket = setup_rocket(&config, Arc::clone(&liftoff_notify)).await?; + let rocket = setup_rocket(&config, first_epoch, Arc::clone(&liftoff_notify)).await?; let monitor_builder = setup_network_monitor(&config, system_version, &rocket); let validator_cache = rocket.state::().unwrap().clone(); @@ -513,7 +524,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> { let uptime_updater = HistoricalUptimeUpdater::new(storage); tokio::spawn(async move { uptime_updater.run().await }); - if let Some(rewarder) = setup_rewarder(&config, &rocket, &nymd_client) { + if let Some(rewarder) = setup_rewarder(&config, first_epoch, &rocket, &nymd_client) { info!("Periodic rewarding is starting..."); tokio::spawn(async move { rewarder.run().await }); } else { diff --git a/validator-api/src/network_monitor/monitor/preparer.rs b/validator-api/src/network_monitor/monitor/preparer.rs index 160e5e8eda..6fed3f643a 100644 --- a/validator-api/src/network_monitor/monitor/preparer.rs +++ b/validator-api/src/network_monitor/monitor/preparer.rs @@ -8,7 +8,7 @@ use crate::network_monitor::test_packet::{NodeType, TestPacket}; use crate::network_monitor::test_route::TestRoute; use crypto::asymmetric::{encryption, identity}; use log::info; -use mixnet_contract::{Addr, GatewayBond, Layer, MixNodeBond}; +use mixnet_contract_common::{Addr, GatewayBond, Layer, MixNodeBond}; use nymsphinx::addressing::clients::Recipient; use nymsphinx::forwarding::packet::MixPacket; use rand::seq::SliceRandom; diff --git a/validator-api/src/node_status_api/mod.rs b/validator-api/src/node_status_api/mod.rs index 3316aec6f4..697fca4403 100644 --- a/validator-api/src/node_status_api/mod.rs +++ b/validator-api/src/node_status_api/mod.rs @@ -1,9 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::storage; use rocket::fairing::AdHoc; -use std::path::PathBuf; use std::time::Duration; pub(crate) mod local_guard; @@ -16,20 +14,35 @@ pub(crate) const FIFTEEN_MINUTES: Duration = Duration::from_secs(900); pub(crate) const ONE_HOUR: Duration = Duration::from_secs(3600); pub(crate) const ONE_DAY: Duration = Duration::from_secs(86400); -pub(crate) fn stage(database_path: PathBuf) -> AdHoc { - AdHoc::on_ignite("SQLx Stage", |rocket| async { - rocket - .attach(storage::ValidatorApiStorage::stage(database_path)) - .mount( - "/v1/status", - routes![ - routes::mixnode_report, - routes::gateway_report, - routes::mixnode_uptime_history, - routes::gateway_uptime_history, - routes::mixnode_core_status_count, - routes::gateway_core_status_count, - ], - ) +pub(crate) fn stage_full() -> AdHoc { + AdHoc::on_ignite("Node Status API Stage", |rocket| async { + rocket.mount( + "/v1/status", + routes![ + routes::mixnode_report, + routes::gateway_report, + routes::mixnode_uptime_history, + routes::gateway_uptime_history, + routes::mixnode_core_status_count, + routes::gateway_core_status_count, + routes::get_mixnode_status, + routes::get_mixnode_reward_estimation, + routes::get_mixnode_stake_saturation, + ], + ) + }) +} + +// in the minimal variant we would not have access to endpoints relying on existence +// of the network monitor and the associated storage +pub(crate) fn stage_minimal() -> AdHoc { + AdHoc::on_ignite("Node Status API Stage", |rocket| async { + rocket.mount( + "/v1/status", + routes![ + routes::get_mixnode_status, + routes::get_mixnode_stake_saturation, + ], + ) }) } diff --git a/validator-api/src/node_status_api/models.rs b/validator-api/src/node_status_api/models.rs index a3fbe6ee63..4ad57950dd 100644 --- a/validator-api/src/node_status_api/models.rs +++ b/validator-api/src/node_status_api/models.rs @@ -208,22 +208,24 @@ pub struct HistoricalUptime { } pub(crate) struct ErrorResponse { - error: ValidatorApiStorageError, + error_message: String, status: Status, } impl ErrorResponse { - pub(crate) fn new(error: ValidatorApiStorageError, status: Status) -> Self { - ErrorResponse { error, status } + pub(crate) fn new(error_message: impl Into, status: Status) -> Self { + ErrorResponse { + error_message: error_message.into(), + status, + } } } impl<'r, 'o: 'r> Responder<'r, 'o> for ErrorResponse { fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> { - let message = format!("{}", self.error); Response::build() .header(ContentType::Plain) - .sized_body(message.len(), Cursor::new(message)) + .sized_body(self.error_message.len(), Cursor::new(self.error_message)) .status(self.status) .ok() } @@ -269,9 +271,3 @@ impl Display for ValidatorApiStorageError { } } } - -#[derive(Serialize)] -pub struct CoreNodeStatus { - pub(crate) identity: String, - pub(crate) count: i32, -} diff --git a/validator-api/src/node_status_api/routes.rs b/validator-api/src/node_status_api/routes.rs index 7b732a5977..b4a9530566 100644 --- a/validator-api/src/node_status_api/routes.rs +++ b/validator-api/src/node_status_api/routes.rs @@ -2,92 +2,179 @@ // SPDX-License-Identifier: Apache-2.0 use crate::node_status_api::models::{ - CoreNodeStatus, ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport, + ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport, MixnodeUptimeHistory, }; use crate::storage::ValidatorApiStorage; +use crate::{Epoch, ValidatorCache}; use rocket::http::Status; use rocket::serde::json::Json; use rocket::State; +use time::OffsetDateTime; +use validator_api_requests::models::{ + CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, + StakeSaturationResponse, +}; -#[get("/mixnode//report")] +#[get("/mixnode//report")] pub(crate) async fn mixnode_report( storage: &State, - pubkey: &str, + identity: &str, ) -> Result, ErrorResponse> { storage - .construct_mixnode_report(pubkey) + .construct_mixnode_report(identity) .await .map(Json) - .map_err(|err| ErrorResponse::new(err, Status::NotFound)) + .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } -#[get("/gateway//report")] +#[get("/gateway//report")] pub(crate) async fn gateway_report( storage: &State, - pubkey: &str, + identity: &str, ) -> Result, ErrorResponse> { storage - .construct_gateway_report(pubkey) + .construct_gateway_report(identity) .await .map(Json) - .map_err(|err| ErrorResponse::new(err, Status::NotFound)) + .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } -#[get("/mixnode//history")] +#[get("/mixnode//history")] pub(crate) async fn mixnode_uptime_history( storage: &State, - pubkey: &str, + identity: &str, ) -> Result, ErrorResponse> { storage - .get_mixnode_uptime_history(pubkey) + .get_mixnode_uptime_history(identity) .await .map(Json) - .map_err(|err| ErrorResponse::new(err, Status::NotFound)) + .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } -#[get("/gateway//history")] +#[get("/gateway//history")] pub(crate) async fn gateway_uptime_history( storage: &State, - pubkey: &str, + identity: &str, ) -> Result, ErrorResponse> { storage - .get_gateway_uptime_history(pubkey) + .get_gateway_uptime_history(identity) .await .map(Json) - .map_err(|err| ErrorResponse::new(err, Status::NotFound)) + .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } -#[get("/mixnode//core-status-count?")] +#[get("/mixnode//core-status-count?")] pub(crate) async fn mixnode_core_status_count( storage: &State, - pubkey: &str, + identity: &str, since: Option, -) -> Json { +) -> Json { let count = storage - .get_core_mixnode_status_count(pubkey, since) + .get_core_mixnode_status_count(identity, since) .await .unwrap_or_default(); - Json(CoreNodeStatus { - identity: pubkey.to_string(), + Json(CoreNodeStatusResponse { + identity: identity.to_string(), count, }) } -#[get("/gateway//core-status-count?")] +#[get("/gateway//core-status-count?")] pub(crate) async fn gateway_core_status_count( storage: &State, - pubkey: &str, + identity: &str, since: Option, -) -> Json { +) -> Json { let count = storage - .get_core_gateway_status_count(pubkey, since) + .get_core_gateway_status_count(identity, since) .await .unwrap_or_default(); - Json(CoreNodeStatus { - identity: pubkey.to_string(), + Json(CoreNodeStatusResponse { + identity: identity.to_string(), count, }) } + +#[get("/mixnode//status")] +pub(crate) async fn get_mixnode_status( + cache: &State, + identity: String, +) -> Json { + Json(MixnodeStatusResponse { + status: cache.mixnode_status(identity).await, + }) +} + +#[get("/mixnode//reward-estimation")] +pub(crate) async fn get_mixnode_reward_estimation( + cache: &State, + storage: &State, + first_epoch: &State, + identity: String, +) -> Result, ErrorResponse> { + let (bond, status) = cache.mixnode_details(&identity).await; + if let Some(bond) = bond { + let epoch_reward_params = cache.epoch_reward_params().await; + let as_at = epoch_reward_params.timestamp(); + let epoch_reward_params = epoch_reward_params.into_inner(); + + let current_epoch = first_epoch.current(OffsetDateTime::now_utc()); + let uptime = storage + .get_average_mixnode_uptime_in_interval( + &identity, + current_epoch.start_unix_timestamp(), + current_epoch.end_unix_timestamp(), + ) + .await + .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))?; + + let (estimated_total_node_reward, estimated_operator_reward, estimated_delegators_reward) = + epoch_reward_params.estimate_reward(&bond, uptime.u8(), status.is_active()); + + Ok(Json(RewardEstimationResponse { + estimated_total_node_reward, + estimated_operator_reward, + estimated_delegators_reward, + current_epoch_start: current_epoch.start_unix_timestamp(), + current_epoch_end: current_epoch.end_unix_timestamp(), + current_epoch_uptime: uptime.u8(), + as_at, + })) + } else { + Err(ErrorResponse::new( + "mixnode bond not found", + Status::NotFound, + )) + } +} + +#[get("/mixnode//stake-saturation")] +pub(crate) async fn get_mixnode_stake_saturation( + cache: &State, + identity: String, +) -> Result, ErrorResponse> { + let (bond, _) = cache.mixnode_details(&identity).await; + if let Some(bond) = bond { + let epoch_reward_params = cache.epoch_reward_params().await; + let as_at = epoch_reward_params.timestamp(); + let epoch_reward_params = epoch_reward_params.into_inner(); + + let saturation = bond.stake_saturation( + epoch_reward_params.circulating_supply, + epoch_reward_params.rewarded_set_size, + ); + + Ok(Json(StakeSaturationResponse { + saturation: saturation.to_num(), + as_at, + })) + } else { + Err(ErrorResponse::new( + "mixnode bond not found", + Status::NotFound, + )) + } +} diff --git a/validator-api/src/nymd_client.rs b/validator-api/src/nymd_client.rs index f38503efd7..1481ad2682 100644 --- a/validator-api/src/nymd_client.rs +++ b/validator-api/src/nymd_client.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::config::Config; -use crate::rewarding::{error::RewardingError, MixnodeToReward}; +use crate::rewarding::{error::RewardingError, EpochRewardParams, MixnodeToReward}; use config::defaults::DEFAULT_VALIDATOR_API_PORT; -use mixnet_contract::{ +use mixnet_contract_common::{ ContractStateParams, Delegation, ExecuteMsg, GatewayBond, IdentityKey, MixNodeBond, MixnodeRewardingStatusResponse, RewardingIntervalResponse, MIXNODE_DELEGATORS_PAGE_LIMIT, }; @@ -98,34 +98,6 @@ impl Client { Ok(time) } - pub(crate) async fn get_reward_pool(&self) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.0.read().await.get_reward_pool().await?) - } - - pub(crate) async fn get_circulating_supply(&self) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.0.read().await.get_circulating_supply().await?) - } - - pub(crate) async fn get_sybil_resistance_percent(&self) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.0.read().await.get_sybil_resistance_percent().await?) - } - - pub(crate) async fn get_epoch_reward_percent(&self) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.0.read().await.get_epoch_reward_percent().await?) - } - pub(crate) async fn get_mixnodes(&self) -> Result, ValidatorClientError> where C: CosmWasmClient + Sync, @@ -158,9 +130,34 @@ impl Client { self.0.read().await.get_current_rewarding_interval().await } + pub(crate) async fn get_current_epoch_reward_params( + &self, + ) -> Result + where + C: CosmWasmClient + Sync, + { + let this = self.0.read().await; + + let state = this.get_contract_settings().await?; + let reward_pool = this.get_reward_pool().await?; + let epoch_reward_percent = this.get_epoch_reward_percent().await?; + + let epoch_reward_params = EpochRewardParams { + reward_pool, + circulating_supply: this.get_circulating_supply().await?, + sybil_resistance_percent: this.get_sybil_resistance_percent().await?, + rewarded_set_size: state.mixnode_rewarded_set_size, + active_set_size: state.mixnode_active_set_size, + period_reward_pool: (reward_pool / 100) * epoch_reward_percent as u128, + active_set_work_factor: state.active_set_work_factor, + }; + + Ok(epoch_reward_params) + } + pub(crate) async fn get_rewarding_status( &self, - mix_identity: mixnet_contract::IdentityKey, + mix_identity: mixnet_contract_common::IdentityKey, rewarding_interval_nonce: u32, ) -> Result where diff --git a/validator-api/src/rewarding/mod.rs b/validator-api/src/rewarding/mod.rs index bccd83e4ee..74222bb16b 100644 --- a/validator-api/src/rewarding/mod.rs +++ b/validator-api/src/rewarding/mod.rs @@ -12,8 +12,10 @@ use crate::storage::models::{ use crate::storage::ValidatorApiStorage; use config::defaults::DENOM; use log::{error, info}; -use mixnet_contract::mixnode::NodeRewardParams; -use mixnet_contract::{ExecuteMsg, IdentityKey, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT}; +use mixnet_contract_common::mixnode::NodeRewardParams; +use mixnet_contract_common::{ + ExecuteMsg, IdentityKey, MixNodeBond, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT, +}; use std::convert::TryInto; use std::time::Duration; use time::OffsetDateTime; @@ -23,14 +25,66 @@ use validator_client::nymd::SigningNymdClient; pub(crate) mod epoch; pub(crate) mod error; -struct EpochRewardParams { - reward_pool: u128, - circulating_supply: u128, - sybil_resistance_percent: u8, - rewarded_set_size: u32, - active_set_size: u32, - period_reward_pool: u128, - active_set_work_factor: u8, +#[derive(Copy, Clone)] +pub(crate) struct EpochRewardParams { + pub(crate) reward_pool: u128, + pub(crate) circulating_supply: u128, + pub(crate) sybil_resistance_percent: u8, + pub(crate) rewarded_set_size: u32, + pub(crate) active_set_size: u32, + pub(crate) period_reward_pool: u128, + pub(crate) active_set_work_factor: u8, +} + +impl EpochRewardParams { + // technically it's identical to what would have been derived with a Default implementation, + // however, I prefer to be explicit about it, as a `Default::default` value makes no sense + // apart from the `ValidatorCacheInner` context, where this value is not going to be touched anyway + // (it's guarded behind an `initialised` flag) + pub(crate) fn new_empty() -> Self { + EpochRewardParams { + reward_pool: 0, + circulating_supply: 0, + sybil_resistance_percent: 0, + rewarded_set_size: 0, + active_set_size: 0, + period_reward_pool: 0, + active_set_work_factor: 0, + } + } + + pub(crate) fn estimate_reward( + &self, + node: &MixNodeBond, + uptime: u8, + in_active_set: bool, + ) -> (u128, u128, u128) { + let node_reward_params = NodeRewardParams::new( + self.period_reward_pool, + self.rewarded_set_size.into(), + self.active_set_size.into(), + 0, + self.circulating_supply, + uptime.into(), + self.sybil_resistance_percent, + in_active_set, + self.active_set_work_factor, + ); + + let total_node_reward = node.reward(&node_reward_params); + let operator_reward = node.operator_reward(&node_reward_params); + let delegators_reward = + node.reward_delegation(node.total_delegation().amount, &node_reward_params); + + ( + total_node_reward + .reward() + .checked_to_num() + .unwrap_or_default(), + operator_reward, + delegators_reward, + ) + } } #[derive(Debug, Clone)] @@ -117,24 +171,6 @@ impl Rewarder { } } - async fn epoch_reward_params(&self) -> Result { - let state = self.nymd_client.get_contract_settings().await?; - let reward_pool = self.nymd_client.get_reward_pool().await?; - let epoch_reward_percent = self.nymd_client.get_epoch_reward_percent().await?; - - let epoch_reward_params = EpochRewardParams { - reward_pool, - circulating_supply: self.nymd_client.get_circulating_supply().await?, - sybil_resistance_percent: self.nymd_client.get_sybil_resistance_percent().await?, - rewarded_set_size: state.mixnode_rewarded_set_size, - active_set_size: state.mixnode_active_set_size, - period_reward_pool: (reward_pool / 100) * epoch_reward_percent as u128, - active_set_work_factor: state.active_set_work_factor, - }; - - Ok(epoch_reward_params) - } - /// Obtains the current number of delegators that have delegated their stake towards this particular mixnode. /// /// # Arguments @@ -170,7 +206,7 @@ impl Rewarder { // and the lack of port data / verloc data will eventually be balanced out anyway // by people hesitating to delegate to nodes without them and thus those nodes disappearing // from the active set (once introduced) - let epoch_reward_params = self.epoch_reward_params().await?; + let epoch_reward_params = self.nymd_client.get_current_epoch_reward_params().await?; info!("Rewarding pool stats"); info!( diff --git a/validator-api/validator-api-requests/Cargo.toml b/validator-api/validator-api-requests/Cargo.toml new file mode 100644 index 0000000000..946dd5703d --- /dev/null +++ b/validator-api/validator-api-requests/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "validator-api-requests" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = "1.0" +ts-rs = { version = "5.1", optional = true } + diff --git a/validator-api/validator-api-requests/src/lib.rs b/validator-api/validator-api-requests/src/lib.rs new file mode 100644 index 0000000000..9beea87f0d --- /dev/null +++ b/validator-api/validator-api-requests/src/lib.rs @@ -0,0 +1,4 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod models; diff --git a/validator-api/validator-api-requests/src/models.rs b/validator-api/validator-api-requests/src/models.rs new file mode 100644 index 0000000000..fddd64d788 --- /dev/null +++ b/validator-api/validator-api-requests/src/models.rs @@ -0,0 +1,53 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[serde(rename_all = "snake_case")] +pub enum MixnodeStatus { + Active, // in both the active set and the rewarded set + Standby, // only in the rewarded set + Inactive, // in neither the rewarded set nor the active set, but is bonded + NotFound, // doesn't even exist in the bonded set +} + +impl MixnodeStatus { + pub fn is_active(&self) -> bool { + *self == MixnodeStatus::Active + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +pub struct CoreNodeStatusResponse { + pub identity: String, + pub count: i32, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +pub struct MixnodeStatusResponse { + pub status: MixnodeStatus, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +pub struct RewardEstimationResponse { + pub estimated_total_node_reward: u128, + pub estimated_operator_reward: u128, + pub estimated_delegators_reward: u128, + + pub current_epoch_start: i64, + pub current_epoch_end: i64, + pub current_epoch_uptime: u8, + pub as_at: i64, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +pub struct StakeSaturationResponse { + pub saturation: f32, + pub as_at: i64, +}