diff --git a/Cargo.lock b/Cargo.lock index e417e0bc27..9f17b2da89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,7 +555,7 @@ dependencies = [ [[package]] name = "client-core" -version = "0.12.0" +version = "1.0.0-rc.1" dependencies = [ "config", "crypto", @@ -742,6 +742,16 @@ dependencies = [ "tendermint-proto", ] +[[package]] +name = "cosmos-sdk-proto" +version = "0.9.0" +source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" +dependencies = [ + "prost", + "prost-types", + "tendermint-proto", +] + [[package]] name = "cosmrs" version = "0.4.1" @@ -749,7 +759,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d" dependencies = [ "bip32", - "cosmos-sdk-proto", + "cosmos-sdk-proto 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ecdsa 0.13.4", + "eyre", + "getrandom 0.2.5", + "k256 0.10.4", + "prost", + "prost-types", + "rand_core 0.6.3", + "serde", + "serde_json", + "subtle-encoding", + "tendermint", + "thiserror", +] + +[[package]] +name = "cosmrs" +version = "0.4.1" +source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" +dependencies = [ + "bip32", + "cosmos-sdk-proto 0.9.0 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)", "ecdsa 0.13.4", "eyre", "getrandom 0.2.5", @@ -767,31 +798,31 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.0-beta6" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dddc1443004c6340e55ca66d98e9d2f1a44aadf4ce2bed2c4f29baa8a15e7b7" +checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3" dependencies = [ "digest 0.9.0", "ed25519-zebra", "k256 0.9.6", - "rand_core 0.5.1", + "rand_core 0.6.3", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0-beta6" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4f0f10f165b8bcc558a13cddb498140960544519aa0581532c766dd80b5598" +checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45" dependencies = [ "syn", ] [[package]] name = "cosmwasm-std" -version = "1.0.0-beta6" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0f3145097b692b2d95fa5d2c7c6fdd60f193ccc709857e7e1987a608725300" +checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0" dependencies = [ "base64", "cosmwasm-crypto", @@ -866,7 +897,7 @@ version = "0.1.0" dependencies = [ "bls12_381", "coconut-interface", - "cosmrs", + "cosmrs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "crypto", "network-defaults", "rand 0.7.3", @@ -1113,20 +1144,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.11.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7ee1963302b0ac2a9d42fe0faec826209c17452bfd36fbfd9d002a88929261" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c087ff98fb0475db4c2b5298a5fd12b2848d2854b39d1115d930ee6da24d1eed" +checksum = "5e8b7f9a758c030d375520df947323c052704f784561fc28dcaab4f988c50a30" dependencies = [ "cosmwasm-std", "schemars", @@ -1364,16 +1384,17 @@ dependencies = [ [[package]] name = "ed25519-zebra" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" dependencies = [ "curve25519-dalek", "hex", - "rand_core 0.5.1", + "rand_core 0.6.3", "serde", "sha2", "thiserror", + "zeroize", ] [[package]] @@ -1512,7 +1533,7 @@ dependencies = [ [[package]] name = "explorer-api" -version = "0.1.0" +version = "1.0.0-rc.1" dependencies = [ "chrono", "humantime-serde", @@ -1856,7 +1877,7 @@ name = "gateway-client" version = "0.1.0" dependencies = [ "coconut-interface", - "cosmrs", + "cosmrs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "credentials", "crypto", "fluvio-wasm-timer", @@ -2952,7 +2973,7 @@ dependencies = [ [[package]] name = "nym-client" -version = "0.12.1" +version = "1.0.0-rc.1" dependencies = [ "clap 2.34.0", "client-core", @@ -2986,7 +3007,7 @@ dependencies = [ [[package]] name = "nym-gateway" -version = "0.12.1" +version = "1.0.0-rc.1" dependencies = [ "anyhow", "async-trait", @@ -3032,7 +3053,7 @@ dependencies = [ [[package]] name = "nym-mixnode" -version = "0.12.1" +version = "1.0.0-rc.1" dependencies = [ "anyhow", "bs58", @@ -3070,7 +3091,7 @@ dependencies = [ [[package]] name = "nym-network-requester" -version = "0.12.0" +version = "1.0.0-rc.1" dependencies = [ "clap 2.34.0", "dirs", @@ -3091,7 +3112,7 @@ dependencies = [ [[package]] name = "nym-socks5-client" -version = "0.12.1" +version = "1.0.0-rc.1" dependencies = [ "clap 2.34.0", "client-core", @@ -3126,7 +3147,7 @@ dependencies = [ [[package]] name = "nym-validator-api" -version = "0.12.0" +version = "1.0.0-rc.1" dependencies = [ "anyhow", "async-trait", @@ -3163,6 +3184,7 @@ dependencies = [ "thiserror", "time 0.3.7", "tokio", + "tokio-stream", "topology", "url", "validator-api-requests", @@ -6071,7 +6093,7 @@ dependencies = [ "coconut-interface", "colored", "config", - "cosmrs", + "cosmrs 0.4.1 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)", "cosmwasm-std", "flate2", "futures", @@ -6143,11 +6165,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vesting-contract" -version = "0.1.0" +version = "1.0.0" dependencies = [ "config", "cosmwasm-std", - "cw-storage-plus 0.12.1", + "cw-storage-plus", "mixnet-contract-common", "schemars", "serde", @@ -6161,7 +6183,7 @@ version = "0.1.0" dependencies = [ "config", "cosmwasm-std", - "cw-storage-plus 0.11.1", + "cw-storage-plus", "mixnet-contract-common", "schemars", "serde", diff --git a/clients/client-core/Cargo.toml b/clients/client-core/Cargo.toml index f566d30557..9b2cdaf2c7 100644 --- a/clients/client-core/Cargo.toml +++ b/clients/client-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "client-core" -version = "0.12.0" +version = "1.0.0-rc.1" authors = ["Dave Hrycyszyn "] edition = "2021" diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 9adeaa4924..1f11c15060 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-client" -version = "0.12.1" +version = "1.0.0-rc.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] edition = "2021" rust-version = "1.56" diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 6443190796..a607ee4930 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-socks5-client" -version = "0.12.1" +version = "1.0.0-rc.1" authors = ["Dave Hrycyszyn "] edition = "2021" rust-version = "1.56" diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index 72321e98e3..77aadd6ef3 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nym-client-wasm" authors = ["Dave Hrycyszyn ", "Jedrzej Stuczynski "] -version = "0.12.0" +version = "1.0.0-rc.1" edition = "2021" keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] license = "Apache-2.0" diff --git a/common/client-libs/gateway-client/src/packet_router.rs b/common/client-libs/gateway-client/src/packet_router.rs index 8fb35aed5f..7d650ad695 100644 --- a/common/client-libs/gateway-client/src/packet_router.rs +++ b/common/client-libs/gateway-client/src/packet_router.rs @@ -72,7 +72,12 @@ impl PacketRouter { if !received_acks.is_empty() { trace!("routing acks"); - self.ack_sender.unbounded_send(received_acks).unwrap(); + match self.ack_sender.unbounded_send(received_acks) { + Ok(_) => {} + Err(e) => { + error!("failed to send ack: {:?}", e); + } + }; } } } diff --git a/common/client-libs/gateway-client/src/socket_state.rs b/common/client-libs/gateway-client/src/socket_state.rs index 9fba57208f..5de854921c 100644 --- a/common/client-libs/gateway-client/src/socket_state.rs +++ b/common/client-libs/gateway-client/src/socket_state.rs @@ -74,7 +74,7 @@ impl PartiallyDelegated { // This would also require NOT discarding any text responses here. // TODO: those can return the "send confirmations" - perhaps it should be somehow worked around? - Message::Text(text) => debug!( + Message::Text(text) => trace!( "received a text message - probably a response to some previous query! - {}", text ), diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index cd0ed34c0e..d507d8ef27 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -37,7 +37,7 @@ prost = { version = "0.9", default-features = false, optional = true } flate2 = { version = "1.0.20", optional = true } sha2 = { version = "0.9.5", optional = true } itertools = { version = "0.10", optional = true } -cosmwasm-std = { version = "1.0.0-beta3", optional = true } +cosmwasm-std = { version = "1.0.0-beta6", optional = true } [dev-dependencies] ts-rs = "6.1.2" diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index 216251b6f7..d8effaf060 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -599,6 +599,7 @@ impl NymdClient { &self, mix_identity: IdentityKey, delegator: &AccountId, + proxy: Option, ) -> Result where C: CosmWasmClient + Sync, @@ -606,6 +607,7 @@ impl NymdClient { let request = QueryMsg::GetDelegationDetails { mix_identity, delegator: delegator.to_string(), + proxy, }; self.client .query_contract_smart(self.mixnet_contract_address()?, &request) diff --git a/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml b/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml index 26c0b3d4af..74ca0bf3ec 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml +++ b/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml @@ -7,4 +7,4 @@ 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" +cosmwasm-std = "1.0.0-beta6" diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml index 0217635117..280a2d8e0d 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml @@ -7,7 +7,7 @@ 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" +cosmwasm-std = "1.0.0-beta6" serde = { version = "1.0", features = ["derive"] } serde_repr = "0.1" diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs index 5e6c328ecf..645245d8e6 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs @@ -9,6 +9,23 @@ use cosmwasm_std::{Coin, Uint128}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::Display; +use std::hash::{Hash, Hasher}; + +type OwnerAddressBytes = Vec; +type BlockHeight = u64; + +pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec { + if let Some(proxy) = &proxy { + address + .as_bytes() + .iter() + .zip(proxy.as_bytes()) + .map(|(x, y)| x ^ y) + .collect() + } else { + address.as_bytes().to_vec() + } +} #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] pub struct Delegation { @@ -19,12 +36,24 @@ pub struct Delegation { pub proxy: Option, // proxy address used to delegate the funds on behalf of anouther address } +impl Eq for Delegation {} + +#[allow(clippy::derive_hash_xor_eq)] +impl Hash for Delegation { + fn hash(&self, state: &mut H) { + self.owner.hash(state); + self.node_identity.hash(state); + self.block_height.hash(state); + self.proxy.hash(state); + } +} + impl Delegation { pub fn new( owner: Addr, node_identity: IdentityKey, amount: Coin, - block_height: u64, + block_height: BlockHeight, proxy: Option, ) -> Self { Delegation { @@ -36,15 +65,30 @@ impl Delegation { } } - // TODO: change that to use .joined_key() and return Vec - pub fn storage_key(&self) -> (IdentityKey, Vec, u64) { + pub fn storage_key(&self) -> (IdentityKey, OwnerAddressBytes, BlockHeight) { ( self.node_identity(), - self.owner().as_bytes().to_vec(), + self.proxy_storage_key(), self.block_height(), ) } + pub fn event_storage_key(&self) -> (OwnerAddressBytes, BlockHeight, IdentityKey) { + ( + self.proxy_storage_key(), + self.block_height(), + self.node_identity(), + ) + } + + pub fn proxy_storage_key(&self) -> OwnerAddressBytes { + generate_storage_key(&self.owner, self.proxy.as_ref()) + } + + pub fn proxy(&self) -> Option<&Addr> { + self.proxy.as_ref() + } + pub fn increment_amount(&mut self, amount: Uint128, at_height: Option) { self.amount.amount += amount; if let Some(at_height) = at_height { @@ -86,10 +130,10 @@ pub struct PagedMixDelegationsResponse { } impl PagedMixDelegationsResponse { - pub fn new(delegations: Vec, start_next_after: Option<(Addr, u64)>) -> Self { + pub fn new(delegations: Vec, start_next_after: Option<(String, u64)>) -> Self { PagedMixDelegationsResponse { delegations, - start_next_after: start_next_after.map(|(s, h)| (s.to_string(), h)), + start_next_after, } } } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index a62485deb3..0931e7f751 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -mod delegation; +pub mod delegation; pub mod error; pub mod events; mod gateway; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index 41702fcc8e..ddcf52e382 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -78,6 +78,35 @@ impl PendingUndelegate { pub fn block_height(&self) -> u64 { self.block_height } + + pub fn proxy_storage_key(&self) -> Vec { + if let Some(proxy) = &self.proxy { + self.delegate() + .as_bytes() + .iter() + .zip(proxy.as_bytes()) + .map(|(x, y)| x ^ y) + .collect() + } else { + self.delegate().as_bytes().to_vec() + } + } + + pub fn storage_key(&self) -> (IdentityKey, Vec) { + (self.mix_identity(), self.proxy_storage_key()) + } + + pub fn delegation_key(&self, block_height: u64) -> (IdentityKey, Vec, u64) { + (self.mix_identity(), self.proxy_storage_key(), block_height) + } + + pub fn event_storage_key(&self) -> (Vec, u64, IdentityKey) { + ( + self.proxy_storage_key(), + self.block_height(), + self.mix_identity(), + ) + } } #[cfg_attr(test, derive(ts_rs::TS))] diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 7648baa15f..12c20d08fb 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -15,6 +15,9 @@ pub struct InstantiateMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + UpdateRewardingValidatorAddress { + address: String, + }, InitEpoch {}, ReconcileDelegations {}, CheckpointMixnodes {}, @@ -101,6 +104,9 @@ pub enum ExecuteMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { + GetRewardingValidatorAddress {}, + GetAllDelegationKeys {}, + DebugGetAllDelegationValues {}, GetContractVersion {}, GetMixNodes { limit: Option, @@ -137,6 +143,7 @@ pub enum QueryMsg { GetDelegationDetails { mix_identity: IdentityKey, delegator: String, + proxy: Option, }, LayerDistribution {}, GetRewardPool {}, diff --git a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml index 273835eb24..523f8a2d1f 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml @@ -6,11 +6,11 @@ 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" +cosmwasm-std = "1.0.0-beta6" mixnet-contract-common = { path = "../mixnet-contract" } serde = { version = "1.0", features = ["derive"] } schemars = "0.8" -cw-storage-plus = "0.11.1" +cw-storage-plus = "0.13.1" config = { path = "../../config" } [dev-dependencies] diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 0efb879812..587865525c 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -41,7 +41,7 @@ checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4" [[package]] name = "bandwidth-claim" -version = "0.1.0" +version = "1.0.0-rc.1" dependencies = [ "bandwidth-claim-contract", "config", @@ -241,22 +241,22 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.0-beta6" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dddc1443004c6340e55ca66d98e9d2f1a44aadf4ce2bed2c4f29baa8a15e7b7" +checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3" dependencies = [ "digest 0.9.0", "ed25519-zebra", "k256", - "rand_core 0.5.1", + "rand_core 0.6.3", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0-beta6" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4f0f10f165b8bcc558a13cddb498140960544519aa0581532c766dd80b5598" +checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45" dependencies = [ "syn", ] @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.0.0-beta6" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0f3145097b692b2d95fa5d2c7c6fdd60f193ccc709857e7e1987a608725300" +checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0" dependencies = [ "base64", "cosmwasm-crypto", @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.0.0-beta3" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a4e55f0d64fed54cd2202301b8d466af8de044589247dabd77a4222f52f749" +checksum = "07f856099c824aa8f2488e62d1da3fc06383d3fdbc764573595f451be43441a2" dependencies = [ "cosmwasm-std", "serde", @@ -383,20 +383,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.11.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7ee1963302b0ac2a9d42fe0faec826209c17452bfd36fbfd9d002a88929261" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c087ff98fb0475db4c2b5298a5fd12b2848d2854b39d1115d930ee6da24d1eed" +checksum = "5e8b7f9a758c030d375520df947323c052704f784561fc28dcaab4f988c50a30" dependencies = [ "cosmwasm-std", "schemars", @@ -473,16 +462,17 @@ dependencies = [ [[package]] name = "ed25519-zebra" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" dependencies = [ "curve25519-dalek", "hex", - "rand_core 0.5.1", + "rand_core 0.6.3", "serde", "sha2", "thiserror", + "zeroize", ] [[package]] @@ -843,7 +833,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mixnet-contract" -version = "0.1.0" +version = "1.0.0" dependencies = [ "az", "bs58", @@ -852,8 +842,9 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "crypto", - "cw-storage-plus 0.12.1", + "cw-storage-plus", "fixed", + "hex", "mixnet-contract-common", "rand", "rand_chacha", @@ -1550,11 +1541,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vesting-contract" -version = "0.1.0" +version = "1.0.0" dependencies = [ "config", "cosmwasm-std", - "cw-storage-plus 0.12.1", + "cw-storage-plus", "mixnet-contract-common", "schemars", "serde", @@ -1568,7 +1559,7 @@ version = "0.1.0" dependencies = [ "config", "cosmwasm-std", - "cw-storage-plus 0.11.1", + "cw-storage-plus", "mixnet-contract-common", "schemars", "serde", diff --git a/contracts/bandwidth-claim/Cargo.toml b/contracts/bandwidth-claim/Cargo.toml index 390baf1ab7..58d0f8e283 100644 --- a/contracts/bandwidth-claim/Cargo.toml +++ b/contracts/bandwidth-claim/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bandwidth-claim" -version = "0.1.0" +version = "1.0.0-rc.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -14,8 +14,8 @@ config = { path = "../../common/config"} [dependencies] bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" } -cosmwasm-std = "1.0.0-beta3" -cosmwasm-storage = "1.0.0-beta3" +cosmwasm-std = "1.0.0-beta6" +cosmwasm-storage = "1.0.0-beta6" schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index 11f9c3a83b..c7214c9d62 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mixnet-contract" -version = "0.1.0" +version = "1.0.0" authors = ["Dave Hrycyszyn "] edition = "2021" @@ -20,9 +20,9 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet- vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } config = { path = "../../common/config"} -cosmwasm-std = "1.0.0-beta3" -cosmwasm-storage = "1.0.0-beta3" -cw-storage-plus = "0.12.1" +cosmwasm-std = "1.0.0-beta6" +cosmwasm-storage = "1.0.0-beta6" +cw-storage-plus = "0.13.1" az = "1.2.0" bs58 = "0.4.0" @@ -30,6 +30,7 @@ schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } time = { version = "0.3", features = ["macros"] } +hex = "0.4.3" [dev-dependencies] cosmwasm-schema = "1.0.0-beta3" diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 7ed42f47da..a81e0b538a 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -18,9 +18,10 @@ use crate::interval::queries::{ use crate::interval::transactions::{init_epoch, try_init_epoch}; use crate::mixnet_contract_settings::models::ContractState; use crate::mixnet_contract_settings::queries::{ - query_contract_settings_params, query_contract_version, + query_contract_settings_params, query_contract_version, query_rewarding_validator_address, }; use crate::mixnet_contract_settings::storage as mixnet_params_storage; +use crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address; use crate::mixnodes::bonding_queries as mixnode_queries; use crate::mixnodes::bonding_queries::query_mixnodes_paged; use crate::mixnodes::layer_queries::query_layer_distribution; @@ -32,7 +33,7 @@ use cosmwasm_std::{ entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128, }; use mixnet_contract_common::{ - ContractStateParams, Delegation, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, + ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, }; use time::OffsetDateTime; @@ -96,6 +97,9 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::UpdateRewardingValidatorAddress { address } => { + try_update_rewarding_validator_address(deps, info, address) + } ExecuteMsg::InitEpoch {} => try_init_epoch(info, deps.storage, env), ExecuteMsg::BondMixnode { mix_node, @@ -276,6 +280,9 @@ pub fn execute( #[entry_point] pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { let query_res = match msg { + QueryMsg::GetRewardingValidatorAddress {} => { + to_binary(&query_rewarding_validator_address(deps)?) + } QueryMsg::GetContractVersion {} => to_binary(&query_contract_version()), QueryMsg::GetMixNodes { start_after, limit } => { to_binary(&query_mixnodes_paged(deps, start_after, limit)?) @@ -312,11 +319,13 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result to_binary(&query_mixnode_delegation( deps.storage, deps.api, mix_identity, delegator, + proxy, )?), QueryMsg::GetRewardPool {} => to_binary(&query_reward_pool(deps)?), QueryMsg::GetCirculatingSupply {} => to_binary(&query_circulating_supply(deps)?), @@ -363,66 +372,19 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result to_binary( &query_pending_delegation_events(deps.storage, owner_address)?, ), + QueryMsg::GetAllDelegationKeys {} => to_binary( + &crate::delegations::queries::query_all_delegation_keys(deps.storage)?, + ), + QueryMsg::DebugGetAllDelegationValues {} => to_binary( + &crate::delegations::queries::debug_query_all_delegation_values(deps.storage)?, + ), }; Ok(query_res?) } -// MIGRATE OLD DELEGATION STORAGE -// applied on QAnet -#[allow(dead_code)] -fn migrate_delegations(deps: DepsMut<'_>) -> Result<(), ContractError> { - use crate::delegations::storage::{ - DelegationIndex, DELEGATION_MIXNODE_IDX_NAMESPACE, DELEGATION_OWNER_IDX_NAMESPACE, - DELEGATION_PK_NAMESPACE, - }; - use cosmwasm_std::Order; - use cw_storage_plus::{IndexedMap, MultiIndex}; - - type PrimaryKey = Vec; - - fn old_delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> { - let indexes = DelegationIndex { - owner: MultiIndex::new( - |d| d.owner.clone(), - DELEGATION_PK_NAMESPACE, - DELEGATION_OWNER_IDX_NAMESPACE, - ), - mixnode: MultiIndex::new( - |d| d.node_identity.clone(), - DELEGATION_PK_NAMESPACE, - DELEGATION_MIXNODE_IDX_NAMESPACE, - ), - }; - - IndexedMap::new(DELEGATION_PK_NAMESPACE, indexes) - } - - let old_delegations = old_delegations() - .range(deps.storage, None, None, Order::Ascending) - .filter_map(|r| r.ok()) - .map(|(_key, delegation)| delegation) - .collect::>(); - - for delegation in old_delegations { - crate::delegations::storage::delegations().save( - deps.storage, - ( - delegation.node_identity(), - delegation.owner().as_bytes().to_vec(), - delegation.block_height(), - ), - &delegation, - )?; - } - Ok(()) -} - #[entry_point] pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { - // TODO: Uncomment mainnet - // migrate_delegations(deps)?; - Ok(Default::default()) } diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index c0f016151e..4dacb34035 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -43,14 +43,18 @@ pub(crate) fn query_delegator_delegations_paged( .idx .owner .prefix(validated_owner) - .range(deps.storage, start, None, Order::Ascending) + .range_raw(deps.storage, start, None, Order::Ascending) .take(limit) .map(|record| record.map(|r| r.1)) .collect::>>()?; - let start_next_after = delegations - .last() - .map(|delegation| delegation.node_identity()); + let start_next_after = if delegations.len() < limit { + None + } else { + delegations + .last() + .map(|delegation| delegation.node_identity()) + }; Ok(PagedDelegatorDelegationsResponse::new( delegations, @@ -58,17 +62,70 @@ pub(crate) fn query_delegator_delegations_paged( )) } +pub fn query_all_delegation_keys(storage: &dyn Storage) -> Result, ContractError> { + Ok(storage::delegations() + .keys_raw(storage, None, None, Order::Ascending) + .map(hex::encode) + .collect()) +} + +use std::collections::HashSet; + +// This should only be exposed directly on the contract via nymd binary, not through the nymd clients +pub fn debug_query_all_delegation_values( + storage: &dyn Storage, +) -> Result, ContractError> { + use crate::delegations::storage::{ + DelegationIndex, DELEGATION_MIXNODE_IDX_NAMESPACE, DELEGATION_OWNER_IDX_NAMESPACE, + DELEGATION_PK_NAMESPACE, + }; + + use cw_storage_plus::{IndexedMap, MultiIndex}; + + type PrimaryKey = Vec; + + fn all_delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> { + let indexes = DelegationIndex { + owner: MultiIndex::new( + |d| d.owner.clone(), + DELEGATION_PK_NAMESPACE, + DELEGATION_OWNER_IDX_NAMESPACE, + ), + mixnode: MultiIndex::new( + |d| d.node_identity.clone(), + DELEGATION_PK_NAMESPACE, + DELEGATION_MIXNODE_IDX_NAMESPACE, + ), + }; + + IndexedMap::new(DELEGATION_PK_NAMESPACE, indexes) + } + + let all_delegations = all_delegations() + .range(storage, None, None, Order::Ascending) + .filter_map(|r| r.ok()) + .map(|(_key, delegation)| delegation) + .collect::>(); + + Ok(all_delegations) +} + // queries for delegation value of given address for particular node pub(crate) fn query_mixnode_delegation( storage: &dyn Storage, api: &dyn Api, mix_identity: IdentityKey, delegator: String, + proxy: Option, ) -> Result, ContractError> { let validated_delegator = api.addr_validate(&delegator)?; + let proxy = proxy.map(|p| api.addr_validate(&p).expect("Invalid proxy address")); let storage_key = ( mix_identity.clone(), - validated_delegator.as_bytes().to_vec(), + mixnet_contract_common::delegation::generate_storage_key( + &validated_delegator, + proxy.as_ref(), + ), ); let delegations = storage::delegations() @@ -99,21 +156,30 @@ pub(crate) fn query_mixnode_delegations_paged( .min(storage::DELEGATION_PAGE_MAX_LIMIT) as usize; let start = start_after.map(|(addr, height)| { - Bound::ExclusiveRaw((mix_identity.clone(), addr.as_bytes(), height).joined_key()) + Bound::exclusive(( + hex::decode(addr).expect("Could not hex decode proxy_storage_key"), + height, + )) }); let delegations = storage::delegations() - .idx - .mixnode - .prefix(mix_identity) + .sub_prefix(mix_identity) .range(deps.storage, start, None, Order::Ascending) .take(limit) - .map(|record| record.map(|r| r.1)) - .collect::>>()?; + .filter_map(|r| r.ok()) + .map(|record| record.1) + .collect::>(); - let start_next_after = delegations - .last() - .map(|delegation| (delegation.owner(), delegation.block_height())); + let start_next_after = if delegations.len() < limit { + None + } else { + delegations.last().map(|delegation| { + ( + hex::encode(delegation.proxy_storage_key()), + delegation.block_height(), + ) + }) + }; Ok(PagedMixDelegationsResponse::new( delegations, @@ -127,18 +193,22 @@ pub(crate) mod tests { use crate::support::tests::test_helpers; use config::defaults::DENOM; use cosmwasm_std::{coin, Addr, Storage}; + use rand::Rng; pub fn store_n_mix_delegations(n: u32, storage: &mut dyn Storage, node_identity: &str) { for i in 0..n { let address = format!("address{}", i); - test_helpers::save_dummy_delegation(storage, node_identity, address); + test_helpers::save_dummy_delegation(storage, node_identity, address, 1); } } #[cfg(test)] mod querying_for_mixnode_delegations_paged { + use std::collections::HashSet; + use super::*; use mixnet_contract_common::IdentityKey; + use rand::{distributions::Alphanumeric, SeedableRng}; #[test] fn retrieval_obeys_limits() { @@ -203,39 +273,37 @@ pub(crate) mod tests { #[test] fn pagination_works() { + let dummy_seed = [42u8; 32]; + let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); + let mut deps = test_helpers::init_contract(); let node_identity: IdentityKey = "foo".into(); - let node_identity2: IdentityKey = "bar".into(); - test_helpers::save_dummy_delegation(&mut deps.storage, &node_identity, "100"); - test_helpers::save_dummy_delegation(&mut deps.storage, &node_identity2, "100"); + let mut delegation_test_data = vec![]; + let mut returned_delegation_data = HashSet::new(); - let per_page = 2; - let page1 = query_mixnode_delegations_paged( - deps.as_ref(), - node_identity.clone(), - None, - Option::from(per_page), - ) - .unwrap(); + // Crete a bunch of randomly ordered (in storage) delegations + for _ in 0..200 { + delegation_test_data.push(( + rng.clone() + .sample_iter(&Alphanumeric) + .take(30) + .map(char::from) + .collect::(), + rng.gen::() as u64, + )) + } - // page should have 1 result on it - assert_eq!(1, page1.delegations.len()); + for (address, block_height) in delegation_test_data.iter() { + test_helpers::save_dummy_delegation( + &mut deps.storage, + &node_identity, + address, + *block_height, + ); + } - // save another - test_helpers::save_dummy_delegation(&mut deps.storage, &node_identity, "200"); - - // page1 should have 2 results on it - let page1 = query_mixnode_delegations_paged( - deps.as_ref(), - node_identity.clone(), - None, - Option::from(per_page), - ) - .unwrap(); - assert_eq!(2, page1.delegations.len()); - - test_helpers::save_dummy_delegation(&mut deps.storage, &node_identity, "300"); + let per_page = 100; // page1 still has 2 results let page1 = query_mixnode_delegations_paged( @@ -245,10 +313,20 @@ pub(crate) mod tests { Option::from(per_page), ) .unwrap(); - println!("{:?}", page1); + let start_after = page1.start_next_after.unwrap(); - assert_eq!(2, page1.delegations.len()); - assert_eq!(("200".to_string(), 12345), start_after); + assert_eq!(100, page1.delegations.len()); + assert_eq!( + (( + "5874735a724c52587679656777795a446a754a746c59694735423165694a".to_string(), + 1594717548 + )), + start_after + ); + + for delegation in page1.delegations { + returned_delegation_data.insert(delegation.owner().to_string()); + } // retrieving the next page should start after the last key on this page @@ -259,37 +337,33 @@ pub(crate) mod tests { Option::from(per_page), ) .unwrap(); - println!("{:?}", page2); - assert_eq!(1, page2.delegations.len()); - assert_eq!(page2.delegations.last().unwrap().owner(), "300"); - // save another one - test_helpers::save_dummy_delegation(&mut deps.storage, &node_identity, "400"); + let start_after = page2.start_next_after.unwrap(); + assert_eq!( + ( + "7a6b48546c63674f57417948384e6f494a326c6b5a63767668597346696b".to_string(), + 3448133410 + ), + start_after + ); - let page2 = query_mixnode_delegations_paged( + for delegation in page2.delegations { + returned_delegation_data.insert(delegation.owner().to_string()); + } + + let page3 = query_mixnode_delegations_paged( deps.as_ref(), node_identity.clone(), Option::from(start_after), Option::from(per_page), ) .unwrap(); - // println!("{:?}", page2); - // now we have 2 pages, with 2 results on the second page - assert_eq!(2, page2.delegations.len()); - assert_eq!(page2.delegations.last().unwrap().owner(), "400"); - - // Should be empty - let page3 = query_mixnode_delegations_paged( - deps.as_ref(), - node_identity, - page2.start_next_after, - Option::from(per_page), - ) - .unwrap(); - - assert!(page3.delegations.is_empty()); assert!(page3.start_next_after.is_none()); + + for delegation in delegation_test_data { + assert!(returned_delegation_data.contains(&*delegation.0)); + } } } @@ -317,7 +391,8 @@ pub(crate) mod tests { &deps.storage, &deps.api, node_identity, - delegation_owner.to_string() + delegation_owner.to_string(), + None ) ) } @@ -340,7 +415,8 @@ pub(crate) mod tests { &deps.storage, &deps.api, node_identity1.clone(), - delegation_owner1.to_string() + delegation_owner1.to_string(), + None ) ); @@ -366,7 +442,8 @@ pub(crate) mod tests { &deps.storage, &deps.api, node_identity1.clone(), - delegation_owner1.to_string() + delegation_owner1.to_string(), + None ) ); @@ -392,7 +469,8 @@ pub(crate) mod tests { &deps.storage, &deps.api, node_identity1, - delegation_owner1.to_string() + delegation_owner1.to_string(), + None ) ) } @@ -404,7 +482,7 @@ pub(crate) mod tests { fn store_n_reverse_delegations(n: u32, storage: &mut dyn Storage, delegation_owner: &str) { for i in 0..n { let node_identity = format!("node{}", i); - test_helpers::save_dummy_delegation(storage, node_identity, delegation_owner); + test_helpers::save_dummy_delegation(storage, node_identity, delegation_owner, 1); } } @@ -475,9 +553,18 @@ pub(crate) mod tests { let mut deps = test_helpers::init_contract(); let delegation_owner = "bar".to_string(); - test_helpers::save_dummy_delegation(&mut deps.storage, "100", &delegation_owner); + for j in 0..20 { + for i in 0..10 { + test_helpers::save_dummy_delegation( + &mut deps.storage, + format!("{}-{}", j, i), + delegation_owner.clone(), + i, + ); + } + } - let per_page = 2; + let per_page = 100; let page1 = query_delegator_delegations_paged( deps.as_ref(), delegation_owner.clone(), @@ -486,60 +573,30 @@ pub(crate) mod tests { ) .unwrap(); - // page should have 1 result on it - assert_eq!(1, page1.delegations.len()); + let start_after = page1.start_next_after.unwrap(); + assert_eq!(per_page as usize, page1.delegations.len()); + assert_eq!(start_after, "9-9".to_string()); - // save another - test_helpers::save_dummy_delegation(&mut deps.storage, "200", &delegation_owner); - - // page1 should have 2 results on it - let page1 = query_delegator_delegations_paged( - deps.as_ref(), - delegation_owner.clone(), - None, - Option::from(per_page), - ) - .unwrap(); - assert_eq!(2, page1.delegations.len()); - - test_helpers::save_dummy_delegation(&mut deps.storage, "300", &delegation_owner); - - // page1 still has 2 results - let page1 = query_delegator_delegations_paged( - deps.as_ref(), - delegation_owner.clone(), - None, - Option::from(per_page), - ) - .unwrap(); - assert_eq!(2, page1.delegations.len()); - - // retrieving the next page should start after the last key on this page - let start_after: IdentityKey = page1.start_next_after.unwrap(); let page2 = query_delegator_delegations_paged( deps.as_ref(), delegation_owner.clone(), - Option::from(start_after), + Some(start_after), Option::from(per_page), ) .unwrap(); - assert_eq!(1, page2.delegations.len()); + let start_after = page2.start_next_after.unwrap(); + assert_eq!(start_after, "19-9".to_string()); - // save another one - test_helpers::save_dummy_delegation(&mut deps.storage, "400", &delegation_owner); - - let start_after = String::from("2"); - let page2 = query_delegator_delegations_paged( + let page3 = query_delegator_delegations_paged( deps.as_ref(), - delegation_owner, - Option::from(start_after), + delegation_owner.clone(), + Some(start_after), Option::from(per_page), ) .unwrap(); - // now we have 2 pages, with 2 results on the second page - assert_eq!(2, page2.delegations.len()); + assert!(page3.start_next_after.is_none()); } } } diff --git a/contracts/mixnet/src/delegations/storage.rs b/contracts/mixnet/src/delegations/storage.rs index e9a73a30a7..c65e8f0768 100644 --- a/contracts/mixnet/src/delegations/storage.rs +++ b/contracts/mixnet/src/delegations/storage.rs @@ -24,9 +24,9 @@ type OwnerAddress = Vec; type PrimaryKey = (IdentityKey, OwnerAddress, BlockHeight); pub(crate) struct DelegationIndex<'a> { - pub(crate) owner: MultiIndex<'a, Addr, Delegation>, + pub(crate) owner: MultiIndex<'a, Addr, Delegation, PrimaryKey>, - pub(crate) mixnode: MultiIndex<'a, IdentityKey, Delegation>, + pub(crate) mixnode: MultiIndex<'a, IdentityKey, Delegation, PrimaryKey>, } impl<'a> IndexList for DelegationIndex<'a> { diff --git a/contracts/mixnet/src/delegations/transactions.rs b/contracts/mixnet/src/delegations/transactions.rs index 565bb8e70d..c43411eb80 100644 --- a/contracts/mixnet/src/delegations/transactions.rs +++ b/contracts/mixnet/src/delegations/transactions.rs @@ -4,7 +4,6 @@ use super::storage::{self, PENDING_DELEGATION_EVENTS}; use crate::error::ContractError; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::mixnodes::storage as mixnodes_storage; -use crate::support::helpers::generate_storage_key; use config::defaults::DENOM; use cosmwasm_std::{ coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Order, @@ -192,19 +191,18 @@ pub(crate) fn _try_delegate_to_mixnode( }); } - let maybe_proxy_storage = generate_storage_key(&delegate, proxy.as_ref()); - let storage_key = (maybe_proxy_storage, block_height, mix_identity.to_string()); + let delegation = Delegation::new( + delegate.to_owned(), + mix_identity.to_string(), + amount.clone(), + block_height, + proxy.clone(), + ); storage::PENDING_DELEGATION_EVENTS.save( storage, - storage_key, - &DelegationEvent::Delegate(Delegation::new( - delegate.to_owned(), - mix_identity.to_string(), - amount.clone(), - block_height, - proxy.clone(), - )), + delegation.event_storage_key(), + &DelegationEvent::Delegate(delegation), )?; Ok(Response::new().add_event(new_pending_delegation_event( @@ -246,17 +244,9 @@ pub(crate) fn try_reconcile_undelegation( pending_undelegate: &PendingUndelegate, ) -> Result { let delegation_map = storage::delegations(); - let maybe_proxy_storage = generate_storage_key( - &pending_undelegate.delegate(), - pending_undelegate.proxy().as_ref(), - ); - let storage_key = ( - pending_undelegate.mix_identity(), - maybe_proxy_storage.clone(), - ); let any_delegations = delegation_map - .prefix(storage_key.clone()) + .prefix(pending_undelegate.storage_key()) .keys(storage, None, None, cosmwasm_std::Order::Ascending) .filter_map(|v| v.ok()) .next() @@ -287,7 +277,7 @@ pub(crate) fn try_reconcile_undelegation( // Might want to introduce paging here let delegation_heights = delegation_map - .prefix(storage_key) + .prefix(pending_undelegate.storage_key()) .keys(storage, None, None, cosmwasm_std::Order::Ascending) .filter_map(|v| v.ok()) .collect::>(); @@ -317,14 +307,15 @@ pub(crate) fn try_reconcile_undelegation( } for h in delegation_heights { - let storage_key = ( - pending_undelegate.mix_identity(), - maybe_proxy_storage.clone(), - h, - ); - let delegation = delegation_map.load(storage, storage_key.clone())?; + let delegation = + delegation_map.load(storage, pending_undelegate.delegation_key(h).clone())?; total_delegation += delegation.amount.amount; - delegation_map.replace(storage, storage_key, None, Some(&delegation))?; + delegation_map.replace( + storage, + pending_undelegate.delegation_key(h), + None, + Some(&delegation), + )?; } let bank_msg = BankMsg::Send { @@ -386,19 +377,17 @@ pub(crate) fn _try_remove_delegation_from_mixnode( ) -> Result { let delegate = deps.api.addr_validate(delegate)?; + let event = PendingUndelegate::new( + mix_identity.to_string(), + delegate.clone(), + proxy.clone(), + env.block.height, + ); + PENDING_DELEGATION_EVENTS.save( deps.storage, - ( - delegate.as_bytes().to_vec(), - env.block.height, - mix_identity.to_string(), - ), - &DelegationEvent::Undelegate(PendingUndelegate::new( - mix_identity.to_string(), - delegate.clone(), - proxy.clone(), - env.block.height, - )), + event.event_storage_key(), + &DelegationEvent::Undelegate(event), )?; Ok(Response::new().add_event(new_pending_undelegation_event( @@ -738,6 +727,7 @@ mod tests { &deps.api, identity, delegation_owner.to_string(), + None, ) .unwrap(); @@ -1074,6 +1064,7 @@ mod tests { &deps.api, identity.clone(), delegation_owner.clone().into_string(), + None, ) .unwrap(); @@ -1143,6 +1134,7 @@ mod tests { &deps.api, identity.clone(), delegation_owner.clone().into_string(), + None, ) .unwrap(); diff --git a/contracts/mixnet/src/mixnet_contract_settings/queries.rs b/contracts/mixnet/src/mixnet_contract_settings/queries.rs index e36791b947..40c5274e37 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/queries.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/queries.rs @@ -11,6 +11,12 @@ pub(crate) fn query_contract_settings_params(deps: Deps<'_>) -> StdResult) -> StdResult { + storage::CONTRACT_STATE + .load(deps.storage) + .map(|settings| settings.rewarding_validator_address.to_string()) +} + pub(crate) fn query_contract_version() -> MixnetContractVersion { // as per docs // env! macro will expand to the value of the named environment variable at diff --git a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs index 2901270b5b..bf4e7613e6 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/transactions.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/transactions.rs @@ -3,12 +3,30 @@ use super::storage; use crate::error::ContractError; +use cosmwasm_std::Addr; use cosmwasm_std::DepsMut; use cosmwasm_std::MessageInfo; use cosmwasm_std::Response; use mixnet_contract_common::events::new_settings_update_event; use mixnet_contract_common::ContractStateParams; +pub fn try_update_rewarding_validator_address( + deps: DepsMut<'_>, + info: MessageInfo, + address: String, +) -> Result { + let mut state = storage::CONTRACT_STATE.load(deps.storage)?; + + if info.sender != state.owner { + return Err(ContractError::Unauthorized); + } + + state.rewarding_validator_address = Addr::unchecked(address); + storage::CONTRACT_STATE.save(deps.storage, &state)?; + + Ok(Response::default()) +} + pub(crate) fn try_update_contract_settings( deps: DepsMut<'_>, info: MessageInfo, @@ -48,12 +66,45 @@ pub mod tests { use super::*; use crate::contract::{INITIAL_GATEWAY_PLEDGE, INITIAL_MIXNODE_PLEDGE}; use crate::error::ContractError; + use crate::mixnet_contract_settings::queries::query_rewarding_validator_address; use crate::mixnet_contract_settings::transactions::try_update_contract_settings; use crate::support::tests::test_helpers; use cosmwasm_std::testing::mock_info; use cosmwasm_std::Response; use mixnet_contract_common::ContractStateParams; + #[test] + fn update_contract_rewarding_validtor_address() { + let mut deps = test_helpers::init_contract(); + + let info = mock_info("not-the-creator", &[]); + let res = try_update_rewarding_validator_address( + deps.as_mut(), + info, + "not-the-creator".to_string(), + ); + assert_eq!(res, Err(ContractError::Unauthorized)); + + let info = mock_info("creator", &[]); + let res = try_update_rewarding_validator_address( + deps.as_mut(), + info, + "new-good-address".to_string(), + ); + assert_eq!(res, Ok(Response::default())); + + let state = storage::CONTRACT_STATE.load(&deps.storage).unwrap(); + assert_eq!( + state.rewarding_validator_address, + Addr::unchecked("new-good-address") + ); + + assert_eq!( + state.rewarding_validator_address, + query_rewarding_validator_address(deps.as_ref()).unwrap() + ); + } + #[test] fn updating_contract_settings() { let mut deps = test_helpers::init_contract(); diff --git a/contracts/mixnet/src/support/helpers.rs b/contracts/mixnet/src/support/helpers.rs index d99c72f722..a8ed1fc450 100644 --- a/contracts/mixnet/src/support/helpers.rs +++ b/contracts/mixnet/src/support/helpers.rs @@ -40,19 +40,6 @@ pub(crate) fn epoch_reward_params( Ok(epoch_reward_params) } -pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec { - if let Some(proxy) = &proxy { - address - .as_bytes() - .iter() - .zip(proxy.as_bytes()) - .map(|(x, y)| x ^ y) - .collect() - } else { - address.as_bytes().to_vec() - } -} - // check if the target address has already bonded a mixnode or gateway, // in either case, return an appropriate error pub(crate) fn ensure_no_existing_bond( diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index fa595e128e..78ba6082fa 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -103,12 +103,13 @@ pub mod test_helpers { storage: &mut dyn Storage, mix: impl Into, owner: impl Into, + block_height: u64, ) { let delegation = Delegation { owner: Addr::unchecked(owner.into()), node_identity: mix.into(), amount: coin(12345, DENOM), - block_height: 12345, + block_height: block_height, proxy: None, }; diff --git a/contracts/vesting/Cargo.toml b/contracts/vesting/Cargo.toml index a54899a41b..c11f8b8f65 100644 --- a/contracts/vesting/Cargo.toml +++ b/contracts/vesting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vesting-contract" -version = "0.1.0" +version = "1.0.0" authors = ["Drazen Urch "] edition = "2021" @@ -18,8 +18,8 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet- vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } config = { path = "../../common/config" } -cosmwasm-std = { version = "1.0.0-beta4"} -cw-storage-plus = { version = "0.12.1", features = ["iterator"] } +cosmwasm-std = { version = "1.0.0-beta6"} +cw-storage-plus = { version = "0.13.1", features = ["iterator"] } schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index f0084b203d..39fcd654dd 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "explorer-api" -version = "0.1.0" +version = "1.0.0-rc.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 93ea3178fb..e304bb4dc4 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-gateway" -version = "0.12.1" +version = "1.0.0-rc.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] description = "Implementation of the Nym Mixnet Gateway" edition = "2021" diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index e1e88d48d2..0166209753 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-mixnode" -version = "0.12.1" +version = "1.0.0-rc.1" authors = [ "Dave Hrycyszyn ", "Jędrzej Stuczyński ", diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 9ca3f04426..4a7efe07fa 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -778,8 +778,7 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0254ffee603f5301d6a66963d9e1cc5091479c22e2e925e1f7689c8027a0828" +source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" dependencies = [ "prost", "prost-types", @@ -789,8 +788,7 @@ dependencies = [ [[package]] name = "cosmrs" version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d" +source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496" dependencies = [ "bip32", "cosmos-sdk-proto", @@ -811,35 +809,36 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.0-beta5" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8904127a5b9e325ef5d6b2b3f997dcd74943cd35097139b1a4d15b1b6bccae66" +checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3" dependencies = [ "digest 0.9.0", "ed25519-zebra", "k256 0.9.6", - "rand_core 0.5.1", + "rand_core 0.6.3", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0-beta5" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a14364ac4d9d085867929d0cf3e94b1d2100121ce02c33c72961406830002613" +checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45" dependencies = [ "syn", ] [[package]] name = "cosmwasm-std" -version = "1.0.0-beta5" +version = "1.0.0-beta7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ece12e5bbde434b93937d7b2107e6291f11d69ffa72398c50e8bab41d451d3" +checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0" dependencies = [ "base64", "cosmwasm-crypto", "cosmwasm-derive", + "forward_ref", "schemars", "serde", "serde-json-wasm", @@ -1035,20 +1034,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.11.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7ee1963302b0ac2a9d42fe0faec826209c17452bfd36fbfd9d002a88929261" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-storage-plus" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c087ff98fb0475db4c2b5298a5fd12b2848d2854b39d1115d930ee6da24d1eed" +checksum = "5e8b7f9a758c030d375520df947323c052704f784561fc28dcaab4f988c50a30" dependencies = [ "cosmwasm-std", "schemars", @@ -1343,16 +1331,17 @@ dependencies = [ [[package]] name = "ed25519-zebra" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" dependencies = [ "curve25519-dalek", "hex", - "rand_core 0.5.1", + "rand_core 0.6.3", "serde", "sha2 0.9.9", "thiserror", + "zeroize", ] [[package]] @@ -1583,6 +1572,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -5458,11 +5453,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vesting-contract" -version = "0.1.0" +version = "1.0.0" dependencies = [ "config", "cosmwasm-std", - "cw-storage-plus 0.12.1", + "cw-storage-plus", "mixnet-contract-common", "schemars", "serde", @@ -5476,7 +5471,7 @@ version = "0.1.0" dependencies = [ "config", "cosmwasm-std", - "cw-storage-plus 0.11.1", + "cw-storage-plus", "mixnet-contract-common", "schemars", "serde", diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index 914c97812a..6f8990388b 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-network-requester" -version = "0.12.0" +version = "1.0.0-rc.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] edition = "2021" diff --git a/validator-api/Cargo.toml b/validator-api/Cargo.toml index 0eb27db044..3e7057d00f 100644 --- a/validator-api/Cargo.toml +++ b/validator-api/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-validator-api" -version = "0.12.0" +version = "1.0.0-rc.1" authors = [ "Dave Hrycyszyn ", "Jędrzej Stuczyński ", @@ -31,6 +31,7 @@ rocket = { version = "0.5.0-rc.1", features = ["json"] } serde = "1.0" serde_json = "1.0" tokio = { version = "1.4", features = ["rt-multi-thread", "macros", "signal", "time"] } +tokio-stream = "0.1.8" rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" } url = "2.2" thiserror = "1" diff --git a/validator-api/src/config/mod.rs b/validator-api/src/config/mod.rs index ab8debc22c..05e22003eb 100644 --- a/validator-api/src/config/mod.rs +++ b/validator-api/src/config/mod.rs @@ -34,7 +34,8 @@ const DEFAULT_PER_NODE_TEST_PACKETS: usize = 3; const DEFAULT_CACHE_INTERVAL: Duration = Duration::from_secs(30); const DEFAULT_MONITOR_THRESHOLD: u8 = 60; -const DEFAULT_MIN_RELIABILITY: u8 = 50; +const DEFAULT_MIN_MIXNODE_RELIABILITY: u8 = 50; +const DEFAULT_MIN_GATEWAY_RELIABILITY: u8 = 20; #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] pub struct Config { @@ -109,8 +110,8 @@ impl Default for Base { #[serde(default)] pub struct NetworkMonitor { // Mixnodes and gateways with relialability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set. - min_reliability: u8, - + min_mixnode_reliability: u8, // defaults to 50 + min_gateway_reliability: u8, // defaults to 20 /// Specifies whether network monitoring service is enabled in this process. enabled: bool, @@ -192,7 +193,8 @@ impl NetworkMonitor { impl Default for NetworkMonitor { fn default() -> Self { NetworkMonitor { - min_reliability: DEFAULT_MIN_RELIABILITY, + min_mixnode_reliability: DEFAULT_MIN_MIXNODE_RELIABILITY, + min_gateway_reliability: DEFAULT_MIN_GATEWAY_RELIABILITY, enabled: false, testnet_mode: false, all_validator_apis: default_api_endpoints(), @@ -334,6 +336,16 @@ impl Config { self } + pub fn with_min_mixnode_reliability(mut self, min_mixnode_reliability: u8) -> Self { + self.network_monitor.min_mixnode_reliability = min_mixnode_reliability; + self + } + + pub fn with_min_gateway_reliability(mut self, min_gateway_reliability: u8) -> Self { + self.network_monitor.min_gateway_reliability = min_gateway_reliability; + self + } + #[cfg(not(feature = "coconut"))] pub fn with_eth_private_key(mut self, eth_private_key: String) -> Self { self.network_monitor.eth_private_key = eth_private_key; @@ -423,8 +435,12 @@ impl Config { self.network_monitor.minimum_test_routes } - pub fn get_min_reliability(&self) -> u8 { - self.network_monitor.min_reliability + pub fn get_min_mixnode_reliability(&self) -> u8 { + self.network_monitor.min_mixnode_reliability + } + + pub fn get_min_gateway_reliability(&self) -> u8 { + self.network_monitor.min_gateway_reliability } pub fn get_route_test_packets(&self) -> usize { diff --git a/validator-api/src/config/template.rs b/validator-api/src/config/template.rs index a9c9005fb2..815bc8e8e7 100644 --- a/validator-api/src/config/template.rs +++ b/validator-api/src/config/template.rs @@ -21,7 +21,8 @@ mixnet_contract_address = '{{ base.mixnet_contract_address }}' [network_monitor] # Mixnodes and gateways with relialability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set. -min_reliability = {{ network_monitor.min_reliability }} +min_mixnode_reliability = {{ network_monitor.min_mixnode_reliability }} # deafults to 50 +min_gateway_reliability = {{ network_monitor.min_gateway_reliability }} # defaults to 20 # Specifies whether network monitoring service is enabled in this process. enabled = {{ network_monitor.enabled }} diff --git a/validator-api/src/main.rs b/validator-api/src/main.rs index 8c9a7ab799..14001328aa 100644 --- a/validator-api/src/main.rs +++ b/validator-api/src/main.rs @@ -67,6 +67,9 @@ const ETH_PRIVATE_KEY: &str = "eth_private_key"; const REWARDING_MONITOR_THRESHOLD_ARG: &str = "monitor-threshold"; +const MIN_MIXNODE_RELIABILITY_ARG: &str = "min_mixnode_reliability"; +const MIN_GATEWAY_RELIABILITY_ARG: &str = "min_gateway_reliability"; + fn parse_validators(raw: &str) -> Vec { raw.split(',') .map(|raw_validator| { @@ -284,6 +287,24 @@ fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config { config = config.with_minimum_interval_monitor_threshold(monitor_threshold) } + if let Some(reliability) = matches + .value_of(MIN_MIXNODE_RELIABILITY_ARG) + .map(|t| t.parse::()) + { + config = config.with_min_mixnode_reliability( + reliability.expect("Provided reliability is not a u8 number!"), + ) + } + + if let Some(reliability) = matches + .value_of(MIN_GATEWAY_RELIABILITY_ARG) + .map(|t| t.parse::()) + { + config = config.with_min_gateway_reliability( + reliability.expect("Provided reliability is not a u8 number!"), + ) + } + #[cfg(feature = "coconut")] if let Some(keypair_path) = matches.value_of(KEYPAIR_ARG) { let keypair_bs58 = std::fs::read_to_string(keypair_path) diff --git a/validator-api/src/network_monitor/gateways_reader.rs b/validator-api/src/network_monitor/gateways_reader.rs index 2d3b140885..53c0fd224c 100644 --- a/validator-api/src/network_monitor/gateways_reader.rs +++ b/validator-api/src/network_monitor/gateways_reader.rs @@ -2,181 +2,41 @@ // SPDX-License-Identifier: Apache-2.0 use crypto::asymmetric::identity; -use futures::stream::Stream; -use futures::task::Context; use gateway_client::{AcknowledgementReceiver, MixnetMessageReceiver}; -use std::pin::Pin; -use std::task::{Poll, Waker}; +use tokio_stream::StreamMap; -/// Constant used to determine maximum number of times the GatewayReader can poll. It basically -/// tries to solve the same problem that `FuturesUnordered` has: https://github.com/rust-lang/futures-rs/issues/2047 -const YIELD_EVERY: usize = 32; - -// TODO: Originally I set it to (identity::PublicKey, Vec>) and I definitely -// had a reason for doing so, but right now I can't remember what that was... pub(crate) type GatewayMessages = Vec>; -pub(crate) struct GatewayChannel { - id: identity::PublicKey, - message_receiver: MixnetMessageReceiver, - ack_receiver: AcknowledgementReceiver, - is_closed: bool, -} - -impl GatewayChannel { - pub(crate) fn new( - id: identity::PublicKey, - message_receiver: MixnetMessageReceiver, - ack_receiver: AcknowledgementReceiver, - ) -> Self { - GatewayChannel { - id, - message_receiver, - ack_receiver, - is_closed: false, - } - } -} - -impl Stream for GatewayChannel { - type Item = Vec>; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.is_closed { - return Poll::Ready(None); - } - - let mut polled = 0; - - // empty the ack channel if anything is on it (we don't care about the content at all at the - // moment) - while let Poll::Ready(_ack) = Pin::new(&mut self.ack_receiver).poll_next(cx) { - polled += 1; - } - - let item = futures::ready!(Pin::new(&mut self.message_receiver).poll_next(cx)); - - match item { - None => Poll::Ready(None), - // if we managed to get an item, try to also read additional ones - Some(mut messages) => { - while let Poll::Ready(new_item) = Pin::new(&mut self.message_receiver).poll_next(cx) - { - polled += 1; - match new_item { - None => { - self.is_closed = true; - cx.waker().wake_by_ref(); - return Poll::Ready(Some(messages)); - } - Some(mut additional_messages) => { - messages.append(&mut additional_messages); - - // it is fine enough to use the same constant here as in the main GatewayReader - // as only a single channel, i.e. the main gateway will be capable - // of returning more than 2 values - if polled >= YIELD_EVERY { - cx.waker().wake_by_ref(); - break; - } - } - } - } - Poll::Ready(Some(messages)) - } - } - } -} - pub(crate) struct GatewaysReader { - latest_read: usize, - // channels: FuturesUnordered, - channels: Vec, - waker: Option, + ack_map: StreamMap, + stream_map: StreamMap, } impl GatewaysReader { pub(crate) fn new() -> Self { GatewaysReader { - latest_read: 0, - channels: Vec::new(), - waker: None, + ack_map: StreamMap::new(), + stream_map: StreamMap::new(), } } - fn remove_nth(&mut self, i: usize) { - self.channels.remove(i); + pub fn stream_map(&mut self) -> &mut StreamMap { + &mut self.stream_map } - // todo: if we find that this method is called frequently, perhaps the vector should get - // replaced with different data structure - pub(crate) fn remove_by_key(&mut self, key: identity::PublicKey) { - match self.channels.iter().position(|item| item.id == key) { - Some(i) => { - self.channels.remove(i); - } - // this shouldn't ever get thrown, so perhaps a panic would be more in order? - None => error!( - "tried to remove gateway reader {} but it doesn't exist!", - key.to_base58_string() - ), - } - } - - fn poll_nth( + pub fn add_recievers( &mut self, - cx: &mut Context<'_>, - i: usize, - ) -> Option>> { - if let Poll::Ready(item) = Pin::new(&mut self.channels[i]).poll_next(cx) { - self.latest_read = i; - match item { - // Some(messages) => return Some(Poll::Ready(Some((self.channels[i].id, messages)))), - Some(messages) => return Some(Poll::Ready(Some(messages))), - // remove dead channel - None => self.remove_nth(i), - } - } - None + id: identity::PublicKey, + message_receiver: MixnetMessageReceiver, + ack_receiver: AcknowledgementReceiver, + ) { + let channel_id = id.to_string(); + self.stream_map.insert(channel_id.clone(), message_receiver); + self.ack_map.insert(channel_id, ack_receiver); } - pub(crate) fn insert_channel(&mut self, channel: GatewayChannel) { - self.channels.push(channel); - if let Some(waker) = self.waker.take() { - waker.wake() - } - } -} - -// TODO: not sure if this will scale well, but I don't know what would be a good alternative, -// perhaps try to somehow incorporate FuturesUnordered? -// also, perhaps reading should be done in parallel? -impl Stream for GatewaysReader { - // item represents gateway that returned messages alongside the actual messages - type Item = GatewayMessages; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.latest_read >= self.channels.len() { - self.latest_read = 0; - } - - // don't start reading from beginning each time to at least slightly help with the bias - for i in self.latest_read..self.channels.len() { - if let Some(item) = self.poll_nth(cx, i) { - return item; - } - } - - for i in 0..self.latest_read { - if let Some(item) = self.poll_nth(cx, i) { - return item; - } - } - - // if we have no channels available, store the waker to be woken when a new one is pushed - if self.channels.is_empty() { - self.waker = Some(cx.waker().clone()) - } - Poll::Pending + pub fn remove_recievers(&mut self, id: &str) { + self.stream_map.remove(id); + self.ack_map.remove(id); } } diff --git a/validator-api/src/network_monitor/monitor/gateways_pinger.rs b/validator-api/src/network_monitor/monitor/gateways_pinger.rs index b62c780db0..f7d47ca302 100644 --- a/validator-api/src/network_monitor/monitor/gateways_pinger.rs +++ b/validator-api/src/network_monitor/monitor/gateways_pinger.rs @@ -44,14 +44,17 @@ impl GatewayPinger { } async fn ping_and_cleanup_all_gateways(&self) { - info!(target: "GatewayPinger", "Pinging all active gateways"); + info!("Pinging all active gateways"); let lock_acquire_start = Instant::now(); let active_gateway_clients_guard = self.gateway_clients.lock().await; - trace!(target: "GatewayPinger", "Acquiring lock took {:?}", Instant::now().duration_since(lock_acquire_start)); + trace!( + "Acquiring lock took {:?}", + Instant::now().duration_since(lock_acquire_start) + ); if active_gateway_clients_guard.is_empty() { - debug!(target: "GatewayPinger", "no gateways to ping"); + debug!("no gateways to ping"); return; } @@ -89,7 +92,6 @@ impl GatewayPinger { { Err(_timeout) => { warn!( - target: "GatewayPinger", "we timed out trying to ping {} - assuming the connection is dead.", active_client.gateway_identity().to_base58_string(), ); @@ -97,7 +99,6 @@ impl GatewayPinger { } Ok(Err(err)) => { warn!( - target: "GatewayPinger", "failed to send ping message to gateway {} - {} - assuming the connection is dead.", active_client.gateway_identity().to_base58_string(), err, @@ -112,25 +113,34 @@ impl GatewayPinger { } } + info!( + "Purging {} gateways, acquiring lock", + clients_to_purge.len() + ); // purge all dead connections // reacquire the guard let lock_acquire_start = Instant::now(); let mut active_gateway_clients_guard = self.gateway_clients.lock().await; - trace!(target: "GatewayPinger", "Acquiring lock took {:?}", Instant::now().duration_since(lock_acquire_start)); + info!( + "Acquiring lock took {:?}", + Instant::now().duration_since(lock_acquire_start) + ); for gateway_id in clients_to_purge.into_iter() { if let Some(removed_handle) = active_gateway_clients_guard.remove(&gateway_id) { if !removed_handle.is_invalid().await { + info!("Handle is invalid, purging"); // it was not invalidated by the packet sender meaning it probably was some unbonded node // that was never cleared self.notify_connection_failure(gateway_id); } + info!("Handle is not invalid, not purged") } } let ping_end = Instant::now(); let time_taken = ping_end.duration_since(ping_start); - debug!(target: "GatewayPinger", "Pinging all active gateways took {:?}", time_taken); + info!("Pinging all active gateways took {:?}", time_taken); } pub(crate) async fn run(&self) { diff --git a/validator-api/src/network_monitor/monitor/mod.rs b/validator-api/src/network_monitor/monitor/mod.rs index 6dc3483167..efd034710c 100644 --- a/validator-api/src/network_monitor/monitor/mod.rs +++ b/validator-api/src/network_monitor/monitor/mod.rs @@ -42,7 +42,8 @@ pub(super) struct Monitor { /// The minimum number of test routes that need to be constructed (and working) in order for /// a monitor test run to be valid. minimum_test_routes: usize, - min_reliability: u8, + min_mixnode_reliability: u8, + min_gateway_reliability: u8, } impl Monitor { @@ -67,7 +68,8 @@ impl Monitor { route_test_packets: config.get_route_test_packets(), test_routes: config.get_test_routes(), minimum_test_routes: config.get_minimum_test_routes(), - min_reliability: config.get_min_reliability(), + min_mixnode_reliability: config.get_min_mixnode_reliability(), + min_gateway_reliability: config.get_min_gateway_reliability(), } } @@ -78,7 +80,7 @@ impl Monitor { // uptime calculations for result in test_summary.mixnode_results.iter() { - if result.reliability < self.min_reliability { + if result.reliability < self.min_mixnode_reliability { self.packet_preparer .validator_cache() .insert_mixnodes_blacklist(result.identity.clone()) @@ -92,7 +94,7 @@ impl Monitor { } for result in test_summary.gateway_results.iter() { - if result.reliability < self.min_reliability { + if result.reliability < self.min_gateway_reliability { self.packet_preparer .validator_cache() .insert_gateways_blacklist(result.identity.clone()) @@ -175,9 +177,9 @@ impl Monitor { for entry in results.iter() { if *entry.1 == self.route_test_packets { - debug!("✔️ {} succeeded", entry.0) + info!("✔️ {} succeeded", entry.0) } else { - debug!( + info!( "❌️ {} failed ({}/{} received)", entry.0, entry.1, self.route_test_packets ) @@ -198,7 +200,7 @@ impl Monitor { } async fn prepare_test_routes(&mut self) -> Option> { - info!(target: "Monitor", "Generating test routes..."); + info!("Generating test routes..."); // keep track of nodes that should not be used for route construction let mut blacklist = HashSet::new(); @@ -207,7 +209,7 @@ impl Monitor { let mut current_attempt = 0; // todo: tweak this to something more appropriate - let max_attempts = self.test_routes * 2; + let max_attempts = self.test_routes * 10; 'outer: loop { if current_attempt >= max_attempts { @@ -275,13 +277,12 @@ impl Monitor { .set_new_test_nonce(self.test_nonce) .await; - debug!("Sending packets to all gateways..."); + info!("Sending packets to all gateways..."); self.packet_sender .send_packets(prepared_packets.packets) .await; - debug!( - target: "Monitor", + info!( "Sending is over, waiting for {:?} before checking what we received", self.packet_delivery_timeout ); @@ -291,6 +292,8 @@ impl Monitor { let received = self.received_processor.return_received().await; let total_received = received.len(); + info!("Test routes: {:?}", routes); + info!("Received {}/{} packets", total_received, total_sent); let summary = self.summary_producer.produce_summary( prepared_packets.tested_mixnodes, @@ -308,11 +311,14 @@ impl Monitor { } async fn test_run(&mut self) { - info!(target: "Monitor", "Starting test run no. {}", self.test_nonce); + info!("Starting test run no. {}", self.test_nonce); let start = Instant::now(); if let Some(test_routes) = self.prepare_test_routes().await { - debug!(target: "Monitor", "Determined reliable routes to test all other nodes against. : {:?}", test_routes); + info!( + "Determined reliable routes to test all other nodes against. : {:?}", + test_routes + ); self.test_network_against(&test_routes).await; } else { error!("We failed to construct sufficient number of test routes to test the network against") diff --git a/validator-api/src/network_monitor/monitor/preparer.rs b/validator-api/src/network_monitor/monitor/preparer.rs index 2b48c8da1d..2169fc3cba 100644 --- a/validator-api/src/network_monitor/monitor/preparer.rs +++ b/validator-api/src/network_monitor/monitor/preparer.rs @@ -25,6 +25,7 @@ type Id = String; type Owner = Addr; #[derive(Clone)] +#[allow(dead_code)] pub(crate) enum InvalidNode { Outdated(Id, Owner, Version), Malformed(Id, Owner), @@ -188,8 +189,8 @@ impl PacketPreparer { info!("Waiting for minimal topology to be online"); let initialisation_backoff = Duration::from_secs(30); loop { - let gateways = self.validator_cache.gateways().await; - let mixnodes = self.validator_cache.rewarded_set().await; + let gateways = self.validator_cache.gateways_all().await; + let mixnodes = self.validator_cache.mixnodes_all().await; if gateways.len() < minimum_full_routes { info!( @@ -201,7 +202,7 @@ impl PacketPreparer { } let mut layered_mixes = HashMap::new(); - for mix in mixnodes.into_inner() { + for mix in mixnodes { let layer = mix.layer; let mixes = layered_mixes.entry(layer).or_insert_with(Vec::new); mixes.push(mix) @@ -229,7 +230,7 @@ impl PacketPreparer { } async fn all_mixnodes_and_gateways(&self) -> (Vec, Vec) { - info!(target: "Monitor", "Obtaining network topology..."); + info!("Obtaining network topology..."); let mixnodes = self.validator_cache.mixnodes_all().await; let gateways = self.validator_cache.gateways_all().await; @@ -239,9 +240,6 @@ impl PacketPreparer { pub(crate) fn try_parse_mix_bond(&self, mix: &MixNodeBond) -> Result { let identity = mix.mix_node.identity_key.clone(); - if !self.check_version_compatibility(&mix.mix_node.version) { - return Err(identity); - } mix.try_into().map_err(|_| identity) } @@ -250,9 +248,6 @@ impl PacketPreparer { gateway: &GatewayBond, ) -> Result { let identity = gateway.gateway.identity_key.clone(); - if !self.check_version_compatibility(&gateway.gateway.version) { - return Err(identity); - } gateway.try_into().map_err(|_| identity) } @@ -270,19 +265,10 @@ impl PacketPreparer { // separate mixes into layers for easier selection let mut layered_mixes = HashMap::new(); for mix in mixnodes { - // filter out mixes on the blacklist - if blacklist.contains(&mix.mix_node.identity_key) { - continue; - } let layer = mix.layer; let mixes = layered_mixes.entry(layer).or_insert_with(Vec::new); mixes.push(mix) } - // filter out gateways on the blacklist - let gateways = gateways - .into_iter() - .filter(|gateway| !blacklist.contains(&gateway.gateway.identity_key)) - .collect::>(); // get all nodes from each layer... let l1 = layered_mixes.get(&Layer::One)?; @@ -297,9 +283,6 @@ impl PacketPreparer { let rand_l3 = l3.choose_multiple(&mut rng, n).collect::>(); let rand_gateways = gateways.choose_multiple(&mut rng, n).collect::>(); - // let rand_gateways = gateways.iter().filter(|g| g.gateway.host == "109.74.196.254".to_string()).collect::>(); - // let rand_gateways = gateways.iter().filter(|g| g.gateway.host == "185.3.94.33".to_string()).collect::>(); - // the unwrap on `min()` is fine as we know the iterator is not empty let most_available = *[ rand_l1.len(), @@ -312,9 +295,10 @@ impl PacketPreparer { .unwrap(); if most_available == 0 { - // it's impossible to generate a single route + error!("Cannot construct test routes. No nodes or gateways available"); None } else { + trace!("Generating test routes..."); let mut routes = Vec::new(); for i in 0..most_available { let node_1 = match self.try_parse_mix_bond(rand_l1[i]) { @@ -358,14 +342,11 @@ impl PacketPreparer { gateway, )) } + info!("{:?}", routes); Some(routes) } } - fn check_version_compatibility(&self, node_version: &str) -> bool { - version_checker::is_minor_version_compatible(node_version, &self.system_version) - } - fn create_packet_sender(&self, gateway: &gateway::Node) -> Recipient { Recipient::new( self.self_public_identity, @@ -404,14 +385,6 @@ impl PacketPreparer { let mut parsed_nodes = Vec::new(); let mut invalid_nodes = Vec::new(); for mixnode in nodes { - if !self.check_version_compatibility(&mixnode.mix_node.version) { - invalid_nodes.push(InvalidNode::Outdated( - mixnode.mix_node.identity_key, - mixnode.owner, - mixnode.mix_node.version, - )); - continue; - } if let Ok(parsed_node) = (&mixnode).try_into() { parsed_nodes.push(parsed_node) } else { @@ -431,14 +404,6 @@ impl PacketPreparer { let mut parsed_nodes = Vec::new(); let mut invalid_nodes = Vec::new(); for gateway in nodes { - if !self.check_version_compatibility(&gateway.gateway.version) { - invalid_nodes.push(InvalidNode::Outdated( - gateway.gateway.identity_key, - gateway.owner, - gateway.gateway.version, - )); - continue; - } if let Ok(parsed_node) = (&gateway).try_into() { parsed_nodes.push(parsed_node) } else { @@ -460,18 +425,17 @@ impl PacketPreparer { // (remember that "idle" nodes are still part of that set) // we don't care about other nodes, i.e. nodes that are bonded but will not get // any reward during the current rewarding interval - let (rewarded_set, all_gateways) = self.all_mixnodes_and_gateways().await; + let (mixnodes, gateways) = self.all_mixnodes_and_gateways().await; - let (mixes, invalid_mixnodes) = self.filter_outdated_and_malformed_mixnodes(rewarded_set); - let (gateways, invalid_gateways) = - self.filter_outdated_and_malformed_gateways(all_gateways); + let (mixnodes, invalid_mixnodes) = self.filter_outdated_and_malformed_mixnodes(mixnodes); + let (gateways, invalid_gateways) = self.filter_outdated_and_malformed_gateways(gateways); - let tested_mixnodes = mixes.iter().map(|node| node.into()).collect::>(); + let tested_mixnodes = mixnodes.iter().map(|node| node.into()).collect::>(); let tested_gateways = gateways.iter().map(|node| node.into()).collect::>(); let packets_to_create = (test_routes.len() * self.per_node_test_packets) * (tested_mixnodes.len() + tested_gateways.len()); - info!(target: "TestPreparer", "Need to create {} mix packets", packets_to_create); + info!("Need to create {} mix packets", packets_to_create); let mut all_gateway_packets = HashMap::new(); @@ -483,10 +447,10 @@ impl PacketPreparer { let gateway_owner = test_route.gateway_owner(); // it's actually going to be a tiny bit more due to gateway testing, but it's a good enough approximation - let mut mix_packets = Vec::with_capacity(mixes.len() * self.per_node_test_packets); + let mut mix_packets = Vec::with_capacity(mixnodes.len() * self.per_node_test_packets); // and for each mixnode... - for mixnode in &mixes { + for mixnode in &mixnodes { let test_packet = TestPacket::from_mixnode(mixnode, test_route.id(), test_nonce); let topology = test_route.substitute_mix(mixnode); // produce n mix packets diff --git a/validator-api/src/network_monitor/monitor/receiver.rs b/validator-api/src/network_monitor/monitor/receiver.rs index 5cefb3894d..f837cb9f80 100644 --- a/validator-api/src/network_monitor/monitor/receiver.rs +++ b/validator-api/src/network_monitor/monitor/receiver.rs @@ -1,7 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::network_monitor::gateways_reader::{GatewayChannel, GatewayMessages, GatewaysReader}; +use crate::network_monitor::gateways_reader::{GatewayMessages, GatewaysReader}; use crate::network_monitor::monitor::processor::ReceivedProcessorSender; use crypto::asymmetric::identity; use futures::channel::mpsc; @@ -40,10 +40,12 @@ impl PacketReceiver { fn process_gateway_update(&mut self, update: GatewayClientUpdate) { match update { GatewayClientUpdate::New(id, (message_receiver, ack_receiver)) => { - let channel = GatewayChannel::new(id, message_receiver, ack_receiver); - self.gateways_reader.insert_channel(channel); + self.gateways_reader + .add_recievers(id, message_receiver, ack_receiver); + } + GatewayClientUpdate::Failure(id) => { + self.gateways_reader.remove_recievers(&id.to_string()); } - GatewayClientUpdate::Failure(id) => self.gateways_reader.remove_by_key(id), } } @@ -62,7 +64,9 @@ impl PacketReceiver { // similarly gateway reader will never return a `None` as it's implemented // as an infinite stream that returns Poll::Pending if it doesn't have anything // to return - messages = self.gateways_reader.next() => self.process_gateway_messages(messages.unwrap()), + Some((_gateway_id, message)) = self.gateways_reader.stream_map().next() => { + self.process_gateway_messages(message) + } } } } diff --git a/validator-api/src/network_monitor/monitor/sender.rs b/validator-api/src/network_monitor/monitor/sender.rs index f650cc4b04..29fd943321 100644 --- a/validator-api/src/network_monitor/monitor/sender.rs +++ b/validator-api/src/network_monitor/monitor/sender.rs @@ -232,14 +232,13 @@ impl PacketSender { ) -> Result<(), GatewayClientError> { let gateway_id = client.gateway_identity().to_base58_string(); info!( - target: "MessageSender", "Got {} packets to send to gateway {}", mix_packets.len(), gateway_id ); if mix_packets.len() <= max_sending_rate { - debug!(target: "MessageSender","Everything is going to get sent as one."); + debug!("Everything is going to get sent as one."); client.batch_send_mix_packets(mix_packets).await?; } else { let packets_per_time_chunk = @@ -248,7 +247,6 @@ impl PacketSender { let total_expected_time = Duration::from_secs_f64(mix_packets.len() as f64 / max_sending_rate as f64); info!( - target: "MessageSender", "With our rate of {} packets/s it should take around {:?} to send it all to {} ...", max_sending_rate, total_expected_time, gateway_id ); @@ -268,7 +266,7 @@ impl PacketSender { // this way we won't have to do reallocations in here as they're unavoidable when // splitting a vector into multiple vectors while let Some(retained) = split_off_vec(&mut mix_packets, packets_per_time_chunk) { - trace!(target: "MessageSender","Sending {} packets...", mix_packets.len()); + trace!("Sending {} packets...", mix_packets.len()); if mix_packets.len() == 1 { client.send_mix_packet(mix_packets.pop().unwrap()).await?; @@ -280,7 +278,7 @@ impl PacketSender { mix_packets = retained; } - debug!(target: "MessageSender", "Done sending"); + debug!("Done sending"); } Ok(())