Merge branch 'sprint/wallet-multiple-accounts' into feature/multi-address-wallet

This commit is contained in:
fmtabbara
2022-04-12 10:33:43 +01:00
55 changed files with 840 additions and 617 deletions
Generated
+60 -38
View File
@@ -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",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "0.12.0"
version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "0.12.1"
version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.56"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "0.12.1"
version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.56"
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "0.12.0"
version = "1.0.0-rc.1"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
@@ -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);
}
};
}
}
}
@@ -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
),
@@ -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"
@@ -599,6 +599,7 @@ impl<C> NymdClient<C> {
&self,
mix_identity: IdentityKey,
delegator: &AccountId,
proxy: Option<String>,
) -> Result<Delegation, NymdError>
where
C: CosmWasmClient + Sync,
@@ -606,6 +607,7 @@ impl<C> NymdClient<C> {
let request = QueryMsg::GetDelegationDetails {
mix_identity,
delegator: delegator.to_string(),
proxy,
};
self.client
.query_contract_smart(self.mixnet_contract_address()?, &request)
@@ -39,6 +39,10 @@ impl Client {
self.url = new_url
}
pub fn current_url(&self) -> &Url {
&self.url
}
async fn query_validator_api<T, K, V>(
&self,
path: PathSegments<'_>,
@@ -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"
@@ -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"
@@ -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<u8>;
type BlockHeight = u64;
pub fn generate_storage_key(address: &Addr, proxy: Option<&Addr>) -> Vec<u8> {
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<Addr>, // 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<H: Hasher>(&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<Addr>,
) -> Self {
Delegation {
@@ -36,15 +65,30 @@ impl Delegation {
}
}
// TODO: change that to use .joined_key() and return Vec<u8>
pub fn storage_key(&self) -> (IdentityKey, Vec<u8>, 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<u64>) {
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<Delegation>, start_next_after: Option<(Addr, u64)>) -> Self {
pub fn new(delegations: Vec<Delegation>, 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,
}
}
}
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod delegation;
pub mod delegation;
pub mod error;
pub mod events;
mod gateway;
@@ -78,6 +78,35 @@ impl PendingUndelegate {
pub fn block_height(&self) -> u64 {
self.block_height
}
pub fn proxy_storage_key(&self) -> Vec<u8> {
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<u8>) {
(self.mix_identity(), self.proxy_storage_key())
}
pub fn delegation_key(&self, block_height: u64) -> (IdentityKey, Vec<u8>, u64) {
(self.mix_identity(), self.proxy_storage_key(), block_height)
}
pub fn event_storage_key(&self) -> (Vec<u8>, u64, IdentityKey) {
(
self.proxy_storage_key(),
self.block_height(),
self.mix_identity(),
)
}
}
#[cfg_attr(test, derive(ts_rs::TS))]
@@ -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<u32>,
@@ -137,6 +143,7 @@ pub enum QueryMsg {
GetDelegationDetails {
mix_identity: IdentityKey,
delegator: String,
proxy: Option<String>,
},
LayerDistribution {},
GetRewardPool {},
@@ -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]
+22 -31
View File
@@ -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",
+3 -3
View File
@@ -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"] }
+5 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "mixnet-contract"
version = "0.1.0"
version = "1.0.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
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"
+17 -55
View File
@@ -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<Response, ContractError> {
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<QueryResponse, ContractError> {
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<QueryResponse, C
QueryMsg::GetDelegationDetails {
mix_identity,
delegator,
proxy,
} => 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<QueryResponse, C
QueryMsg::GetPendingDelegationEvents { owner_address } => 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<u8>;
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::<Vec<Delegation>>();
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<Response, ContractError> {
// TODO: Uncomment mainnet
// migrate_delegations(deps)?;
Ok(Default::default())
}
+170 -113
View File
@@ -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::<StdResult<Vec<_>>>()?;
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<Vec<String>, 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<HashSet<Delegation>, 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<u8>;
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::<HashSet<Delegation>>();
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<String>,
) -> Result<Vec<Delegation>, 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::<StdResult<Vec<_>>>()?;
.filter_map(|r| r.ok())
.map(|record| record.1)
.collect::<Vec<Delegation>>();
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::<String>(),
rng.gen::<u32>() 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());
}
}
}
+2 -2
View File
@@ -24,9 +24,9 @@ type OwnerAddress = Vec<u8>;
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<Delegation> for DelegationIndex<'a> {
@@ -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<ReconcileUndelegateResponse, ContractError> {
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::<Vec<u64>>();
@@ -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<Response, ContractError> {
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();
@@ -11,6 +11,12 @@ pub(crate) fn query_contract_settings_params(deps: Deps<'_>) -> StdResult<Contra
.map(|settings| settings.params)
}
pub fn query_rewarding_validator_address(deps: Deps<'_>) -> StdResult<String> {
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
@@ -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<Response, ContractError> {
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();
-13
View File
@@ -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<u8> {
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(
+2 -1
View File
@@ -103,12 +103,13 @@ pub mod test_helpers {
storage: &mut dyn Storage,
mix: impl Into<String>,
owner: impl Into<String>,
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,
};
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "vesting-contract"
version = "0.1.0"
version = "1.0.0"
authors = ["Drazen Urch <durch@users.noreply.github.com>"]
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"] }
+1 -1
View File
@@ -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
+41 -2
View File
@@ -1,18 +1,30 @@
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
const DEFAULT_CACHE_VALIDITY: Duration = Duration::from_secs(60 * 30);
#[derive(Clone)]
pub(crate) struct Cache<T: Clone> {
inner: HashMap<String, CacheItem<T>>,
cache_validity_duration: Duration,
}
impl<T: Clone> Cache<T> {
pub(crate) fn new() -> Self {
Cache {
inner: HashMap::new(),
cache_validity_duration: DEFAULT_CACHE_VALIDITY,
}
}
// it felt like this might be an useful addition if we want to keep our caches with different
// validity durations
#[allow(unused)]
pub(crate) fn with_validity_duration(mut self, new_cache_validity: Duration) -> Self {
self.cache_validity_duration = new_cache_validity;
self
}
pub(crate) fn len(&self) -> usize {
self.inner.len()
}
@@ -27,7 +39,7 @@ impl<T: Clone> Cache<T> {
pub(crate) fn get(&self, key: &str) -> Option<T> {
self.inner
.get(key)
.filter(|cache_item| cache_item.valid_until > SystemTime::now())
.filter(|cache_item| cache_item.valid_until >= SystemTime::now())
.map(|cache_item| cache_item.value.clone())
}
@@ -35,11 +47,31 @@ impl<T: Clone> Cache<T> {
self.inner.insert(
key.to_string(),
CacheItem {
valid_until: SystemTime::now() + Duration::from_secs(60 * 30),
valid_until: SystemTime::now() + self.cache_validity_duration,
value,
},
);
}
#[allow(unused)]
pub(crate) fn remove(&mut self, key: &str) -> Option<T> {
self.inner.remove(key).map(|item| item.value)
}
#[allow(unused)]
pub(crate) fn remove_if_expired(&mut self, key: &str) -> Option<T> {
if self.inner.get(key)?.has_expired() {
self.remove(key)
} else {
None
}
}
// it seems like something should be running on timer calling this method on all of our caches
#[allow(unused)]
pub(crate) fn remove_all_expired(&mut self) {
self.inner.retain(|_, v| !v.has_expired())
}
}
#[derive(Clone)]
@@ -47,3 +79,10 @@ pub(crate) struct CacheItem<T> {
pub(crate) value: T,
pub(crate) valid_until: std::time::SystemTime,
}
impl<T> CacheItem<T> {
fn has_expired(&self) -> bool {
let now = SystemTime::now();
self.valid_until < now
}
}
+25 -2
View File
@@ -1,7 +1,28 @@
use network_defaults::{default_api_endpoints, default_nymd_endpoints, DEFAULT_NETWORK};
use reqwest::Url;
use std::sync::Arc;
use validator_client::nymd::QueryNymdClient;
pub(crate) fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
// since this is just a query client, we don't need any locking mechanism to keep sequence numbers in check
// nor we need to access any of its methods taking mutable reference (like updating api URL)
// when that becomes a requirement, we would simply put an extra RwLock (or Mutex) in here
#[derive(Clone)]
pub(crate) struct ThreadsafeValidatorClient(
pub(crate) Arc<validator_client::Client<QueryNymdClient>>,
);
impl ThreadsafeValidatorClient {
pub(crate) fn new() -> Self {
new_validator_client()
}
pub(crate) fn api_endpoint(&self) -> &Url {
self.0.validator_api.current_url()
}
}
pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient {
let network = DEFAULT_NETWORK;
let mixnet_contract = network.mixnet_contract_address().to_string();
let nymd_url = default_nymd_endpoints()[0].clone();
@@ -16,5 +37,7 @@ pub(crate) fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
None,
);
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!")
ThreadsafeValidatorClient(Arc::new(
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"),
))
}
+1 -1
View File
@@ -42,7 +42,7 @@ impl ExplorerApi {
async fn run(&mut self) {
info!("Explorer API starting up...");
let validator_api_url = network_defaults::default_api_endpoints()[0].clone();
let validator_api_url = self.state.inner.validator_client.api_endpoint();
info!("Using validator API - {}", validator_api_url);
// spawn concurrent tasks
@@ -1,11 +1,15 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::ThreadsafeValidatorClient;
use mixnet_contract_common::Delegation;
pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec<Delegation> {
let client = crate::client::new_nymd_client();
pub(crate) async fn get_single_mixnode_delegations(
client: &ThreadsafeValidatorClient,
pubkey: &str,
) -> Vec<Delegation> {
let delegates = match client
.0
.get_all_nymd_single_mixnode_delegations(pubkey.to_string())
.await
{
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::ThreadsafeValidatorClient;
use crate::mix_node::models::EconomicDynamicsStats;
pub(crate) async fn retrieve_mixnode_econ_stats(
client: &ThreadsafeValidatorClient,
identity: &str,
) -> Option<EconomicDynamicsStats> {
let stake_saturation = client
.0
.validator_api
.get_mixnode_stake_saturation(identity)
.await
.ok()?;
let inclusion_probability = client
.0
.validator_api
.get_mixnode_inclusion_probability(identity)
.await
.ok()?;
let reward_estimation = client
.0
.validator_api
.get_mixnode_reward_estimation(identity)
.await
.ok()?;
Some(EconomicDynamicsStats {
stake_saturation: stake_saturation.saturation,
active_set_inclusion_probability: inclusion_probability.in_active,
reserve_set_inclusion_probability: inclusion_probability.in_reserve,
estimated_total_node_reward: reward_estimation.estimated_total_node_reward,
estimated_operator_reward: reward_estimation.estimated_operator_reward,
estimated_delegators_reward: reward_estimation.estimated_delegators_reward,
current_interval_uptime: reward_estimation.current_interval_uptime,
})
}
+36 -4
View File
@@ -11,8 +11,11 @@ use rocket_okapi::settings::OpenApiSettings;
use mixnet_contract_common::Delegation;
use crate::mix_node::models::{NodeDescription, NodeStats, PrettyDetailedMixNodeBond};
use crate::mix_nodes::delegations::get_single_mixnode_delegations;
use crate::mix_node::delegations::get_single_mixnode_delegations;
use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats;
use crate::mix_node::models::{
EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond,
};
use crate::state::ExplorerApiStateContext;
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
@@ -21,6 +24,7 @@ pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>,
get_by_id,
get_description,
get_stats,
get_economic_dynamics_stats,
]
}
@@ -43,8 +47,11 @@ pub(crate) async fn get_by_id(
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/delegations")]
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<Delegation>> {
Json(get_single_mixnode_delegations(pubkey).await)
pub(crate) async fn get_delegations(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Json<Vec<Delegation>> {
Json(get_single_mixnode_delegations(&state.inner.validator_client, pubkey).await)
}
#[openapi(tag = "mix_node")]
@@ -134,6 +141,31 @@ pub(crate) async fn get_stats(
}
}
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/economic-dynamics-stats")]
pub(crate) async fn get_economic_dynamics_stats(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Option<Json<EconomicDynamicsStats>> {
match state.inner.mixnode.get_econ_stats(pubkey).await {
Some(cache_value) => {
trace!("Returning cached value for {}", pubkey);
Some(Json(cache_value))
}
None => {
trace!("No valid cache value for {}", pubkey);
// get fresh value from the validator API
let econ_stats =
retrieve_mixnode_econ_stats(&state.inner.validator_client, pubkey).await?;
// update cache
state.inner.mixnode.set_econ_stats(pubkey, econ_stats).await;
Some(Json(econ_stats))
}
}
}
async fn get_mix_node_description(host: &str, port: &u16) -> Result<NodeDescription, ReqwestError> {
reqwest::get(format!("http://{}:{}/description", host, port))
.await?
+2
View File
@@ -1,2 +1,4 @@
pub(crate) mod delegations;
pub(crate) mod econ_stats;
pub(crate) mod http;
pub(crate) mod models;
+32
View File
@@ -32,6 +32,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
pub(crate) struct MixNodeCache {
pub(crate) descriptions: Cache<NodeDescription>,
pub(crate) node_stats: Cache<NodeStats>,
pub(crate) econ_stats: Cache<EconomicDynamicsStats>,
}
#[derive(Clone)]
@@ -45,6 +46,7 @@ impl ThreadsafeMixNodeCache {
inner: Arc::new(RwLock::new(MixNodeCache {
descriptions: Cache::new(),
node_stats: Cache::new(),
econ_stats: Cache::new(),
})),
}
}
@@ -57,6 +59,10 @@ impl ThreadsafeMixNodeCache {
self.inner.read().await.node_stats.get(identity_key)
}
pub(crate) async fn get_econ_stats(&self, identity_key: &str) -> Option<EconomicDynamicsStats> {
self.inner.read().await.econ_stats.get(identity_key)
}
pub(crate) async fn set_description(&self, identity_key: &str, description: NodeDescription) {
self.inner
.write()
@@ -72,6 +78,18 @@ impl ThreadsafeMixNodeCache {
.node_stats
.set(identity_key, node_stats);
}
pub(crate) async fn set_econ_stats(
&self,
identity_key: &str,
econ_stats: EconomicDynamicsStats,
) {
self.inner
.write()
.await
.econ_stats
.set(identity_key, econ_stats);
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
@@ -103,3 +121,17 @@ pub(crate) struct NodeStats {
packets_sent_since_last_update: u64,
packets_explicitly_dropped_since_last_update: u64,
}
#[derive(Serialize, Clone, Copy, Deserialize, JsonSchema)]
pub(crate) struct EconomicDynamicsStats {
pub(crate) stake_saturation: f32,
pub(crate) active_set_inclusion_probability: f32,
pub(crate) reserve_set_inclusion_probability: f32,
pub(crate) estimated_total_node_reward: u64,
pub(crate) estimated_operator_reward: u64,
pub(crate) estimated_delegators_reward: u64,
pub(crate) current_interval_uptime: u8,
}
-1
View File
@@ -3,7 +3,6 @@
use std::time::Duration;
pub(crate) mod delegations;
pub(crate) mod http;
pub(crate) mod location;
pub(crate) mod models;
+6
View File
@@ -5,6 +5,7 @@ use chrono::{DateTime, Utc};
use log::info;
use serde::{Deserialize, Serialize};
use crate::client::ThreadsafeValidatorClient;
use mixnet_contract_common::MixNodeBond;
use crate::country_statistics::country_nodes_distribution::{
@@ -28,6 +29,9 @@ pub struct ExplorerApiState {
pub(crate) mixnodes: ThreadsafeMixNodesCache,
pub(crate) ping: ThreadsafePingCache,
pub(crate) validators: ThreadsafeValidatorCache,
// TODO: discuss with @MS whether this is an appropriate spot for it
pub(crate) validator_client: ThreadsafeValidatorClient,
}
impl ExplorerApiState {
@@ -75,6 +79,7 @@ impl ExplorerApiStateContext {
),
ping: ThreadsafePingCache::new(),
validators: ThreadsafeValidatorCache::new(),
validator_client: ThreadsafeValidatorClient::new(),
}
}
_ => {
@@ -90,6 +95,7 @@ impl ExplorerApiStateContext {
mixnodes: ThreadsafeMixNodesCache::new(),
ping: ThreadsafePingCache::new(),
validators: ThreadsafeValidatorCache::new(),
validator_client: ThreadsafeValidatorClient::new(),
}
}
}
+14 -8
View File
@@ -8,21 +8,16 @@ use validator_client::nymd::error::NymdError;
use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse};
use validator_client::ValidatorClientError;
use crate::client::new_nymd_client;
use crate::mix_nodes::CACHE_REFRESH_RATE;
use crate::state::ExplorerApiStateContext;
pub(crate) struct ExplorerApiTasks {
state: ExplorerApiStateContext,
validator_client: validator_client::Client<QueryNymdClient>,
}
impl ExplorerApiTasks {
pub(crate) fn new(state: ExplorerApiStateContext) -> Self {
ExplorerApiTasks {
state,
validator_client: new_nymd_client(),
}
ExplorerApiTasks { state }
}
// a helper to remove duplicate code when grabbing active/rewarded/all mixnodes
@@ -31,7 +26,7 @@ impl ExplorerApiTasks {
F: FnOnce(&'a validator_client::Client<QueryNymdClient>) -> Fut,
Fut: Future<Output = Result<Vec<MixNodeBond>, ValidatorClientError>>,
{
let bonds = match f(&self.validator_client).await {
let bonds = match f(&self.state.inner.validator_client.0).await {
Ok(result) => result,
Err(e) => {
error!("Unable to retrieve mixnode bonds: {:?}", e);
@@ -51,18 +46,29 @@ impl ExplorerApiTasks {
async fn retrieve_all_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
info!("About to retrieve all gateways...");
self.validator_client.get_cached_gateways().await
self.state
.inner
.validator_client
.0
.get_cached_gateways()
.await
}
async fn retrieve_all_validators(&self) -> Result<ValidatorResponse, NymdError> {
info!("About to retrieve all validators...");
let height = self
.state
.inner
.validator_client
.0
.nymd
.get_current_block_height()
.await?;
let response: ValidatorResponse = self
.state
.inner
.validator_client
.0
.nymd
.get_validators(height.value(), Paging::All)
.await?;
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "0.12.1"
version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Mixnet Gateway"
edition = "2021"
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "0.12.1"
version = "1.0.0-rc.1"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+23 -26
View File
@@ -809,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",
@@ -1033,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",
@@ -1341,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]]
@@ -1581,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"
@@ -5456,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",
@@ -5474,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",
@@ -3,7 +3,7 @@
[package]
name = "nym-network-requester"
version = "0.12.0"
version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
+2 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-validator-api"
version = "0.12.0"
version = "1.0.0-rc.1"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
@@ -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"
+22 -6
View File
@@ -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 {
+2 -1
View File
@@ -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 }}
+21
View File
@@ -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<Url> {
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::<u8>())
{
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::<u8>())
{
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)
@@ -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<Vec<u8>>) and I definitely
// had a reason for doing so, but right now I can't remember what that was...
pub(crate) type GatewayMessages = Vec<Vec<u8>>;
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<Vec<u8>>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
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<GatewayChannel>,
channels: Vec<GatewayChannel>,
waker: Option<Waker>,
ack_map: StreamMap<String, AcknowledgementReceiver>,
stream_map: StreamMap<String, MixnetMessageReceiver>,
}
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<String, MixnetMessageReceiver> {
&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<Poll<Option<GatewayMessages>>> {
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<Option<Self::Item>> {
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);
}
}
@@ -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) {
@@ -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<Vec<TestRoute>> {
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")
@@ -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<MixNodeBond>, Vec<GatewayBond>) {
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<mix::Node, String> {
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<gateway::Node, String> {
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::<Vec<_>>();
// 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::<Vec<_>>();
let rand_gateways = gateways.choose_multiple(&mut rng, n).collect::<Vec<_>>();
// let rand_gateways = gateways.iter().filter(|g| g.gateway.host == "109.74.196.254".to_string()).collect::<Vec<_>>();
// let rand_gateways = gateways.iter().filter(|g| g.gateway.host == "185.3.94.33".to_string()).collect::<Vec<_>>();
// 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::<Vec<_>>();
let tested_mixnodes = mixnodes.iter().map(|node| node.into()).collect::<Vec<_>>();
let tested_gateways = gateways.iter().map(|node| node.into()).collect::<Vec<_>>();
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
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// 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)
}
}
}
}
@@ -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(())