merge develop

This commit is contained in:
Tommy Verrall
2022-01-14 15:46:09 +00:00
153 changed files with 3253 additions and 1301 deletions
Generated
+227 -72
View File
@@ -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,12 +7424,18 @@ dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"getrandom 0.2.3",
"mixnet-contract",
"rand 0.8.4",
"mixnet-contract-common",
"schemars",
"serde",
"thiserror",
"vesting-contract-common",
]
[[package]]
name = "vesting-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
]
[[package]]
+4 -1
View File
@@ -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 = [
+2 -1
View File
@@ -21,7 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
### Developing
@@ -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"]
@@ -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<C> Client<C> {
pub async fn get_rewarding_status(
&self,
mix_identity: mixnet_contract::IdentityKey,
mix_identity: mixnet_contract_common::IdentityKey,
rewarding_interval_nonce: u32,
) -> Result<MixnodeRewardingStatusResponse, ValidatorClientError>
where
@@ -297,8 +301,8 @@ impl<C> Client<C> {
pub async fn get_all_nymd_single_mixnode_delegations(
&self,
identity: mixnet_contract::IdentityKey,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
identity: mixnet_contract_common::IdentityKey,
) -> Result<Vec<mixnet_contract_common::Delegation>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
@@ -420,6 +424,12 @@ impl ApiClient {
Ok(self.validator_api.get_active_mixnodes().await?)
}
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes().await?)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?)
}
@@ -428,6 +438,55 @@ impl ApiClient {
Ok(self.validator_api.get_gateways().await?)
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_gateway_core_status_count(identity, since)
.await?)
}
pub async fn get_mixnode_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_mixnode_core_status_count(identity, since)
.await?)
}
pub async fn get_mixnode_status(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
Ok(self.validator_api.get_mixnode_status(identity).await?)
}
pub async fn get_mixnode_reward_estimation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<RewardEstimationResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_mixnode_reward_estimation(identity)
.await?)
}
pub async fn get_mixnode_stake_saturation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<StakeSaturationResponse, ValidatorClientError> {
Ok(self
.validator_api
.get_mixnode_stake_saturation(identity)
.await?)
}
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
@@ -9,6 +9,7 @@ pub mod validator_api;
pub use crate::client::ApiClient;
pub use crate::error::ValidatorClientError;
pub use validator_api_requests::*;
#[cfg(feature = "nymd-client")]
pub use client::{Client, Config};
@@ -20,6 +20,7 @@ pub enum Operation {
BondMixnodeOnBehalf,
UnbondMixnode,
UnbondMixnodeOnBehalf,
UpdateMixnodeConfig,
DelegateToMixnode,
DelegateToMixnodeOnBehalf,
UndelegateFromMixnode,
@@ -57,6 +58,7 @@ impl fmt::Display for Operation {
Operation::BondMixnode => f.write_str("BondMixnode"),
Operation::BondMixnodeOnBehalf => f.write_str("BondMixnodeOnBehalf"),
Operation::UnbondMixnode => f.write_str("UnbondMixnode"),
Operation::UpdateMixnodeConfig => f.write_str("UpdateMixnodeConfig"),
Operation::UnbondMixnodeOnBehalf => f.write_str("UnbondMixnodeOnBehalf"),
Operation::BondGateway => f.write_str("BondGateway"),
Operation::BondGatewayOnBehalf => f.write_str("BondGatewayOnBehalf"),
@@ -94,6 +96,7 @@ impl Operation {
Operation::BondMixnodeOnBehalf => 200_000u64.into(),
Operation::UnbondMixnode => 175_000u64.into(),
Operation::UnbondMixnodeOnBehalf => 175_000u64.into(),
Operation::UpdateMixnodeConfig => 175_000u64.into(),
Operation::DelegateToMixnode => 175_000u64.into(),
Operation::DelegateToMixnodeOnBehalf => 175_000u64.into(),
Operation::UndelegateFromMixnode => 175_000u64.into(),
@@ -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<C> NymdClient<C> {
pub async fn get_rewarding_status(
&self,
mix_identity: mixnet_contract::IdentityKey,
mix_identity: mixnet_contract_common::IdentityKey,
rewarding_interval_nonce: u32,
) -> Result<MixnodeRewardingStatusResponse, NymdError>
where
@@ -773,6 +773,31 @@ impl<C> NymdClient<C> {
.await
}
/// Update the configuration of a mixnode. Right now, only possible for profit margin.
pub async fn update_mixnode_config(
&self,
profit_margin_percent: u8,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
let req = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
self.client
.execute(
self.address(),
self.mixnet_contract_address()?,
&req,
fee,
"Updating mixnode configuration from rust!",
Vec::new(),
)
.await
}
/// Delegates specified amount of stake to particular mixnode.
pub async fn delegate_to_mixnode(
&self,
@@ -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]
@@ -1,16 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::validator_api::error::ValidatorAPIError;
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use mixnet_contract::{GatewayBond, MixNodeBond};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use serde::{Deserialize, Serialize};
use 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<T>(&self, path: PathSegments<'_>) -> Result<T, ValidatorAPIError>
async fn query_validator_api<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, ValidatorAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path);
let url = create_api_url(&self.url, path, params);
Ok(self.reqwest_client.get(url).send().await?.json().await?)
}
async fn post_validator_api<B, T>(
async fn post_validator_api<B, T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, ValidatorAPIError>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
V: AsRef<str>,
{
let url = create_api_url(&self.url, path);
let url = create_api_url(&self.url, path, params);
Ok(self
.reqwest_client
.post(url)
@@ -59,18 +76,142 @@ impl Client {
}
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES])
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
.await
}
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS])
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await
}
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE])
self.query_validator_api(
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
NO_PARAMS,
)
.await
}
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
NO_PARAMS,
)
.await
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
if let Some(since) = since {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::GATEWAY,
identity,
CORE_STATUS_COUNT,
],
&[(SINCE_ARG, since.to_string())],
)
.await
} else {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::GATEWAY,
identity,
],
NO_PARAMS,
)
.await
}
}
pub async fn get_mixnode_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
if let Some(since) = since {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
CORE_STATUS_COUNT,
],
&[(SINCE_ARG, since.to_string())],
)
.await
} else {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
],
NO_PARAMS,
)
.await
}
}
pub async fn get_mixnode_status(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::STATUS,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_reward_estimation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::REWARD_ESTIMATION,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnode_stake_saturation(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
identity,
routes::STAKE_SATURATION,
],
NO_PARAMS,
)
.await
}
pub async fn blind_sign(
@@ -79,6 +220,7 @@ impl Client {
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
self.post_validator_api(
&[routes::API_VERSION, routes::COCONUT_BLIND_SIGN],
NO_PARAMS,
request_body,
)
.await
@@ -87,13 +229,20 @@ impl Client {
pub async fn get_coconut_verification_key(
&self,
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY])
.await
self.query_validator_api(
&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY],
NO_PARAMS,
)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
let mut path_segments = url
.path_segments_mut()
@@ -108,6 +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
}
@@ -122,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()
);
}
}
@@ -8,6 +8,18 @@ pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways";
pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded";
pub const COCONUT_BLIND_SIGN: &str = "blind_sign";
pub const COCONUT_VERIFICATION_KEY: &str = "verification_key";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode";
pub const GATEWAY: &str = "gateway";
pub const CORE_STATUS_COUNT: &str = "core-status-count";
pub const SINCE_ARG: &str = "since";
pub const STATUS: &str = "status";
pub const REWARD_ESTIMATION: &str = "reward-estimation";
pub const STAKE_SATURATION: &str = "stake-saturation";
@@ -0,0 +1,10 @@
[package]
name = "contracts-common"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0-beta3"
@@ -0,0 +1,30 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::Event;
/// Looks up value of particular attribute in the provided event. If it fails to find it,
/// the function panics.
///
/// # Arguments
///
/// * `event`: event to search through.
/// * `key`: key associated with the particular attribute
pub fn must_find_attribute(event: &Event, key: &str) -> String {
may_find_attribute(event, key).unwrap()
}
/// Looks up value of particular attribute in the provided event. Returns None if it does not exist.
///
/// # Arguments
///
/// * `event`: event to search through.
/// * `key`: key associated with the particular attribute
pub fn may_find_attribute(event: &Event, key: &str) -> Option<String> {
for attr in &event.attributes {
if attr.key == key {
return Some(attr.value.clone());
}
}
None
}
@@ -0,0 +1,4 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod events;
@@ -1,5 +1,5 @@
[package]
name = "mixnet-contract"
name = "mixnet-contract-common"
version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
@@ -14,10 +14,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 = []
@@ -0,0 +1,353 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<Addr>,
amount: &Coin,
mix_identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
}
pub fn new_undelegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
old_delegation: &Delegation,
mix_identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event
.add_attribute(AMOUNT_KEY, old_delegation.amount.to_string())
.add_attribute(
DELEGATION_HEIGHT_KEY,
old_delegation.block_height.to_string(),
)
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
}
pub fn new_gateway_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_gateway_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
assigned_layer: Layer,
) -> Event {
let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef,
) -> Event {
let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity);
if let Some(proxy) = proxy {
event = event.add_attribute(PROXY_KEY, proxy)
}
// coin implements Display trait and we use that implementation here
event.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_settings_update_event(
old_params: &ContractStateParams,
new_params: &ContractStateParams,
) -> Event {
let mut event = Event::new(SETTINGS_UPDATE_EVENT_TYPE);
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
event = event
.add_attribute(
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
old_params.minimum_mixnode_pledge,
)
.add_attribute(
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
new_params.minimum_mixnode_pledge,
)
}
if old_params.minimum_gateway_pledge != new_params.minimum_gateway_pledge {
event = event
.add_attribute(
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
old_params.minimum_gateway_pledge,
)
.add_attribute(
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
new_params.minimum_gateway_pledge,
)
}
if old_params.mixnode_rewarded_set_size != new_params.mixnode_rewarded_set_size {
event = event
.add_attribute(
OLD_MIXNODE_REWARDED_SET_SIZE_KEY,
old_params.mixnode_rewarded_set_size.to_string(),
)
.add_attribute(
NEW_MIXNODE_REWARDED_SET_SIZE_KEY,
new_params.mixnode_rewarded_set_size.to_string(),
)
}
if old_params.mixnode_active_set_size != new_params.mixnode_active_set_size {
event = event
.add_attribute(
OLD_MIXNODE_ACTIVE_SET_SIZE_KEY,
old_params.mixnode_active_set_size.to_string(),
)
.add_attribute(
NEW_MIXNODE_ACTIVE_SET_SIZE_KEY,
new_params.mixnode_active_set_size.to_string(),
)
}
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(),
)
}
@@ -3,6 +3,7 @@
mod delegation;
pub mod error;
pub mod events;
mod gateway;
pub mod mixnode;
mod msg;
@@ -53,6 +53,16 @@ pub enum Layer {
Three = 3,
}
impl From<Layer> for String {
fn from(layer: Layer) -> Self {
if layer == Layer::Gateway {
"gateway".to_string()
} else {
(layer as u8).to_string()
}
}
}
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct NodeRewardParams {
period_reward_pool: Uint128,
@@ -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)
}
@@ -20,6 +20,9 @@ pub enum ExecuteMsg {
owner_signature: String,
},
UnbondMixnode {},
UpdateMixnodeConfig {
profit_margin_percent: u8,
},
BondGateway {
gateway: Gateway,
owner_signature: String,
@@ -0,0 +1,9 @@
[package]
name = "vesting-contract-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0-beta3"
@@ -0,0 +1,133 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, Coin, Event, Timestamp};
// event types
pub const WITHDRAW_EVENT_TYPE: &str = "vested_coins_withdraw";
pub const OWNERSHIP_TRANSFER_EVENT_TYPE: &str = "ownership_transfer";
pub const STAKING_ADDRESS_UPDATE_EVENT_TYPE: &str = "staking_address_update";
pub const NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE: &str = "new_periodic_vesting_account";
pub const VESTING_DELEGATION_EVENT_TYPE: &str = "vesting_delegation";
pub const VESTING_UNDELEGATION_EVENT_TYPE: &str = "vesting_undelegation";
pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding";
pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding";
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation";
// attributes that are used in multiple places
pub const OWNER_KEY: &str = "owner";
pub const AMOUNT_KEY: &str = "amount";
// event-specific attributes
// withdraw
pub const REMAINING_SPENDABLE_KEY: &str = "remaining_spendable";
// ownership transfer
pub const FROM_ACCOUNT_KEY: &str = "from";
pub const TO_ACCOUNT_KEY: &str = "to";
pub const NO_VALUE_VALUE: &str = "none";
// new vesting account
pub const START_TIME_KEY: &str = "start_time";
pub const STAKING_ADDRESS_KEY: &str = "staking_address";
// OPEN QUESTION: would it make sense to also emit amount of vesting/locked coins here?
// however, then it would require additional storage reads.
pub fn new_vested_coins_withdraw_event(
address: &Addr,
amount: &Coin,
remaining_spendable: &Coin,
) -> Event {
Event::new(WITHDRAW_EVENT_TYPE)
.add_attribute(OWNER_KEY, address)
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(REMAINING_SPENDABLE_KEY, remaining_spendable.to_string())
}
pub fn new_ownership_transfer_event(from: &Addr, to: &Addr) -> Event {
Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE)
.add_attribute(FROM_ACCOUNT_KEY, from)
.add_attribute(TO_ACCOUNT_KEY, to)
}
pub fn new_staking_address_update_event(from: &Option<Addr>, to: &Option<Addr>) -> Event {
let mut event = Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE);
if let Some(from) = from {
event = event.add_attribute(FROM_ACCOUNT_KEY, from)
} else {
event = event.add_attribute(FROM_ACCOUNT_KEY, NO_VALUE_VALUE);
}
if let Some(to) = to {
event = event.add_attribute(TO_ACCOUNT_KEY, to)
} else {
event = event.add_attribute(TO_ACCOUNT_KEY, NO_VALUE_VALUE);
}
event
}
pub fn new_periodic_vesting_account_event(
owner_address: &Addr,
amount: &Coin,
staking_address: &Option<Addr>,
start_time: Timestamp,
) -> Event {
let mut event = Event::new(NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE)
.add_attribute(OWNER_KEY, owner_address)
.add_attribute(AMOUNT_KEY, amount.to_string());
if let Some(staking_address) = staking_address {
event = event.add_attribute(STAKING_ADDRESS_KEY, staking_address);
}
event.add_attribute(START_TIME_KEY, start_time.to_string())
}
// In most cases the events are rather barebone as there's no point in attaching
// bunch of data to them as it would be redundant. It is because in most cases when the event is emitted
// a call to the mixnet contract is made that throws another event with relevant attributes already attached.
pub fn new_vesting_gateway_bonding_event() -> Event {
Event::new(VESTING_GATEWAY_BONDING_EVENT_TYPE)
}
pub fn new_vesting_gateway_unbonding_event() -> Event {
Event::new(VESTING_GATEWAY_UNBONDING_EVENT_TYPE)
}
pub fn new_vesting_mixnode_bonding_event() -> Event {
Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE)
}
pub fn new_vesting_mixnode_unbonding_event() -> Event {
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
}
pub fn new_vesting_delegation_event() -> Event {
Event::new(VESTING_DELEGATION_EVENT_TYPE)
}
pub fn new_vesting_undelegation_event() -> Event {
Event::new(VESTING_UNDELEGATION_EVENT_TYPE)
}
pub fn new_track_mixnode_unbond_event() -> Event {
Event::new(TRACK_MIXNODE_UNBOND_EVENT_TYPE)
}
pub fn new_track_gateway_unbond_event() -> Event {
Event::new(TRACK_GATEWAY_UNBOND_EVENT_TYPE)
}
pub fn new_track_undelegation_event() -> Event {
Event::new(TRACK_UNDELEGATION_EVENT_TYPE)
}
@@ -0,0 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod events;
+1 -1
View File
@@ -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"
+12
View File
@@ -8,11 +8,15 @@ use url::Url;
pub mod eth_contract;
#[cfg(network = "milhon")]
pub mod milhon;
#[cfg(network = "qa")]
pub mod qa;
#[cfg(network = "sandbox")]
pub mod sandbox;
#[cfg(network = "milhon")]
pub use milhon::*;
#[cfg(network = "qa")]
pub use qa::*;
#[cfg(network = "sandbox")]
pub use sandbox::*;
@@ -58,6 +62,14 @@ pub fn default_validators() -> Vec<ValidatorDetails> {
]
}
#[cfg(network = "qa")]
pub fn default_validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
"https://qa-validator.nymtech.net",
Some("https://qa-validator.nymtech.net/api"),
)]
}
#[cfg(network = "sandbox")]
pub fn default_validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
+17
View File
@@ -0,0 +1,17 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const BECH32_PREFIX: &str = "nymt";
pub const DENOM: &str = "unymt";
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
// Name of the event triggered by the eth contract. If the event name is changed,
// this would also need to be changed; It is currently tested against the json abi
pub const ETH_EVENT_NAME: &str = "Burned";
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
+1 -1
View File
@@ -25,7 +25,7 @@ crypto = { path = "../crypto" }
topology = { path = "../topology" }
[dev-dependencies]
mixnet-contract = { path = "../mixnet-contract" }
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -13,7 +13,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
## internal
crypto = { path = "../crypto" }
mixnet-contract = { path = "../mixnet-contract" }
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nymsphinx-addressing = { path = "../nymsphinx/addressing" }
nymsphinx-types = { path = "../nymsphinx/types" }
version-checker = { path = "../version-checker" }
+1 -1
View File
@@ -3,7 +3,7 @@
use crate::{filter, NetworkAddress};
use crypto::asymmetric::{encryption, identity};
use mixnet_contract::GatewayBond;
use mixnet_contract_common::GatewayBond;
use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress};
use nymsphinx_types::Node as SphinxNode;
use std::convert::{TryFrom, TryInto};
+2 -2
View File
@@ -3,7 +3,7 @@
use crate::filter::VersionFilterable;
use log::warn;
use mixnet_contract::{GatewayBond, MixNodeBond};
use mixnet_contract_common::{GatewayBond, MixNodeBond};
use nymsphinx_addressing::nodes::NodeIdentity;
use nymsphinx_types::Node as SphinxNode;
use rand::Rng;
@@ -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() {
+1 -1
View File
@@ -3,7 +3,7 @@
use crate::{filter, NetworkAddress};
use crypto::asymmetric::{encryption, identity};
use mixnet_contract::{Layer, MixNodeBond};
use mixnet_contract_common::{Layer, MixNodeBond};
use nymsphinx_addressing::nodes::NymNodeRoutingAddress;
use nymsphinx_types::Node as SphinxNode;
use std::convert::{TryFrom, TryInto};
+41 -60
View File
@@ -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"
@@ -326,7 +333,7 @@ dependencies = [
"log",
"nymsphinx-types",
"pemstore",
"rand 0.7.3",
"rand",
"subtle-encoding",
"x25519-dalek",
]
@@ -458,7 +465,7 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"rand",
"serde",
"sha2",
"zeroize",
@@ -592,10 +599,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@@ -833,21 +838,6 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "mixnet-contract"
version = "0.1.0"
dependencies = [
"az",
"cosmwasm-std",
"fixed",
"log",
"network-defaults",
"schemars",
"serde",
"serde_repr",
"thiserror",
]
[[package]]
name = "mixnet-contracts"
version = "0.1.0"
dependencies = [
"bs58",
"config",
@@ -857,9 +847,9 @@ dependencies = [
"crypto",
"cw-storage-plus",
"fixed",
"mixnet-contract",
"rand 0.7.3",
"rand_chacha 0.2.2",
"mixnet-contract-common",
"rand",
"rand_chacha",
"schemars",
"serde",
"thiserror",
@@ -867,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"
@@ -1067,21 +1073,9 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_chacha",
"rand_core 0.5.1",
"rand_hc 0.2.0",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc 0.3.1",
"rand_hc",
]
[[package]]
@@ -1094,16 +1088,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.3",
]
[[package]]
name = "rand_core"
version = "0.5.1"
@@ -1129,7 +1113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56"
dependencies = [
"num-traits",
"rand 0.7.3",
"rand",
]
[[package]]
@@ -1141,15 +1125,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core 0.6.3",
]
[[package]]
name = "regex"
version = "1.5.4"
@@ -1330,7 +1305,7 @@ dependencies = [
"hmac",
"lioness",
"log",
"rand 0.7.3",
"rand",
"rand_distr",
"sha2",
"subtle 2.4.1",
@@ -1558,12 +1533,18 @@ dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"getrandom 0.2.3",
"mixnet-contract",
"rand 0.8.4",
"mixnet-contract-common",
"schemars",
"serde",
"thiserror",
"vesting-contract-common",
]
[[package]]
name = "vesting-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
]
[[package]]
+2 -2
View File
@@ -1,5 +1,5 @@
[package]
name = "mixnet-contracts"
name = "mixnet-contract"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2018"
@@ -16,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" }
+1 -1
View File
@@ -4,7 +4,7 @@
extern crate mixnet_contract;
use cosmwasm_schema::{export_schema, remove_schemas, schema_for};
use mixnet_contract::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg};
use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg};
use std::env::current_dir;
use std::fs::create_dir_all;
+12 -2
View File
@@ -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);
@@ -104,6 +106,14 @@ pub fn execute(
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, info)
}
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
} => crate::mixnodes::transactions::try_update_mixnode_config(
deps,
env,
info,
profit_margin_percent,
),
ExecuteMsg::BondGateway {
gateway,
owner_signature,
@@ -292,7 +302,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() {
+9 -13
View File
@@ -7,10 +7,10 @@ use cosmwasm_std::Deps;
use cosmwasm_std::Order;
use cosmwasm_std::StdResult;
use cw_storage_plus::{Bound, PrimaryKey};
use mixnet_contract::PagedAllDelegationsResponse;
use mixnet_contract::PagedDelegatorDelegationsResponse;
use mixnet_contract::PagedMixDelegationsResponse;
use mixnet_contract::{Delegation, IdentityKey};
use mixnet_contract_common::{
Delegation, IdentityKey, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
PagedMixDelegationsResponse,
};
pub(crate) fn query_all_network_delegations_paged(
deps: Deps,
@@ -140,7 +140,7 @@ pub(crate) mod tests {
#[cfg(test)]
mod querying_for_mixnode_delegations_paged {
use super::*;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
#[test]
fn retrieval_obeys_limits() {
@@ -281,7 +281,7 @@ pub(crate) mod tests {
mod querying_for_all_mixnode_delegations_paged {
use super::*;
use crate::support::tests::test_helpers;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
#[test]
fn retrieval_obeys_limits() {
@@ -444,7 +444,7 @@ pub(crate) mod tests {
// add delegation from a different address
let delegation = Delegation::new(
delegation_owner2.clone(),
delegation_owner2,
node_identity1.clone(),
coin(1234, DENOM),
1234,
@@ -474,7 +474,7 @@ pub(crate) mod tests {
// add delegation for a different node
let delegation = Delegation::new(
delegation_owner1.clone(),
node_identity2.clone(),
node_identity2,
coin(1234, DENOM),
1234,
None,
@@ -493,11 +493,7 @@ pub(crate) mod tests {
identity: node_identity1.clone(),
address: Addr::unchecked(delegation_owner1.clone())
}),
query_mixnode_delegation(
deps.as_ref(),
node_identity1.clone(),
delegation_owner1.to_string()
)
query_mixnode_delegation(deps.as_ref(), node_identity1, delegation_owner1.to_string())
)
}
+7 -7
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex};
use mixnet_contract::{Addr, Delegation, IdentityKey};
use mixnet_contract_common::{Addr, Delegation, IdentityKey};
// storage prefixes
const DELEGATION_PK_NAMESPACE: &str = "dl";
@@ -64,7 +64,7 @@ pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, Delega
mod tests {
use crate::delegations::storage;
use cosmwasm_std::Addr;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
#[cfg(test)]
mod reverse_mix_delegations {
@@ -74,7 +74,7 @@ mod tests {
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::{coin, Order};
use cw_storage_plus::PrimaryKey;
use mixnet_contract::Delegation;
use mixnet_contract_common::Delegation;
#[test]
fn reverse_mix_delegation_exists() {
@@ -94,7 +94,7 @@ mod tests {
storage::delegations()
.save(
&mut deps.storage,
(node_identity.clone(), delegation_owner.clone()).joined_key(),
(node_identity, delegation_owner.clone()).joined_key(),
&dummy_data,
)
.unwrap();
@@ -131,7 +131,7 @@ mod tests {
// add delegation for a different node
let dummy_data = Delegation::new(
delegation_owner1.clone(),
node_identity2.clone(),
node_identity2,
delegation.clone(),
mock_env().block.height,
None,
@@ -156,14 +156,14 @@ mod tests {
let dummy_data = Delegation::new(
delegation_owner2.clone(),
node_identity1.clone(),
delegation.clone(),
delegation,
mock_env().block.height,
None,
);
storage::delegations()
.save(
&mut deps.storage,
(node_identity1.clone(), delegation_owner2.clone()).joined_key(),
(node_identity1.clone(), delegation_owner2).joined_key(),
&dummy_data,
)
.unwrap();
@@ -7,8 +7,8 @@ use crate::support::helpers::generate_storage_key;
use config::defaults::DENOM;
use cosmwasm_std::{coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use cw_storage_plus::PrimaryKey;
use mixnet_contract::Delegation;
use mixnet_contract::IdentityKey;
use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event};
use mixnet_contract_common::{Delegation, IdentityKey};
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
@@ -113,16 +113,21 @@ pub(crate) fn _try_delegate_to_mixnode(
}
None => Delegation::new(
delegate.to_owned(),
mix_identity,
amount,
mix_identity.clone(),
amount.clone(),
env.block.height,
proxy,
proxy.clone(),
),
})
},
)?;
Ok(Response::default())
Ok(Response::new().add_event(new_delegation_event(
&delegate,
&proxy,
&amount,
&mix_identity,
)))
}
pub(crate) fn try_remove_delegation_from_mixnode(
@@ -204,14 +209,19 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
let msg = Some(VestingContractExecuteMsg::TrackUndelegation {
owner: delegate.as_str().to_string(),
mix_identity: mix_identity.clone(),
amount: old_delegation.amount,
amount: old_delegation.amount.clone(),
});
let track_undelegation_msg = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_undelegation_msg);
}
Ok(response)
Ok(response.add_event(new_undelegation_event(
&delegate,
&proxy,
&old_delegation,
&mix_identity,
)))
}
}
}
@@ -530,7 +540,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env1,
mock_info(delegation_owner1.as_str(), &[delegation1.clone()]),
mock_info(delegation_owner1.as_str(), &[delegation1]),
identity.clone(),
)
.unwrap();
@@ -544,7 +554,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env2,
mock_info(delegation_owner2.as_str(), &[delegation2.clone()]),
mock_info(delegation_owner2.as_str(), &[delegation2]),
identity.clone(),
)
.unwrap();
@@ -699,7 +709,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &vec![delegation_amount.clone()]),
mock_info(delegation_owner.as_str(), &[delegation_amount.clone()]),
identity.clone(),
)
.unwrap();
@@ -722,6 +732,7 @@ mod tests {
#[cfg(test)]
mod removing_mix_stake_delegation {
use crate::delegations::queries::query_mixnode_delegation;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
@@ -774,11 +785,27 @@ mod tests {
identity.clone(),
)
.unwrap();
assert_eq!(
Ok(Response::new().add_message(BankMsg::Send {
let delegation = query_mixnode_delegation(
deps.as_ref(),
identity.clone(),
delegation_owner.clone().into_string(),
)
.unwrap();
let expected_response = Response::new()
.add_message(BankMsg::Send {
to_address: delegation_owner.clone().into(),
amount: coins(100, DENOM),
})),
})
.add_event(new_undelegation_event(
&delegation_owner,
&None,
&delegation,
&identity,
));
assert_eq!(
Ok(expected_response),
try_remove_delegation_from_mixnode(
deps.as_mut(),
mock_info(delegation_owner.as_str(), &[]),
@@ -819,12 +846,27 @@ mod tests {
identity.clone(),
)
.unwrap();
try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
assert_eq!(
Ok(Response::new().add_message(BankMsg::Send {
let delegation = query_mixnode_delegation(
deps.as_ref(),
identity.clone(),
delegation_owner.clone().into_string(),
)
.unwrap();
let expected_response = Response::new()
.add_message(BankMsg::Send {
to_address: delegation_owner.clone().into(),
amount: coins(100, DENOM),
})),
})
.add_event(new_undelegation_event(
&delegation_owner,
&None,
&delegation,
&identity,
));
try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
assert_eq!(
Ok(expected_response),
try_remove_delegation_from_mixnode(
deps.as_mut(),
mock_info(delegation_owner.as_str(), &[]),
@@ -853,7 +895,7 @@ mod tests {
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner1.as_str(), &[delegation1.clone()]),
mock_info(delegation_owner1.as_str(), &[delegation1]),
identity.clone(),
)
.is_ok());
+1 -1
View File
@@ -3,7 +3,7 @@
use config::defaults::DENOM;
use cosmwasm_std::{Addr, StdError};
use mixnet_contract::IdentityKey;
use mixnet_contract_common::IdentityKey;
use thiserror::Error;
/// Custom errors for contract failure conditions.
+3 -1
View File
@@ -5,7 +5,9 @@ use super::storage;
use crate::mixnodes::storage::{BOND_PAGE_DEFAULT_LIMIT, BOND_PAGE_MAX_LIMIT}; // Keeps gateway and mixnode retrieval in sync by re-using the constant. Could be split into its own constant.
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use mixnet_contract::{GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse};
use mixnet_contract_common::{
GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse,
};
pub(crate) fn query_gateways_paged(
deps: Deps,
+3 -5
View File
@@ -3,7 +3,7 @@
use cosmwasm_std::Addr;
use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex};
use mixnet_contract::{GatewayBond, IdentityKeyRef};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
// storage prefixes
const GATEWAYS_PK_NAMESPACE: &str = "gt";
@@ -41,9 +41,7 @@ mod tests {
use cosmwasm_std::StdResult;
use cosmwasm_std::Storage;
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract::GatewayBond;
use mixnet_contract::IdentityKey;
use mixnet_contract::{Gateway, IdentityKeyRef};
use mixnet_contract_common::{Gateway, GatewayBond, IdentityKey, IdentityKeyRef};
// currently this is only used in tests but may become useful later on
pub(crate) fn read_gateway_pledge_amount(
@@ -87,7 +85,7 @@ mod tests {
let gateway_bond = GatewayBond {
pledge_amount: coin(pledge_amount, DENOM),
owner: node_owner.clone(),
owner: node_owner,
block_height: 12_345,
gateway: Gateway {
identity_key: node_identity.clone(),
+35 -30
View File
@@ -9,7 +9,8 @@ use config::defaults::DENOM;
use cosmwasm_std::{
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
};
use mixnet_contract::{Gateway, GatewayBond, Layer};
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
use mixnet_contract_common::{Gateway, GatewayBond, Layer};
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_gateway(
@@ -97,12 +98,24 @@ pub(crate) fn _try_add_gateway(
&gateway.identity_key,
)?;
let bond = GatewayBond::new(pledge, owner, env.block.height, gateway, proxy);
let gateway_identity = gateway.identity_key.clone();
let bond = GatewayBond::new(
pledge.clone(),
owner.clone(),
env.block.height,
gateway,
proxy.clone(),
);
storage::gateways().save(deps.storage, bond.identity(), &bond)?;
mixnet_params_storage::increment_layer_count(deps.storage, Layer::Gateway)?;
Ok(Response::new())
Ok(Response::new().add_event(new_gateway_bonding_event(
&owner,
&proxy,
&pledge,
&gateway_identity,
)))
}
pub fn try_remove_gateway_on_behalf(
@@ -155,23 +168,24 @@ pub(crate) fn _try_remove_gateway(
// decrement layer count
mixnet_params_storage::decrement_layer_count(deps.storage, Layer::Gateway)?;
let mut response = Response::new()
.add_message(return_tokens)
.add_attribute("action", "unbond")
.add_attribute("address", owner.clone())
.add_attribute("gateway_bond", gateway_bond.to_string());
let mut response = Response::new().add_message(return_tokens);
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondGateway {
owner: owner.as_str().to_string(),
amount: gateway_bond.pledge_amount,
amount: gateway_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_unbond_message);
}
Ok(response)
Ok(response.add_event(new_gateway_unbonding_event(
&owner,
&proxy,
&gateway_bond.pledge_amount,
gateway_bond.identity(),
)))
}
fn validate_gateway_pledge(
@@ -212,12 +226,10 @@ pub mod tests {
use crate::support::tests;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use cosmwasm_std::attr;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, BankMsg, Response};
use cosmwasm_std::{from_binary, Addr, Uint128};
use mixnet_contract::Gateway;
use mixnet_contract::{ExecuteMsg, PagedGatewayResponse, QueryMsg};
use mixnet_contract_common::{ExecuteMsg, Gateway, PagedGatewayResponse, QueryMsg};
#[test]
fn gateway_add() {
@@ -454,19 +466,6 @@ pub mod tests {
let msg = ExecuteMsg::UnbondGateway {};
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
// we should see log messages come back showing an unbond message
let expected_attributes = vec![
attr("action", "unbond"),
attr("address", "fred"),
attr(
"gateway_bond",
format!(
"amount: {} {}, owner: fred, identity: {}",
INITIAL_GATEWAY_PLEDGE, DENOM, fred_identity
),
),
];
// we should see a funds transfer from the contract back to fred
let expected_message = BankMsg::Send {
to_address: String::from(info.sender),
@@ -474,11 +473,17 @@ pub mod tests {
};
// run the executor and check that we got back the correct results
let expected = Response::new()
.add_attributes(expected_attributes)
.add_message(expected_message);
let expected_response =
Response::new()
.add_message(expected_message)
.add_event(new_gateway_unbonding_event(
&Addr::unchecked("fred"),
&None,
&tests::fixtures::good_gateway_pledge()[0],
&fred_identity,
));
assert_eq!(remove_fred, expected);
assert_eq!(expected_response, remove_fred);
// only 1 node now exists, owned by bob:
let gateway_bonds = tests::queries::get_gateways(&mut deps);
@@ -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};
@@ -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<ContractStateParams> {
storage::CONTRACT_STATE
@@ -5,8 +5,7 @@ use crate::mixnet_contract_settings::models::ContractState;
use cosmwasm_std::StdResult;
use cosmwasm_std::Storage;
use cw_storage_plus::Item;
use mixnet_contract::Layer;
use mixnet_contract::LayerDistribution;
use mixnet_contract_common::{Layer, LayerDistribution};
pub(crate) const CONTRACT_STATE: Item<ContractState> = Item::new("config");
pub(crate) const LAYERS: Item<LayerDistribution> = Item::new("layers");
@@ -6,7 +6,8 @@ use crate::error::ContractError;
use cosmwasm_std::DepsMut;
use cosmwasm_std::MessageInfo;
use cosmwasm_std::Response;
use mixnet_contract::ContractStateParams;
use mixnet_contract_common::events::new_settings_update_event;
use mixnet_contract_common::ContractStateParams;
pub(crate) fn try_update_contract_settings(
deps: DepsMut,
@@ -34,10 +35,12 @@ pub(crate) fn try_update_contract_settings(
return Err(ContractError::InvalidActiveSetSize);
}
let response = Response::new().add_event(new_settings_update_event(&state.params, &params));
state.params = params;
storage::CONTRACT_STATE.save(deps.storage, &state)?;
Ok(Response::default())
Ok(response)
}
#[cfg(test)]
@@ -49,7 +52,7 @@ pub mod tests {
use crate::support::tests::test_helpers;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Response;
use mixnet_contract::ContractStateParams;
use mixnet_contract_common::ContractStateParams;
#[test]
fn updating_contract_settings() {
@@ -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);
}
}
@@ -4,7 +4,9 @@
use super::storage;
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use mixnet_contract::{IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
use mixnet_contract_common::{
IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse,
};
pub fn query_mixnodes_paged(
deps: Deps,
@@ -3,7 +3,7 @@
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use cosmwasm_std::{Deps, StdResult};
use mixnet_contract::LayerDistribution;
use mixnet_contract_common::LayerDistribution;
pub(crate) fn query_layer_distribution(deps: Deps) -> StdResult<LayerDistribution> {
mixnet_params_storage::LAYERS.load(deps.storage)
+3 -8
View File
@@ -4,7 +4,7 @@
use config::defaults::DENOM;
use cosmwasm_std::{StdResult, Storage, Uint128};
use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex};
use mixnet_contract::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond};
use mixnet_contract_common::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
@@ -49,7 +49,6 @@ pub(crate) struct StoredMixnodeBond {
pub layer: Layer,
pub block_height: u64,
pub mix_node: MixNode,
pub profit_margin_percent: Option<u8>,
pub proxy: Option<Addr>,
}
@@ -60,7 +59,6 @@ impl StoredMixnodeBond {
layer: Layer,
block_height: u64,
mix_node: MixNode,
profit_margin_percent: Option<u8>,
proxy: Option<Addr>,
) -> Self {
StoredMixnodeBond {
@@ -69,7 +67,6 @@ impl StoredMixnodeBond {
layer,
block_height,
mix_node,
profit_margin_percent,
proxy,
}
}
@@ -141,8 +138,7 @@ mod tests {
use config::defaults::DENOM;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract::IdentityKey;
use mixnet_contract::MixNode;
use mixnet_contract_common::{IdentityKey, MixNode};
#[test]
fn mixnode_single_read_retrieval() {
@@ -173,14 +169,13 @@ mod tests {
let mixnode_bond = StoredMixnodeBond {
pledge_amount: coin(pledge_value, DENOM),
owner: node_owner.clone(),
owner: node_owner,
layer: Layer::One,
block_height: 12_345,
mix_node: MixNode {
identity_key: node_identity.clone(),
..tests::fixtures::mix_node_fixture()
},
profit_margin_percent: None,
proxy: None,
};
+141 -33
View File
@@ -11,7 +11,8 @@ use config::defaults::DENOM;
use cosmwasm_std::{
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
};
use mixnet_contract::MixNode;
use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event};
use mixnet_contract_common::MixNode;
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_mixnode(
@@ -109,13 +110,12 @@ fn _try_add_mixnode(
let layer = layer_distribution.choose_with_fewest();
let stored_bond = StoredMixnodeBond::new(
pledge_amount,
owner,
pledge_amount.clone(),
owner.clone(),
layer,
env.block.height,
mix_node,
None,
proxy,
proxy.clone(),
);
// technically we don't have to set the total_delegation bucket, but it makes things easier
@@ -133,7 +133,13 @@ fn _try_add_mixnode(
mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?;
Ok(Response::new())
Ok(Response::new().add_event(new_mixnode_bonding_event(
&owner,
&proxy,
&pledge_amount,
identity,
stored_bond.layer,
)))
}
pub fn try_remove_mixnode_on_behalf(
@@ -186,22 +192,60 @@ pub(crate) fn _try_remove_mixnode(
// decrement layer count
mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?;
let mut response = Response::new()
.add_message(return_tokens)
.add_attribute("action", "unbond")
.add_attribute("mixnode_bond", mixnode_bond.to_string());
let mut response = Response::new().add_message(return_tokens);
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondMixnode {
owner: owner.as_str().to_string(),
amount: mixnode_bond.pledge_amount,
amount: mixnode_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_unbond_message);
}
Ok(response)
Ok(response.add_event(new_mixnode_unbonding_event(
&owner,
&proxy,
&mixnode_bond.pledge_amount,
mixnode_bond.identity(),
)))
}
pub(crate) fn try_update_mixnode_config(
deps: DepsMut,
env: Env,
info: MessageInfo,
profit_margin_percent: u8,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(info.sender.as_ref())?;
let mix_identity = storage::mixnodes()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(ContractError::NoAssociatedMixNodeBond { owner })?
.1
.identity()
.clone();
// We don't have to check lower bound as its an u8
if profit_margin_percent > 100 {
return Err(ContractError::InvalidProfitMarginPercent(
profit_margin_percent,
));
}
storage::mixnodes().update(deps.storage, &mix_identity, |mixnode_bond_opt| {
mixnode_bond_opt
.map(|mut mixnode_bond| {
mixnode_bond.mix_node.profit_margin_percent = profit_margin_percent;
mixnode_bond.block_height = env.block.height;
mixnode_bond
})
.ok_or(ContractError::NoBondFound)
})?;
Ok(Response::new())
}
fn validate_mixnode_pledge(
@@ -242,13 +286,12 @@ pub mod tests {
use crate::support::tests;
use crate::support::tests::test_helpers;
use config::defaults::DENOM;
use cosmwasm_std::attr;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, BankMsg, Response};
use cosmwasm_std::{from_binary, Addr, Uint128};
use mixnet_contract::Layer;
use mixnet_contract::MixNode;
use mixnet_contract::{ExecuteMsg, LayerDistribution, PagedMixnodeResponse, QueryMsg};
use mixnet_contract_common::{
ExecuteMsg, Layer, LayerDistribution, MixNode, PagedMixnodeResponse, QueryMsg,
};
#[test]
fn mixnode_add() {
@@ -507,18 +550,6 @@ pub mod tests {
let msg = ExecuteMsg::UnbondMixnode {};
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
// we should see log messages come back showing an unbond message
let expected_attributes = vec![
attr("action", "unbond"),
attr(
"mixnode_bond",
format!(
"amount: {}{}, owner: fred, identity: {}",
INITIAL_MIXNODE_PLEDGE, DENOM, fred_identity
),
),
];
// we should see a funds transfer from the contract back to fred
let expected_message = BankMsg::Send {
to_address: String::from(info.sender),
@@ -526,11 +557,17 @@ pub mod tests {
};
// run the executor and check that we got back the correct results
let expected = Response::new()
.add_attributes(expected_attributes)
.add_message(expected_message);
let expected_response =
Response::new()
.add_message(expected_message)
.add_event(new_mixnode_unbonding_event(
&Addr::unchecked("fred"),
&None,
&tests::fixtures::good_gateway_pledge()[0],
&fred_identity,
));
assert_eq!(remove_fred, expected);
assert_eq!(expected_response, remove_fred);
// only 1 node now exists, owned by bob:
let mix_node_bonds = tests::queries::get_mix_nodes(&mut deps);
@@ -587,6 +624,77 @@ pub mod tests {
);
}
#[test]
fn updating_mixnode_config() {
let sender = "bob";
let mut deps = test_helpers::init_contract();
let info = mock_info(sender, &[]);
// try updating a non existing mixnode bond
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent: 10,
};
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!(
ret,
Err(ContractError::NoAssociatedMixNodeBond {
owner: Addr::unchecked(sender)
})
);
test_helpers::add_mixnode(
sender,
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
// check the initial profit margin is set to the fixture value
let fixture_profit_margin = tests::fixtures::mix_node_fixture().profit_margin_percent;
assert_eq!(
fixture_profit_margin,
storage::mixnodes()
.idx
.owner
.item(deps.as_ref().storage, Addr::unchecked("bob"))
.unwrap()
.unwrap()
.1
.mix_node
.profit_margin_percent
);
// try updating with an invalid value
let profit_margin_percent = 101;
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!(
ret,
Err(ContractError::InvalidProfitMarginPercent(
profit_margin_percent
))
);
let profit_margin_percent = fixture_profit_margin + 10;
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
profit_margin_percent,
storage::mixnodes()
.idx
.owner
.item(deps.as_ref().storage, Addr::unchecked("bob"))
.unwrap()
.unwrap()
.1
.mix_node
.profit_margin_percent
);
}
#[test]
fn validating_mixnode_bond() {
// you must send SOME funds
@@ -649,6 +757,6 @@ pub mod tests {
assert_eq!(alice_node.mix_node.identity_key, alice_identity);
assert_eq!(alice_node.layer, Layer::One);
assert_eq!(bob_node.mix_node.identity_key, bob_identity);
assert_eq!(bob_node.layer, mixnet_contract::Layer::Two);
assert_eq!(bob_node.layer, mixnet_contract_common::Layer::Two);
}
}
+2 -2
View File
@@ -5,8 +5,8 @@ use super::storage;
use crate::error::ContractError;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{Addr, Storage, Uint128};
use mixnet_contract::mixnode::DelegatorRewardParams;
use mixnet_contract::{
use mixnet_contract_common::mixnode::DelegatorRewardParams;
use mixnet_contract_common::{
IdentityKey, IdentityKeyRef, PendingDelegatorRewarding, RewardingResult, RewardingStatus,
};
+13 -22
View File
@@ -4,7 +4,7 @@
use super::storage;
use cosmwasm_std::Uint128;
use cosmwasm_std::{Deps, StdResult};
use mixnet_contract::{IdentityKey, MixnodeRewardingStatusResponse};
use mixnet_contract_common::{IdentityKey, MixnodeRewardingStatusResponse};
pub(crate) fn query_reward_pool(deps: Deps) -> StdResult<Uint128> {
storage::REWARD_POOL.load(deps.storage)
@@ -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),
+1 -1
View File
@@ -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<Uint128> = Item::new("pool");
pub(crate) const REWARDING_STATUS: Map<(U32Key, IdentityKey), RewardingStatus> = Map::new("rm");
+180 -127
View File
@@ -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(&current_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(&current_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),
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::error::ContractError;
use crate::gateways::storage as gateways_storage;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{Addr, Deps, Storage};
use mixnet_contract::IdentityKeyRef;
use mixnet_contract_common::IdentityKeyRef;
pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec<u8> {
if let Some(proxy) = &proxy {
@@ -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,
)
}
@@ -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;
+1 -1
View File
@@ -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<Coin>, deps: DepsMut) -> String {
@@ -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,
};
+3 -4
View File
@@ -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"}
@@ -22,6 +23,4 @@ cw-storage-plus = { version = "0.10.3", features = ["iterator"] }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
rand = {version = "0.8.4", features = ["std_rng"]}
getrandom = { version = "0.2.3", features = ["js"]}
thiserror = { version = "1.0.23" }
+39 -16
View File
@@ -7,10 +7,15 @@ use crate::traits::{
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM};
use cosmwasm_std::{
entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Timestamp, Uint128,
};
use mixnet_contract::{Gateway, IdentityKey, MixNode};
use mixnet_contract_common::{Gateway, IdentityKey, MixNode};
use vesting_contract_common::events::{
new_ownership_transfer_event, new_periodic_vesting_account_event,
new_staking_address_update_event, new_track_gateway_unbond_event,
new_track_mixnode_unbond_event, new_track_undelegation_event, new_vested_coins_withdraw_event,
};
// We're using a 24 month vesting period with 3 months sub-periods.
// There are 8 three month periods in two years
@@ -96,6 +101,10 @@ pub fn try_withdraw_vested_coins(
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
if amount.denom != DENOM {
return Err(ContractError::WrongDenom(amount.denom, DENOM.to_string()));
}
let address = info.sender.clone();
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
if address != account.owner_address() {
@@ -111,12 +120,16 @@ pub fn try_withdraw_vested_coins(
let send_tokens = BankMsg::Send {
to_address: account.owner_address().as_str().to_string(),
amount: vec![amount],
amount: vec![amount.clone()],
};
Ok(Response::new()
.add_attribute("action", "whitdraw")
.add_message(send_tokens))
.add_message(send_tokens)
.add_event(new_vested_coins_withdraw_event(
&address,
&amount,
&coin(new_balance, &amount.denom),
)))
} else {
Err(ContractError::InsufficientSpendable(
account.owner_address().as_str().to_string(),
@@ -135,7 +148,7 @@ fn try_transfer_ownership(
let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
if address == account.owner_address() {
account.transfer_ownership(&to_address, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_ownership_transfer_event(&address, &to_address)))
} else {
Err(ContractError::NotOwner(account.owner_address().to_string()))
}
@@ -150,8 +163,9 @@ fn try_update_staking_address(
let to_address = to_address.and_then(|x| deps.api.addr_validate(&x).ok());
let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
if address == account.owner_address() {
account.update_staking_address(to_address, deps.storage)?;
Ok(Response::default())
let old = account.staking_address().cloned();
account.update_staking_address(to_address.clone(), deps.storage)?;
Ok(Response::new().add_event(new_staking_address_update_event(&old, &to_address)))
} else {
Err(ContractError::NotOwner(account.owner_address().to_string()))
}
@@ -186,7 +200,7 @@ pub fn try_track_unbond_gateway(
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.try_track_unbond_gateway(amount, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_track_gateway_unbond_event()))
}
pub fn try_bond_mixnode(
@@ -217,7 +231,7 @@ pub fn try_track_unbond_mixnode(
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.try_track_unbond_mixnode(amount, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_track_mixnode_unbond_event()))
}
fn try_track_undelegation(
@@ -232,7 +246,7 @@ fn try_track_undelegation(
}
let account = account_from_address(address, deps.storage, deps.api)?;
account.track_undelegation(mix_identity, amount, deps.storage)?;
Ok(Response::default())
Ok(Response::new().add_event(new_track_undelegation_event()))
}
fn try_delegate_to_mixnode(
@@ -275,15 +289,24 @@ fn try_create_periodic_vesting_account(
};
let start_time = start_time.unwrap_or_else(|| env.block.time.seconds());
let periods = populate_vesting_periods(start_time, NUM_VESTING_PERIODS);
let start_time = Timestamp::from_seconds(start_time);
Account::new(
owner_address,
staking_address,
coin,
Timestamp::from_seconds(start_time),
owner_address.clone(),
staking_address.clone(),
coin.clone(),
start_time,
periods,
deps.storage,
)?;
Ok(Response::default())
Ok(
Response::new().add_event(new_periodic_vesting_account_event(
&owner_address,
&coin,
&staking_address,
start_time,
)),
)
}
#[entry_point]
+4 -4
View File
@@ -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),
+1 -2
View File
@@ -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};
+2 -1
View File
@@ -1,8 +1,9 @@
use crate::errors::ContractError;
use crate::vesting::Account;
use cosmwasm_std::{Addr, Api, Storage};
use cw_storage_plus::Map;
use cw_storage_plus::{Item, Map};
pub const KEY: Item<u32> = Item::new("key");
const ACCOUNTS: Map<Addr, Account> = Map::new("acc");
pub fn delete_account(address: &Addr, storage: &mut dyn Storage) -> Result<(), ContractError> {
+1 -1
View File
@@ -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(
@@ -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(
@@ -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(
@@ -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<Response, ContractError> {
@@ -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(),
@@ -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<Response, ContractError> {
@@ -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(),
+7 -16
View File
@@ -1,12 +1,10 @@
use super::{PledgeData, VestingPeriod};
use crate::contract::NUM_VESTING_PERIODS;
use crate::errors::ContractError;
use crate::storage::save_account;
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 rand::rngs::StdRng;
use rand::{RngCore, SeedableRng};
use mixnet_contract_common::IdentityKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -20,17 +18,10 @@ const BALANCE_SUFFIX: &str = "ba";
const PLEDGE_SUFFIX: &str = "bo";
const GATEWAY_SUFFIX: &str = "ga";
fn generate_storage_key(b: &[u8], storage: &dyn Storage) -> Result<String, ContractError> {
let mut rng = StdRng::seed_from_u64(b.iter().fold(0, |acc, x| acc + *x as u64));
// Be paranoid and check for collisions
loop {
let key = rng.next_u64().to_string();
let balance_key = format!("{}{}", key, BALANCE_SUFFIX);
let balance: Item<Uint128> = Item::new(&balance_key);
if balance.may_load(storage)?.is_none() {
return Ok(key);
}
}
fn generate_storage_key(storage: &mut dyn Storage) -> Result<u32, ContractError> {
let key = KEY.may_load(storage)?.unwrap_or(0) + 1;
KEY.save(storage, &key)?;
Ok(key)
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -52,7 +43,7 @@ impl Account {
periods: Vec<VestingPeriod>,
storage: &mut dyn Storage,
) -> Result<Self, ContractError> {
let storage_key = generate_storage_key(owner_address.as_bytes(), storage)?;
let storage_key = generate_storage_key(storage)?.to_string();
let amount = coin.amount;
let account = Account {
owner_address,
+3 -6
View File
@@ -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() {
@@ -78,10 +78,7 @@ mod tests {
// Test key collision avoidance
let account_again = vesting_account_fixture(&mut deps.storage, &env);
assert_eq!(
created_account.balance_key(),
"5032709489228919411ba".to_string()
);
assert_eq!(created_account.balance_key(), "1ba".to_string());
assert_ne!(created_account.balance_key(), account_again.balance_key());
}
@@ -127,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", &[]);
+1 -1
View File
@@ -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"] }
@@ -1,10 +1,12 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mix_nodes::location::{GeoLocation, Location};
use crate::state::ExplorerApiStateContext;
use log::{info, warn};
use reqwest::Error as ReqwestError;
use thiserror::Error;
use crate::mix_nodes::{GeoLocation, Location};
use crate::state::ExplorerApiStateContext;
pub(crate) struct GeoLocateTask {
state: ExplorerApiStateContext,
}
@@ -34,7 +36,15 @@ impl GeoLocateTask {
}
async fn locate_mix_nodes(&mut self) {
let mixnode_bonds = self.state.inner.mix_nodes.get().await.value;
// I'm unwrapping to the default value to get rid of an extra indentation level from the `if let Some(...) = ...`
// If the value is None, we'll unwrap to an empty hashmap and the `values()` loop won't do any work anyway
let mixnode_bonds = self
.state
.inner
.mix_nodes
.get_mixnodes()
.await
.unwrap_or_default();
for (i, cache_item) in mixnode_bonds.values().enumerate() {
if self
+3 -5
View File
@@ -36,13 +36,11 @@ impl ExplorerApi {
async fn run(&mut self) {
info!("Explorer API starting up...");
info!(
"Using validator API - {}",
network_defaults::default_api_endpoints()[0].clone()
);
let validator_api_url = network_defaults::default_api_endpoints()[0].clone();
info!("Using validator API - {}", validator_api_url);
// spawn concurrent tasks
mix_nodes::tasks::MixNodesTasks::new(self.state.clone()).start();
mix_nodes::tasks::MixNodesTasks::new(self.state.clone(), validator_api_url).start();
country_statistics::distribution::CountryStatisticsDistributionTask::new(
self.state.clone(),
)
+7 -17
View File
@@ -1,16 +1,16 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
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_common::Delegation;
use reqwest::Error as ReqwestError;
use rocket::serde::json::Json;
use rocket::{Route, State};
use rocket_okapi::okapi::openapi3::OpenApi;
use rocket_okapi::openapi_get_routes_spec;
use rocket_okapi::settings::OpenApiSettings;
use serde::Serialize;
use mixnet_contract::{Addr, Coin, Delegation, Layer, MixNode};
use crate::mix_node::models::{NodeDescription, NodeStats};
use crate::mix_nodes::{get_mixnode_delegations, get_single_mixnode_delegations, Location};
use crate::state::ExplorerApiStateContext;
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
@@ -21,16 +21,6 @@ pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>,
]
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub(crate) struct PrettyMixNodeBondWithLocation {
pub location: Option<Location>,
pub pledge_amount: Coin,
pub total_delegation: Coin,
pub owner: Addr,
pub layer: Layer,
pub mix_node: MixNode,
}
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/delegations")]
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<Delegation>> {
+28 -6
View File
@@ -1,11 +1,33 @@
use std::sync::Arc;
use std::time::SystemTime;
use serde::Deserialize;
use serde::Serialize;
use tokio::sync::RwLock;
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mix_node::cache::Cache;
use crate::mix_nodes::location::Location;
use mixnet_contract_common::{Addr, Coin, Layer, MixNode};
use serde::Deserialize;
use serde::Serialize;
use std::sync::Arc;
use std::time::SystemTime;
use tokio::sync::RwLock;
#[derive(Clone, Debug, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub(crate) 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
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub(crate) struct PrettyDetailedMixNodeBond {
pub location: Option<Location>,
pub status: MixnodeStatus,
pub pledge_amount: Coin,
pub total_delegation: Coin,
pub owner: Addr,
pub layer: Layer,
pub mix_node: MixNode,
}
pub(crate) struct MixNodeCache {
pub(crate) descriptions: Cache<NodeDescription>,
+31
View File
@@ -0,0 +1,31 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use mixnet_contract_common::Delegation;
pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec<Delegation> {
let client = super::utils::new_nymd_client();
let delegates = match client
.get_all_nymd_single_mixnode_delegations(pubkey.to_string())
.await
{
Ok(result) => result,
Err(e) => {
error!("Could not get delegations for mix node {}: {:?}", pubkey, e);
vec![]
}
};
delegates
}
pub(crate) async fn get_mixnode_delegations() -> Vec<Delegation> {
let client = super::utils::new_nymd_client();
let delegates = match client.get_all_network_delegations().await {
Ok(result) => result,
Err(e) => {
error!("Could not get all mix delegations: {:?}", e);
vec![]
}
};
delegates
}
+4 -5
View File
@@ -1,12 +1,11 @@
use crate::mix_node::models::PrettyDetailedMixNodeBond;
use crate::state::ExplorerApiStateContext;
use rocket::serde::json::Json;
use rocket::{Route, State};
use rocket_okapi::okapi::openapi3::OpenApi;
use rocket_okapi::openapi_get_routes_spec;
use rocket_okapi::settings::OpenApiSettings;
use crate::mix_node::http::PrettyMixNodeBondWithLocation;
use crate::state::ExplorerApiStateContext;
pub fn mix_nodes_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![settings: list]
}
@@ -15,6 +14,6 @@ pub fn mix_nodes_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>,
#[get("/")]
pub(crate) async fn list(
state: &State<ExplorerApiStateContext>,
) -> Json<Vec<PrettyMixNodeBondWithLocation>> {
Json(state.inner.mix_nodes.get_mixnodes_with_location().await)
) -> Json<Vec<PrettyDetailedMixNodeBond>> {
Json(state.inner.mix_nodes.get_detailed_mixnodes().await)
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mix_nodes::utils::map_2_letter_to_3_letter_country_code;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
pub(crate) type LocationCache = HashMap<String, LocationCacheItem>;
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
pub(crate) struct GeoLocation {
pub(crate) ip: String,
pub(crate) country_code: String,
pub(crate) country_name: String,
pub(crate) region_code: String,
pub(crate) region_name: String,
pub(crate) city: String,
pub(crate) zip_code: String,
pub(crate) time_zone: String,
pub(crate) latitude: f32,
pub(crate) longitude: f32,
pub(crate) metro_code: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct LocationCacheItem {
pub(crate) location: Option<Location>,
pub(crate) valid_until: SystemTime,
}
#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
pub(crate) struct Location {
pub(crate) two_letter_iso_country_code: String,
pub(crate) three_letter_iso_country_code: String,
pub(crate) country_name: String,
pub(crate) lat: f32,
pub(crate) lng: f32,
}
impl Location {
pub(crate) fn new(geo_location: GeoLocation) -> Self {
let three_letter_iso_country_code = map_2_letter_to_3_letter_country_code(&geo_location);
Location {
country_name: geo_location.country_name,
two_letter_iso_country_code: geo_location.country_code,
three_letter_iso_country_code,
lat: geo_location.latitude,
lng: geo_location.longitude,
}
}
}
impl LocationCacheItem {
pub(crate) fn new_from_location(location: Option<Location>) -> Self {
LocationCacheItem {
location,
valid_until: SystemTime::now() + Duration::from_secs(60 * 60 * 24), // valid for 1 day
}
}
}
+11 -238
View File
@@ -1,241 +1,14 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::time::Duration;
pub(crate) mod delegations;
pub(crate) mod http;
pub(crate) mod location;
pub(crate) mod models;
pub(crate) mod tasks;
mod utils;
pub(crate) mod utils;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use rocket::tokio::sync::RwLock;
use serde::{Deserialize, Serialize};
use crate::mix_node::http::PrettyMixNodeBondWithLocation;
use crate::mix_nodes::utils::map_2_letter_to_3_letter_country_code;
use mixnet_contract::{Delegation, MixNodeBond};
use network_defaults::{
default_api_endpoints, default_nymd_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS,
};
use validator_client::nymd::QueryNymdClient;
pub(crate) type LocationCache = HashMap<String, LocationCacheItem>;
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
pub(crate) struct GeoLocation {
pub(crate) ip: String,
pub(crate) country_code: String,
pub(crate) country_name: String,
pub(crate) region_code: String,
pub(crate) region_name: String,
pub(crate) city: String,
pub(crate) zip_code: String,
pub(crate) time_zone: String,
pub(crate) latitude: f32,
pub(crate) longitude: f32,
pub(crate) metro_code: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct LocationCacheItem {
pub(crate) location: Option<Location>,
pub(crate) valid_until: SystemTime,
}
#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
pub(crate) struct Location {
pub(crate) two_letter_iso_country_code: String,
pub(crate) three_letter_iso_country_code: String,
pub(crate) country_name: String,
pub(crate) lat: f32,
pub(crate) lng: f32,
}
impl Location {
pub(crate) fn new(geo_location: GeoLocation) -> Self {
let three_letter_iso_country_code = map_2_letter_to_3_letter_country_code(&geo_location);
Location {
country_name: geo_location.country_name,
two_letter_iso_country_code: geo_location.country_code,
three_letter_iso_country_code,
lat: geo_location.latitude,
lng: geo_location.longitude,
}
}
}
impl LocationCacheItem {
pub(crate) fn new_from_location(location: Option<Location>) -> Self {
LocationCacheItem {
location,
valid_until: SystemTime::now() + Duration::from_secs(60 * 60 * 24), // valid for 1 day
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct MixNodesResult {
pub(crate) valid_until: SystemTime,
pub(crate) value: HashMap<String, MixNodeBond>,
location_cache: LocationCache,
}
#[derive(Clone)]
pub(crate) struct ThreadsafeMixNodesResult {
inner: Arc<RwLock<MixNodesResult>>,
}
impl ThreadsafeMixNodesResult {
pub(crate) fn new() -> Self {
ThreadsafeMixNodesResult {
inner: Arc::new(RwLock::new(MixNodesResult {
value: HashMap::new(),
valid_until: SystemTime::now() - Duration::from_secs(60), // in the past
location_cache: LocationCache::new(),
})),
}
}
pub(crate) fn new_with_location_cache(location_cache: LocationCache) -> Self {
ThreadsafeMixNodesResult {
inner: Arc::new(RwLock::new(MixNodesResult {
value: HashMap::new(),
valid_until: SystemTime::now() - Duration::from_secs(60), // in the past
location_cache,
})),
}
}
pub(crate) async fn is_location_valid(&self, identity_key: &str) -> bool {
self.inner
.read()
.await
.location_cache
.get(identity_key)
.map(|cache_item| cache_item.valid_until > SystemTime::now())
.unwrap_or(false)
}
pub(crate) async fn get_location_cache(&self) -> LocationCache {
self.inner.read().await.location_cache.clone()
}
pub(crate) async fn set_location(&self, identity_key: &str, location: Option<Location>) {
let mut guard = self.inner.write().await;
// cache the location for this mix node so that it can be used when the mix node list is refreshed
guard.location_cache.insert(
identity_key.to_string(),
LocationCacheItem::new_from_location(location),
);
}
pub(crate) async fn get(&self) -> MixNodesResult {
// check ttl
let valid_until = self.inner.read().await.valid_until;
if valid_until < SystemTime::now() {
// force reload
self.refresh().await;
}
// return in-memory cache
self.inner.read().await.clone()
}
pub(crate) async fn get_mixnodes_with_location(&self) -> Vec<PrettyMixNodeBondWithLocation> {
let guard = self.inner.read().await;
guard
.value
.values()
.map(|bond| {
let location = guard.location_cache.get(&bond.mix_node.identity_key);
let copy = bond.clone();
PrettyMixNodeBondWithLocation {
location: location.and_then(|l| l.location.clone()),
pledge_amount: copy.pledge_amount,
total_delegation: copy.total_delegation,
owner: copy.owner,
layer: copy.layer,
mix_node: copy.mix_node,
}
})
.collect()
}
pub(crate) async fn refresh(&self) {
// get mixnodes and cache the new value
let value = retrieve_mixnodes().await;
let location_cache = self.inner.read().await.location_cache.clone();
*self.inner.write().await = MixNodesResult {
value: value
.into_iter()
.map(|bond| (bond.mix_node.identity_key.to_string(), bond))
.collect(),
valid_until: SystemTime::now() + Duration::from_secs(30),
location_cache,
};
}
}
pub(crate) async fn retrieve_mixnodes() -> Vec<MixNodeBond> {
let client = new_validator_client();
info!("About to retrieve mixnode bonds...");
let bonds: Vec<MixNodeBond> = match client.get_cached_mixnodes().await {
Ok(result) => result,
Err(e) => {
error!("Unable to retrieve mixnode bonds: {:?}", e);
vec![]
}
};
info!("Fetched {} mixnode bonds", bonds.len());
bonds
}
pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec<Delegation> {
let client = new_nymd_client();
let delegates = match client
.get_all_nymd_single_mixnode_delegations(pubkey.to_string())
.await
{
Ok(result) => result,
Err(e) => {
error!("Could not get delegations for mix node {}: {:?}", pubkey, e);
vec![]
}
};
delegates
}
pub(crate) async fn get_mixnode_delegations() -> Vec<Delegation> {
let client = new_nymd_client();
let delegates = match client.get_all_network_delegations().await {
Ok(result) => result,
Err(e) => {
error!("Could not get all mix delegations: {:?}", e);
vec![]
}
};
delegates
}
fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
let mixnet_contract = DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string();
let nymd_url = default_nymd_endpoints()[0].clone();
let api_url = default_api_endpoints()[0].clone();
let client_config = validator_client::Config::new(
nymd_url,
api_url,
Some(mixnet_contract.parse().unwrap()),
None,
);
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!")
}
// TODO: inject constants
fn new_validator_client() -> validator_client::ApiClient {
validator_client::ApiClient::new(default_api_endpoints()[0].clone())
}
pub(crate) const MIXNODES_CACHE_REFRESH_RATE: Duration = Duration::from_secs(30);
pub(crate) const MIXNODES_CACHE_ENTRY_TTL: Duration = Duration::from_secs(60);
+150
View File
@@ -0,0 +1,150 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
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_common::MixNodeBond;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use tokio::sync::RwLock;
#[derive(Clone, Debug)]
pub(crate) struct MixNodesResult {
pub(crate) valid_until: SystemTime,
pub(crate) all_mixnodes: HashMap<String, MixNodeBond>,
active_mixnodes: HashSet<String>,
rewarded_mixnodes: HashSet<String>,
}
impl MixNodesResult {
fn new() -> Self {
MixNodesResult {
valid_until: SystemTime::now() - Duration::from_secs(60), // in the past
all_mixnodes: HashMap::new(),
active_mixnodes: HashSet::new(),
rewarded_mixnodes: HashSet::new(),
}
}
fn determine_node_status(&self, public_key: &str) -> MixnodeStatus {
if self.active_mixnodes.contains(public_key) {
MixnodeStatus::Active
} else if self.rewarded_mixnodes.contains(public_key) {
MixnodeStatus::Standby
} else {
MixnodeStatus::Inactive
}
}
fn is_valid(&self) -> bool {
self.valid_until >= SystemTime::now()
}
fn get_mixnode(&self, pubkey: &str) -> Option<MixNodeBond> {
if self.is_valid() {
self.all_mixnodes.get(pubkey).cloned()
} else {
None
}
}
fn get_mixnodes(&self) -> Option<HashMap<String, MixNodeBond>> {
if self.is_valid() {
Some(self.all_mixnodes.clone())
} else {
None
}
}
}
#[derive(Clone)]
pub(crate) struct ThreadsafeMixNodesResult {
mixnode_results: Arc<RwLock<MixNodesResult>>,
location_cache: Arc<RwLock<LocationCache>>,
}
impl ThreadsafeMixNodesResult {
pub(crate) fn new() -> Self {
ThreadsafeMixNodesResult {
mixnode_results: Arc::new(RwLock::new(MixNodesResult::new())),
location_cache: Arc::new(RwLock::new(LocationCache::new())),
}
}
pub(crate) fn new_with_location_cache(location_cache: LocationCache) -> Self {
ThreadsafeMixNodesResult {
mixnode_results: Arc::new(RwLock::new(MixNodesResult::new())),
location_cache: Arc::new(RwLock::new(location_cache)),
}
}
pub(crate) async fn is_location_valid(&self, identity_key: &str) -> bool {
self.location_cache
.read()
.await
.get(identity_key)
.map(|cache_item| cache_item.valid_until > SystemTime::now())
.unwrap_or(false)
}
pub(crate) async fn get_location_cache(&self) -> LocationCache {
self.location_cache.read().await.clone()
}
pub(crate) async fn set_location(&self, identity_key: &str, location: Option<Location>) {
// cache the location for this mix node so that it can be used when the mix node list is refreshed
self.location_cache.write().await.insert(
identity_key.to_string(),
LocationCacheItem::new_from_location(location),
);
}
pub(crate) async fn get_mixnode(&self, pubkey: &str) -> Option<MixNodeBond> {
self.mixnode_results.read().await.get_mixnode(pubkey)
}
pub(crate) async fn get_mixnodes(&self) -> Option<HashMap<String, MixNodeBond>> {
self.mixnode_results.read().await.get_mixnodes()
}
pub(crate) async fn get_detailed_mixnodes(&self) -> Vec<PrettyDetailedMixNodeBond> {
let mixnodes_guard = self.mixnode_results.read().await;
let location_guard = self.location_cache.read().await;
mixnodes_guard
.all_mixnodes
.values()
.map(|bond| {
let location = location_guard.get(&bond.mix_node.identity_key);
let copy = bond.clone();
PrettyDetailedMixNodeBond {
location: location.and_then(|l| l.location.clone()),
status: mixnodes_guard.determine_node_status(&bond.mix_node.identity_key),
pledge_amount: copy.pledge_amount,
total_delegation: copy.total_delegation,
owner: copy.owner,
layer: copy.layer,
mix_node: copy.mix_node,
}
})
.collect()
}
pub(crate) async fn update_cache(
&self,
all_bonds: Vec<MixNodeBond>,
rewarded_nodes: HashSet<String>,
active_nodes: HashSet<String>,
) {
let mut guard = self.mixnode_results.write().await;
guard.all_mixnodes = all_bonds
.into_iter()
.map(|bond| (bond.mix_node.identity_key.to_string(), bond))
.collect();
guard.rewarded_mixnodes = rewarded_nodes;
guard.active_mixnodes = active_nodes;
guard.valid_until = SystemTime::now() + MIXNODES_CACHE_ENTRY_TTL;
}
}
+73 -4
View File
@@ -1,24 +1,93 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mix_nodes::MIXNODES_CACHE_REFRESH_RATE;
use crate::state::ExplorerApiStateContext;
use mixnet_contract_common::MixNodeBond;
use reqwest::Url;
use std::future::Future;
use validator_client::ValidatorClientError;
pub(crate) struct MixNodesTasks {
state: ExplorerApiStateContext,
validator_api_client: validator_client::ApiClient,
}
impl MixNodesTasks {
pub(crate) fn new(state: ExplorerApiStateContext) -> Self {
MixNodesTasks { state }
pub(crate) fn new(state: ExplorerApiStateContext, validator_api_endpoint: Url) -> Self {
MixNodesTasks {
state,
validator_api_client: validator_client::ApiClient::new(validator_api_endpoint),
}
}
// a helper to remove duplicate code when grabbing active/rewarded/all mixnodes
async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec<MixNodeBond>
where
F: FnOnce(&'a validator_client::ApiClient) -> Fut,
Fut: Future<Output = Result<Vec<MixNodeBond>, ValidatorClientError>>,
{
let bonds = match f(&self.validator_api_client).await {
Ok(result) => result,
Err(e) => {
error!("Unable to retrieve mixnode bonds: {:?}", e);
vec![]
}
};
info!("Fetched {} mixnode bonds", bonds.len());
bonds
}
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBond> {
info!("About to retrieve all mixnode bonds...");
self.retrieve_mixnodes(validator_client::ApiClient::get_cached_mixnodes)
.await
}
async fn retrieve_rewarded_mixnodes(&self) -> Vec<MixNodeBond> {
info!("About to retrieve rewarded mixnode bonds...");
self.retrieve_mixnodes(validator_client::ApiClient::get_cached_rewarded_mixnodes)
.await
}
async fn retrieve_active_mixnodes(&self) -> Vec<MixNodeBond> {
info!("About to retrieve active mixnode bonds...");
self.retrieve_mixnodes(validator_client::ApiClient::get_cached_active_mixnodes)
.await
}
async fn update_mixnode_cache(&self) {
let all_bonds = self.retrieve_all_mixnodes().await;
let rewarded_nodes = self
.retrieve_rewarded_mixnodes()
.await
.into_iter()
.map(|bond| bond.mix_node.identity_key)
.collect();
let active_nodes = self
.retrieve_active_mixnodes()
.await
.into_iter()
.map(|bond| bond.mix_node.identity_key)
.collect();
self.state
.inner
.mix_nodes
.update_cache(all_bonds, rewarded_nodes, active_nodes)
.await;
}
pub(crate) fn start(self) {
info!("Spawning mix nodes task runner...");
tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(std::time::Duration::from_secs(2));
let mut interval_timer = tokio::time::interval(MIXNODES_CACHE_REFRESH_RATE);
loop {
// wait for the next interval tick
interval_timer.tick().await;
info!("Updating mix node cache...");
self.state.inner.mix_nodes.refresh().await;
self.update_mixnode_cache().await;
info!("Done");
}
});
+23 -1
View File
@@ -1,5 +1,12 @@
use crate::mix_nodes::GeoLocation;
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mix_nodes::location::GeoLocation;
use isocountry::CountryCode;
use network_defaults::{
default_api_endpoints, default_nymd_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS,
};
use validator_client::nymd::QueryNymdClient;
pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String {
match CountryCode::for_alpha2(&geo.country_code) {
@@ -13,3 +20,18 @@ pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String
}
}
}
pub(crate) fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
let mixnet_contract = DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string();
let nymd_url = default_nymd_endpoints()[0].clone();
let api_url = default_api_endpoints()[0].clone();
let client_config = validator_client::Config::new(
nymd_url,
api_url,
Some(mixnet_contract.parse().unwrap()),
None,
);
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!")
}
+1 -1
View File
@@ -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;
+4 -3
View File
@@ -5,13 +5,14 @@ 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,
};
use crate::mix_node::models::ThreadsafeMixNodeCache;
use crate::mix_nodes::{LocationCache, ThreadsafeMixNodesResult};
use crate::mix_nodes::location::LocationCache;
use crate::mix_nodes::models::ThreadsafeMixNodesResult;
use crate::ping::models::ThreadsafePingCache;
// TODO: change to an environment variable with a default value
@@ -27,7 +28,7 @@ pub struct ExplorerApiState {
impl ExplorerApiState {
pub(crate) async fn get_mix_node(&self, pubkey: &str) -> Option<MixNodeBond> {
self.mix_nodes.get().await.value.get(pubkey).cloned()
self.mix_nodes.get_mixnode(pubkey).await
}
}
+215 -61
View File
@@ -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,12 +5336,18 @@ dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"getrandom 0.2.3",
"mixnet-contract",
"rand 0.8.4",
"mixnet-contract-common",
"schemars",
"serde",
"thiserror",
"vesting-contract-common",
]
[[package]]
name = "vesting-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
]
[[package]]
+6 -6
View File
@@ -24,18 +24,18 @@ dirs = "4.0"
bip39 = "1.0"
thiserror = "1.0"
tendermint-rpc = "0.23.0"
url = "2.0"
url = "2.2"
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"]
+8
View File
@@ -46,6 +46,14 @@ impl FromStr for Denom {
}
}
impl TryFrom<CosmosDenom> for Denom {
type Error = BackendError;
fn try_from(value: CosmosDenom) -> Result<Self, Self::Error> {
Denom::from_str(&value.to_string())
}
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Coin {
+21 -14
View File
@@ -5,15 +5,12 @@ use config::defaults::{default_validators, ValidatorDetails, DEFAULT_MIXNET_CONT
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::str::FromStr;
use tendermint_rpc::Url;
use url::Url;
mod template;
use template::config_template;
use crate::error::BackendError;
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Config {
@@ -68,23 +65,33 @@ impl NymConfig for Config {
}
impl Config {
pub fn get_nymd_validator_url(&self) -> Result<Url, BackendError> {
pub fn get_nymd_validator_url(&self) -> Url {
// TODO make this a random choice
if let Some(validator_details) = self.base.validators.first() {
match tendermint_rpc::Url::from_str(&validator_details.nymd_url().to_string()) {
Ok(url) => Ok(url),
Err(e) => Err(e.into()),
}
validator_details.nymd_url()
} else {
panic!("No validators found in config")
}
}
pub fn get_mixnet_contract_address(&self) -> String {
self.base.mixnet_contract_address.clone()
pub fn get_validator_api_url(&self) -> Url {
// TODO make this a random choice
if let Some(validator_details) = self.base.validators.first() {
validator_details.api_url().expect("no api url provided")
} else {
panic!("No validators found in config")
}
}
// pub fn get_mnemonic(&self) -> String {
// self.base.mnemonic.clone()
// }
pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId {
self
.base
.mixnet_contract_address
.parse()
.expect("stored mixnet contract address is not a valid account address")
}
pub fn get_vesting_contract_address(&self) -> Option<cosmrs::AccountId> {
None
}
}
+6
View File
@@ -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}")]
+17 -5
View File
@@ -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;
@@ -36,13 +37,14 @@ fn main() {
mixnet::bond::bond_mixnode,
mixnet::bond::unbond_gateway,
mixnet::bond::unbond_mixnode,
mixnet::bond::update_mixnode,
mixnet::bond::mixnode_bond_details,
mixnet::bond::gateway_bond_details,
mixnet::delegate::delegate_to_mixnode,
mixnet::delegate::get_reverse_mix_delegations_paged,
mixnet::delegate::undelegate_from_mixnode,
mixnet::send::send,
utils::get_approximate_fee,
utils::outdated_get_approximate_fee,
utils::major_to_minor,
utils::minor_to_major,
utils::owns_gateway,
@@ -62,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!())
@@ -71,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",
}
}
@@ -1,14 +1,16 @@
use crate::client;
use crate::coin::{Coin, Denom};
use crate::config::Config;
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use bip39::{Language, Mnemonic};
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use validator_client::nymd::{AccountId, NymdClient, SigningNymdClient};
use validator_client::nymd::SigningNymdClient;
use validator_client::Client;
#[cfg_attr(test, derive(ts_rs::TS))]
#[derive(Serialize, Deserialize)]
@@ -37,14 +39,14 @@ pub async fn connect_with_mnemonic(
_connect_with_mnemonic(mnemonic, &r_state.config())
};
let contract_address = client.mixnet_contract_address()?.to_string();
let client_address = client.address().to_string();
let denom = client.denom()?;
let contract_address = client.nymd.mixnet_contract_address()?.to_string();
let client_address = client.nymd.address().to_string();
let denom = client.nymd.denom()?;
let account = Account {
contract_address,
client_address,
denom: Denom::from_str(&denom.to_string())?,
denom: denom.try_into()?,
mnemonic: None,
};
@@ -58,8 +60,8 @@ pub async fn connect_with_mnemonic(
pub async fn get_balance(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Balance, BackendError> {
match client!(state)
.get_mixnet_balance(client!(state).address())
match nymd_client!(state)
.get_mixnet_balance(nymd_client!(state).address())
.await
{
Ok(Some(coin)) => {
@@ -73,7 +75,7 @@ pub async fn get_balance(
})
}
Ok(None) => Err(BackendError::NoBalance(
client!(state).address().to_string(),
nymd_client!(state).address().to_string(),
)),
Err(e) => Err(BackendError::from(e)),
}
@@ -94,13 +96,15 @@ fn random_mnemonic() -> Mnemonic {
Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap()
}
fn _connect_with_mnemonic(mnemonic: Mnemonic, config: &Config) -> NymdClient<SigningNymdClient> {
match NymdClient::connect_with_mnemonic(
config.get_nymd_validator_url().unwrap(),
Some(AccountId::from_str(&config.get_mixnet_contract_address()).unwrap()),
None,
fn _connect_with_mnemonic(mnemonic: Mnemonic, config: &Config) -> Client<SigningNymdClient> {
match validator_client::Client::new_signing(
validator_client::Config::new(
config.get_nymd_validator_url(),
config.get_validator_api_url(),
Some(config.get_mixnet_contract_address()),
config.get_vesting_contract_address(),
),
mnemonic,
None,
) {
Ok(client) => client,
Err(e) => panic!("{}", e),
@@ -1,8 +1,8 @@
use crate::client;
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;
@@ -48,7 +48,7 @@ impl TryFrom<TauriContractStateParams> for ContractStateParams {
pub async fn get_contract_settings(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriContractStateParams, BackendError> {
Ok(client!(state).get_contract_settings().await?.into())
Ok(nymd_client!(state).get_contract_settings().await?.into())
}
#[tauri::command]
@@ -57,7 +57,7 @@ pub async fn update_contract_settings(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<TauriContractStateParams, BackendError> {
let mixnet_contract_settings_params: ContractStateParams = params.try_into()?;
client!(state)
nymd_client!(state)
.update_contract_settings(mixnet_contract_settings_params.clone())
.await?;
Ok(mixnet_contract_settings_params.into())
@@ -1,9 +1,9 @@
use crate::client;
use crate::coin::Coin;
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;
@@ -15,7 +15,7 @@ pub async fn bond_gateway(
owner_signature: String,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
nymd_client!(state)
.bond_gateway(gateway, owner_signature, pledge.try_into()?)
.await?;
Ok(())
@@ -25,7 +25,7 @@ pub async fn bond_gateway(
pub async fn unbond_gateway(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state).unbond_gateway().await?;
nymd_client!(state).unbond_gateway().await?;
Ok(())
}
@@ -33,7 +33,7 @@ pub async fn unbond_gateway(
pub async fn unbond_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state).unbond_mixnode().await?;
nymd_client!(state).unbond_mixnode().await?;
Ok(())
}
@@ -44,19 +44,30 @@ pub async fn bond_mixnode(
pledge: Coin,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
nymd_client!(state)
.bond_mixnode(mixnode, owner_signature, pledge.try_into()?)
.await?;
Ok(())
}
#[tauri::command]
pub async fn update_mixnode(
profit_margin_percent: u8,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
nymd_client!(state)
.update_mixnode_config(profit_margin_percent)
.await?;
Ok(())
}
#[tauri::command]
pub async fn mixnode_bond_details(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Option<MixNodeBond>, BackendError> {
let guard = state.read().await;
let client = guard.client()?;
let bond = client.owns_mixnode(client.address()).await?;
let bond = client.nymd.owns_mixnode(client.nymd.address()).await?;
Ok(bond)
}
@@ -66,6 +77,6 @@ pub async fn gateway_bond_details(
) -> Result<Option<GatewayBond>, BackendError> {
let guard = state.read().await;
let client = guard.client()?;
let bond = client.owns_gateway(client.address()).await?;
let bond = client.nymd.owns_gateway(client.nymd.address()).await?;
Ok(bond)
}
@@ -1,10 +1,10 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
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;
@@ -16,11 +16,11 @@ pub async fn delegate_to_mixnode(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, BackendError> {
let delegation: CosmWasmCoin = amount.try_into()?;
client!(state)
nymd_client!(state)
.delegate_to_mixnode(identity, &delegation)
.await?;
Ok(DelegationResult::new(
&client!(state).address().to_string(),
&nymd_client!(state).address().to_string(),
identity,
Some(delegation.into()),
))
@@ -31,9 +31,11 @@ pub async fn undelegate_from_mixnode(
identity: &str,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<DelegationResult, BackendError> {
client!(state).remove_mixnode_delegation(identity).await?;
nymd_client!(state)
.remove_mixnode_delegation(identity)
.await?;
Ok(DelegationResult::new(
&client!(state).address().to_string(),
&nymd_client!(state).address().to_string(),
identity,
None,
))
@@ -44,8 +46,8 @@ pub async fn get_reverse_mix_delegations_paged(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<PagedDelegatorDelegationsResponse, BackendError> {
Ok(
client!(state)
.get_delegator_delegations_paged(client!(state).address().to_string(), None, None)
nymd_client!(state)
.get_delegator_delegations_paged(nymd_client!(state).address().to_string(), None, None)
.await?,
)
}
@@ -1,6 +1,6 @@
use crate::client;
use crate::coin::Coin;
use crate::error::BackendError;
use crate::nymd_client;
use crate::state::State;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
@@ -51,13 +51,13 @@ pub async fn send(
) -> Result<TauriTxResult, BackendError> {
let address = AccountId::from_str(address)?;
let cosmos_amount: CosmosCoin = amount.clone().try_into()?;
let result = client!(state)
let result = nymd_client!(state)
.send(&address, vec![cosmos_amount], memo)
.await?;
Ok(TauriTxResult::new(
result,
TransactionDetails {
from_address: client!(state).address().to_string(),
from_address: nymd_client!(state).address().to_string(),
to_address: address.to_string(),
amount,
},

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