Merge branch 'sprint/wallet-multiple-accounts' into feature/multi-address-wallet
This commit is contained in:
Generated
+60
-38
@@ -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,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,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,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,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]
|
||||
|
||||
Generated
+22
-31
@@ -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",
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
Vendored
+41
-2
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!"),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+6
-2
@@ -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
|
||||
{
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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?
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
pub(crate) mod delegations;
|
||||
pub(crate) mod econ_stats;
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod models;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) mod delegations;
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod location;
|
||||
pub(crate) mod models;
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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>",
|
||||
|
||||
Generated
+23
-26
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user