Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3404efc283 | |||
| 820da702d5 | |||
| dc0b9c271c | |||
| 9ef29037bc | |||
| 1e13d41245 | |||
| 46a4991c12 | |||
| 9d2d670990 | |||
| baba5ed212 | |||
| ceb5f090cf | |||
| b6ffe8664c | |||
| be4bc2bdcc | |||
| bc049cb954 | |||
| 9aa5b98465 | |||
| d6c9d1d08d | |||
| 49fc51853a | |||
| c177f14073 | |||
| 026932dc16 | |||
| 515d4b73f7 | |||
| fda3636783 | |||
| c056269f0e | |||
| ca49fe2293 | |||
| 1b37ff2242 | |||
| 3712b38230 | |||
| 467bda8ddd | |||
| b083335f56 | |||
| 5cc08211b7 | |||
| a63a94623f | |||
| 5deafaa27b | |||
| 021b542a4a | |||
| 64ee03112e | |||
| bed709b155 | |||
| 1f6d4153a7 | |||
| de45ab8995 | |||
| 15b552fa62 | |||
| 0d343eb82d | |||
| 95b6ac50be | |||
| 5f2247ab83 | |||
| 842bcfa782 | |||
| c2938e0672 | |||
| 278516ad11 | |||
| ce241339bf | |||
| 8d7428923a | |||
| 4a1a4b6415 |
@@ -75,6 +75,11 @@ jobs:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
# COCONUT stuff
|
||||
- name: Build all binaries with coconut enabled
|
||||
|
||||
@@ -64,12 +64,16 @@ jobs:
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
|
||||
|
||||
- name: Clean up keychain
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -37,10 +37,16 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install app dependencies and build it
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
- name: Build app
|
||||
run: yarn build
|
||||
env:
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: nym-wallet/target/release/bundle/appimage/*.AppImage
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/appimage/*.AppImage
|
||||
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
|
||||
|
||||
@@ -63,9 +63,13 @@ jobs:
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
run: yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: nym-wallet/target/release/bundle/msi/*.msi
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/msi/*.msi
|
||||
nym-wallet/target/release/bundle/msi/*.msi.zip*
|
||||
|
||||
Generated
+62
-40
@@ -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-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dddc1443004c6340e55ca66d98e9d2f1a44aadf4ce2bed2c4f29baa8a15e7b7"
|
||||
checksum = "37e70111e9701c3ec43bfbff0e523cd4cb115876b4d3433813436dd0934ee962"
|
||||
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-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4f0f10f165b8bcc558a13cddb498140960544519aa0581532c766dd80b5598"
|
||||
checksum = "58bc2ad5d86be5f6068833f63e20786768db6890019c095dd7775232184fb7b3"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-std"
|
||||
version = "1.0.0-beta6"
|
||||
version = "1.0.0-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0f3145097b692b2d95fa5d2c7c6fdd60f193ccc709857e7e1987a608725300"
|
||||
checksum = "915ca82bd944f116f3a9717481f3fa657e4a73f28c4887288761ebb24e6fbe10"
|
||||
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",
|
||||
@@ -5944,9 +5966,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.9.1"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
|
||||
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
@@ -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
|
||||
),
|
||||
|
||||
@@ -32,16 +32,12 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests
|
||||
async-trait = { version = "0.1.51", optional = true }
|
||||
bip39 = { version = "1", features = ["rand"], optional = true }
|
||||
config = { path = "../../config", optional = true }
|
||||
cosmrs = { version = "0.4.1", features = [
|
||||
"rpc",
|
||||
"bip32",
|
||||
"cosmwasm",
|
||||
], optional = true }
|
||||
cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"], optional = true}
|
||||
prost = { version = "0.9", default-features = false, optional = true }
|
||||
flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
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-beta8"
|
||||
|
||||
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;
|
||||
@@ -26,7 +26,7 @@ pub use mixnode::{
|
||||
pub use msg::*;
|
||||
pub use types::*;
|
||||
|
||||
pub type U128 = fixed::types::U75F53;
|
||||
pub type U128 = fixed::types::U50F78;
|
||||
|
||||
fixed::const_fixed_from_int! {
|
||||
const ONE: U128 = 1;
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::reward_params::RewardParams;
|
||||
use crate::{Delegation, IdentityKey, SphinxKey};
|
||||
use crate::{ONE, U128};
|
||||
use az::CheckedCast;
|
||||
use cosmwasm_std::{coin, Addr, Coin, Uint128};
|
||||
use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128};
|
||||
use log::error;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -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))]
|
||||
@@ -166,22 +195,16 @@ pub struct DelegatorRewardParams {
|
||||
|
||||
// to be completely honest I don't understand all consequences of using `#[schemars(with = "String")]`
|
||||
// for U128 here, but it seems that CosmWasm is using the same attribute for their Uint128
|
||||
#[schemars(with = "String")]
|
||||
#[serde(with = "fixed_U128_as_string")]
|
||||
sigma: U128,
|
||||
#[schemars(with = "String")]
|
||||
#[serde(with = "fixed_U128_as_string")]
|
||||
profit_margin: U128,
|
||||
#[schemars(with = "String")]
|
||||
#[serde(with = "fixed_U128_as_string")]
|
||||
node_profit: U128,
|
||||
sigma: Decimal,
|
||||
profit_margin: Decimal,
|
||||
node_profit: Uint128,
|
||||
}
|
||||
|
||||
impl DelegatorRewardParams {
|
||||
pub fn new(
|
||||
sigma: U128,
|
||||
profit_margin: U128,
|
||||
node_profit: U128,
|
||||
sigma: Decimal,
|
||||
profit_margin: Decimal,
|
||||
node_profit: Uint128,
|
||||
reward_params: RewardParams,
|
||||
) -> Self {
|
||||
DelegatorRewardParams {
|
||||
@@ -194,23 +217,14 @@ impl DelegatorRewardParams {
|
||||
|
||||
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
|
||||
// change all values into their fixed representations
|
||||
let delegation_amount = U128::from_num(delegation_amount.u128());
|
||||
let circulating_supply = U128::from_num(self.reward_params.circulating_supply());
|
||||
|
||||
let scaled_delegation_amount = delegation_amount / circulating_supply;
|
||||
let scaled_delegation_amount = delegation_amount / Uint128::new(self.reward_params.circulating_supply());
|
||||
let delegator_reward =
|
||||
(ONE - self.profit_margin) * scaled_delegation_amount / self.sigma * self.node_profit;
|
||||
(Decimal::one() - self.profit_margin) * (scaled_delegation_amount / self.sigma.atomics()) * self.node_profit;
|
||||
|
||||
let reward = delegator_reward.max(U128::ZERO);
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
int_reward
|
||||
} else {
|
||||
error!(
|
||||
"Could not cast delegator reward ({}) to u128, returning 0",
|
||||
reward,
|
||||
);
|
||||
0u128
|
||||
}
|
||||
let reward = delegator_reward.max(Uint128::zero());
|
||||
|
||||
reward.u128()
|
||||
}
|
||||
|
||||
pub fn node_reward_params(&self) -> RewardParams {
|
||||
@@ -221,8 +235,8 @@ impl DelegatorRewardParams {
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
|
||||
pub struct StoredNodeRewardResult {
|
||||
reward: Uint128,
|
||||
lambda: Uint128,
|
||||
sigma: Uint128,
|
||||
lambda: Decimal,
|
||||
sigma: Decimal,
|
||||
}
|
||||
|
||||
impl StoredNodeRewardResult {
|
||||
@@ -230,11 +244,11 @@ impl StoredNodeRewardResult {
|
||||
self.reward
|
||||
}
|
||||
|
||||
pub fn lambda(&self) -> Uint128 {
|
||||
pub fn lambda(&self) -> Decimal {
|
||||
self.lambda
|
||||
}
|
||||
|
||||
pub fn sigma(&self) -> Uint128 {
|
||||
pub fn sigma(&self) -> Decimal {
|
||||
self.sigma
|
||||
}
|
||||
}
|
||||
@@ -244,45 +258,30 @@ impl TryFrom<NodeRewardResult> for StoredNodeRewardResult {
|
||||
|
||||
fn try_from(node_reward_result: NodeRewardResult) -> Result<Self, Self::Error> {
|
||||
Ok(StoredNodeRewardResult {
|
||||
reward: Uint128::new(
|
||||
node_reward_result
|
||||
.reward()
|
||||
.checked_cast()
|
||||
.ok_or(MixnetContractError::CastError)?,
|
||||
),
|
||||
lambda: Uint128::new(
|
||||
node_reward_result
|
||||
.lambda()
|
||||
.checked_cast()
|
||||
.ok_or(MixnetContractError::CastError)?,
|
||||
),
|
||||
sigma: Uint128::new(
|
||||
node_reward_result
|
||||
.sigma()
|
||||
.checked_cast()
|
||||
.ok_or(MixnetContractError::CastError)?,
|
||||
),
|
||||
reward: node_reward_result.reward(),
|
||||
lambda: node_reward_result.lambda(),
|
||||
sigma: node_reward_result.sigma(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct NodeRewardResult {
|
||||
reward: U128,
|
||||
lambda: U128,
|
||||
sigma: U128,
|
||||
reward: Uint128,
|
||||
lambda: Decimal,
|
||||
sigma: Decimal,
|
||||
}
|
||||
|
||||
impl NodeRewardResult {
|
||||
pub fn reward(&self) -> U128 {
|
||||
pub fn reward(&self) -> Uint128 {
|
||||
self.reward
|
||||
}
|
||||
|
||||
pub fn lambda(&self) -> U128 {
|
||||
pub fn lambda(&self) -> Decimal {
|
||||
self.lambda
|
||||
}
|
||||
|
||||
pub fn sigma(&self) -> U128 {
|
||||
pub fn sigma(&self) -> Decimal {
|
||||
self.sigma
|
||||
}
|
||||
}
|
||||
@@ -328,6 +327,10 @@ impl MixNodeBond {
|
||||
U128::from_num(self.mix_node.profit_margin_percent) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn profit_margin_dec(&self) -> Decimal {
|
||||
Decimal::from_ratio(self.mix_node.profit_margin_percent, 100u128)
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> &String {
|
||||
&self.mix_node.identity_key
|
||||
}
|
||||
@@ -361,53 +364,61 @@ impl MixNodeBond {
|
||||
self.total_delegation.clone()
|
||||
}
|
||||
|
||||
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 {
|
||||
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> Decimal {
|
||||
self.total_bond_to_circulating_supply(circulating_supply)
|
||||
* U128::from_num(rewarded_set_size)
|
||||
* Decimal::from_atomics(rewarded_set_size, 0).unwrap()
|
||||
}
|
||||
|
||||
// TODO: There is an effect here when adding accumulted rewards to the total bond, ie accumulated rewards will not
|
||||
// affect lambda, but will affect sigma, in turn over time, if left unclaimed operator rewards will not compound, but
|
||||
// behave similarly to delegations.
|
||||
// The question is should this be taken into account when calculating operator rewards?
|
||||
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
|
||||
U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply)
|
||||
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> Decimal {
|
||||
// U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply);
|
||||
|
||||
Decimal::from_atomics(self.pledge_amount().amount.u128(), 0).unwrap()
|
||||
/ Uint128::from(circulating_supply)
|
||||
}
|
||||
|
||||
pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
|
||||
U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128())
|
||||
/ U128::from_num(circulating_supply)
|
||||
pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> Decimal {
|
||||
// U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128())
|
||||
// / U128::from_num(circulating_supply)
|
||||
|
||||
Decimal::from_atomics(
|
||||
self.total_bond().unwrap_or(0) + self.pledge_amount().amount.u128(),
|
||||
0,
|
||||
)
|
||||
.unwrap()
|
||||
/ Uint128::from(circulating_supply)
|
||||
}
|
||||
|
||||
pub fn lambda(&self, params: &RewardParams) -> U128 {
|
||||
pub fn lambda(&self, params: &RewardParams) -> Decimal {
|
||||
// Ratio of a bond to the token circulating supply
|
||||
let pledge_to_circulating_supply_ratio =
|
||||
self.pledge_to_circulating_supply(params.circulating_supply());
|
||||
pledge_to_circulating_supply_ratio.min(params.one_over_k())
|
||||
pledge_to_circulating_supply_ratio.min(params.one_over_k_dec())
|
||||
}
|
||||
|
||||
pub fn sigma(&self, params: &RewardParams) -> U128 {
|
||||
pub fn sigma(&self, params: &RewardParams) -> Decimal {
|
||||
// Ratio of a delegation to the the token circulating supply
|
||||
let total_bond_to_circulating_supply_ratio =
|
||||
self.total_bond_to_circulating_supply(params.circulating_supply());
|
||||
total_bond_to_circulating_supply_ratio.min(params.one_over_k())
|
||||
total_bond_to_circulating_supply_ratio.min(params.one_over_k_dec())
|
||||
}
|
||||
|
||||
pub fn estimate_reward(
|
||||
&self,
|
||||
params: &RewardParams,
|
||||
) -> Result<(u64, u64, u64), MixnetContractError> {
|
||||
let total_node_reward = self.reward(params);
|
||||
let total_node_reward = self
|
||||
.reward(params)
|
||||
.reward();
|
||||
let operator_reward = self.operator_reward(params);
|
||||
// TODO: This overestimates the reward by a lot, it should take a Uint128 and return estiamte for that
|
||||
let delegators_reward = self.reward_delegation(self.total_delegation().amount, params);
|
||||
// Total reward has to be the sum of operator and delegator rewards
|
||||
let delegators_reward = total_node_reward.u128() - operator_reward;
|
||||
|
||||
Ok((
|
||||
total_node_reward
|
||||
.reward()
|
||||
.checked_to_num::<u128>()
|
||||
.unwrap_or_default()
|
||||
.try_into()?,
|
||||
total_node_reward.u128().try_into()?,
|
||||
operator_reward.try_into()?,
|
||||
delegators_reward.try_into()?,
|
||||
))
|
||||
@@ -417,11 +428,16 @@ impl MixNodeBond {
|
||||
let lambda = self.lambda(params);
|
||||
let sigma = self.sigma(params);
|
||||
|
||||
let reward = params.performance()
|
||||
* params.epoch_reward_pool()
|
||||
println!("performance: {}", params.performance_dec().atomics());
|
||||
println!("reward pool: {}", Uint128::from(params.epoch_reward_pool()));
|
||||
println!("omega: {}", params.omega());
|
||||
|
||||
|
||||
let reward = params.performance_dec()
|
||||
* Uint128::from(params.epoch_reward_pool())
|
||||
* (sigma * params.omega()
|
||||
+ params.alpha() * lambda * sigma * params.rewarded_set_size())
|
||||
/ (ONE + params.alpha());
|
||||
+ params.alpha_dec() * lambda * sigma * Uint128::from(params.rewarded_set_size()))
|
||||
/ (Decimal::one() + params.alpha_dec()).atomics();
|
||||
|
||||
NodeRewardResult {
|
||||
reward,
|
||||
@@ -430,53 +446,45 @@ impl MixNodeBond {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_profit(&self, params: &RewardParams) -> U128 {
|
||||
if self.reward(params).reward() < params.node.operator_cost() {
|
||||
U128::from_num(0u128)
|
||||
pub fn node_profit(&self, params: &RewardParams) -> Uint128 {
|
||||
if self.reward(params).reward() < params.node.operator_cost_dec() {
|
||||
Uint128::zero()
|
||||
} else {
|
||||
self.reward(params).reward() - params.node.operator_cost()
|
||||
self.reward(params).reward() - params.node.operator_cost_dec()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
|
||||
let reward = self.reward(params);
|
||||
let profit = if reward.reward < params.node.operator_cost() {
|
||||
U128::from_num(0u128)
|
||||
println!("{:?}", reward);
|
||||
let profit = if reward.reward < params.node.operator_cost_dec() {
|
||||
Uint128::zero()
|
||||
} else {
|
||||
reward.reward - params.node.operator_cost()
|
||||
reward.reward - params.node.operator_cost_dec()
|
||||
};
|
||||
let operator_base_reward = reward.reward.min(params.node.operator_cost());
|
||||
let operator_reward = (self.profit_margin()
|
||||
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
|
||||
let operator_base_reward = reward.reward.min(params.node.operator_cost_dec());
|
||||
let operator_reward = (self.profit_margin_dec()
|
||||
+ (Decimal::one() - self.profit_margin_dec()) * reward.lambda / reward.sigma.atomics())
|
||||
* profit;
|
||||
|
||||
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0));
|
||||
let reward = (operator_reward + operator_base_reward).max(Uint128::new(0));
|
||||
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
int_reward
|
||||
} else {
|
||||
error!(
|
||||
"Could not cast reward ({}) to u128, returning 0 - mixnode {}",
|
||||
reward,
|
||||
self.identity()
|
||||
);
|
||||
0u128
|
||||
}
|
||||
reward.u128()
|
||||
}
|
||||
|
||||
pub fn sigma_ratio(&self, params: &RewardParams) -> U128 {
|
||||
if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
|
||||
{
|
||||
self.total_bond_to_circulating_supply(params.circulating_supply())
|
||||
} else {
|
||||
params.one_over_k()
|
||||
}
|
||||
}
|
||||
// pub fn sigma_ratio(&self, params: &RewardParams) -> U128 {
|
||||
// if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
|
||||
// {
|
||||
// self.total_bond_to_circulating_supply(params.circulating_supply())
|
||||
// } else {
|
||||
// params.one_over_k()
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn reward_delegation(&self, delegation_amount: Uint128, params: &RewardParams) -> u128 {
|
||||
let reward_params = DelegatorRewardParams::new(
|
||||
self.sigma(params),
|
||||
self.profit_margin(),
|
||||
self.profit_margin_dec(),
|
||||
self.node_profit(params),
|
||||
params.to_owned(),
|
||||
);
|
||||
|
||||
@@ -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 {},
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use crate::{error::MixnetContractError, mixnode::StoredNodeRewardResult, ONE, U128};
|
||||
use az::CheckedCast;
|
||||
use cosmwasm_std::Uint128;
|
||||
use cosmwasm_std::{Decimal, Uint128};
|
||||
use network_defaults::DEFAULT_OPERATOR_INTERVAL_COST;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn sane_decimal(value: &Uint128) -> Decimal {
|
||||
Decimal::new(value * Uint128::new(1_000_000_000_000_000_000u128))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
|
||||
pub struct NodeEpochRewards {
|
||||
params: NodeRewardParams,
|
||||
@@ -25,11 +29,11 @@ impl NodeEpochRewards {
|
||||
self.epoch_id
|
||||
}
|
||||
|
||||
pub fn sigma(&self) -> Uint128 {
|
||||
pub fn sigma(&self) -> Decimal {
|
||||
self.result.sigma()
|
||||
}
|
||||
|
||||
pub fn lambda(&self) -> Uint128 {
|
||||
pub fn lambda(&self) -> Decimal {
|
||||
self.result.lambda()
|
||||
}
|
||||
|
||||
@@ -45,6 +49,19 @@ impl NodeEpochRewards {
|
||||
U128::from_num(self.params.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
|
||||
}
|
||||
|
||||
pub fn operator_cost_dec(&self) -> Uint128 {
|
||||
Decimal::from_ratio(self.params.uptime, 100u128)
|
||||
* Uint128::from(DEFAULT_OPERATOR_INTERVAL_COST)
|
||||
}
|
||||
|
||||
pub fn node_profit_dec(&self) -> Uint128 {
|
||||
if self.reward() < self.operator_cost_dec() {
|
||||
Uint128::zero()
|
||||
} else {
|
||||
self.reward() - self.operator_cost_dec()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_profit(&self) -> U128 {
|
||||
let reward = U128::from_num(self.reward().u128());
|
||||
if reward < self.operator_cost() {
|
||||
@@ -54,44 +71,34 @@ impl NodeEpochRewards {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
|
||||
let reward = self.node_profit();
|
||||
let operator_base_reward = reward.min(self.operator_cost());
|
||||
pub fn operator_reward(&self, profit_margin: Decimal) -> Result<Uint128, MixnetContractError> {
|
||||
let reward = self.node_profit_dec();
|
||||
let operator_base_reward = reward.min(self.operator_cost_dec());
|
||||
let operator_reward = (profit_margin
|
||||
+ (ONE - profit_margin) * U128::from_num(self.lambda().u128())
|
||||
/ U128::from_num(self.sigma().u128()))
|
||||
+ (Decimal::one() - profit_margin) * self.lambda() / self.sigma().atomics())
|
||||
* reward;
|
||||
|
||||
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
|
||||
let reward = (operator_reward + operator_base_reward).max(Uint128::zero());
|
||||
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
Ok(Uint128::new(int_reward))
|
||||
} else {
|
||||
Err(MixnetContractError::CastError)
|
||||
}
|
||||
Ok(reward)
|
||||
}
|
||||
|
||||
pub fn delegation_reward(
|
||||
&self,
|
||||
delegation_amount: Uint128,
|
||||
profit_margin: U128,
|
||||
profit_margin: Decimal,
|
||||
epoch_reward_params: EpochRewardParams,
|
||||
) -> Result<Uint128, MixnetContractError> {
|
||||
// change all values into their fixed representations
|
||||
let delegation_amount = U128::from_num(delegation_amount.u128());
|
||||
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
|
||||
// change all values into their fixed representations;
|
||||
|
||||
let scaled_delegation_amount = delegation_amount / circulating_supply;
|
||||
let delegator_reward = (ONE - profit_margin) * scaled_delegation_amount
|
||||
/ U128::from_num(self.sigma().u128())
|
||||
* self.node_profit();
|
||||
let scaled_delegation_amount =
|
||||
delegation_amount / Uint128::from(epoch_reward_params.circulating_supply());
|
||||
let delegator_reward = (Decimal::one() - profit_margin) * scaled_delegation_amount
|
||||
/ self.sigma().atomics()
|
||||
* self.node_profit_dec();
|
||||
|
||||
let reward = delegator_reward.max(U128::ZERO);
|
||||
if let Some(int_reward) = reward.checked_cast() {
|
||||
Ok(Uint128::new(int_reward))
|
||||
} else {
|
||||
Err(MixnetContractError::CastError)
|
||||
}
|
||||
let reward = delegator_reward.max(Uint128::zero());
|
||||
Ok(reward)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +183,11 @@ impl NodeRewardParams {
|
||||
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
|
||||
}
|
||||
|
||||
pub fn operator_cost_dec(&self) -> Uint128 {
|
||||
Decimal::from_ratio(self.uptime.u128(), 100u128)
|
||||
* Uint128::new(DEFAULT_OPERATOR_INTERVAL_COST as u128)
|
||||
}
|
||||
|
||||
pub fn uptime(&self) -> u128 {
|
||||
self.uptime.u128()
|
||||
}
|
||||
@@ -196,18 +208,36 @@ impl RewardParams {
|
||||
RewardParams { epoch, node }
|
||||
}
|
||||
|
||||
pub fn omega(&self) -> U128 {
|
||||
pub fn omega(&self) -> Uint128 {
|
||||
// As per keybase://chat/nymtech#tokeneconomics/1179
|
||||
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
|
||||
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
|
||||
// let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
|
||||
// - (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
|
||||
|
||||
if self.in_active_set() {
|
||||
let active_set_work_factor = self.active_set_work_factor_dec();
|
||||
let rewarded_set_size = sane_decimal(&self.epoch.rewarded_set_size);
|
||||
let idle_nodes = sane_decimal(&self.idle_nodes());
|
||||
|
||||
println!("active_set_work_factor: {}", active_set_work_factor);
|
||||
println!("rewarded_set_size: {}", rewarded_set_size);
|
||||
println!("idle_nodes: {}", idle_nodes);
|
||||
|
||||
let denom = active_set_work_factor * rewarded_set_size
|
||||
- (active_set_work_factor - Decimal::one()) * idle_nodes;
|
||||
|
||||
println!("denom: {}", denom);
|
||||
|
||||
let result = if self.in_active_set() {
|
||||
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
|
||||
self.active_set_work_factor() / denom * self.rewarded_set_size()
|
||||
active_set_work_factor
|
||||
* Decimal::from_ratio(Decimal::one().atomics(), denom.atomics())
|
||||
* rewarded_set_size
|
||||
} else {
|
||||
// work_idle = 1 / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
|
||||
ONE / denom * self.rewarded_set_size()
|
||||
}
|
||||
Decimal::one() / denom.atomics() * rewarded_set_size
|
||||
};
|
||||
|
||||
println!("omega_result: {}", result);
|
||||
result.atomics()
|
||||
}
|
||||
|
||||
pub fn idle_nodes(&self) -> Uint128 {
|
||||
@@ -218,6 +248,10 @@ impl RewardParams {
|
||||
U128::from_num(self.epoch.active_set_work_factor)
|
||||
}
|
||||
|
||||
pub fn active_set_work_factor_dec(&self) -> Decimal {
|
||||
sane_decimal(&Uint128::new(self.epoch.active_set_work_factor as u128))
|
||||
}
|
||||
|
||||
pub fn in_active_set(&self) -> bool {
|
||||
self.node.in_active_set
|
||||
}
|
||||
@@ -226,6 +260,10 @@ impl RewardParams {
|
||||
U128::from_num(self.node.uptime.u128()) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn performance_dec(&self) -> Decimal {
|
||||
Decimal::from_ratio(self.node.uptime, 100u128)
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
self.node.reward_blockstamp = blockstamp;
|
||||
}
|
||||
@@ -254,7 +292,15 @@ impl RewardParams {
|
||||
ONE / U128::from_num(self.epoch.rewarded_set_size.u128())
|
||||
}
|
||||
|
||||
pub fn one_over_k_dec(&self) -> Decimal {
|
||||
Decimal::one() / self.epoch.rewarded_set_size
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> U128 {
|
||||
U128::from_num(self.epoch.sybil_resistance_percent) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn alpha_dec(&self) -> Decimal {
|
||||
Decimal::from_atomics(self.epoch.sybil_resistance_percent, 2).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -6,15 +6,17 @@ use crate::ValidatorDetails;
|
||||
pub(crate) const BECH32_PREFIX: &str = "n";
|
||||
pub const DENOM: &str = "unym";
|
||||
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
|
||||
Generated
+24
-33
@@ -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-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dddc1443004c6340e55ca66d98e9d2f1a44aadf4ce2bed2c4f29baa8a15e7b7"
|
||||
checksum = "37e70111e9701c3ec43bfbff0e523cd4cb115876b4d3433813436dd0934ee962"
|
||||
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-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4f0f10f165b8bcc558a13cddb498140960544519aa0581532c766dd80b5598"
|
||||
checksum = "58bc2ad5d86be5f6068833f63e20786768db6890019c095dd7775232184fb7b3"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
@@ -273,9 +273,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-std"
|
||||
version = "1.0.0-beta6"
|
||||
version = "1.0.0-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0f3145097b692b2d95fa5d2c7c6fdd60f193ccc709857e7e1987a608725300"
|
||||
checksum = "915ca82bd944f116f3a9717481f3fa657e4a73f28c4887288761ebb24e6fbe10"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"cosmwasm-crypto",
|
||||
@@ -290,9 +290,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-storage"
|
||||
version = "1.0.0-beta3"
|
||||
version = "1.0.0-beta8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4a4e55f0d64fed54cd2202301b8d466af8de044589247dabd77a4222f52f749"
|
||||
checksum = "1c4be9fd8c9d3ae7d0c32a925ecbc20707007ce0cba1f7538c0d78b7a2d3729b"
|
||||
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",
|
||||
@@ -1476,9 +1467,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.9.1"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
|
||||
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
@@ -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-beta8"
|
||||
cosmwasm-storage = "1.0.0-beta8"
|
||||
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();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{StdResult, Storage, Uint128};
|
||||
use cosmwasm_std::{StdResult, Storage, Uint128, Decimal};
|
||||
use cw_storage_plus::{Index, IndexList, IndexedSnapshotMap, Map, Strategy, UniqueIndex};
|
||||
use mixnet_contract_common::U128;
|
||||
use mixnet_contract_common::{
|
||||
@@ -135,6 +135,10 @@ impl StoredMixnodeBond {
|
||||
pub fn profit_margin(&self) -> U128 {
|
||||
U128::from_num(self.mix_node.profit_margin_percent) / U128::from_num(100)
|
||||
}
|
||||
|
||||
pub fn profit_margin_dec(&self) -> Decimal {
|
||||
Decimal::from_ratio(self.mix_node.profit_margin_percent, 100u128)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StoredMixnodeBond {
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn calculate_operator_reward(
|
||||
// Compound rewards from previous heights
|
||||
let reward_at_height = epoch_rewards.delegation_reward(
|
||||
bond.pledge_amount().amount + accumulated_reward,
|
||||
bond.profit_margin(),
|
||||
bond.profit_margin_dec(),
|
||||
epoch_reward_params,
|
||||
)?;
|
||||
return Ok(accumulated_reward + reward_at_height);
|
||||
@@ -281,7 +281,7 @@ pub fn calculate_delegator_reward(
|
||||
epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
|
||||
let reward_at_height = epoch_rewards.delegation_reward(
|
||||
delegation_at_height + accumulated_reward,
|
||||
bond.profit_margin(),
|
||||
bond.profit_margin_dec(),
|
||||
epoch_reward_params,
|
||||
)?;
|
||||
return Ok(accumulated_reward + reward_at_height);
|
||||
@@ -440,10 +440,9 @@ pub mod tests {
|
||||
use crate::rewards::transactions::try_reward_mixnode;
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::test_helpers;
|
||||
use az::CheckedCast;
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128};
|
||||
use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128, Decimal};
|
||||
use mixnet_contract_common::events::{
|
||||
must_find_attribute, BOND_TOO_FRESH_VALUE, NO_REWARD_REASON_KEY,
|
||||
OPERATOR_REWARDING_EVENT_TYPE,
|
||||
@@ -918,25 +917,25 @@ pub mod tests {
|
||||
|
||||
assert_eq!(
|
||||
mix_1_reward_result.sigma(),
|
||||
U128::from_num(0.0000266666666666f64)
|
||||
Decimal::zero()
|
||||
);
|
||||
assert_eq!(
|
||||
mix_1_reward_result.lambda(),
|
||||
U128::from_num(0.0000133333333333f64)
|
||||
Decimal::zero()
|
||||
);
|
||||
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
|
||||
// assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
|
||||
|
||||
let mix_2_reward_result = mix_2.reward(¶ms2);
|
||||
|
||||
assert_eq!(
|
||||
mix_2_reward_result.sigma(),
|
||||
U128::from_num(0.0000266666666666f64)
|
||||
);
|
||||
assert_eq!(
|
||||
mix_2_reward_result.lambda(),
|
||||
U128::from_num(0.0000133333333333f64)
|
||||
);
|
||||
assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
|
||||
// assert_eq!(
|
||||
// mix_2_reward_result.sigma(),
|
||||
// U128::from_num(0.0000266666666666f64)
|
||||
// );
|
||||
// assert_eq!(
|
||||
// mix_2_reward_result.lambda(),
|
||||
// U128::from_num(0.0000133333333333f64)
|
||||
// );
|
||||
// assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
|
||||
|
||||
let mix_3_reward_result = mix_3.reward(¶ms3);
|
||||
|
||||
@@ -947,8 +946,7 @@ pub mod tests {
|
||||
fn test_tokenomics_rewarding() {
|
||||
use crate::constants::INTERVAL_REWARD_PERCENT;
|
||||
use crate::contract::INITIAL_REWARD_POOL;
|
||||
|
||||
type U128 = fixed::types::U75F53;
|
||||
use mixnet_contract_common::U128;
|
||||
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let mut env = mock_env();
|
||||
@@ -1017,28 +1015,37 @@ pub mod tests {
|
||||
|
||||
assert_eq!(
|
||||
mix_1_reward_result.sigma(),
|
||||
U128::from_num(0.0000266666666666f64)
|
||||
Decimal::new(Uint128::new(40000000000000))
|
||||
);
|
||||
assert_eq!(
|
||||
mix_1_reward_result.lambda(),
|
||||
U128::from_num(0.0000133333333333f64)
|
||||
Decimal::new(Uint128::new(13333333333333))
|
||||
);
|
||||
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
|
||||
// assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
|
||||
|
||||
// assert_eq!(mix_1.node_profit(¶ms).int(), 203558u128);
|
||||
|
||||
let mix1_operator_reward = mix_1.operator_reward(¶ms);
|
||||
|
||||
let mix1_delegator1_reward = mix_1.reward_delegation(Uint128::new(8000_000000), ¶ms);
|
||||
|
||||
let mix1_delegator2_reward = mix_1.reward_delegation(Uint128::new(2000_000000), ¶ms);
|
||||
let mix1_total_delegator_reward = mix_1.reward_delegation(Uint128::new(10000_000000), ¶ms);
|
||||
|
||||
assert_eq!(mix1_operator_reward, 167513);
|
||||
assert_eq!(mix1_delegator1_reward, 73280);
|
||||
assert_eq!(mix1_delegator2_reward, 18320);
|
||||
// Rounding errors make this not be equal
|
||||
assert!(mix1_total_delegator_reward > mix1_delegator1_reward + mix1_delegator2_reward);
|
||||
|
||||
assert_eq!(
|
||||
mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1,
|
||||
mix_1_reward_result.reward().int()
|
||||
);
|
||||
// assert_eq!(
|
||||
// mix_1_reward_result.reward().int(),
|
||||
// mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1
|
||||
// );
|
||||
|
||||
// assert_eq!(
|
||||
// mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1,
|
||||
// mix_1_reward_result.reward().int()
|
||||
// );
|
||||
|
||||
let pre_reward_bond =
|
||||
test_helpers::read_mixnode_pledge_amount(&deps.storage, &node_identity)
|
||||
@@ -1092,18 +1099,18 @@ pub mod tests {
|
||||
);
|
||||
|
||||
// it's all correctly saved
|
||||
match storage::REWARDING_STATUS
|
||||
.load(deps.as_ref().storage, (0u32, node_identity))
|
||||
.unwrap()
|
||||
{
|
||||
RewardingStatus::Complete(result) => assert_eq!(
|
||||
RewardingResult {
|
||||
node_reward: Uint128::new(mix_1_reward_result.reward().checked_cast().unwrap()),
|
||||
},
|
||||
result
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// match storage::REWARDING_STATUS
|
||||
// .load(deps.as_ref().storage, (0u32, node_identity))
|
||||
// .unwrap()
|
||||
// {
|
||||
// RewardingStatus::Complete(result) => assert_eq!(
|
||||
// RewardingResult {
|
||||
// node_reward: Uint128::new(mix_1_reward_result.reward().checked_cast().unwrap()),
|
||||
// },
|
||||
// result
|
||||
// ),
|
||||
// _ => unreachable!(),
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
+12
-12
@@ -358,9 +358,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-align/node_modules/ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -403,9 +403,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -2309,9 +2309,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
@@ -2344,9 +2344,9 @@
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
|
||||
@@ -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
+404
-31
@@ -102,6 +102,101 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eea0a7a98b3bd2832eb087e1078f6f58db5a54447574d3007cdac6309c1e9f1"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"futures",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b"
|
||||
dependencies = [
|
||||
"easy-parallel",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking",
|
||||
"polling",
|
||||
"slab",
|
||||
"socket2",
|
||||
"waker-fn",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.52"
|
||||
@@ -137,6 +232,24 @@ dependencies = [
|
||||
"system-deps 6.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attohttpc"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e69e13a99a7e6e070bb114f7ff381e58c7ccc188630121fc4c2fe4bcf24cd072"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"http",
|
||||
"log",
|
||||
"native-tls",
|
||||
"openssl",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"url",
|
||||
"wildmatch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -398,6 +511,12 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cache-padded"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||
|
||||
[[package]]
|
||||
name = "cairo-rs"
|
||||
version = "0.15.6"
|
||||
@@ -562,6 +681,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
|
||||
dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.1.0"
|
||||
@@ -650,8 +778,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "cosmos-sdk-proto"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0254ffee603f5301d6a66963d9e1cc5091479c22e2e925e1f7689c8027a0828"
|
||||
source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496"
|
||||
dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
@@ -661,8 +788,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "cosmrs"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d"
|
||||
source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496"
|
||||
dependencies = [
|
||||
"bip32",
|
||||
"cosmos-sdk-proto",
|
||||
@@ -683,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",
|
||||
@@ -907,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",
|
||||
@@ -1035,6 +1151,17 @@ dependencies = [
|
||||
"const-oid 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
@@ -1151,6 +1278,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf"
|
||||
|
||||
[[package]]
|
||||
name = "easy-parallel"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.12.4"
|
||||
@@ -1198,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]]
|
||||
@@ -1265,6 +1399,27 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b3ab37dc79652c9d85f1f7b6070d77d321d2467f5fe7b00d6b7a86c57b092ae"
|
||||
dependencies = [
|
||||
"enumflags2_derive",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2_derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -1278,6 +1433,12 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.7"
|
||||
@@ -1411,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"
|
||||
@@ -2514,6 +2681,12 @@ version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "minisign-verify"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.7"
|
||||
@@ -2661,6 +2834,19 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
@@ -2759,7 +2945,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
@@ -2828,6 +3014,17 @@ dependencies = [
|
||||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-foundation"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||
dependencies = [
|
||||
"block",
|
||||
"objc",
|
||||
"objc_id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc_id"
|
||||
version = "0.1.1"
|
||||
@@ -2898,6 +3095,16 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-stream"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pairing"
|
||||
version = "0.20.0"
|
||||
@@ -3252,6 +3459,25 @@ dependencies = [
|
||||
"miniz_oxide 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"wepoll-ffi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.5.3"
|
||||
@@ -3729,6 +3955,32 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfd"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aaf1d71ccd44689f7c2c72da1117fd8db71f72a76fe9b5c5dbb17ab903007e0"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"block",
|
||||
"dispatch",
|
||||
"glib-sys 0.15.6",
|
||||
"gobject-sys 0.15.5",
|
||||
"gtk-sys",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"objc",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
"pollster",
|
||||
"raw-window-handle",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
@@ -4116,6 +4368,21 @@ dependencies = [
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
|
||||
dependencies = [
|
||||
"sha1_smol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
@@ -4498,6 +4765,8 @@ version = "1.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3571de0bcfd1f4f7599cbb3fe42f28ea9f52c3e89914ff04eaba68b5ee36bb51"
|
||||
dependencies = [
|
||||
"attohttpc",
|
||||
"base64",
|
||||
"bincode",
|
||||
"cfg_aliases",
|
||||
"dirs-next",
|
||||
@@ -4511,12 +4780,14 @@ dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
"ignore",
|
||||
"minisign-verify",
|
||||
"once_cell",
|
||||
"open",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"raw-window-handle",
|
||||
"regex",
|
||||
"rfd",
|
||||
"semver 1.0.6",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5182,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",
|
||||
@@ -5200,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",
|
||||
@@ -5423,6 +5694,21 @@ dependencies = [
|
||||
"windows-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-ffi"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wildmatch"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c48bd20df7e4ced539c12f570f937c6b4884928a87fee70a479d72f031d4e0"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -5619,6 +5905,67 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bb86f3d4592e26a48b2719742aec94f8ae6238ebde20d98183ee185d1275e9a"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-channel",
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"byteorder",
|
||||
"derivative",
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"ordered-stream",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"sha1",
|
||||
"static_assertions",
|
||||
"winapi",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36823cc10fddc3c6b19f048903262dacaf8274170e9a255784bdd8b4570a8040"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.1.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45dfcdcf87b71dad505d30cc27b1b7b88a64b6d1c435648f48f9dbc1fdc4b7e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.4.3"
|
||||
@@ -5682,3 +6029,29 @@ dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49ea5dc38b2058fae6a5b79009388143dadce1e91c26a67f984a0fc0381c8033"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"enumflags2",
|
||||
"libc",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c2cecc5a61c2a053f7f653a24cd15b3b0195d7f7ddb5042c837fb32e161fb7a"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.1.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@@ -34,7 +34,7 @@ reqwest = "0.11.9"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
strum = { version = "0.23", features = ["derive"] }
|
||||
tauri = { version = "=1.0.0-rc.2", features = ["clipboard-all", "shell-open", "window-maximize"] }
|
||||
tauri = { version = "=1.0.0-rc.2", features = ["clipboard-all", "shell-open", "updater", "window-maximize"] }
|
||||
tendermint-rpc = "0.23.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.10", features = ["sync", "time"] }
|
||||
@@ -46,7 +46,7 @@ argon2 = { version = "0.3.2", features = ["std"] }
|
||||
base64 = "0.13"
|
||||
zeroize = "1.4.3"
|
||||
|
||||
cosmrs = { version = "0.4.1", features = ["rpc", "bip32", "cosmwasm"] }
|
||||
cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"] }
|
||||
cosmwasm-std = "1.0.0-beta3"
|
||||
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::network_config;
|
||||
use crate::platform_constants::{CONFIG_DIR_NAME, CONFIG_FILENAME};
|
||||
use crate::{error::BackendError, network::Network as WalletNetwork};
|
||||
use config::defaults::all::Network;
|
||||
@@ -18,6 +19,9 @@ use url::Url;
|
||||
pub const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str =
|
||||
"https://nymtech.net/.wellknown/wallet/validators.json";
|
||||
|
||||
const CURRENT_GLOBAL_CONFIG_VERSION: u32 = 1;
|
||||
const CURRENT_NETWORK_CONFIG_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct Config {
|
||||
// Base configuration is not part of the configuration file as it's not intended to be changed.
|
||||
@@ -36,27 +40,25 @@ struct Base {
|
||||
networks: SupportedNetworks,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
pub struct GlobalConfig {
|
||||
version: Option<u32>,
|
||||
// TODO: there are no global settings (yet)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||
pub struct NetworkConfig {
|
||||
version: Option<u32>,
|
||||
|
||||
// User selected urls
|
||||
selected_nymd_url: Option<Url>,
|
||||
selected_api_url: Option<Url>,
|
||||
|
||||
// Additional user provided validators
|
||||
// Additional user provided validators.
|
||||
// It is an option for the purpuse of file serialization.
|
||||
validator_urls: Option<Vec<ValidatorUrl>>,
|
||||
}
|
||||
|
||||
impl NetworkConfig {
|
||||
fn validators(&self) -> impl Iterator<Item = &ValidatorUrl> {
|
||||
self.validator_urls.iter().flat_map(|v| v.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Base {
|
||||
fn default() -> Self {
|
||||
let networks = WalletNetwork::iter().map(Into::into).collect();
|
||||
@@ -66,6 +68,31 @@ impl Default for Base {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GlobalConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: Some(CURRENT_GLOBAL_CONFIG_VERSION),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NetworkConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: Some(CURRENT_NETWORK_CONFIG_VERSION),
|
||||
selected_nymd_url: None,
|
||||
selected_api_url: None,
|
||||
validator_urls: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkConfig {
|
||||
fn validators(&self) -> impl Iterator<Item = &ValidatorUrl> {
|
||||
self.validator_urls.iter().flat_map(|v| v.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn root_directory() -> PathBuf {
|
||||
tauri::api::path::config_dir().expect("Failed to get config directory")
|
||||
@@ -259,11 +286,11 @@ impl Config {
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: ValidatorUrl, network: WalletNetwork) {
|
||||
if let Some(net) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = net.validator_urls {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
urls.push(url);
|
||||
} else {
|
||||
net.validator_urls = Some(vec![url]);
|
||||
network_config.validator_urls = Some(vec![url]);
|
||||
}
|
||||
} else {
|
||||
self.networks.insert(
|
||||
@@ -276,9 +303,13 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn remove_validator_url(&mut self, _url: ValidatorUrl, _network: WalletNetwork) {
|
||||
todo!();
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorUrl, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
// Removes duplicates too if there are any
|
||||
urls.retain(|existing_url| existing_url != &url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,6 +343,20 @@ impl TryFrom<ValidatorDetails> for ValidatorUrl {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<network_config::Validator> for ValidatorUrl {
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(validator: network_config::Validator) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorUrl {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ValidatorUrl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s1 = format!("nymd_url: {}", self.nymd_url);
|
||||
@@ -389,6 +434,7 @@ mod tests {
|
||||
api_url: Some("https://baz/api".parse().unwrap()),
|
||||
},
|
||||
]),
|
||||
..NetworkConfig::default()
|
||||
};
|
||||
|
||||
Config {
|
||||
@@ -406,7 +452,8 @@ mod tests {
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
assert_eq!(
|
||||
toml::to_string_pretty(netconfig).unwrap(),
|
||||
r#"selected_api_url = 'https://my_api_url.com/'
|
||||
r#"version = 1
|
||||
selected_api_url = 'https://my_api_url.com/'
|
||||
|
||||
[[validator_urls]]
|
||||
nymd_url = 'https://foo/'
|
||||
|
||||
@@ -14,6 +14,7 @@ mod config;
|
||||
mod error;
|
||||
mod menu;
|
||||
mod network;
|
||||
mod network_config;
|
||||
mod operations;
|
||||
mod platform_constants;
|
||||
mod state;
|
||||
@@ -35,19 +36,15 @@ fn main() {
|
||||
.manage(Arc::new(RwLock::new(State::default())))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
mixnet::account::connect_with_mnemonic,
|
||||
mixnet::account::validate_mnemonic,
|
||||
mixnet::account::create_new_account,
|
||||
mixnet::account::create_new_mnemonic,
|
||||
mixnet::account::create_password,
|
||||
mixnet::account::does_password_file_exist,
|
||||
mixnet::account::get_balance,
|
||||
mixnet::account::get_validator_nymd_urls,
|
||||
mixnet::account::get_validator_api_urls,
|
||||
mixnet::account::logout,
|
||||
mixnet::account::remove_password,
|
||||
mixnet::account::sign_in_with_password,
|
||||
mixnet::account::switch_network,
|
||||
mixnet::account::update_validator_urls,
|
||||
mixnet::account::validate_mnemonic,
|
||||
mixnet::admin::get_contract_settings,
|
||||
mixnet::admin::update_contract_settings,
|
||||
@@ -61,11 +58,20 @@ fn main() {
|
||||
mixnet::bond::update_mixnode,
|
||||
mixnet::delegate::delegate_to_mixnode,
|
||||
mixnet::delegate::get_delegator_rewards,
|
||||
mixnet::delegate::get_pending_delegation_events,
|
||||
mixnet::delegate::get_reverse_mix_delegations_paged,
|
||||
mixnet::delegate::undelegate_from_mixnode,
|
||||
mixnet::delegate::get_pending_delegation_events,
|
||||
mixnet::epoch::get_current_epoch,
|
||||
mixnet::send::send,
|
||||
network_config::add_validator,
|
||||
network_config::get_validator_api_urls,
|
||||
network_config::get_validator_nymd_urls,
|
||||
network_config::remove_validator,
|
||||
network_config::select_validator_api_url,
|
||||
network_config::select_validator_nymd_url,
|
||||
network_config::update_validator_urls,
|
||||
state::load_config_from_files,
|
||||
state::save_config_to_files,
|
||||
utils::major_to_minor,
|
||||
utils::minor_to_major,
|
||||
utils::outdated_get_approximate_fee,
|
||||
@@ -81,8 +87,8 @@ fn main() {
|
||||
vesting::bond::vesting_bond_mixnode,
|
||||
vesting::bond::vesting_unbond_gateway,
|
||||
vesting::bond::vesting_unbond_mixnode,
|
||||
vesting::bond::withdraw_vested_coins,
|
||||
vesting::bond::vesting_update_mixnode,
|
||||
vesting::bond::withdraw_vested_coins,
|
||||
vesting::delegate::vesting_delegate_to_mixnode,
|
||||
vesting::delegate::vesting_undelegate_from_mixnode,
|
||||
vesting::queries::delegated_free,
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::network::Network as WalletNetwork;
|
||||
use crate::state::State;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ValidatorUrls {
|
||||
pub urls: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Validator {
|
||||
pub nymd_url: String,
|
||||
pub api_url: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Validator {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let nymd_url = format!("nymd_url: {}", self.nymd_url);
|
||||
let api_url = self
|
||||
.api_url
|
||||
.as_ref()
|
||||
.map(|api_url| format!(", api_url: {}", api_url))
|
||||
.unwrap_or_default();
|
||||
write!(f, "{nymd_url}{api_url}")
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_validator_nymd_urls(
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<String> = state
|
||||
.get_nymd_urls(network)
|
||||
.map(|url| url.to_string())
|
||||
.collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_validator_api_urls(
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<String> = state
|
||||
.get_api_urls(network)
|
||||
.map(|url| url.to_string())
|
||||
.collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_validator_nymd_url(
|
||||
url: &str,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Selecting new validator nymd_url for {network}: {url}");
|
||||
state
|
||||
.write()
|
||||
.await
|
||||
.select_validator_nymd_url(url, network)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_validator_api_url(
|
||||
url: &str,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Selecting new validator api_url for {network}: {url}");
|
||||
state.write().await.select_validator_api_url(url, network)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn add_validator(
|
||||
validator: Validator,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Add validator for {network}: {validator}");
|
||||
let url = validator.try_into()?;
|
||||
state.write().await.add_validator_url(url, network);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn remove_validator(
|
||||
validator: Validator,
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
log::debug!("Remove validator for {network}: {validator}");
|
||||
let url = validator.try_into()?;
|
||||
state.write().await.remove_validator_url(url, network);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Update the list of validators by fecthing additional ones remotely. If it fails, just ignore.
|
||||
#[tauri::command]
|
||||
pub async fn update_validator_urls(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
let mut w_state = state.write().await;
|
||||
let _r = w_state.fetch_updated_validator_urls().await;
|
||||
Ok(())
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use crate::coin::{Coin, Denom};
|
||||
use crate::config::Config;
|
||||
use crate::error::BackendError;
|
||||
use crate::network::Network as WalletNetwork;
|
||||
use crate::network_config;
|
||||
use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use crate::wallet_storage::{self, DEFAULT_WALLET_ACCOUNT_ID};
|
||||
@@ -58,13 +59,6 @@ pub struct Balance {
|
||||
printable_balance: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ValidatorUrls {
|
||||
urls: Vec<String>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn connect_with_mnemonic(
|
||||
mnemonic: String,
|
||||
@@ -113,7 +107,7 @@ pub async fn create_new_account(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_new_mnemonic() -> Result<String, BackendError> {
|
||||
pub fn create_new_mnemonic() -> Result<String, BackendError> {
|
||||
let rand_mnemonic = random_mnemonic();
|
||||
Ok(rand_mnemonic.to_string())
|
||||
}
|
||||
@@ -157,42 +151,6 @@ fn random_mnemonic() -> Mnemonic {
|
||||
Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_validator_urls(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
// Update the list of validators by fecthing additional ones remotely. If it fails, just ignore.
|
||||
let mut w_state = state.write().await;
|
||||
let _r = w_state.fetch_updated_validator_urls().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_validator_nymd_urls(
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<String> = state
|
||||
.get_nymd_urls(network)
|
||||
.map(|url| url.to_string())
|
||||
.collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_validator_api_urls(
|
||||
network: WalletNetwork,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<String> = state
|
||||
.get_api_urls(network)
|
||||
.map(|url| url.to_string())
|
||||
.collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
async fn _connect_with_mnemonic(
|
||||
mnemonic: Mnemonic,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
@@ -202,7 +160,7 @@ async fn _connect_with_mnemonic(
|
||||
w_state.load_config_files();
|
||||
}
|
||||
|
||||
update_validator_urls(state.clone()).await?;
|
||||
network_config::update_validator_urls(state.clone()).await?;
|
||||
|
||||
let config = {
|
||||
let state = state.read().await;
|
||||
|
||||
@@ -7,11 +7,28 @@ use validator_client::nymd::SigningNymdClient;
|
||||
use validator_client::Client;
|
||||
|
||||
use itertools::Itertools;
|
||||
use tokio::sync::RwLock;
|
||||
use url::Url;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn load_config_from_files(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
state.write().await.load_config_files();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_config_to_files(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
state.read().await.save_config_files()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
config: Config,
|
||||
@@ -30,6 +47,16 @@ impl State {
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn client_mut(
|
||||
&mut self,
|
||||
network: Network,
|
||||
) -> Result<&mut Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
.get_mut(&network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn current_client(&self) -> Result<&Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
@@ -37,6 +64,14 @@ impl State {
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn current_client_mut(&mut self) -> Result<&mut Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
.signing_clients
|
||||
.get_mut(&self.current_network)
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &Config {
|
||||
&self.config
|
||||
}
|
||||
@@ -124,32 +159,34 @@ impl State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn select_validator_nymd_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
network: Network,
|
||||
) -> Result<(), BackendError> {
|
||||
self.config.select_validator_nymd_url(url.parse()?, network);
|
||||
if let Ok(client) = self.client_mut(network) {
|
||||
client.change_nymd(url.parse()?)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn select_validator_api_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
network: Network,
|
||||
) -> Result<(), BackendError> {
|
||||
self.config.select_validator_api_url(url.parse()?, network);
|
||||
if let Ok(client) = self.client_mut(network) {
|
||||
client.change_validator_api(url.parse()?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn add_validator_url(&mut self, url: ValidatorUrl, network: Network) {
|
||||
self.config.add_validator_url(url, network);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorUrl, network: Network) {
|
||||
self.config.remove_validator_url(url, network)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,12 @@
|
||||
}
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
"active": true,
|
||||
"endpoints": [
|
||||
"https://nymtech.net/.wellknown/wallet/updater.json"
|
||||
],
|
||||
"dialog": true,
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo="
|
||||
},
|
||||
"allowlist": {
|
||||
"window": {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ClientAddressDisplay } from './ClientAddress';
|
||||
|
||||
export default {
|
||||
title: 'Wallet / Client Address',
|
||||
component: ClientAddressDisplay,
|
||||
} as ComponentMeta<typeof ClientAddressDisplay>;
|
||||
|
||||
const Template: ComponentStory<typeof ClientAddressDisplay> = (args) => (
|
||||
<Box display="flex" alignContent="center">
|
||||
<ClientAddressDisplay {...args} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
address: 'n222gnd9k6rytn6tz7pf8d2d4dawl7e9cr26111',
|
||||
};
|
||||
|
||||
export const WithCopy = Template.bind({});
|
||||
WithCopy.args = {
|
||||
address: 'n222gnd9k6rytn6tz7pf8d2d4dawl7e9cr26111',
|
||||
withCopy: true,
|
||||
};
|
||||
|
||||
export const WithLabel = Template.bind({});
|
||||
WithLabel.args = {
|
||||
withLabel: true,
|
||||
address: 'n222gnd9k6rytn6tz7pf8d2d4dawl7e9cr26111',
|
||||
};
|
||||
|
||||
export const ShowEntireAddress = Template.bind({});
|
||||
ShowEntireAddress.args = {
|
||||
withLabel: true,
|
||||
showEntireAddress: true,
|
||||
address: 'n222gnd9k6rytn6tz7pf8d2d4dawl7e9cr26111',
|
||||
};
|
||||
|
||||
export const Empty = Template.bind({});
|
||||
Empty.args = {};
|
||||
|
||||
export const EmptyWithLabelAndCopy = Template.bind({});
|
||||
EmptyWithLabelAndCopy.args = {
|
||||
withLabel: true,
|
||||
withCopy: true,
|
||||
};
|
||||
@@ -1,20 +1,58 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import React, { FC, useContext } from 'react';
|
||||
import { Box, Typography, Tooltip } from '@mui/material';
|
||||
import { ClientContext } from '../context/main';
|
||||
import { CopyToClipboard } from './CopyToClipboard';
|
||||
import { splice } from '../utils';
|
||||
|
||||
export const ClientAddress = ({ withCopy }: { withCopy?: boolean }) => {
|
||||
const { clientDetails } = useContext(ClientContext);
|
||||
const AddressTooltip: FC<{ visible?: boolean; address?: string; children: React.ReactElement<any, any> }> = ({
|
||||
visible,
|
||||
address,
|
||||
children,
|
||||
}) => {
|
||||
if (!visible) {
|
||||
return children;
|
||||
}
|
||||
if (!address) {
|
||||
return children;
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="body2" component="span" sx={{ color: 'grey.600' }}>
|
||||
Address:
|
||||
</Typography>{' '}
|
||||
<Typography variant="body2" component="span" color="nym.background.dark" sx={{ mr: 1 }}>
|
||||
{splice(4, 35, clientDetails?.client_address)}
|
||||
</Typography>
|
||||
{withCopy && <CopyToClipboard text={clientDetails?.client_address} iconButton />}
|
||||
</Box>
|
||||
<Tooltip title={address} arrow>
|
||||
{children}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
type ClientAddressProps = {
|
||||
withLabel?: boolean;
|
||||
withCopy?: boolean;
|
||||
showEntireAddress?: boolean;
|
||||
};
|
||||
|
||||
export const ClientAddressDisplay: FC<ClientAddressProps & { address?: string }> = ({
|
||||
withLabel,
|
||||
withCopy,
|
||||
showEntireAddress,
|
||||
address,
|
||||
}) => (
|
||||
<Box>
|
||||
{withLabel && (
|
||||
<>
|
||||
<Typography variant="body2" component="span" sx={{ color: 'grey.600' }}>
|
||||
Address:
|
||||
</Typography>{' '}
|
||||
</>
|
||||
)}
|
||||
|
||||
<AddressTooltip address={address} visible={!showEntireAddress}>
|
||||
<Typography variant="body2" component="span" color="nym.background.dark" sx={{ mr: 1 }}>
|
||||
{showEntireAddress ? address || '' : splice(6, address)}
|
||||
</Typography>
|
||||
</AddressTooltip>
|
||||
{withCopy && <CopyToClipboard text={address} iconButton />}
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const ClientAddress: FC<ClientAddressProps> = ({ ...props }) => {
|
||||
const { clientDetails } = useContext(ClientContext);
|
||||
return <ClientAddressDisplay {...props} address={clientDetails?.client_address} />;
|
||||
};
|
||||
|
||||
@@ -1,40 +1,44 @@
|
||||
import React from 'react';
|
||||
import { NymLogo } from '@nymproject/react';
|
||||
import { NymWordmark } from '@nymproject/react';
|
||||
import { Box, Container } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AppBar, Nav } from '../components';
|
||||
|
||||
export const ApplicationLayout: React.FC = ({ children }) => (
|
||||
<Box
|
||||
sx={{
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '240px auto',
|
||||
gridTemplateRows: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
export const ApplicationLayout: React.FC = ({ children }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
background: '#121726',
|
||||
overflow: 'auto',
|
||||
py: 4,
|
||||
px: 5,
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '240px auto',
|
||||
gridTemplateRows: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<NymLogo width={45} />
|
||||
<Box
|
||||
sx={{
|
||||
background: '#121726',
|
||||
overflow: 'auto',
|
||||
py: 4,
|
||||
px: 5,
|
||||
}}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box>
|
||||
<Box sx={{ mb: 4 }}>
|
||||
<NymWordmark height={14} fill={theme.palette.background.paper} />
|
||||
</Box>
|
||||
<Nav />
|
||||
</Box>
|
||||
<Nav />
|
||||
</Box>
|
||||
<Container>
|
||||
<AppBar />
|
||||
{children}
|
||||
</Container>
|
||||
</Box>
|
||||
<Container>
|
||||
<AppBar />
|
||||
{children}
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ export const Receive = () => {
|
||||
You can receive tokens by providing this address to the sender
|
||||
</Alert>
|
||||
<Box>
|
||||
<ClientAddress withCopy />
|
||||
<ClientAddress withCopy showEntireAddress />
|
||||
</Box>
|
||||
{clientDetails && <QRCode data-testid="qr-code" value={clientDetails?.client_address} />}
|
||||
</Stack>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, IconButton, Link, Stack, TextField } from '@mui/material';
|
||||
import { Box, IconButton, Stack, TextField } from '@mui/material';
|
||||
import { Visibility, VisibilityOff } from '@mui/icons-material';
|
||||
import { Error } from './error';
|
||||
|
||||
@@ -37,10 +37,9 @@ export const PasswordInput: React.FC<{
|
||||
password: string;
|
||||
error?: string;
|
||||
label: string;
|
||||
showForgottenPassword?: boolean;
|
||||
autoFocus?: boolean;
|
||||
onUpdatePassword: (password: string) => void;
|
||||
}> = ({ password, label, error, showForgottenPassword, autoFocus, onUpdatePassword }) => {
|
||||
}> = ({ password, label, error, autoFocus, onUpdatePassword }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -61,18 +60,6 @@ export const PasswordInput: React.FC<{
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{/* currently unused */}
|
||||
{showForgottenPassword && (
|
||||
<Link
|
||||
underline="none"
|
||||
variant="body2"
|
||||
component="div"
|
||||
sx={{ mt: 1, textAlign: 'right', color: 'info.main', cursor: 'pointer' }}
|
||||
href="/forgotten-password"
|
||||
>
|
||||
Forgotten password?
|
||||
</Link>
|
||||
)}
|
||||
</Box>
|
||||
{error && <Error message={error} />}
|
||||
</Stack>
|
||||
|
||||
@@ -11,7 +11,9 @@ export const CreateMnemonic = () => {
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
generateMnemonic();
|
||||
if (mnemonicWords.length === 0) {
|
||||
generateMnemonic();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
|
||||
@@ -23,8 +25,9 @@ export const CreateMnemonic = () => {
|
||||
</Typography>
|
||||
|
||||
<Alert variant="outlined" severity="warning" sx={{ textAlign: 'center' }}>
|
||||
<Typography>Below is your 24 word mnemonic, please store the mnemonic in a safe place.</Typography>
|
||||
<Typography>This is the only way to access your wallet!</Typography>
|
||||
<Typography>
|
||||
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the future.
|
||||
</Typography>
|
||||
</Alert>
|
||||
|
||||
<WordTiles mnemonicWords={mnemonicWords} showIndex />
|
||||
|
||||
@@ -19,11 +19,11 @@ export const ExistingAccount = () => {
|
||||
Sign in with password
|
||||
</Button>
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Button color="inherit" onClick={() => history.goBack()}>
|
||||
<Button color="inherit" onClick={() => history.push('/welcome')}>
|
||||
Back
|
||||
</Button>
|
||||
<Button color="info" onClick={() => history.push('/confirm-mnemonic')}>
|
||||
Create a password
|
||||
<Button color="info" onClick={() => history.push('/sign-in-mnemonic')}>
|
||||
Forgot password?
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import { Box, Button, FormControl, LinearProgress, Stack } from '@mui/material';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { isPasswordCreated } from 'src/requests';
|
||||
import { MnemonicInput, Subtitle } from '../components';
|
||||
import { ClientContext } from '../../../context/main';
|
||||
|
||||
export const SignInMnemonic = () => {
|
||||
const [mnemonic, setMnemonic] = useState('');
|
||||
const { setError, logIn, error, isLoading } = useContext(ClientContext);
|
||||
const [passwordExists, setPasswordExists] = useState(true);
|
||||
const history = useHistory();
|
||||
|
||||
const checkForPassword = async () => {
|
||||
const hasPassword = await isPasswordCreated();
|
||||
setPasswordExists(hasPassword);
|
||||
};
|
||||
|
||||
const handlePageChange = (page: string) => {
|
||||
setError(undefined);
|
||||
history.push(page);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkForPassword();
|
||||
}, []);
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<Box width="25%">
|
||||
@@ -30,19 +46,16 @@ export const SignInMnemonic = () => {
|
||||
>
|
||||
Sign in with mnemonic
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disableElevation
|
||||
size="large"
|
||||
onClick={() => {
|
||||
setError(undefined);
|
||||
history.push('/existing-account');
|
||||
}}
|
||||
fullWidth
|
||||
sx={{ color: 'common.white', border: '1px solid white', '&:hover': { border: '1px solid white' } }}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Box display="flex" justifyContent={passwordExists ? 'center' : 'space-between'}>
|
||||
<Button color="inherit" onClick={() => handlePageChange('/existing-account')}>
|
||||
Back
|
||||
</Button>
|
||||
{!passwordExists && (
|
||||
<Button color="info" onClick={() => handlePageChange('/confirm-mnemonic')}>
|
||||
Create a password
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Button, LinearProgress, FormControl, Stack, Box } from '@mui/material';
|
||||
import { Box, Button, LinearProgress, FormControl, Stack } from '@mui/material';
|
||||
import { PasswordInput, Subtitle } from '../components';
|
||||
import { ClientContext } from '../../../context/main';
|
||||
|
||||
@@ -36,19 +36,29 @@ export const SignInPassword = () => {
|
||||
>
|
||||
Sign in with password
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disableElevation
|
||||
size="large"
|
||||
onClick={() => {
|
||||
setError(undefined);
|
||||
history.push('/existing-account');
|
||||
}}
|
||||
fullWidth
|
||||
sx={{ color: 'common.white', border: '1px solid white', '&:hover': { border: '1px solid white' } }}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Button
|
||||
color="inherit"
|
||||
disableElevation
|
||||
onClick={() => {
|
||||
setError(undefined);
|
||||
history.push('/existing-account');
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="info"
|
||||
onClick={() => {
|
||||
setError(undefined);
|
||||
history.push('/sign-in-mnemonic');
|
||||
}}
|
||||
size="small"
|
||||
>
|
||||
Forgotten password?
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
|
||||
@@ -59,6 +59,9 @@ export const VerifyMnemonic = () => {
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<Button color="inherit" fullWidth size="large" onClick={() => history.goBack()}>
|
||||
Back
|
||||
</Button>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -25,3 +25,8 @@ export const signInWithPassword = async (password: string): Promise<Account> =>
|
||||
export const signOut = async (): Promise<void> => {
|
||||
await invoke('logout');
|
||||
};
|
||||
|
||||
export const isPasswordCreated = async (): Promise<boolean> => {
|
||||
const res: boolean = await invoke('does_password_file_exist');
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export interface ValidatorUrls {
|
||||
urls: Array<string>;
|
||||
}
|
||||
|
||||
export interface Validator {
|
||||
nymd_url: string;
|
||||
api_url: string | null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import bs58 from 'bs58';
|
||||
import { minor, valid } from 'semver';
|
||||
import { valid } from 'semver';
|
||||
import { userBalance, majorToMinor, getLockedCoins, getSpendableCoins } from '../requests';
|
||||
import { Coin, Network, TCurrency } from '../types';
|
||||
import { Console } from './console';
|
||||
@@ -71,9 +71,7 @@ export const isValidHostname = (value: string) => {
|
||||
|
||||
export const validateVersion = (version: string): boolean => {
|
||||
try {
|
||||
const minorVersion = minor(version);
|
||||
const validVersion = valid(version);
|
||||
return validVersion !== null && minorVersion >= 11;
|
||||
return valid(version) !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
@@ -134,11 +132,9 @@ export const currencyMap = (network?: Network): TCurrency => {
|
||||
}
|
||||
};
|
||||
|
||||
export const splice = (start: number, deleteCount: number, address?: string): string => {
|
||||
export const splice = (size: number, address?: string): string => {
|
||||
if (address) {
|
||||
const array = address.split('');
|
||||
array.splice(start, deleteCount, '...');
|
||||
return array.join('');
|
||||
return `${address.slice(0, size)}...${address.slice(-size)}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
@@ -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 @@ import Wordmark from '@assets/logo/logo-wordmark.svg';
|
||||
import { useTheme } from '@mui/material';
|
||||
import { LogoProps } from './LogoProps';
|
||||
|
||||
export const NymWordmark: React.FC<LogoProps> = ({ height, width }) => {
|
||||
export const NymWordmark: React.FC<LogoProps & { fill?: string }> = ({ height, width, fill }) => {
|
||||
const theme = useTheme();
|
||||
return <Wordmark height={height} width={width} fill={theme.palette.text.primary} />;
|
||||
return <Wordmark height={height} width={width} fill={fill || theme.palette.text.primary} />;
|
||||
};
|
||||
|
||||
@@ -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