diff --git a/.github/workflows/network-explorer.yml b/.github/workflows/network-explorer.yml index e2f424798e..2f5b10de94 100644 --- a/.github/workflows/network-explorer.yml +++ b/.github/workflows/network-explorer.yml @@ -1,6 +1,7 @@ name: CI for Network Explorer on: + workflow_dispatch: push: paths: - 'explorer/**' @@ -75,3 +76,14 @@ jobs: uses: docker://keybaseio/client:stable-node with: args: .github/workflows/support-files/notifications/entry_point.sh + - name: Deploy + if: github.event_name == 'workflow_dispatch' + uses: easingthemes/ssh-deploy@main + env: + SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }} + ARGS: "-rltgoDzvO --delete" + SOURCE: "explorer/dist/" + REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }} + REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }} + TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }} + EXCLUDE: "/dist/, /node_modules/" diff --git a/CHANGELOG.md b/CHANGELOG.md index 191b28da4d..b12ec2d2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// ### Added - socks5 client/websocket client: add `--force-register-gateway` flag, useful when rerunning init ([#1353]) -- nym-connect: initial proof-of-concept of a UI around the socks5 client was added -- nym-connect: add ability to select network requester and gateway ([#1427]) -- nym-connect: add ability to export gateway keys as JSON -- nym-connect: add auto updater - all: added network compilation target to `--help` (or `--version`) commands ([#1256]). - explorer-api: learned how to sum the delegations by owner in a new endpoint. - explorer-api: add apy values to `mix_nodes` endpoint @@ -39,10 +35,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - native & socks5 clients: rerun init will now reuse previous gateway configuration instead of failing ([#1353]) - native & socks5 clients: deduplicate big chunks of init logic - validator: fixed local docker-compose setup to work on Apple M1 ([#1329]) +- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]). +- network-requester: fix filter for suffix-only domains ([#1487]) ### Changed -- nym-connect: reuse config id instead of creating a new id on each connection - validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]] - all: updated all `cosmwasm`-related dependencies to `1.0.0` and `cw-storage-plus` to `0.13.4` [[#1318]] - all: updated `rocket` to `0.5.0-rc.2`. @@ -52,6 +49,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - socks5 client/websocket client: upgrade to latest clap and switched to declarative commandline parsing. - validator-api: fee payment for multisig operations comes from the gateway account instead of the validator APIs' accounts ([#1419]) - multisig-contract: Limit the proposal creating functionality to one address (coconut-bandwidth-contract address) ([#1457]) +- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463]) +- gateway, network-statistics: include gateway id in the sent statistical data ([#1478]) + [#1249]: https://github.com/nymtech/nym/pull/1249 [#1256]: https://github.com/nymtech/nym/pull/1256 @@ -72,8 +72,26 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1393]: https://github.com/nymtech/nym/pull/1393 [#1404]: https://github.com/nymtech/nym/pull/1404 [#1419]: https://github.com/nymtech/nym/pull/1419 -[#1427]: https://github.com/nymtech/nym/pull/1427 [#1457]: https://github.com/nymtech/nym/pull/1457 +[#1463]: https://github.com/nymtech/nym/pull/1463 +[#1478]: https://github.com/nymtech/nym/pull/1478 +[#1482]: https://github.com/nymtech/nym/pull/1482 +[#1487]: https://github.com/nymtech/nym/pull/1487 + +## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22) + +### Added + +- nym-connect: initial proof-of-concept of a UI around the socks5 client was added +- nym-connect: add ability to select network requester and gateway ([#1427]) +- nym-connect: add ability to export gateway keys as JSON +- nym-connect: add auto updater + +### Changed + +- nym-connect: reuse config id instead of creating a new id on each connection + +[#1427]: https://github.com/nymtech/nym/pull/1427 ## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11) diff --git a/Cargo.lock b/Cargo.lock index 8f89dcb1ae..e7bcd513f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -928,7 +928,6 @@ dependencies = [ "coconut-interface", "cosmrs", "crypto", - "network-defaults", "rand 0.7.3", "thiserror", "url", @@ -1591,6 +1590,7 @@ name = "explorer-api" version = "1.0.1" dependencies = [ "chrono", + "clap 3.2.8", "humantime-serde", "isocountry", "itertools", @@ -1606,6 +1606,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "task", "thiserror", "tokio", "validator-client", @@ -2871,7 +2872,6 @@ dependencies = [ "cosmwasm-std", "fixed", "log", - "network-defaults", "schemars", "serde", "serde_repr", @@ -2968,6 +2968,7 @@ name = "network-defaults" version = "0.1.0" dependencies = [ "cfg-if 1.0.0", + "dotenv", "hex-literal", "once_cell", "serde", @@ -3065,7 +3066,6 @@ dependencies = [ "credentials", "crypto", "dirs", - "dotenv", "futures", "gateway-client", "gateway-requests", @@ -3232,7 +3232,6 @@ dependencies = [ "credentials", "crypto", "dirs", - "dotenv", "futures", "gateway-client", "gateway-requests", @@ -5390,7 +5389,6 @@ version = "1.0.1" dependencies = [ "async-trait", "log", - "network-defaults", "reqwest", "serde", "serde_json", @@ -6338,7 +6336,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "vesting-contract" version = "1.0.1" dependencies = [ - "config", "cosmwasm-std", "cw-storage-plus", "mixnet-contract-common", diff --git a/Makefile b/Makefile index d1c2520d42..fb1393d11a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ +test: clippy-all cargo-test wasm fmt test-all: test cargo-test-expensive -test: build clippy-all cargo-test wasm fmt no-clippy: build cargo-test wasm fmt happy: fmt clippy-happy test clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-all-connect diff --git a/clients/client-core/src/config/mod.rs b/clients/client-core/src/config/mod.rs index f797851ce3..1c81d5e079 100644 --- a/clients/client-core/src/config/mod.rs +++ b/clients/client-core/src/config/mod.rs @@ -1,7 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use config::defaults::*; use config::NymConfig; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; @@ -366,7 +365,7 @@ impl Default for Client { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), disabled_credentials_mode: true, - validator_api_urls: default_api_endpoints(), + validator_api_urls: vec![], private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), private_encryption_key_file: Default::default(), diff --git a/clients/credential/src/client.rs b/clients/credential/src/client.rs index 1c3286f7c5..005aab356e 100644 --- a/clients/credential/src/client.rs +++ b/clients/credential/src/client.rs @@ -4,7 +4,7 @@ use crate::error::Result; use crate::{MNEMONIC, NYMD_URL}; use bip39::Mnemonic; -use network_defaults::{DEFAULT_NETWORK, MIX_DENOM, VOUCHER_INFO}; +use network_defaults::{NymNetworkDetails, VOUCHER_INFO}; use std::str::FromStr; use url::Url; use validator_client::nymd; @@ -13,18 +13,23 @@ use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient}; pub(crate) struct Client { nymd_client: NymdClient, + mix_denom_base: String, } impl Client { pub fn new() -> Self { let nymd_url = Url::from_str(NYMD_URL).unwrap(); let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap(); - let config = nymd::Config::try_from_nym_network_details(&DEFAULT_NETWORK.details()) + let network_details = NymNetworkDetails::new_from_env(); + let config = nymd::Config::try_from_nym_network_details(&network_details) .expect("failed to construct valid validator client config with the provided network"); let nymd_client = NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap(); - Client { nymd_client } + Client { + nymd_client, + mix_denom_base: network_details.chain_details.mix_denom.base, + } } pub async fn deposit( @@ -34,7 +39,7 @@ impl Client { encryption_key: String, fee: Option, ) -> Result { - let amount = Coin::new(amount as u128, MIX_DENOM.base.to_string()); + let amount = Coin::new(amount as u128, self.mix_denom_base.clone()); Ok(self .nymd_client .deposit( diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 79b04a7d7b..68b4c6d8b3 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -22,7 +22,6 @@ url = "2.2" clap = { version = "3.2.8", features = ["cargo", "derive"] } dirs = "4.0" -dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being) log = "0.4" # self explanatory pretty_env_logger = "0.4" # for formatting log messages rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index c63ada0383..b1e5da339f 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -3,7 +3,6 @@ use crate::client::config::{Config, SocketType}; use clap::{Parser, Subcommand}; -use network_defaults::DEFAULT_NETWORK; use url::Url; #[cfg(not(feature = "coconut"))] @@ -28,7 +27,6 @@ fn long_version() -> String { {:<20}{} {:<20}{} {:<20}{} -{:<20}{} "#, "Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP"), @@ -46,8 +44,6 @@ fn long_version() -> String { env!("VERGEN_RUSTC_CHANNEL"), "cargo Profile:", env!("VERGEN_CARGO_PROFILE"), - "Network:", - DEFAULT_NETWORK ) } @@ -58,6 +54,10 @@ fn long_version_static() -> &'static str { #[derive(Parser)] #[clap(author = "Nymtech", version, long_version = long_version_static(), about)] pub(crate) struct Cli { + /// Path pointing to an env file that configures the client. + #[clap(long)] + pub(crate) config_env_file: Option, + #[clap(subcommand)] command: Commands, } @@ -113,6 +113,12 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi config .get_base_mut() .set_custom_validator_apis(parse_validators(&raw_validators)); + } else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() { + let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR) + .expect("api validator not set"); + config + .get_base_mut() + .set_custom_validator_apis(parse_validators(&raw_validators)); } if args.disable_socket { diff --git a/clients/native/src/commands/upgrade.rs b/clients/native/src/commands/upgrade.rs index 2ac897cf23..c925b10f08 100644 --- a/clients/native/src/commands/upgrade.rs +++ b/clients/native/src/commands/upgrade.rs @@ -3,7 +3,7 @@ use crate::client::config::{Config, MISSING_VALUE}; -use config::{defaults::default_api_endpoints, NymConfig}; +use config::NymConfig; use version_checker::Version; use clap::Args; @@ -105,15 +105,6 @@ fn minor_0_12_upgrade( print_start_upgrade(&config_version, &to_version); - println!( - "Setting validator API endpoints to {:?}", - default_api_endpoints() - ); - - config - .get_base_mut() - .set_custom_validator_apis(default_api_endpoints()); - config .get_base_mut() .set_custom_version(to_version.to_string().as_ref()); diff --git a/clients/native/src/main.rs b/clients/native/src/main.rs index dd0f644e43..13e82ec660 100644 --- a/clients/native/src/main.rs +++ b/clients/native/src/main.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::{crate_version, Parser}; +use network_defaults::setup_env; pub mod client; pub mod commands; @@ -9,11 +10,11 @@ pub mod websocket; #[tokio::main] async fn main() { - dotenv::dotenv().ok(); setup_logging(); println!("{}", banner()); let args = commands::Cli::parse(); + setup_env(args.config_env_file.clone()); commands::execute(&args).await; } diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 0c431871df..2c79789bc0 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -13,7 +13,6 @@ path = "src/lib.rs" [dependencies] clap = { version = "3.2.8", features = ["cargo", "derive"] } dirs = "4.0" -dotenv = "0.15.0" futures = "0.3" log = "0.4" pin-project = "1.0" diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index ea9d5da052..f9df56bc64 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -3,7 +3,6 @@ use crate::client::config::Config; use clap::{Parser, Subcommand}; -use network_defaults::DEFAULT_NETWORK; use url::Url; pub mod init; @@ -28,7 +27,6 @@ fn long_version() -> String { {:<20}{} {:<20}{} {:<20}{} -{:<20}{} "#, "Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP"), @@ -46,8 +44,6 @@ fn long_version() -> String { env!("VERGEN_RUSTC_CHANNEL"), "cargo Profile:", env!("VERGEN_CARGO_PROFILE"), - "Network:", - DEFAULT_NETWORK ) } @@ -58,6 +54,10 @@ fn long_version_static() -> &'static str { #[derive(Parser)] #[clap(author = "Nymtech", version, long_version = long_version_static(), about)] pub(crate) struct Cli { + /// Path pointing to an env file that configures the client. + #[clap(long)] + pub(crate) config_env_file: Option, + #[clap(subcommand)] command: Commands, } @@ -96,7 +96,7 @@ pub(crate) async fn execute(args: &Cli) { } } -fn parse_validators(raw: &str) -> Vec { +pub fn parse_validators(raw: &str) -> Vec { raw.split(',') .map(|raw_validator| { raw_validator @@ -112,6 +112,10 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi config .get_base_mut() .set_custom_validator_apis(parse_validators(&raw_validators)); + } else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::API_VALIDATOR) { + config + .get_base_mut() + .set_custom_validator_apis(parse_validators(&raw_validators)); } if let Some(port) = args.port { diff --git a/clients/socks5/src/commands/upgrade.rs b/clients/socks5/src/commands/upgrade.rs index 849400d3d6..5e9517ac2e 100644 --- a/clients/socks5/src/commands/upgrade.rs +++ b/clients/socks5/src/commands/upgrade.rs @@ -3,7 +3,7 @@ use crate::client::config::{Config, MISSING_VALUE}; -use config::{defaults::default_api_endpoints, NymConfig}; +use config::NymConfig; use version_checker::Version; use clap::Args; @@ -104,15 +104,6 @@ fn minor_0_12_upgrade( print_start_upgrade(&config_version, &to_version); - println!( - "Setting validator API endpoints to {:?}", - default_api_endpoints() - ); - - config - .get_base_mut() - .set_custom_validator_apis(default_api_endpoints()); - config .get_base_mut() .set_custom_version(to_version.to_string().as_ref()); diff --git a/clients/socks5/src/main.rs b/clients/socks5/src/main.rs index 7571a482e0..fb7a8bb607 100644 --- a/clients/socks5/src/main.rs +++ b/clients/socks5/src/main.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::{crate_version, Parser}; +use network_defaults::setup_env; pub mod client; mod commands; @@ -9,11 +10,11 @@ pub mod socks; #[tokio::main] async fn main() { - dotenv::dotenv().ok(); setup_logging(); println!("{}", banner()); let args = commands::Cli::parse(); + setup_env(args.config_env_file.clone()); commands::execute(&args).await; } diff --git a/clients/socks5/src/socks/mixnet_responses.rs b/clients/socks5/src/socks/mixnet_responses.rs index 5ff06dada2..3146967711 100644 --- a/clients/socks5/src/socks/mixnet_responses.rs +++ b/clients/socks5/src/socks/mixnet_responses.rs @@ -5,7 +5,7 @@ use futures::StreamExt; use log::*; use nymsphinx::receiver::ReconstructedMessage; use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender}; -use socks5_requests::Response; +use socks5_requests::Message; pub(crate) struct MixnetResponseListener { buffer_requester: ReceivedBufferRequestSender, @@ -44,12 +44,16 @@ impl MixnetResponseListener { warn!("this message had a surb - we didn't do anything with it"); } - let response = match Response::try_from_bytes(&raw_message) { + let response = match Message::try_from_bytes(&raw_message) { Err(err) => { warn!("failed to parse received response - {:?}", err); return; } - Ok(data) => data, + Ok(Message::Request(_)) => { + warn!("unexpected request"); + return; + } + Ok(Message::Response(data)) => data, }; self.controller_sender diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 71129dc50c..d820611bf0 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -28,7 +28,7 @@ use mixnet_contract_common::{ RewardedSetUpdateDetails, }; #[cfg(feature = "nymd-client")] -use network_defaults::{all::Network, NymNetworkDetails}; +use network_defaults::NymNetworkDetails; #[cfg(feature = "nymd-client")] use std::collections::{HashMap, HashSet}; @@ -113,9 +113,6 @@ impl Config { #[cfg(feature = "nymd-client")] pub struct Client { - // compatibility : ( - pub network: Network, - // TODO: we really shouldn't be storing a mnemonic here, but removing it would be // non-trivial amount of work and it's out of scope of the current branch mnemonic: Option, @@ -134,9 +131,6 @@ pub struct Client { impl Client { pub fn new_signing( config: Config, - // we need to provide network argument due to compatibility with other components (wallet...) - // that rely on its existence... - network: Network, mnemonic: bip39::Mnemonic, ) -> Result, ValidatorClientError> { let validator_api_client = validator_api::Client::new(config.api_url.clone()); @@ -148,7 +142,6 @@ impl Client { )?; Ok(Client { - network, mnemonic: Some(mnemonic), mixnode_page_limit: config.mixnode_page_limit, gateway_page_limit: config.gateway_page_limit, @@ -176,18 +169,12 @@ impl Client { #[cfg(feature = "nymd-client")] impl Client { - pub fn new_query( - config: Config, - // we need to provide network argument due to compatibility with other components (wallet...) - // that rely on its existence... - network: Network, - ) -> Result, ValidatorClientError> { + pub fn new_query(config: Config) -> Result, ValidatorClientError> { let validator_api_client = validator_api::Client::new(config.api_url.clone()); let nymd_client = NymdClient::connect(config.nymd_config.clone(), config.nymd_url.as_str())?; Ok(Client { - network, mnemonic: None, mixnode_page_limit: config.mixnode_page_limit, gateway_page_limit: config.gateway_page_limit, diff --git a/common/client-libs/validator-client/src/nymd/mod.rs b/common/client-libs/validator-client/src/nymd/mod.rs index ba4c53cd6c..a279324ff8 100644 --- a/common/client-libs/validator-client/src/nymd/mod.rs +++ b/common/client-libs/validator-client/src/nymd/mod.rs @@ -1006,6 +1006,29 @@ impl NymdClient { .await } + #[execute("mixnet")] + fn _compound_reward( + &self, + operator: Option, + delegator: Option, + mix_identity: Option, + proxy: Option, + fee: Option, + ) -> (ExecuteMsg, Option) + where + C: SigningCosmWasmClient + Sync, + { + ( + ExecuteMsg::CompoundReward { + operator, + delegator, + mix_identity, + proxy, + }, + fee, + ) + } + #[execute("mixnet")] fn _compound_operator_reward(&self, fee: Option) -> (ExecuteMsg, Option) where diff --git a/common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs index 9b685a0f0e..46bb118ef0 100644 --- a/common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs +++ b/common/client-libs/validator-client/src/nymd/traits/multisig_signing_client.rs @@ -12,7 +12,6 @@ use multisig_contract_common::msg::ExecuteMsg; use async_trait::async_trait; use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg}; use cw3::Vote; -use network_defaults::DEFAULT_NETWORK; #[async_trait] pub trait MultisigSigningClient { @@ -49,7 +48,10 @@ impl MultisigSigningClient for NymdClien ) -> Result { let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { - funds: Coin::new(voucher_value, DEFAULT_NETWORK.mix_denom().base), + funds: Coin::new( + voucher_value, + self.config.chain_details.mix_denom.base.clone(), + ), }; let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: self.coconut_bandwidth_contract_address().to_string(), diff --git a/common/client-libs/validator-client/src/nymd/wallet/mod.rs b/common/client-libs/validator-client/src/nymd/wallet/mod.rs index 0ea6b081c2..247ffcd743 100644 --- a/common/client-libs/validator-client/src/nymd/wallet/mod.rs +++ b/common/client-libs/validator-client/src/nymd/wallet/mod.rs @@ -207,35 +207,20 @@ mod tests { "acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", "step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball" ]; - let prefixes = vec![ - MAINNET.bech32_prefix(), - SANDBOX.bech32_prefix(), - QA.bech32_prefix(), - ]; + let prefix = MAINNET.bech32_prefix(); - for prefix in prefixes { - let addrs = match prefix.as_ref() { - "nymt" => vec![ - "nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94", - "nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv", - "nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4", - ], - "n" => vec![ - "n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf", - "n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es", - "n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf", - ], - _ => panic!("Test needs to be updated with new bech32 prefix"), - }; - for (idx, mnemonic) in mnemonics.iter().enumerate() { - let wallet = - DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap()) - .unwrap(); - assert_eq!( - wallet.try_derive_accounts().unwrap()[0].address, - addrs[idx].parse().unwrap() - ) - } + let addrs = vec![ + "n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf", + "n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es", + "n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf", + ]; + for (idx, mnemonic) in mnemonics.iter().enumerate() { + let wallet = + DirectSecp256k1HdWallet::from_mnemonic(&prefix, mnemonic.parse().unwrap()).unwrap(); + assert_eq!( + wallet.try_derive_accounts().unwrap()[0].address, + addrs[idx].parse().unwrap() + ) } } } diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs index ea2c1d18b9..20f1e2700d 100644 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs @@ -11,6 +11,7 @@ use crate::{deposit::DepositData, spend_credential::SpendCredentialData}; pub struct InstantiateMsg { pub multisig_addr: String, pub pool_addr: String, + pub mix_denom: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml index 0f6e5d9756..20fc4ecb16 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml @@ -14,7 +14,6 @@ serde = { version = "1.0", features = ["derive"] } serde_repr = "0.1" schemars = "0.8" thiserror = "1.0" -network-defaults = { path = "../../network-defaults" } fixed = { version = "1.1", features = ["serde"] } az = "1.1" log = "0.4.14" diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index d8f09d504a..6d71de8887 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct InstantiateMsg { pub rewarding_validator_address: String, + pub mixnet_denom: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] @@ -32,6 +33,12 @@ pub enum ExecuteMsg { CompoundDelegatorReward { mix_identity: IdentityKey, }, + CompoundReward { + operator: Option, + delegator: Option, + mix_identity: Option, + proxy: Option, + }, BondMixnode { mix_node: MixNode, owner_signature: String, @@ -115,6 +122,7 @@ pub enum ExecuteMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { + GetBlacklistedNodes {}, GetCurrentOperatorCost {}, GetRewardingValidatorAddress {}, GetAllDelegationKeys {}, @@ -205,6 +213,31 @@ pub enum QueryMsg { }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub struct MigrateMsg {} +pub struct MigrateMsg { + pub mixnet_denom: String, + nodes_to_remove: Option>, +} + +impl MigrateMsg { + pub fn nodes_to_remove(&self) -> Vec { + self.nodes_to_remove.clone().unwrap_or_default() + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct NodeToRemove { + owner: String, + proxy: Option, +} + +impl NodeToRemove { + pub fn owner(&self) -> &str { + &self.owner + } + + pub fn proxy(&self) -> Option<&String> { + self.proxy.as_ref() + } +} diff --git a/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs b/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs index e20464bcfc..93fd9f4839 100644 --- a/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs @@ -14,6 +14,7 @@ use cw_utils::{Duration, Expiration, Threshold}; pub struct InstantiateMsg { // this is the group contract that contains the member list pub group_addr: String, + pub coconut_bandwidth_contract_address: String, pub threshold: Threshold, pub max_voting_period: Duration, } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs index f995c241fd..da29e02cae 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -1,6 +1,5 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use config::defaults::MIX_DENOM; use cosmwasm_std::{Coin, Timestamp}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -10,8 +9,8 @@ pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; pub mod events; pub mod messages; -pub fn one_ucoin() -> Coin { - Coin::new(1, MIX_DENOM.base) +pub fn one_ucoin(denom: String) -> Coin { + Coin::new(1, denom) } #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 51802e77ac..a9dd627557 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -7,11 +7,14 @@ use serde::{Deserialize, Serialize}; #[serde(rename_all = "snake_case")] pub struct InitMsg { pub mixnet_contract_address: String, + pub mix_denom: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub struct MigrateMsg {} +pub struct MigrateMsg { + pub mix_denom: String, +} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)] pub struct VestingSpecification { diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 110d6ee224..4ff6b60d2b 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -14,7 +14,6 @@ url = "2.2" # I guess temporarily until we get serde support in coconut up and running coconut-interface = { path = "../coconut-interface" } crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] } -network-defaults = { path = "../network-defaults" } validator-api-requests = { path = "../../validator-api/validator-api-requests" } validator-client = { path = "../client-libs/validator-client" } diff --git a/common/crypto/src/bech32_address_validation.rs b/common/crypto/src/bech32_address_validation.rs index 21db7e5297..f432daab4c 100644 --- a/common/crypto/src/bech32_address_validation.rs +++ b/common/crypto/src/bech32_address_validation.rs @@ -1,4 +1,3 @@ -use config::defaults::DEFAULT_NETWORK; use subtle_encoding::bech32; #[derive(Debug, Clone, PartialEq, Eq)] @@ -15,16 +14,15 @@ pub fn try_bech32_decode(address: &str) -> Result { } } -pub fn validate_bech32_prefix(address: &str) -> Result<(), Bech32Error> { +pub fn validate_bech32_prefix(bech32_prefix: &str, address: &str) -> Result<(), Bech32Error> { let prefix = try_bech32_decode(address)?; - if prefix == DEFAULT_NETWORK.bech32_prefix() { + if prefix == bech32_prefix { Ok(()) } else { Err(Bech32Error::WrongPrefix(format!( "your bech32 address prefix should be {}, not {}", - DEFAULT_NETWORK.bech32_prefix(), - prefix + bech32_prefix, prefix ))) } } @@ -33,6 +31,8 @@ pub fn validate_bech32_prefix(address: &str) -> Result<(), Bech32Error> { mod tests { use super::*; + const TEST_BECH32_PREFIX: &str = "n"; + mod decoding_bech32_addresses { use super::*; @@ -71,9 +71,12 @@ mod tests { assert_eq!( Err(Bech32Error::WrongPrefix(format!( "your bech32 address prefix should be {}, not punk", - DEFAULT_NETWORK.bech32_prefix() + TEST_BECH32_PREFIX ))), - validate_bech32_prefix("punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0") + validate_bech32_prefix( + TEST_BECH32_PREFIX, + "punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0" + ) ) } @@ -82,6 +85,7 @@ mod tests { assert_eq!( Ok(()), validate_bech32_prefix( + TEST_BECH32_PREFIX, "n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g" ) ) diff --git a/common/network-defaults/Cargo.toml b/common/network-defaults/Cargo.toml index 85282473ce..0c84938c7b 100644 --- a/common/network-defaults/Cargo.toml +++ b/common/network-defaults/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] cfg-if = "1.0.0" +dotenv = "0.15.0" hex-literal = "0.3.3" once_cell = "1.7.2" serde = {version = "1.0", features = ["derive"]} diff --git a/common/network-defaults/build.rs b/common/network-defaults/build.rs deleted file mode 100644 index 2c71c67e3d..0000000000 --- a/common/network-defaults/build.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -fn main() { - match option_env!("NETWORK") { - None | Some("mainnet") => println!("cargo:rustc-cfg=network=\"mainnet\"",), - Some("sandbox") => println!("cargo:rustc-cfg=network=\"sandbox\"",), - Some("qa") => println!("cargo:rustc-cfg=network=\"qa\""), - _ => panic!("No such network"), - } -} diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 6281527910..a156394e56 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -3,6 +3,7 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +use std::{env::var, path::PathBuf}; use url::Url; pub mod all; @@ -10,36 +11,10 @@ pub mod eth_contract; pub mod mainnet; pub mod qa; pub mod sandbox; +pub mod var_names; -// The set of defaults that are decided at compile time. Ideally we want to reduce these to a -// minimum. -// Keep DENOM around mostly for use in contracts. (TODO: consider moving it there, or renaming?) -cfg_if::cfg_if! { - if #[cfg(network = "mainnet")] { - pub const DEFAULT_NETWORK: all::Network = all::Network::MAINNET; - pub const MIX_DENOM: DenomDetails = mainnet::MIX_DENOM; - pub const STAKE_DENOM: DenomDetails = mainnet::STAKE_DENOM; - - pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS; - pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS; - - } else if #[cfg(network = "qa")] { - pub const DEFAULT_NETWORK: all::Network = all::Network::QA; - pub const MIX_DENOM: DenomDetails = qa::MIX_DENOM; - pub const STAKE_DENOM: DenomDetails = qa::STAKE_DENOM; - - pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS; - pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS; - - } else if #[cfg(network = "sandbox")] { - pub const DEFAULT_NETWORK: all::Network = all::Network::SANDBOX; - pub const MIX_DENOM: DenomDetails = sandbox::MIX_DENOM; - pub const STAKE_DENOM: DenomDetails = sandbox::STAKE_DENOM; - - pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS; - pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS; - } -} +pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS; +pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS; #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ChainDetails { @@ -82,23 +57,56 @@ impl NymNetworkDetails { NymNetworkDetails::default() } - pub fn new_qa() -> Self { - (&*QA_DEFAULTS).into() - } - - pub fn new_sandbox() -> Self { - (&*SANDBOX_DEFAULTS).into() + pub fn new_from_env() -> Self { + NymNetworkDetails::new() + .with_bech32_account_prefix( + var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"), + ) + .with_mix_denom(DenomDetailsOwned { + base: var(var_names::MIX_DENOM).expect("mix denomination base not set"), + display: var(var_names::MIX_DENOM_DISPLAY) + .expect("mix denomination display not set"), + display_exponent: var(var_names::DENOMS_EXPONENT) + .expect("denomination exponent not set") + .parse() + .expect("denomination exponent is not u32"), + }) + .with_stake_denom(DenomDetailsOwned { + base: var(var_names::STAKE_DENOM).expect("stake denomination base not set"), + display: var(var_names::STAKE_DENOM_DISPLAY) + .expect("stake denomination display not set"), + display_exponent: var(var_names::DENOMS_EXPONENT) + .expect("denomination exponent not set") + .parse() + .expect("denomination exponent is not u32"), + }) + .with_validator_endpoint(ValidatorDetails::new( + var(var_names::NYMD_VALIDATOR).expect("nymd validator not set"), + Some(var(var_names::API_VALIDATOR).expect("api validator not set")), + )) + .with_mixnet_contract(Some( + var(var_names::MIXNET_CONTRACT_ADDRESS).expect("mixnet contract not set"), + )) + .with_vesting_contract(Some( + var(var_names::VESTING_CONTRACT_ADDRESS).expect("vesting contract not set"), + )) + .with_bandwidth_claim_contract(Some( + var(var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS) + .expect("bandwidth claim contract not set"), + )) + .with_coconut_bandwidth_contract(Some( + var(var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS) + .expect("coconut bandwidth contract not set"), + )) + .with_multisig_contract(Some( + var(var_names::MULTISIG_CONTRACT_ADDRESS).expect("multisig contract not set"), + )) } pub fn new_mainnet() -> Self { (&*MAINNET_DEFAULTS).into() } - pub fn current_default() -> Self { - // backwards compatibility reasons - DEFAULT_NETWORK.details() - } - pub fn with_bech32_account_prefix>(mut self, prefix: S) -> Self { self.chain_details.bech32_account_prefix = prefix.into(); self @@ -333,27 +341,21 @@ impl ValidatorDetails { } } -pub fn default_statistics_service_url() -> Url { - DEFAULT_NETWORK - .statistics_service_url() - .parse() - .expect("the provided statistics service url is invalid!") -} - -pub fn default_nymd_endpoints() -> Vec { - DEFAULT_NETWORK - .validators() - .iter() - .map(ValidatorDetails::nymd_url) - .collect() -} - -pub fn default_api_endpoints() -> Vec { - DEFAULT_NETWORK - .validators() - .iter() - .filter_map(ValidatorDetails::api_url) - .collect() +pub fn setup_env(config_env_file: Option) { + match std::env::var(var_names::CONFIGURED) { + // if the configuration is not already set in the env vars + Err(std::env::VarError::NotPresent) => { + if let Some(config_env_file) = config_env_file { + dotenv::from_path(config_env_file) + .expect("Invalid path to environment configuration file"); + } else { + // if nothing is set, the use mainnet defaults + crate::mainnet::export_to_env(); + } + } + Err(_) => crate::mainnet::export_to_env(), + _ => {} + } } // Name of the event triggered by the eth contract. If the event name is changed, diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs index badce8e2bc..2bb7084a12 100644 --- a/common/network-defaults/src/mainnet.rs +++ b/common/network-defaults/src/mainnet.rs @@ -1,6 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::var_names; use crate::{DenomDetails, ValidatorDetails}; pub(crate) const BECH32_PREFIX: &str = "n"; @@ -24,9 +25,48 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy"; pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://127.0.0.1:8090"; +pub const NYMD_VALIDATOR: &str = "https://rpc.nyx.nodes.guru/"; +pub const API_VALIDATOR: &str = "https://validator.nymtech.net/api/"; pub(crate) fn validators() -> Vec { - vec![ValidatorDetails::new( - "https://rpc.nyx.nodes.guru/", - Some("https://validator.nymtech.net/api/"), - )] + vec![ValidatorDetails::new(NYMD_VALIDATOR, Some(API_VALIDATOR))] +} + +pub fn export_to_env() { + std::env::set_var(var_names::CONFIGURED, "true"); + std::env::set_var(var_names::BECH32_PREFIX, BECH32_PREFIX); + std::env::set_var(var_names::MIX_DENOM, MIX_DENOM.base); + std::env::set_var(var_names::MIX_DENOM_DISPLAY, MIX_DENOM.display); + std::env::set_var(var_names::STAKE_DENOM, STAKE_DENOM.base); + std::env::set_var(var_names::STAKE_DENOM_DISPLAY, STAKE_DENOM.display); + std::env::set_var( + var_names::DENOMS_EXPONENT, + STAKE_DENOM.display_exponent.to_string(), + ); + std::env::set_var(var_names::MIXNET_CONTRACT_ADDRESS, MIXNET_CONTRACT_ADDRESS); + std::env::set_var( + var_names::VESTING_CONTRACT_ADDRESS, + VESTING_CONTRACT_ADDRESS, + ); + std::env::set_var( + var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, + BANDWIDTH_CLAIM_CONTRACT_ADDRESS, + ); + std::env::set_var( + var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, + COCONUT_BANDWIDTH_CONTRACT_ADDRESS, + ); + std::env::set_var( + var_names::MULTISIG_CONTRACT_ADDRESS, + MULTISIG_CONTRACT_ADDRESS, + ); + std::env::set_var( + var_names::REWARDING_VALIDATOR_ADDRESS, + REWARDING_VALIDATOR_ADDRESS, + ); + std::env::set_var( + var_names::STATISTICS_SERVICE_DOMAIN_ADDRESS, + STATISTICS_SERVICE_DOMAIN_ADDRESS, + ); + std::env::set_var(var_names::NYMD_VALIDATOR, NYMD_VALIDATOR); + std::env::set_var(var_names::API_VALIDATOR, API_VALIDATOR); } diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs new file mode 100644 index 0000000000..d1f3cf2736 --- /dev/null +++ b/common/network-defaults/src/var_names.rs @@ -0,0 +1,21 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +// Environment variable that, if set, shows the environment is currently configured +pub const CONFIGURED: &str = "CONFIGURED"; + +pub const BECH32_PREFIX: &str = "BECH32_PREFIX"; +pub const MIX_DENOM: &str = "MIX_DENOM"; +pub const MIX_DENOM_DISPLAY: &str = "MIX_DENOM_DISPLAY"; +pub const STAKE_DENOM: &str = "STAKE_DENOM"; +pub const STAKE_DENOM_DISPLAY: &str = "STAKE_DENOM_DISPLAY"; +pub const DENOMS_EXPONENT: &str = "DENOMS_EXPONENT"; +pub const MIXNET_CONTRACT_ADDRESS: &str = "MIXNET_CONTRACT_ADDRESS"; +pub const VESTING_CONTRACT_ADDRESS: &str = "VESTING_CONTRACT_ADDRESS"; +pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "BANDWIDTH_CLAIM_CONTRACT_ADDRESS"; +pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "COCONUT_BANDWIDTH_CONTRACT_ADDRESS"; +pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS"; +pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS"; +pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_ADDRESS"; +pub const NYMD_VALIDATOR: &str = "NYMD_VALIDATOR"; +pub const API_VALIDATOR: &str = "API_VALIDATOR"; diff --git a/common/statistics/Cargo.toml b/common/statistics/Cargo.toml index 1094962c21..9514edd29b 100644 --- a/common/statistics/Cargo.toml +++ b/common/statistics/Cargo.toml @@ -17,5 +17,3 @@ serde_json = "1" sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]} thiserror = "1" tokio = { version = "1.19.1", features = [ "time" ] } - -network-defaults = { path = "../network-defaults" } diff --git a/common/statistics/src/lib.rs b/common/statistics/src/lib.rs index 9e107fc119..5423106648 100644 --- a/common/statistics/src/lib.rs +++ b/common/statistics/src/lib.rs @@ -34,12 +34,16 @@ pub enum StatsData { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct StatsGatewayData { + pub gateway_id: String, pub inbox_count: u32, } impl StatsGatewayData { - pub fn new(inbox_count: u32) -> Self { - StatsGatewayData { inbox_count } + pub fn new(gateway_id: String, inbox_count: u32) -> Self { + StatsGatewayData { + gateway_id, + inbox_count, + } } } diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 9317ca8108..e0676f0832 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -50,7 +50,6 @@ name = "bandwidth-claim" version = "1.0.0" dependencies = [ "bandwidth-claim-contract", - "config", "cosmwasm-std", "cosmwasm-storage", "schemars", @@ -221,7 +220,6 @@ version = "0.1.0" dependencies = [ "bandwidth-claim-contract", "coconut-bandwidth-contract-common", - "config", "cosmwasm-std", "cosmwasm-storage", "cw-controllers", @@ -249,7 +247,6 @@ dependencies = [ "bandwidth-claim-contract", "coconut-bandwidth", "coconut-bandwidth-contract-common", - "config", "cosmwasm-std", "cosmwasm-storage", "cw-controllers", @@ -544,7 +541,6 @@ dependencies = [ "cw4", "cw4-group", "multisig-contract-common", - "network-defaults", "schemars", "serde", "thiserror", @@ -616,6 +612,12 @@ dependencies = [ "generic-array 0.14.5", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dyn-clone" version = "1.0.4" @@ -1080,7 +1082,6 @@ dependencies = [ "cosmwasm-std", "fixed", "log", - "network-defaults", "schemars", "serde", "serde_repr", @@ -1106,6 +1107,7 @@ name = "network-defaults" version = "0.1.0" dependencies = [ "cfg-if", + "dotenv", "hex-literal", "once_cell", "serde", @@ -1390,18 +1392,18 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rfc6979" @@ -1852,7 +1854,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "vesting-contract" version = "1.0.1" dependencies = [ - "config", "cosmwasm-std", "cw-storage-plus", "mixnet-contract-common", diff --git a/contracts/bandwidth-claim/Cargo.toml b/contracts/bandwidth-claim/Cargo.toml index 5d530a5452..b6491dda88 100644 --- a/contracts/bandwidth-claim/Cargo.toml +++ b/contracts/bandwidth-claim/Cargo.toml @@ -9,8 +9,6 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dev-dependencies] -config = { path = "../../common/config"} - [dependencies] bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" } diff --git a/contracts/bandwidth-claim/src/lib.rs b/contracts/bandwidth-claim/src/lib.rs index 9dd88197e7..91deceaefa 100644 --- a/contracts/bandwidth-claim/src/lib.rs +++ b/contracts/bandwidth-claim/src/lib.rs @@ -62,10 +62,11 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result Result { let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?; let pool_addr = deps.api.addr_validate(&msg.pool_addr)?; + let mix_denom = msg.mix_denom; ADMIN.set(deps.branch(), Some(multisig_addr.clone()))?; let cfg = Config { multisig_addr, pool_addr, + mix_denom, }; CONFIG.save(deps.storage, &cfg)?; @@ -74,8 +76,8 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result = Item::new("config"); diff --git a/contracts/coconut-bandwidth/src/storage.rs b/contracts/coconut-bandwidth/src/storage.rs index f3515f92e1..40e33b9f8d 100644 --- a/contracts/coconut-bandwidth/src/storage.rs +++ b/contracts/coconut-bandwidth/src/storage.rs @@ -43,8 +43,8 @@ mod tests { use super::super::storage; use crate::storage::SpendCredential; use crate::support::tests::fixtures; + use crate::support::tests::fixtures::TEST_MIX_DENOM; use coconut_bandwidth_contract_common::spend_credential::SpendCredentialStatus; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::Addr; use cosmwasm_std::Coin; @@ -76,7 +76,7 @@ mod tests { #[test] fn mark_as_spent_credential() { let mut mock_storage = MockStorage::new(); - let funds = Coin::new(100, MIX_DENOM.base); + let funds = Coin::new(100, TEST_MIX_DENOM); let blind_serial_number = "blind_serial_number"; let gateway_cosmos_address: Addr = Addr::unchecked("gateway_cosmos_address"); diff --git a/contracts/coconut-bandwidth/src/support/tests/fixtures.rs b/contracts/coconut-bandwidth/src/support/tests/fixtures.rs index e6b0e64249..c1c68a7f0c 100644 --- a/contracts/coconut-bandwidth/src/support/tests/fixtures.rs +++ b/contracts/coconut-bandwidth/src/support/tests/fixtures.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use coconut_bandwidth_contract_common::spend_credential::{SpendCredential, SpendCredentialData}; -use config::defaults::MIX_DENOM; use cosmwasm_std::{Addr, Coin}; +pub const TEST_MIX_DENOM: &str = "unym"; + pub fn spend_credential_fixture(blinded_serial_number: &str) -> SpendCredential { SpendCredential::new( - Coin::new(100, MIX_DENOM.base), + Coin::new(100, TEST_MIX_DENOM), blinded_serial_number.to_string(), Addr::unchecked("gateway_owner_addr"), ) @@ -15,7 +16,7 @@ pub fn spend_credential_fixture(blinded_serial_number: &str) -> SpendCredential pub fn spend_credential_data_fixture(blinded_serial_number: &str) -> SpendCredentialData { SpendCredentialData::new( - Coin::new(100, MIX_DENOM.base), + Coin::new(100, TEST_MIX_DENOM), blinded_serial_number.to_string(), "gateway_owner_addr".to_string(), ) diff --git a/contracts/coconut-bandwidth/src/support/tests/helpers.rs b/contracts/coconut-bandwidth/src/support/tests/helpers.rs index 60eb675a2d..31a4a6f279 100644 --- a/contracts/coconut-bandwidth/src/support/tests/helpers.rs +++ b/contracts/coconut-bandwidth/src/support/tests/helpers.rs @@ -9,11 +9,14 @@ use coconut_bandwidth_contract_common::msg::InstantiateMsg; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps}; +use super::fixtures::TEST_MIX_DENOM; + pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); let msg = InstantiateMsg { multisig_addr: String::from(MULTISIG_CONTRACT), pool_addr: String::from(POOL_CONTRACT), + mix_denom: TEST_MIX_DENOM.to_string(), }; let env = mock_env(); let info = mock_info("creator", &[]); diff --git a/contracts/coconut-bandwidth/src/transactions.rs b/contracts/coconut-bandwidth/src/transactions.rs index 9b5cf8f35a..92d3a28bd1 100644 --- a/contracts/coconut-bandwidth/src/transactions.rs +++ b/contracts/coconut-bandwidth/src/transactions.rs @@ -15,10 +15,9 @@ use coconut_bandwidth_contract_common::events::{ DEPOSITED_FUNDS_EVENT_TYPE, DEPOSIT_ENCRYPTION_KEY, DEPOSIT_IDENTITY_KEY, DEPOSIT_INFO, DEPOSIT_VALUE, }; -use config::defaults::MIX_DENOM; pub(crate) fn deposit_funds( - _deps: DepsMut<'_>, + deps: DepsMut<'_>, _env: Env, info: MessageInfo, data: DepositData, @@ -29,8 +28,9 @@ pub(crate) fn deposit_funds( if info.funds.len() > 1 { return Err(ContractError::MultipleDenoms); } - if info.funds[0].denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom); + let mix_denom = CONFIG.load(deps.storage)?.mix_denom; + if info.funds[0].denom != mix_denom { + return Err(ContractError::WrongDenom { mix_denom }); } let voucher_value = info.funds.last().unwrap(); @@ -49,8 +49,9 @@ pub(crate) fn spend_credential( _info: MessageInfo, data: SpendCredentialData, ) -> Result { - if data.funds().denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom); + let mix_denom = CONFIG.load(deps.storage)?.mix_denom; + if data.funds().denom != mix_denom { + return Err(ContractError::WrongDenom { mix_denom }); } if storage::spent_credentials().has(deps.storage, data.blinded_serial_number()) { return Err(ContractError::DuplicateBlindedSerialNumber); @@ -84,12 +85,13 @@ pub(crate) fn release_funds( info: MessageInfo, funds: Coin, ) -> Result { - if funds.denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom); + let mix_denom = CONFIG.load(deps.storage)?.mix_denom; + if funds.denom != mix_denom { + return Err(ContractError::WrongDenom { mix_denom }); } let current_balance = deps .querier - .query_balance(env.contract.address, MIX_DENOM.base)?; + .query_balance(env.contract.address, mix_denom)?; if funds.amount > current_balance.amount { return Err(ContractError::NotEnoughFunds); } @@ -133,7 +135,7 @@ mod tests { Err(ContractError::NoCoin) ); - let coin = Coin::new(1000000, MIX_DENOM.base); + let coin = Coin::new(1000000, crate::support::tests::fixtures::TEST_MIX_DENOM); let second_coin = Coin::new(1000000, "some_denom"); let info = mock_info("requester", &[coin, second_coin.clone()]); @@ -145,7 +147,9 @@ mod tests { let info = mock_info("requester", &[second_coin]); assert_eq!( deposit_funds(deps.as_mut(), env, info, data), - Err(ContractError::WrongDenom) + Err(ContractError::WrongDenom { + mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string() + }) ); } @@ -163,7 +167,10 @@ mod tests { verification_key.clone(), encryption_key.clone(), ); - let coin = Coin::new(deposit_value, MIX_DENOM.base); + let coin = Coin::new( + deposit_value, + crate::support::tests::fixtures::TEST_MIX_DENOM, + ); let info = mock_info("requester", &[coin]); let tx = deposit_funds(deps.as_mut(), env.clone(), info, data).unwrap(); @@ -212,7 +219,7 @@ mod tests { let mut deps = helpers::init_contract(); let env = mock_env(); let invalid_admin = "invalid admin"; - let funds = Coin::new(1, MIX_DENOM.base); + let funds = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM); let err = release_funds( deps.as_mut(), @@ -221,7 +228,12 @@ mod tests { Coin::new(1, "invalid denom"), ) .unwrap_err(); - assert_eq!(err, ContractError::WrongDenom); + assert_eq!( + err, + ContractError::WrongDenom { + mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string() + } + ); let err = release_funds( deps.as_mut(), @@ -248,7 +260,7 @@ mod tests { fn valid_release() { let mut deps = helpers::init_contract(); let env = mock_env(); - let coin = Coin::new(1, MIX_DENOM.base); + let coin = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM); deps.querier .update_balance(env.contract.address.clone(), vec![coin.clone()]); @@ -327,10 +339,15 @@ mod tests { String::new(), ); let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data); - assert_eq!(ret.unwrap_err(), ContractError::WrongDenom); + assert_eq!( + ret.unwrap_err(), + ContractError::WrongDenom { + mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string() + } + ); let invalid_data = SpendCredentialData::new( - Coin::new(1, MIX_DENOM.base), + Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM), String::new(), "Blinded Serial Number".to_string(), ); diff --git a/contracts/coconut-test/Cargo.toml b/contracts/coconut-test/Cargo.toml index ec13753706..5955391250 100644 --- a/contracts/coconut-test/Cargo.toml +++ b/contracts/coconut-test/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" } coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" } -config = { path = "../../common/config"} cosmwasm-std = "1.0.0" cosmwasm-storage = "1.0.0" diff --git a/contracts/coconut-test/src/deposit_and_release.rs b/contracts/coconut-test/src/deposit_and_release.rs index a0ff781459..0212741261 100644 --- a/contracts/coconut-test/src/deposit_and_release.rs +++ b/contracts/coconut-test/src/deposit_and_release.rs @@ -7,16 +7,17 @@ use coconut_bandwidth_contract_common::{ deposit::DepositData, msg::{ExecuteMsg, InstantiateMsg}, }; -use config::defaults::MIX_DENOM; use cosmwasm_std::{coins, Addr}; use cw_controllers::AdminError; use cw_multi_test::Executor; +const TEST_MIX_DENOM: &str = "unym"; + #[test] fn deposit_and_release() { - let init_funds = coins(10, MIX_DENOM.base); - let deposit_funds = coins(1, MIX_DENOM.base); - let release_funds = coins(2, MIX_DENOM.base); + let init_funds = coins(10, TEST_MIX_DENOM); + let deposit_funds = coins(1, TEST_MIX_DENOM); + let release_funds = coins(2, TEST_MIX_DENOM); let mut app = mock_app(&init_funds); let multisig_addr = String::from(MULTISIG_CONTRACT); let pool_addr = String::from(POOL_CONTRACT); @@ -26,6 +27,7 @@ fn deposit_and_release() { let msg = InstantiateMsg { multisig_addr: multisig_addr.clone(), pool_addr: pool_addr.clone(), + mix_denom: TEST_MIX_DENOM.to_string(), }; let contract_addr = app .instantiate_contract( @@ -94,6 +96,6 @@ fn deposit_and_release() { &[], ) .unwrap(); - let pool_bal = app.wrap().query_balance(pool_addr, MIX_DENOM.base).unwrap(); + let pool_bal = app.wrap().query_balance(pool_addr, TEST_MIX_DENOM).unwrap(); assert_eq!(pool_bal, deposit_funds[0]); } diff --git a/contracts/coconut-test/src/spend_credential_creates_proposal.rs b/contracts/coconut-test/src/spend_credential_creates_proposal.rs index c47a8f0868..7d07905b97 100644 --- a/contracts/coconut-test/src/spend_credential_creates_proposal.rs +++ b/contracts/coconut-test/src/spend_credential_creates_proposal.rs @@ -6,16 +6,19 @@ use coconut_bandwidth_contract_common::{ }, spend_credential::SpendCredentialData, }; -use config::defaults::MIX_DENOM; use cosmwasm_std::{coins, Addr, Coin, Decimal}; use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg; use cw_multi_test::Executor; use cw_utils::{Duration, Threshold}; use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg; +pub const TEST_COIN_DENOM: &str = "unym"; +pub const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = + "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; + #[test] fn spend_credential_creates_proposal() { - let init_funds = coins(10, MIX_DENOM.base); + let init_funds = coins(10, TEST_COIN_DENOM); let mut app = mock_app(&init_funds); let pool_addr = String::from(POOL_CONTRACT); @@ -42,6 +45,7 @@ fn spend_credential_creates_proposal() { percentage: Decimal::from_ratio(2u128, 3u128), }, max_voting_period: Duration::Height(1000), + coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), }; let multisig_contract_addr = app .instantiate_contract( @@ -58,6 +62,7 @@ fn spend_credential_creates_proposal() { let msg = CoconutBandwidthInstantiateMsg { multisig_addr: multisig_contract_addr.to_string(), pool_addr, + mix_denom: TEST_COIN_DENOM.to_string(), }; let coconut_bandwidth_contract_addr = app .instantiate_contract( @@ -83,7 +88,7 @@ fn spend_credential_creates_proposal() { let msg = CoconutBandwidthExecuteMsg::SpendCredential { data: SpendCredentialData::new( - Coin::new(1, MIX_DENOM.base), + Coin::new(1, TEST_COIN_DENOM), String::from("blinded_serial_number"), String::from("gateway_cosmos_address"), ), @@ -126,7 +131,7 @@ fn spend_credential_creates_proposal() { let msg = CoconutBandwidthExecuteMsg::SpendCredential { data: SpendCredentialData::new( - Coin::new(1, MIX_DENOM.base), + Coin::new(1, TEST_COIN_DENOM), String::from("blinded_serial_number2"), String::from("gateway_cosmos_address"), ), diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index d95f364e50..3eaa1f6035 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -27,16 +27,18 @@ use crate::mixnodes::bonding_queries::{ query_checkpoints_for_mixnode, query_mixnode_at_height, query_mixnodes_paged, }; use crate::mixnodes::layer_queries::query_layer_distribution; +use crate::mixnodes::transactions::_try_remove_mixnode; +use crate::queued_migrations::migrate_config_from_env; use crate::rewards::queries::{ query_circulating_supply, query_reward_pool, query_rewarding_status, query_staking_supply, }; use crate::rewards::storage as rewards_storage; use cosmwasm_std::{ entry_point, to_binary, Addr, Api, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, - Uint128, + Storage, Uint128, }; use mixnet_contract_common::{ - ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, + ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, NodeToRemove, QueryMsg, }; use time::OffsetDateTime; @@ -61,9 +63,14 @@ pub fn debug_with_visibility>(api: &dyn Api, msg: S) { api.debug(&*format!("\n\n\n=========================================\n{}\n=========================================\n\n\n", msg.into())); } -fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState { +fn default_initial_state( + owner: Addr, + mix_denom: String, + rewarding_validator_address: Addr, +) -> ContractState { ContractState { owner, + mix_denom, rewarding_validator_address, params: ContractStateParams { minimum_mixnode_pledge: INITIAL_MIXNODE_PLEDGE, @@ -88,7 +95,7 @@ pub fn instantiate( msg: InstantiateMsg, ) -> Result { let rewarding_validator_address = deps.api.addr_validate(&msg.rewarding_validator_address)?; - let state = default_initial_state(info.sender, rewarding_validator_address); + let state = default_initial_state(info.sender, msg.mixnet_denom, rewarding_validator_address); init_epoch(deps.storage, env)?; mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?; @@ -107,6 +114,19 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { + ExecuteMsg::CompoundReward { + operator, + delegator, + mix_identity, + proxy, + } => crate::rewards::transactions::try_compound_reward( + deps, + env, + operator, + delegator, + mix_identity, + proxy, + ), ExecuteMsg::UpdateRewardingValidatorAddress { address } => { try_update_rewarding_validator_address(deps, info, address) } @@ -122,7 +142,7 @@ pub fn execute( owner_signature, ), ExecuteMsg::UnbondMixnode {} => { - crate::mixnodes::transactions::try_remove_mixnode(env, deps, info) + crate::mixnodes::transactions::try_remove_mixnode(&env, deps.storage, deps.api, info) } ExecuteMsg::UpdateMixnodeConfig { profit_margin_percent, @@ -216,7 +236,13 @@ pub fn execute( owner_signature, ), ExecuteMsg::UnbondMixnodeOnBehalf { owner } => { - crate::mixnodes::transactions::try_remove_mixnode_on_behalf(env, deps, info, owner) + crate::mixnodes::transactions::try_remove_mixnode_on_behalf( + &env, + deps.storage, + deps.api, + info, + owner, + ) } ExecuteMsg::BondGatewayOnBehalf { gateway, @@ -275,7 +301,7 @@ pub fn execute( ) } ExecuteMsg::ReconcileDelegations {} => { - crate::delegations::transactions::try_reconcile_all_delegation_events(deps, info) + crate::delegations::transactions::try_reconcile_all_delegation_events(deps) } ExecuteMsg::CheckpointMixnodes {} => { crate::mixnodes::transactions::try_checkpoint_mixnodes( @@ -316,6 +342,9 @@ pub fn execute( #[entry_point] pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { let query_res = match msg { + QueryMsg::GetBlacklistedNodes {} => to_binary( + &crate::mixnodes::bonding_queries::get_blacklisted_nodes(deps), + ), QueryMsg::GetRewardingValidatorAddress {} => { to_binary(&query_rewarding_validator_address(deps)?) } @@ -443,16 +472,61 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result Result<(), ContractError> { + let mixnode_bond = match crate::mixnodes::storage::mixnodes() + .idx + .owner + .item(storage, owner.clone())? + { + Some(record) => record.1, + None => { + return Err(ContractError::NoAssociatedMixNodeBond { + owner: owner.to_owned(), + }) + } + }; + + crate::mixnodes::storage::MIXNODES_BOND_BLACKLIST.save(storage, mixnode_bond.identity(), &0)?; + + Ok(()) +} + +// Removes nodes we've deemed malicious, returns the pledge to the owners, but does not send any rewards +fn remove_malicious_node( + storage: &mut dyn Storage, + api: &dyn Api, + env: &Env, + node: &NodeToRemove, +) -> Result { + let proxy = node.proxy().map(|p| { + api.addr_validate(p) + .unwrap_or_else(|_| panic!("Invalid address: {}", p)) + }); + let owner_addr = api.addr_validate(node.owner())?; + blacklist_malicious_node(storage, &owner_addr)?; + _try_remove_mixnode(env, storage, api, node.owner(), proxy, false) +} + #[entry_point] -pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { - Ok(Default::default()) +pub fn migrate(deps: DepsMut<'_>, env: Env, msg: MigrateMsg) -> Result { + migrate_config_from_env(deps.storage, &msg)?; + let mut response = Response::new(); + for node in msg.nodes_to_remove().iter() { + let mut sub_response = remove_malicious_node(deps.storage, deps.api, &env, node) + .unwrap_or_else(|_| panic!("Could not remove node: {:?}", node)); + response.messages.append(&mut sub_response.messages); + response.attributes.append(&mut sub_response.attributes); + response.events.append(&mut sub_response.events); + } + + Ok(response) } #[cfg(test)] pub mod tests { use super::*; use crate::support::tests; - use config::defaults::{DEFAULT_NETWORK, MIX_DENOM}; + use crate::support::tests::fixtures::{TEST_COIN_DENOM, TEST_REWARDING_VALIDATOR_ADDRESS}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coins, from_binary}; use mixnet_contract_common::PagedMixnodeResponse; @@ -462,7 +536,8 @@ pub mod tests { let mut deps = mock_dependencies(); let env = mock_env(); let msg = InstantiateMsg { - rewarding_validator_address: DEFAULT_NETWORK.rewarding_validator_address().to_string(), + rewarding_validator_address: TEST_REWARDING_VALIDATOR_ADDRESS.to_string(), + mixnet_denom: TEST_COIN_DENOM.to_string(), }; let info = mock_info("creator", &[]); @@ -484,7 +559,7 @@ pub mod tests { // Contract balance should match what we initialized it as assert_eq!( - coins(0, MIX_DENOM.base), + coins(0, TEST_COIN_DENOM), tests::queries::query_contract_balance(env.contract.address, deps) ); } diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index 654e7fd85f..8b60091e4f 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -199,8 +199,7 @@ pub(crate) fn query_mixnode_delegations_paged( #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::support::tests::test_helpers; - use config::defaults::MIX_DENOM; + use crate::support::tests::{fixtures::TEST_COIN_DENOM, test_helpers}; use cosmwasm_std::{coin, Addr, Storage}; use rand::Rng; @@ -385,7 +384,7 @@ pub(crate) mod tests { let delegation = Delegation::new( delegation_owner.clone(), node_identity.clone(), - coin(1234, MIX_DENOM.base), + coin(1234, TEST_COIN_DENOM), 1234, None, ); @@ -433,7 +432,7 @@ pub(crate) mod tests { let delegation = Delegation::new( delegation_owner2, node_identity1.clone(), - coin(1234, MIX_DENOM.base), + coin(1234, TEST_COIN_DENOM), 1234, None, ); @@ -460,7 +459,7 @@ pub(crate) mod tests { let delegation = Delegation::new( delegation_owner1.clone(), node_identity2, - coin(1234, MIX_DENOM.base), + coin(1234, TEST_COIN_DENOM), 1234, None, ); diff --git a/contracts/mixnet/src/delegations/storage.rs b/contracts/mixnet/src/delegations/storage.rs index 52dd95d834..5715bc2c27 100644 --- a/contracts/mixnet/src/delegations/storage.rs +++ b/contracts/mixnet/src/delegations/storage.rs @@ -76,8 +76,8 @@ mod tests { #[cfg(test)] mod reverse_mix_delegations { use super::*; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::mock_env; use cosmwasm_std::{coin, Order}; use mixnet_contract_common::Delegation; @@ -87,7 +87,7 @@ mod tests { let mut deps = test_helpers::init_contract(); let node_identity: IdentityKey = "foo".into(); let delegation_owner = Addr::unchecked("bar"); - let delegation = coin(12345, MIX_DENOM.base); + let delegation = coin(12345, TEST_COIN_DENOM); let dummy_data = Delegation::new( delegation_owner.clone(), @@ -125,7 +125,7 @@ mod tests { let node_identity2: IdentityKey = "foo2".into(); let delegation_owner1 = Addr::unchecked("bar"); let delegation_owner2 = Addr::unchecked("bar2"); - let delegation = coin(12345, MIX_DENOM.base); + let delegation = coin(12345, TEST_COIN_DENOM); assert!(test_helpers::read_delegation( deps.as_ref().storage, diff --git a/contracts/mixnet/src/delegations/transactions.rs b/contracts/mixnet/src/delegations/transactions.rs index ddf5ba146e..c5e462929e 100644 --- a/contracts/mixnet/src/delegations/transactions.rs +++ b/contracts/mixnet/src/delegations/transactions.rs @@ -4,9 +4,8 @@ use super::storage::{self, PENDING_DELEGATION_EVENTS}; // use crate::contract::debug_with_visibility; // use crate::contract::debug_with_visibility; use crate::error::ContractError; -use crate::mixnet_contract_settings::storage as mixnet_params_storage; +use crate::mixnet_contract_settings::storage::{self as mixnet_params_storage}; use crate::mixnodes::storage as mixnodes_storage; -use config::defaults::MIX_DENOM; use cosmwasm_std::{ coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Order, Response, Storage, Uint128, WasmMsg, @@ -20,16 +19,7 @@ use mixnet_contract_common::{Delegation, IdentityKey}; use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; use vesting_contract_common::one_ucoin; -pub fn try_reconcile_all_delegation_events( - deps: DepsMut<'_>, - info: MessageInfo, -) -> Result { - let state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?; - // check if this is executed by the permitted validator, if not reject the transaction - if info.sender != state.rewarding_validator_address { - return Err(ContractError::Unauthorized); - } - +pub fn try_reconcile_all_delegation_events(deps: DepsMut<'_>) -> Result { _try_reconcile_all_delegation_events(deps.storage, deps.api) } @@ -74,7 +64,10 @@ pub(crate) fn _try_reconcile_all_delegation_events( Ok(response) } -fn validate_delegation_stake(mut delegation: Vec) -> Result { +fn validate_delegation_stake( + mut delegation: Vec, + mix_denom: String, +) -> Result { // check if anything was put as delegation if delegation.is_empty() { return Err(ContractError::EmptyDelegation); @@ -85,8 +78,8 @@ fn validate_delegation_stake(mut delegation: Vec) -> Result Result { // check if the delegation contains any funds of the appropriate denomination - let amount = validate_delegation_stake(info.funds)?; + let amount = + validate_delegation_stake(info.funds, mixnet_params_storage::mix_denom(deps.storage)?)?; _try_delegate_to_mixnode( deps, @@ -124,7 +118,8 @@ pub(crate) fn try_delegate_to_mixnode_on_behalf( delegate: String, ) -> Result { // check if the delegation contains any funds of the appropriate denomination - let amount = validate_delegation_stake(info.funds)?; + let amount = + validate_delegation_stake(info.funds, mixnet_params_storage::mix_denom(deps.storage)?)?; _try_delegate_to_mixnode( deps, @@ -260,6 +255,7 @@ pub(crate) fn try_reconcile_undelegation( pending_undelegate: &PendingUndelegate, ) -> Result { let delegation_map = storage::delegations(); + let mix_denom = mixnet_params_storage::mix_denom(storage)?; // debug_with_visibility(api, "Reconciling undelegations"); @@ -389,7 +385,7 @@ pub(crate) fn try_reconcile_undelegation( .as_ref() .unwrap_or(&pending_undelegate.delegate()) .to_string(), - amount: coins(total_funds.u128(), MIX_DENOM.base), + amount: coins(total_funds.u128(), mix_denom.clone()), }) } else { None @@ -401,10 +397,10 @@ pub(crate) fn try_reconcile_undelegation( let msg = Some(VestingContractExecuteMsg::TrackUndelegation { owner: pending_undelegate.delegate().as_str().to_string(), mix_identity: pending_undelegate.mix_identity(), - amount: Coin::new(total_funds.u128(), MIX_DENOM.base), + amount: Coin::new(total_funds.u128(), mix_denom.clone()), }); - wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin()])?); + wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?); } let event = new_undelegation_event( @@ -468,6 +464,7 @@ mod tests { use cosmwasm_std::coins; use crate::support::tests; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers; use super::*; @@ -482,7 +479,7 @@ mod tests { fn stake_cant_be_empty() { assert_eq!( Err(ContractError::EmptyDelegation), - validate_delegation_stake(vec![]) + validate_delegation_stake(vec![], TEST_COIN_DENOM.to_string()) ) } @@ -490,19 +487,24 @@ mod tests { fn stake_must_have_single_coin_type() { assert_eq!( Err(ContractError::MultipleDenoms), - validate_delegation_stake(vec![ - coin(123, MIX_DENOM.base), - coin(123, "BTC"), - coin(123, "DOGE") - ]) + validate_delegation_stake( + vec![ + coin(123, TEST_COIN_DENOM), + coin(123, "BTC"), + coin(123, "DOGE") + ], + TEST_COIN_DENOM.to_string() + ) ) } #[test] fn stake_coin_must_be_of_correct_type() { assert_eq!( - Err(ContractError::WrongDenom {}), - validate_delegation_stake(coins(123, "DOGE")) + Err(ContractError::WrongDenom { + mix_denom: TEST_COIN_DENOM.to_string() + }), + validate_delegation_stake(coins(123, "DOGE"), TEST_COIN_DENOM.to_string()) ) } @@ -510,16 +512,28 @@ mod tests { fn stake_coin_must_have_value_greater_than_zero() { assert_eq!( Err(ContractError::EmptyDelegation), - validate_delegation_stake(coins(0, MIX_DENOM.base)) + validate_delegation_stake(coins(0, TEST_COIN_DENOM), TEST_COIN_DENOM.to_string()) ) } #[test] fn stake_can_have_any_positive_value() { // this might change in the future, but right now an arbitrary (positive) value can be delegated - assert!(validate_delegation_stake(coins(1, MIX_DENOM.base)).is_ok()); - assert!(validate_delegation_stake(coins(123, MIX_DENOM.base)).is_ok()); - assert!(validate_delegation_stake(coins(10000000000, MIX_DENOM.base)).is_ok()); + assert!(validate_delegation_stake( + coins(1, TEST_COIN_DENOM), + TEST_COIN_DENOM.to_string() + ) + .is_ok()); + assert!(validate_delegation_stake( + coins(123, TEST_COIN_DENOM), + TEST_COIN_DENOM.to_string() + ) + .is_ok()); + assert!(validate_delegation_stake( + coins(10000000000, TEST_COIN_DENOM), + TEST_COIN_DENOM.to_string() + ) + .is_ok()); } } @@ -542,7 +556,7 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("sender", &coins(123, MIX_DENOM.base)), + mock_info("sender", &coins(123, TEST_COIN_DENOM)), "non-existent-mix-identity".into(), ) ); @@ -558,7 +572,7 @@ mod tests { deps.as_mut(), ); let delegation_owner = Addr::unchecked("sender"); - let delegation = coin(123, MIX_DENOM.base); + let delegation = coin(123, TEST_COIN_DENOM); assert!(try_delegate_to_mixnode( deps.as_mut(), mock_env(), @@ -607,7 +621,14 @@ mod tests { deps.as_mut(), ); let delegation_owner = Addr::unchecked("sender"); - try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); + let api = deps.api.clone(); + try_remove_mixnode( + &mock_env(), + deps.as_mut().storage, + &api, + mock_info(mixnode_owner, &[]), + ) + .unwrap(); assert_eq!( Err(ContractError::MixNodeBondNotFound { identity: identity.clone() @@ -615,7 +636,7 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner.as_str(), &coins(123, MIX_DENOM.base)), + mock_info(delegation_owner.as_str(), &coins(123, TEST_COIN_DENOM)), identity, ) ); @@ -630,13 +651,20 @@ mod tests { tests::fixtures::good_mixnode_pledge(), deps.as_mut(), ); - try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); + let api = deps.api.clone(); + try_remove_mixnode( + &mock_env(), + deps.as_mut().storage, + &api, + mock_info(mixnode_owner, &[]), + ) + .unwrap(); let identity = test_helpers::add_mixnode( mixnode_owner, tests::fixtures::good_mixnode_pledge(), deps.as_mut(), ); - let delegation = coin(123, MIX_DENOM.base); + let delegation = coin(123, TEST_COIN_DENOM); let delegation_owner = Addr::unchecked("sender"); assert!(try_delegate_to_mixnode( deps.as_mut(), @@ -686,8 +714,8 @@ mod tests { deps.as_mut(), ); let delegation_owner = Addr::unchecked("sender"); - let delegation1 = coin(100, MIX_DENOM.base); - let delegation2 = coin(50, MIX_DENOM.base); + let delegation1 = coin(100, TEST_COIN_DENOM); + let delegation2 = coin(50, TEST_COIN_DENOM); let mut env = mock_env(); @@ -714,7 +742,7 @@ mod tests { // let expected = Delegation::new( // delegation_owner.clone(), // identity.clone(), - // coin(delegation1.amount.u128() + delegation2.amount.u128(), MIX_DENOM.base), + // coin(delegation1.amount.u128() + delegation2.amount.u128(), TEST_COIN_DENOM), // mock_env().block.height, // None, // ); @@ -749,7 +777,7 @@ mod tests { deps.as_mut(), ); let delegation_owner = Addr::unchecked("sender"); - let delegation = coin(100, MIX_DENOM.base); + let delegation = coin(100, TEST_COIN_DENOM); let env1 = mock_env(); let mut env2 = mock_env(); let initial_height = env1.block.height; @@ -814,8 +842,8 @@ mod tests { ); let delegation_owner1 = Addr::unchecked("sender1"); let delegation_owner2 = Addr::unchecked("sender2"); - let delegation1 = coin(100, MIX_DENOM.base); - let delegation2 = coin(120, MIX_DENOM.base); + let delegation1 = coin(100, TEST_COIN_DENOM); + let delegation2 = coin(120, TEST_COIN_DENOM); let env1 = mock_env(); let mut env2 = mock_env(); let initial_height = env1.block.height; @@ -890,11 +918,18 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)), + mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)), identity.clone(), ) .unwrap(); - try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); + let api = deps.api.clone(); + try_remove_mixnode( + &mock_env(), + deps.as_mut().storage, + &api, + mock_info(mixnode_owner, &[]), + ) + .unwrap(); assert_eq!( Err(ContractError::MixNodeBondNotFound { identity: identity.clone() @@ -902,7 +937,7 @@ mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner.as_str(), &coins(50, MIX_DENOM.base)), + mock_info(delegation_owner.as_str(), &coins(50, TEST_COIN_DENOM)), identity, ) ); @@ -927,14 +962,14 @@ mod tests { assert!(try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner.as_str(), &coins(123, MIX_DENOM.base)), + mock_info(delegation_owner.as_str(), &coins(123, TEST_COIN_DENOM)), identity1.clone(), ) .is_ok()); assert!(try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info(delegation_owner.as_str(), &coins(42, MIX_DENOM.base)), + mock_info(delegation_owner.as_str(), &coins(42, TEST_COIN_DENOM)), identity2.clone(), ) .is_ok()); @@ -944,7 +979,7 @@ mod tests { let expected1 = Delegation::new( delegation_owner.clone(), identity1.clone(), - coin(123, MIX_DENOM.base), + coin(123, TEST_COIN_DENOM), mock_env().block.height, None, ); @@ -952,7 +987,7 @@ mod tests { let expected2 = Delegation::new( delegation_owner.clone(), identity2.clone(), - coin(42, MIX_DENOM.base), + coin(42, TEST_COIN_DENOM), mock_env().block.height, None, ); @@ -988,8 +1023,8 @@ mod tests { tests::fixtures::good_mixnode_pledge(), deps.as_mut(), ); - let delegation1 = coin(123, MIX_DENOM.base); - let delegation2 = coin(234, MIX_DENOM.base); + let delegation1 = coin(123, TEST_COIN_DENOM); + let delegation2 = coin(234, TEST_COIN_DENOM); assert!(try_delegate_to_mixnode( deps.as_mut(), mock_env(), @@ -1025,7 +1060,7 @@ mod tests { deps.as_mut(), ); let delegation_owner = Addr::unchecked("sender"); - let delegation_amount = coin(100, MIX_DENOM.base); + let delegation_amount = coin(100, TEST_COIN_DENOM); try_delegate_to_mixnode( deps.as_mut(), mock_env(), @@ -1035,8 +1070,14 @@ mod tests { .unwrap(); _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); - - try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); + let api = deps.api.clone(); + try_remove_mixnode( + &mock_env(), + deps.as_mut().storage, + &api, + mock_info(mixnode_owner, &[]), + ) + .unwrap(); let expected = Delegation::new( delegation_owner.clone(), @@ -1061,179 +1102,13 @@ mod tests { #[cfg(test)] mod removing_mix_stake_delegation { + use super::*; + use crate::support::tests; use cosmwasm_std::coin; use cosmwasm_std::testing::mock_env; use cosmwasm_std::testing::mock_info; use cosmwasm_std::Addr; - use crate::support::tests; - - use super::*; - - // TODO: Probably delete due to reconciliation logic - //#[ignore] - //#[test] - //fn fails_if_delegation_never_existed() { - // let mut deps = test_helpers::init_contract(); - // let env = mock_env(); - // let mixnode_owner = "bob"; - // let identity = test_helpers::add_mixnode( - // mixnode_owner, - // tests::fixtures::good_mixnode_pledge(), - // deps.as_mut(), - // ); - // let delegation_owner = Addr::unchecked("sender"); - // assert_eq!( - // Err(ContractError::NoMixnodeDelegationFound { - // identity: identity.clone(), - // address: delegation_owner.to_string(), - // }), - // try_remove_delegation_from_mixnode( - // deps.as_mut(), - // env, - // mock_info(delegation_owner.as_str(), &[]), - // identity, - // ) - // ); - //} - - // TODO: Update to work with reconciliation - //#[ignore] - //#[test] - //fn succeeds_if_delegation_existed() { - // let mut deps = test_helpers::init_contract(); - // let mixnode_owner = "bob"; - // let env = mock_env(); - // let identity = test_helpers::add_mixnode( - // mixnode_owner, - // tests::fixtures::good_mixnode_pledge(), - // deps.as_mut(), - // ); - // let delegation_owner = Addr::unchecked("sender"); - // try_delegate_to_mixnode( - // deps.as_mut(), - // mock_env(), - // mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)), - // identity.clone(), - // ) - // .unwrap(); - - // _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); - - // let _delegation = query_mixnode_delegation( - // &deps.storage, - // &deps.api, - // identity.clone(), - // delegation_owner.clone().into_string(), - // None, - // ) - // .unwrap(); - - // let expected_response = Response::new() - // .add_message(BankMsg::Send { - // to_address: delegation_owner.clone().into(), - // amount: coins(100, MIX_DENOM.base), - // }) - // .add_event(new_undelegation_event( - // &delegation_owner, - // &None, - // &identity, - // Uint128::new(100), - // )); - - // assert_eq!( - // Ok(expected_response), - // try_remove_delegation_from_mixnode( - // deps.as_mut(), - // env, - // mock_info(delegation_owner.as_str(), &[]), - // identity.clone(), - // ) - // ); - // assert!(storage::delegations() - // .may_load( - // &deps.storage, - // (identity.clone(), delegation_owner.as_bytes().to_vec(), 0), - // ) - // .unwrap() - // .is_none()); - - // // and total delegation is cleared - // assert_eq!( - // Uint128::zero(), - // mixnodes_storage::TOTAL_DELEGATION - // .load(&deps.storage, &identity) - // .unwrap() - // ) - //} - - // TODO: Update to work with reconciliation - //#[ignore] - //#[test] - //fn succeeds_if_delegation_existed_even_if_node_unbonded() { - // let mut deps = test_helpers::init_contract(); - // let mixnode_owner = "bob"; - // let env = mock_env(); - // let identity = test_helpers::add_mixnode( - // mixnode_owner, - // tests::fixtures::good_mixnode_pledge(), - // deps.as_mut(), - // ); - // let delegation_owner = Addr::unchecked("sender"); - // try_delegate_to_mixnode( - // deps.as_mut(), - // mock_env(), - // mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)), - // identity.clone(), - // ) - // .unwrap(); - - // _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); - - // let _delegation = query_mixnode_delegation( - // &deps.storage, - // &deps.api, - // identity.clone(), - // delegation_owner.clone().into_string(), - // None, - // ) - // .unwrap(); - - // let expected_response = Response::new() - // .add_message(BankMsg::Send { - // to_address: delegation_owner.clone().into(), - // amount: coins(100, MIX_DENOM.base), - // }) - // .add_event(new_undelegation_event( - // &delegation_owner, - // &None, - // &identity, - // Uint128::new(100), - // )); - - // try_remove_mixnode(mock_env(), deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap(); - - // assert_eq!( - // Ok(expected_response), - // try_remove_delegation_from_mixnode( - // deps.as_mut(), - // env, - // mock_info(delegation_owner.as_str(), &[]), - // identity.clone(), - // ) - // ); - - // _try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap(); - - // assert!(test_helpers::read_delegation( - // &deps.storage, - // identity, - // delegation_owner.as_bytes(), - // mock_env().block.height - // ) - // .is_none()); - //} - #[test] fn total_delegation_is_preserved_if_only_some_undelegate() { let mut deps = test_helpers::init_contract(); @@ -1246,8 +1121,8 @@ mod tests { ); let delegation_owner1 = Addr::unchecked("sender1"); let delegation_owner2 = Addr::unchecked("sender2"); - let delegation1 = coin(123, MIX_DENOM.base); - let delegation2 = coin(234, MIX_DENOM.base); + let delegation1 = coin(123, TEST_COIN_DENOM); + let delegation2 = coin(234, TEST_COIN_DENOM); assert!(try_delegate_to_mixnode( deps.as_mut(), mock_env(), diff --git a/contracts/mixnet/src/error.rs b/contracts/mixnet/src/error.rs index c4d4c85fda..e1d3a22335 100644 --- a/contracts/mixnet/src/error.rs +++ b/contracts/mixnet/src/error.rs @@ -1,7 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use config::defaults::MIX_DENOM; use cosmwasm_std::{Addr, StdError}; use mixnet_contract_common::{error::MixnetContractError, IdentityKey}; use thiserror::Error; @@ -33,14 +32,14 @@ pub enum ContractError { #[error("MIXNET ({}): Unauthorized", line!())] Unauthorized, - #[error("MIXNET ({}): Wrong coin denomination, you must send {}", line!(), MIX_DENOM.base)] - WrongDenom, + #[error("MIXNET ({}): Wrong coin denomination, you must send {mix_denom}", line!())] + WrongDenom { mix_denom: String }, #[error("MIXNET ({}): Received multiple coin types during staking", line!())] MultipleDenoms, - #[error("MIXNET ({}): No coin was sent for the bonding, you must send {}", line!(), MIX_DENOM.base)] - NoBondFound, + #[error("MIXNET ({}): No coin was sent for the bonding, you must send {mix_denom}", line!())] + NoBondFound { mix_denom: String }, #[error("MIXNET ({}): Provided active set size is bigger than the rewarded set", line!())] InvalidActiveSetSize, @@ -179,4 +178,11 @@ pub enum ContractError { last_update_time: u64, current_block_time: u64, }, + #[error("`mix_identity` is required when `delegator` is set")] + MissingMixIdentity, + #[error("Compounding has been disabled temporarily")] + CompoundDisabled, + + #[error("Mixnode {identity} has been blacklisted on the network")] + MixnodeBlacklisted { identity: String }, } diff --git a/contracts/mixnet/src/gateways/storage.rs b/contracts/mixnet/src/gateways/storage.rs index 6b79f5ed4e..89786a3019 100644 --- a/contracts/mixnet/src/gateways/storage.rs +++ b/contracts/mixnet/src/gateways/storage.rs @@ -36,7 +36,7 @@ pub(crate) fn gateways<'a>() -> IndexedMap<'a, IdentityKeyRef<'a>, GatewayBond, mod tests { use super::super::storage; use crate::support::tests; - use config::defaults::MIX_DENOM; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::StdResult; use cosmwasm_std::Storage; @@ -84,7 +84,7 @@ mod tests { let pledge_amount = 1000; let gateway_bond = GatewayBond { - pledge_amount: coin(pledge_amount, MIX_DENOM.base), + pledge_amount: coin(pledge_amount, TEST_COIN_DENOM), owner: node_owner, block_height: 12_345, gateway: Gateway { diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index 76689422bd..6315a2b2e2 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -5,7 +5,6 @@ use super::storage; use crate::error::ContractError; use crate::mixnet_contract_settings::storage as mixnet_params_storage; use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature}; -use config::defaults::MIX_DENOM; use cosmwasm_std::{ wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128, }; @@ -26,7 +25,8 @@ pub fn try_add_gateway( .load(deps.storage)? .params .minimum_mixnode_pledge; - let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?; + let mix_denom = mixnet_params_storage::mix_denom(deps.storage)?; + let pledge = validate_gateway_pledge(info.funds, minimum_pledge, mix_denom)?; _try_add_gateway( deps, @@ -52,7 +52,8 @@ pub fn try_add_gateway_on_behalf( .load(deps.storage)? .params .minimum_mixnode_pledge; - let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?; + let mix_denom = mixnet_params_storage::mix_denom(deps.storage)?; + let pledge = validate_gateway_pledge(info.funds, minimum_pledge, mix_denom)?; let proxy = info.sender; _try_add_gateway( @@ -138,6 +139,7 @@ pub(crate) fn _try_remove_gateway( proxy: Option, ) -> Result { let owner = deps.api.addr_validate(owner)?; + let mix_denom = mixnet_params_storage::mix_denom(deps.storage)?; // try to find the node of the sender let gateway_bond = match storage::gateways() .idx @@ -177,7 +179,7 @@ pub(crate) fn _try_remove_gateway( amount: gateway_bond.pledge_amount(), }; - let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?; + let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?; response = response.add_message(track_unbond_message); } @@ -192,10 +194,11 @@ pub(crate) fn _try_remove_gateway( fn validate_gateway_pledge( mut pledge: Vec, minimum_pledge: Uint128, + mix_denom: String, ) -> Result { // check if anything was put as bond if pledge.is_empty() { - return Err(ContractError::NoBondFound); + return Err(ContractError::NoBondFound { mix_denom }); } if pledge.len() > 1 { @@ -203,8 +206,8 @@ fn validate_gateway_pledge( } // check that the denomination is correct - if pledge[0].denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom {}); + if pledge[0].denom != mix_denom { + return Err(ContractError::WrongDenom { mix_denom }); } // check that we have at least 100 coins in our pledge @@ -225,8 +228,8 @@ pub mod tests { use crate::error::ContractError; use crate::gateways::transactions::validate_gateway_pledge; use crate::support::tests; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, BankMsg, Response}; use cosmwasm_std::{from_binary, Addr, Uint128}; @@ -238,7 +241,7 @@ pub mod tests { // if we fail validation (by say not sending enough funds let insufficient_bond = Into::::into(INITIAL_GATEWAY_PLEDGE) - 1; - let info = mock_info("anyone", &coins(insufficient_bond, MIX_DENOM.base)); + let info = mock_info("anyone", &coins(insufficient_bond, TEST_COIN_DENOM)); let (msg, _) = tests::messages::valid_bond_gateway_msg("anyone"); // we are informed that we didn't send enough funds @@ -544,13 +547,26 @@ pub mod tests { #[test] fn validating_gateway_bond() { // you must send SOME funds - let result = validate_gateway_pledge(Vec::new(), INITIAL_GATEWAY_PLEDGE); - assert_eq!(result, Err(ContractError::NoBondFound)); + let result = validate_gateway_pledge( + Vec::new(), + INITIAL_GATEWAY_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); + assert_eq!( + result, + Err(ContractError::NoBondFound { + mix_denom: TEST_COIN_DENOM.to_string() + }) + ); // you must send at least 100 coins... let mut bond = tests::fixtures::good_gateway_pledge(); bond[0].amount = INITIAL_GATEWAY_PLEDGE.checked_sub(Uint128::new(1)).unwrap(); - let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE); + let result = validate_gateway_pledge( + bond.clone(), + INITIAL_GATEWAY_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); assert_eq!( result, Err(ContractError::InsufficientGatewayBond { @@ -562,18 +578,40 @@ pub mod tests { // more than that is still fine let mut bond = tests::fixtures::good_gateway_pledge(); bond[0].amount = INITIAL_GATEWAY_PLEDGE + Uint128::new(1); - let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE); + let result = validate_gateway_pledge( + bond.clone(), + INITIAL_GATEWAY_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); assert!(result.is_ok()); // it must be sent in the defined denom! let mut bond = tests::fixtures::good_gateway_pledge(); bond[0].denom = "baddenom".to_string(); - let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE); - assert_eq!(result, Err(ContractError::WrongDenom {})); + let result = validate_gateway_pledge( + bond.clone(), + INITIAL_GATEWAY_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); + assert_eq!( + result, + Err(ContractError::WrongDenom { + mix_denom: TEST_COIN_DENOM.to_string() + }) + ); let mut bond = tests::fixtures::good_gateway_pledge(); bond[0].denom = "foomp".to_string(); - let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE); - assert_eq!(result, Err(ContractError::WrongDenom {})); + let result = validate_gateway_pledge( + bond.clone(), + INITIAL_GATEWAY_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); + assert_eq!( + result, + Err(ContractError::WrongDenom { + mix_denom: TEST_COIN_DENOM.to_string() + }) + ); } } diff --git a/contracts/mixnet/src/lib.rs b/contracts/mixnet/src/lib.rs index 281675a90d..5b0d0c8d0c 100644 --- a/contracts/mixnet/src/lib.rs +++ b/contracts/mixnet/src/lib.rs @@ -9,5 +9,6 @@ mod gateways; mod interval; mod mixnet_contract_settings; mod mixnodes; +mod queued_migrations; mod rewards; mod support; diff --git a/contracts/mixnet/src/mixnet_contract_settings/models.rs b/contracts/mixnet/src/mixnet_contract_settings/models.rs index 105475a2e1..d271972be7 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/models.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/models.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct ContractState { pub owner: Addr, // only the owner account can update state + pub mix_denom: String, pub rewarding_validator_address: Addr, pub params: ContractStateParams, } diff --git a/contracts/mixnet/src/mixnet_contract_settings/queries.rs b/contracts/mixnet/src/mixnet_contract_settings/queries.rs index 9ed2ac8a2e..845be0dc67 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/queries.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/queries.rs @@ -45,6 +45,7 @@ pub(crate) mod tests { let dummy_state = ContractState { owner: Addr::unchecked("someowner"), + mix_denom: String::from("unym"), rewarding_validator_address: Addr::unchecked("monitor"), params: ContractStateParams { minimum_mixnode_pledge: 123u128.into(), diff --git a/contracts/mixnet/src/mixnet_contract_settings/storage.rs b/contracts/mixnet/src/mixnet_contract_settings/storage.rs index 94f6a73db2..5f4c736d13 100644 --- a/contracts/mixnet/src/mixnet_contract_settings/storage.rs +++ b/contracts/mixnet/src/mixnet_contract_settings/storage.rs @@ -1,7 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::error::ContractError; use crate::mixnet_contract_settings::models::ContractState; use cosmwasm_std::StdResult; use cosmwasm_std::Storage; @@ -11,10 +10,14 @@ use mixnet_contract_common::{Layer, LayerDistribution}; pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new("config"); pub(crate) const LAYERS: Item<'_, LayerDistribution> = Item::new("layers"); -pub fn rewarding_validator_address(storage: &dyn Storage) -> Result { - Ok(CONTRACT_STATE +pub fn rewarding_validator_address(storage: &dyn Storage) -> StdResult { + CONTRACT_STATE .load(storage) - .map(|state| state.rewarding_validator_address.to_string())?) + .map(|state| state.rewarding_validator_address.to_string()) +} + +pub fn mix_denom(storage: &dyn Storage) -> StdResult { + CONTRACT_STATE.load(storage).map(|state| state.mix_denom) } pub fn increment_layer_count(storage: &mut dyn Storage, layer: Layer) -> StdResult<()> { diff --git a/contracts/mixnet/src/mixnodes/bonding_queries.rs b/contracts/mixnet/src/mixnodes/bonding_queries.rs index 2519045675..3c5085e04e 100644 --- a/contracts/mixnet/src/mixnodes/bonding_queries.rs +++ b/contracts/mixnet/src/mixnodes/bonding_queries.rs @@ -8,6 +8,13 @@ use mixnet_contract_common::{ IdentityKey, MixNodeBond, MixOwnershipResponse, MixnodeBondResponse, PagedMixnodeResponse, }; +pub fn get_blacklisted_nodes(deps: Deps<'_>) -> Vec { + storage::MIXNODES_BOND_BLACKLIST + .keys(deps.storage, None, None, Order::Ascending) + .filter_map(|i| i.ok()) + .collect() +} + pub fn query_mixnode_at_height( deps: Deps<'_>, mix_identity: String, @@ -252,10 +259,13 @@ pub(crate) mod tests { let res = query_owns_mixnode(deps.as_ref(), "fred".to_string()).unwrap(); assert!(res.mixnode.is_some()); + let api = deps.api.clone(); + // but after unbonding it, he doesn't own one anymore crate::mixnodes::transactions::try_remove_mixnode( - env, - deps.as_mut(), + &env, + deps.as_mut().storage, + &api, mock_info("fred", &[]), ) .unwrap(); diff --git a/contracts/mixnet/src/mixnodes/storage.rs b/contracts/mixnet/src/mixnodes/storage.rs index a45e8c74a2..ad54790b8b 100644 --- a/contracts/mixnet/src/mixnodes/storage.rs +++ b/contracts/mixnet/src/mixnodes/storage.rs @@ -1,7 +1,6 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use config::defaults::MIX_DENOM; use cosmwasm_std::{StdResult, Storage, Uint128}; use cw_storage_plus::{Index, IndexList, IndexedSnapshotMap, Map, Strategy, UniqueIndex}; use mixnet_contract_common::{ @@ -11,6 +10,8 @@ use mixnet_contract_common::{SphinxKey, U128}; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; +use crate::mixnet_contract_settings::storage::mix_denom; + // storage prefixes const TOTAL_DELEGATION_NAMESPACE: &str = "td"; const MIXNODES_PK_NAMESPACE: &str = "mn"; @@ -18,6 +19,7 @@ const MIXNODES_PK_CHECKPOINTS: &str = "mn__check"; const MIXNODES_PK_CHANGELOG: &str = "mn__change"; const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno"; const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns"; +const MIXNODES_BOND_BLACKLIST_NAMESPACE: &str = "mbb"; const LAST_PM_UPDATE_NAMESPACE: &str = "lpm"; @@ -31,6 +33,9 @@ pub(crate) const TOTAL_DELEGATION: Map<'_, IdentityKeyRef<'_>, Uint128> = pub(crate) const LAST_PM_UPDATE_TIME: Map<'_, IdentityKeyRef<'_>, u64> = Map::new(LAST_PM_UPDATE_NAMESPACE); +pub(crate) const MIXNODES_BOND_BLACKLIST: Map<'_, IdentityKeyRef<'_>, u8> = + Map::new(MIXNODES_BOND_BLACKLIST_NAMESPACE); + pub(crate) struct MixnodeBondIndex<'a> { pub(crate) owner: UniqueIndex<'a, Addr, StoredMixnodeBond>, @@ -171,7 +176,7 @@ pub(crate) fn read_full_mixnode_bond( Ok(Some(MixNodeBond { pledge_amount: stored_bond.pledge_amount, total_delegation: Coin { - denom: MIX_DENOM.base.to_owned(), + denom: mix_denom(storage)?, amount: total_delegation.unwrap_or_default(), }, owner: stored_bond.owner, @@ -190,7 +195,8 @@ mod tests { use super::super::storage; use super::*; use crate::support::tests; - use config::defaults::MIX_DENOM; + use crate::support::tests::fixtures::TEST_COIN_DENOM; + use crate::support::tests::test_helpers::init_contract; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{coin, Addr, Uint128}; use mixnet_contract_common::{IdentityKey, MixNode}; @@ -211,7 +217,7 @@ mod tests { #[test] fn reading_mixnode_bond() { - let mut mock_storage = MockStorage::new(); + let mut mock_storage = init_contract().storage; let node_owner: Addr = Addr::unchecked("node-owner"); let node_identity: IdentityKey = "nodeidentity".into(); @@ -223,7 +229,7 @@ mod tests { let pledge_value = 1000000000; let mixnode_bond = StoredMixnodeBond { - pledge_amount: coin(pledge_value, MIX_DENOM.base), + pledge_amount: coin(pledge_value, TEST_COIN_DENOM), owner: node_owner, layer: Layer::One, block_height: 12_345, diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index e0ff269665..0b8b6153e3 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -3,18 +3,17 @@ use super::storage::{self, LAST_PM_UPDATE_TIME}; use crate::error::ContractError; -use crate::mixnet_contract_settings::storage as mixnet_params_storage; +use crate::mixnet_contract_settings::storage::{self as mixnet_params_storage, mix_denom}; use crate::mixnodes::layer_queries::query_layer_distribution; use crate::mixnodes::storage::StoredMixnodeBond; use crate::support::helpers::{ensure_no_existing_bond, validate_node_identity_signature}; -use config::defaults::MIX_DENOM; use cosmwasm_std::{ - wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Storage, Uint128, + wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Storage, Uint128, }; use mixnet_contract_common::events::{ new_checkpoint_mixnodes_event, new_mixnode_bonding_event, new_mixnode_unbonding_event, }; -use mixnet_contract_common::MixNode; +use mixnet_contract_common::{IdentityKeyRef, MixNode}; use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg; use vesting_contract_common::one_ucoin; @@ -48,7 +47,7 @@ pub fn try_add_mixnode( .load(deps.storage)? .params .minimum_mixnode_pledge; - let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?; + let pledge = validate_mixnode_pledge(info.funds, minimum_pledge, mix_denom(deps.storage)?)?; _try_add_mixnode( deps, @@ -74,7 +73,7 @@ pub fn try_add_mixnode_on_behalf( .load(deps.storage)? .params .minimum_mixnode_pledge; - let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?; + let pledge = validate_mixnode_pledge(info.funds, minimum_pledge, mix_denom(deps.storage)?)?; let proxy = info.sender; _try_add_mixnode( @@ -88,6 +87,15 @@ pub fn try_add_mixnode_on_behalf( ) } +pub fn is_blacklisted( + storage: &dyn Storage, + identity: IdentityKeyRef, +) -> Result { + Ok(storage::MIXNODES_BOND_BLACKLIST + .may_load(storage, identity)? + .is_some()) +} + fn _try_add_mixnode( deps: DepsMut<'_>, env: Env, @@ -101,6 +109,12 @@ fn _try_add_mixnode( // if the client has an active bonded mixnode or gateway, don't allow bonding ensure_no_existing_bond(deps.storage, &owner)?; + if is_blacklisted(deps.storage, &mix_node.identity_key)? { + return Err(ContractError::MixnodeBlacklisted { + identity: mix_node.identity_key, + }); + }; + // We don't have to check lower bound as its an u8 if mix_node.profit_margin_percent > 100 { return Err(ContractError::InvalidProfitMarginPercent( @@ -168,45 +182,47 @@ fn _try_add_mixnode( } pub fn try_remove_mixnode_on_behalf( - env: Env, - deps: DepsMut<'_>, + env: &Env, + storage: &mut dyn Storage, + api: &dyn Api, info: MessageInfo, owner: String, ) -> Result { let proxy = info.sender; - _try_remove_mixnode(env, deps, &owner, Some(proxy)) + _try_remove_mixnode(env, storage, api, &owner, Some(proxy), true) } pub fn try_remove_mixnode( - env: Env, - deps: DepsMut<'_>, + env: &Env, + storage: &mut dyn Storage, + api: &dyn Api, info: MessageInfo, ) -> Result { - _try_remove_mixnode(env, deps, info.sender.as_ref(), None) + _try_remove_mixnode(env, storage, api, info.sender.as_ref(), None, true) } pub(crate) fn _try_remove_mixnode( - env: Env, - deps: DepsMut<'_>, + env: &Env, + storage: &mut dyn Storage, + api: &dyn Api, owner: &str, proxy: Option, + collect_rewards: bool, ) -> Result { - let owner = deps.api.addr_validate(owner)?; + let owner = api.addr_validate(owner)?; - crate::rewards::transactions::_try_compound_operator_reward( - deps.storage, - deps.api, - env.block.height, - &owner, - None, - )?; + if collect_rewards { + crate::rewards::transactions::_try_compound_operator_reward( + storage, + api, + env.block.height, + &owner, + None, + )?; + } // try to find the node of the sender - let mixnode_bond = match storage::mixnodes() - .idx - .owner - .item(deps.storage, owner.clone())? - { + let mixnode_bond = match storage::mixnodes().idx.owner.item(storage, owner.clone())? { Some(record) => record.1, None => return Err(ContractError::NoAssociatedMixNodeBond { owner }), }; @@ -226,10 +242,10 @@ pub(crate) fn _try_remove_mixnode( }; // remove the bond - storage::mixnodes().remove(deps.storage, mixnode_bond.identity(), env.block.height)?; + storage::mixnodes().remove(storage, mixnode_bond.identity(), env.block.height)?; // decrement layer count - mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?; + mixnet_params_storage::decrement_layer_count(storage, mixnode_bond.layer)?; let mut response = Response::new(); @@ -239,7 +255,7 @@ pub(crate) fn _try_remove_mixnode( amount: mixnode_bond.pledge_amount(), }; - let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?; + let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom(storage)?)])?; response = response.add_message(track_unbond_message); } @@ -288,6 +304,7 @@ pub(crate) fn _try_update_mixnode_config( .item(deps.storage, owner.clone())? .ok_or(ContractError::NoAssociatedMixNodeBond { owner })? .1; + let mix_denom = mix_denom(deps.storage)?; if proxy != mixnode_bond.proxy { return Err(ContractError::ProxyMismatch { @@ -329,7 +346,9 @@ pub(crate) fn _try_update_mixnode_config( mixnode_bond.block_height = env.block.height; mixnode_bond }) - .ok_or(ContractError::NoBondFound) + .ok_or(ContractError::NoBondFound { + mix_denom: mix_denom.clone(), + }) }, )?; @@ -342,7 +361,7 @@ pub(crate) fn _try_update_mixnode_config( // and they could potentially leak 1 unym per transaction, altough I'm pretty sure transaction fees make that silly. let return_one_ucoint = BankMsg::Send { to_address: proxy.as_str().to_string(), - amount: vec![one_ucoin()], + amount: vec![one_ucoin(mix_denom)], }; response = response.add_message(return_one_ucoint); } @@ -353,10 +372,11 @@ pub(crate) fn _try_update_mixnode_config( fn validate_mixnode_pledge( mut pledge: Vec, minimum_pledge: Uint128, + mix_denom: String, ) -> Result { // check if anything was put as bond if pledge.is_empty() { - return Err(ContractError::NoBondFound); + return Err(ContractError::NoBondFound { mix_denom }); } if pledge.len() > 1 { @@ -364,8 +384,8 @@ fn validate_mixnode_pledge( } // check that the denomination is correct - if pledge[0].denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom {}); + if pledge[0].denom != mix_denom { + return Err(ContractError::WrongDenom { mix_denom }); } // check that we have at least MIXNODE_BOND coins in our pledge @@ -386,8 +406,8 @@ pub mod tests { use crate::error::ContractError; use crate::mixnodes::transactions::validate_mixnode_pledge; use crate::support::tests; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, BankMsg, Response}; use cosmwasm_std::{from_binary, Addr, Uint128}; @@ -402,7 +422,7 @@ pub mod tests { // if we don't send enough funds let insufficient_bond = Into::::into(INITIAL_MIXNODE_PLEDGE) - 1; - let info = mock_info("anyone", &coins(insufficient_bond, MIX_DENOM.base)); + let info = mock_info("anyone", &coins(insufficient_bond, TEST_COIN_DENOM)); let (msg, _) = tests::messages::valid_bond_mixnode_msg("anyone"); // we are informed that we didn't send enough funds @@ -808,13 +828,26 @@ pub mod tests { #[test] fn validating_mixnode_bond() { // you must send SOME funds - let result = validate_mixnode_pledge(Vec::new(), INITIAL_MIXNODE_PLEDGE); - assert_eq!(result, Err(ContractError::NoBondFound)); + let result = validate_mixnode_pledge( + Vec::new(), + INITIAL_MIXNODE_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); + assert_eq!( + result, + Err(ContractError::NoBondFound { + mix_denom: TEST_COIN_DENOM.to_string() + }) + ); // you must send at least 100 coins... let mut bond = tests::fixtures::good_mixnode_pledge(); bond[0].amount = INITIAL_MIXNODE_PLEDGE.checked_sub(Uint128::new(1)).unwrap(); - let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE); + let result = validate_mixnode_pledge( + bond.clone(), + INITIAL_MIXNODE_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); assert_eq!( result, Err(ContractError::InsufficientMixNodeBond { @@ -826,19 +859,41 @@ pub mod tests { // more than that is still fine let mut bond = tests::fixtures::good_mixnode_pledge(); bond[0].amount = INITIAL_MIXNODE_PLEDGE + Uint128::new(1); - let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE); + let result = validate_mixnode_pledge( + bond.clone(), + INITIAL_MIXNODE_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); assert!(result.is_ok()); // it must be sent in the defined denom! let mut bond = tests::fixtures::good_mixnode_pledge(); bond[0].denom = "baddenom".to_string(); - let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE); - assert_eq!(result, Err(ContractError::WrongDenom {})); + let result = validate_mixnode_pledge( + bond.clone(), + INITIAL_MIXNODE_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); + assert_eq!( + result, + Err(ContractError::WrongDenom { + mix_denom: TEST_COIN_DENOM.to_string() + }) + ); let mut bond = tests::fixtures::good_mixnode_pledge(); bond[0].denom = "foomp".to_string(); - let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE); - assert_eq!(result, Err(ContractError::WrongDenom {})); + let result = validate_mixnode_pledge( + bond.clone(), + INITIAL_MIXNODE_PLEDGE, + TEST_COIN_DENOM.to_string(), + ); + assert_eq!( + result, + Err(ContractError::WrongDenom { + mix_denom: TEST_COIN_DENOM.to_string() + }) + ); } #[test] diff --git a/contracts/mixnet/src/queued_migrations.rs b/contracts/mixnet/src/queued_migrations.rs new file mode 100644 index 0000000000..a0d329fdf6 --- /dev/null +++ b/contracts/mixnet/src/queued_migrations.rs @@ -0,0 +1,38 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::{Addr, Response, Storage}; +use cw_storage_plus::Item; +use mixnet_contract_common::{ContractStateParams, MigrateMsg}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::error::ContractError; +use crate::mixnet_contract_settings::models::ContractState; +use crate::mixnet_contract_settings::storage::CONTRACT_STATE; + +pub fn migrate_config_from_env( + storage: &mut dyn Storage, + msg: &MigrateMsg, +) -> Result { + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] + pub struct OldContractState { + pub owner: Addr, + pub mix_denom: String, + pub rewarding_validator_address: Addr, + pub params: ContractStateParams, + } + const OLD_CONTRACT_STATE: Item<'_, OldContractState> = Item::new("config"); + + let old_state = OLD_CONTRACT_STATE.load(storage)?; + let new_state = ContractState { + owner: old_state.owner, + mix_denom: msg.mixnet_denom.clone(), + rewarding_validator_address: old_state.rewarding_validator_address, + params: old_state.params, + }; + + CONTRACT_STATE.save(storage, &new_state)?; + + Ok(Default::default()) +} diff --git a/contracts/mixnet/src/rewards/queries.rs b/contracts/mixnet/src/rewards/queries.rs index 035382ddb7..9abcd66a0f 100644 --- a/contracts/mixnet/src/rewards/queries.rs +++ b/contracts/mixnet/src/rewards/queries.rs @@ -83,7 +83,7 @@ pub(crate) mod tests { use crate::delegations::transactions::try_delegate_to_mixnode; use crate::interval::storage::{save_epoch, save_epoch_reward_params}; use crate::rewards::transactions::try_reward_mixnode; - use config::defaults::MIX_DENOM; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use cosmwasm_std::{coin, Addr}; use mixnet_contract_common::{ Interval, RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT, @@ -187,7 +187,7 @@ pub(crate) mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &[coin(200_000000, MIX_DENOM.base)], + &[coin(200_000000, TEST_COIN_DENOM)], ), node_identity.clone(), ) diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index 927390e8fd..e0f59de6e9 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -10,11 +10,12 @@ use crate::contract::debug_with_visibility; use crate::delegations::storage as delegations_storage; use crate::delegations::transactions::_try_delegate_to_mixnode; use crate::error::ContractError; +use crate::mixnet_contract_settings::storage::mix_denom; use crate::mixnodes::storage::mixnodes; use crate::mixnodes::storage::{self as mixnodes_storage, StoredMixnodeBond}; +use crate::mixnodes::transactions::is_blacklisted; use crate::rewards::helpers; use crate::support::helpers::{is_authorized, operator_cost_at_epoch}; -use config::defaults::MIX_DENOM; use cosmwasm_std::{ coins, wasm_execute, Addr, Api, BankMsg, Coin, DepsMut, Env, MessageInfo, Order, Response, Storage, Uint128, @@ -69,6 +70,7 @@ fn _try_claim_operator_reward( proxy: Option, ) -> Result { let owner = api.addr_validate(owner)?; + let mix_denom = mix_denom(storage)?; let bond = match crate::mixnodes::storage::mixnodes() .idx @@ -105,7 +107,7 @@ fn _try_claim_operator_reward( let return_tokens = BankMsg::Send { to_address: proxy.as_ref().unwrap_or(&owner).to_string(), - amount: coins(reward.u128(), MIX_DENOM.base), + amount: coins(reward.u128(), mix_denom.clone()), }; let mut response = Response::default() @@ -115,10 +117,10 @@ fn _try_claim_operator_reward( if let Some(proxy) = proxy { let msg = Some(VestingContractExecuteMsg::TrackReward { address: owner.to_string(), - amount: Coin::new(reward.u128(), MIX_DENOM.base), + amount: Coin::new(reward.u128(), mix_denom.clone()), }); - let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?; + let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?; response = response.add_message(wasm_msg); } @@ -134,6 +136,7 @@ pub fn _try_claim_delegator_reward( proxy: Option, ) -> Result { let owner = api.addr_validate(owner)?; + let mix_denom = mix_denom(storage)?; let key = mixnet_contract_common::delegation::generate_storage_key(&owner, proxy.as_ref()); let reward = calculate_delegator_reward(storage, api, key.clone(), mix_identity)?; @@ -153,7 +156,7 @@ pub fn _try_claim_delegator_reward( let return_tokens = BankMsg::Send { to_address: proxy.as_ref().unwrap_or(&owner).to_string(), - amount: coins(reward.u128(), MIX_DENOM.base), + amount: coins(reward.u128(), mix_denom.clone()), }; let mut response = @@ -169,10 +172,10 @@ pub fn _try_claim_delegator_reward( if let Some(proxy) = proxy { let msg = Some(VestingContractExecuteMsg::TrackReward { address: owner.to_string(), - amount: Coin::new(reward.u128(), MIX_DENOM.base), + amount: Coin::new(reward.u128(), mix_denom.clone()), }); - let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?; + let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?; response = response.add_message(wasm_msg); } @@ -370,6 +373,56 @@ pub fn try_compound_delegator_reward_on_behalf( ) } +pub fn try_compound_reward( + deps: DepsMut<'_>, + env: Env, + operator: Option, + delegator: Option, + mix_identity: Option, + proxy: Option, +) -> Result { + let proxy = proxy.and_then(|p| deps.api.addr_validate(&p).ok()); + if let Some(operator_address) = operator { + let operator_address = deps.api.addr_validate(&operator_address)?; + let reward = _try_compound_operator_reward( + deps.storage, + deps.api, + env.block.height, + &operator_address, + proxy, + )?; + Ok( + Response::default().add_event(new_compound_operator_reward_event( + &operator_address, + reward, + )), + ) + } else if let Some(delegator_address) = delegator { + if mix_identity.is_none() { + return Err(ContractError::MissingMixIdentity); + } + + let delegator_address = deps.api.addr_validate(&delegator_address)?; + let reward = _try_compound_delegator_reward( + env.block.height, + deps, + delegator_address.as_str(), + mix_identity.as_ref().unwrap(), + proxy, + )?; + Ok( + Response::default().add_event(new_compound_delegator_reward_event( + &delegator_address, + &None, + reward, + &mix_identity.unwrap(), + )), + ) + } else { + Ok(Response::default()) + } +} + pub fn try_compound_delegator_reward( deps: DepsMut<'_>, env: Env, @@ -403,13 +456,14 @@ pub fn _try_compound_delegator_reward( proxy: Option, ) -> Result { let delegation_map = crate::delegations::storage::delegations(); + let mix_denom = mix_denom(deps.storage)?; let key = mixnet_contract_common::delegation::generate_storage_key( &deps.api.addr_validate(owner_address)?, proxy.as_ref(), ); let reward = calculate_delegator_reward(deps.storage, deps.api, key.clone(), mix_identity)?; - let mut compounded_delegation = reward; + let mut total_delegation_delegate = Uint128::zero(); // Might want to introduce paging here let delegation_heights = delegation_map @@ -421,7 +475,7 @@ pub fn _try_compound_delegator_reward( for h in delegation_heights { let delegation = delegation_map.load(deps.storage, (mix_identity.to_string(), key.clone(), h))?; - compounded_delegation += delegation.amount.amount; + total_delegation_delegate += delegation.amount.amount; delegation_map.replace( deps.storage, (mix_identity.to_string(), key.clone(), h), @@ -430,7 +484,22 @@ pub fn _try_compound_delegator_reward( )?; } + let compounded_delegation = total_delegation_delegate + reward; + if compounded_delegation != Uint128::zero() { + mixnodes_storage::TOTAL_DELEGATION.update::<_, ContractError>( + deps.storage, + mix_identity, + |total_delegation| { + // since we know that the target node exists and because the total_delegation bucket + // entry is created whenever the node itself is added, the unwrap here is fine + // as the entry MUST exist + Ok(total_delegation + .unwrap() + .saturating_sub(total_delegation_delegate)) + }, + )?; + _try_delegate_to_mixnode( deps.branch(), block_height, @@ -438,7 +507,7 @@ pub fn _try_compound_delegator_reward( owner_address, Coin { amount: compounded_delegation, - denom: MIX_DENOM.base.to_string(), + denom: mix_denom, }, proxy, )?; @@ -469,6 +538,10 @@ pub fn calculate_delegator_reward( key: Vec, mix_identity: &str, ) -> Result { + if is_blacklisted(storage, mix_identity)? { + return Ok(Uint128::zero()); + }; + let last_claimed_height = storage::DELEGATOR_REWARD_CLAIMED_HEIGHT .load(storage, (key.clone(), mix_identity.to_string())) .unwrap_or(0); @@ -724,9 +797,9 @@ pub mod tests { use crate::rewards::transactions::try_reward_mixnode; use crate::support::helpers::{current_operator_epoch_cost, epochs_in_interval}; use crate::support::tests; + use crate::support::tests::fixtures::TEST_COIN_DENOM; use crate::support::tests::test_helpers; use az::CheckedCast; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coin, coins, Addr, StdError, Timestamp, Uint128}; use mixnet_contract_common::events::{ @@ -744,7 +817,7 @@ pub mod tests { let mut deps = test_helpers::init_contract(); let mut env = mock_env(); let sender = rewarding_validator_address(&deps.storage).unwrap(); - let info = mock_info(&sender, &coins(1000, MIX_DENOM.base)); + let info = mock_info(&sender, &coins(1000, TEST_COIN_DENOM)); crate::interval::transactions::init_epoch(&mut deps.storage, env.clone()).unwrap(); // bond the node @@ -885,7 +958,7 @@ pub mod tests { let initial_bond = 10000_000000; let initial_delegation = 20000_000000; let mixnode_bond = StoredMixnodeBond { - pledge_amount: coin(initial_bond, MIX_DENOM.base), + pledge_amount: coin(initial_bond, TEST_COIN_DENOM), owner: node_owner, layer: Layer::One, block_height: env.block.height, @@ -925,7 +998,7 @@ pub mod tests { &Delegation::new( Addr::unchecked("delegator"), node_identity.clone(), - coin(initial_delegation, MIX_DENOM.base), + coin(initial_delegation, TEST_COIN_DENOM), env.block.height, None, ), @@ -1109,7 +1182,7 @@ pub mod tests { assert_eq!(staking_supply, 100_000_000_000_000u128); let sender = Addr::unchecked("alice"); - let stake = coins(10_000_000_000, MIX_DENOM.base); + let stake = coins(10_000_000_000, TEST_COIN_DENOM); let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng()); let owner_signature = keypair @@ -1160,14 +1233,14 @@ pub mod tests { let node_owner: Addr = Addr::unchecked("johnny"); let node_identity_2 = test_helpers::add_mixnode( node_owner.as_str(), - coins(10_000_000_000, MIX_DENOM.base), + coins(10_000_000_000, TEST_COIN_DENOM), deps.as_mut(), ); try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]), + mock_info("alice_d1", &[coin(8000_000000, TEST_COIN_DENOM)]), node_identity_1.clone(), ) .unwrap(); @@ -1175,7 +1248,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("alice_d2", &[coin(2000_000000, MIX_DENOM.base)]), + mock_info("alice_d2", &[coin(2000_000000, TEST_COIN_DENOM)]), node_identity_1.clone(), ) .unwrap(); @@ -1183,7 +1256,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("bob_d1", &[coin(8000_000000, MIX_DENOM.base)]), + mock_info("bob_d1", &[coin(8000_000000, TEST_COIN_DENOM)]), node_identity_2.clone(), ) .unwrap(); @@ -1191,7 +1264,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("bob_d2", &[coin(2000_000000, MIX_DENOM.base)]), + mock_info("bob_d2", &[coin(2000_000000, TEST_COIN_DENOM)]), node_identity_2.clone(), ) .unwrap(); @@ -1199,14 +1272,14 @@ pub mod tests { let node_owner: Addr = Addr::unchecked("alicebob"); let node_identity_3 = test_helpers::add_mixnode( node_owner.as_str(), - coins(10_000_000_000 * 2, MIX_DENOM.base), + coins(10_000_000_000 * 2, TEST_COIN_DENOM), deps.as_mut(), ); try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("alicebob_d1", &[coin(8000_000000 * 2, MIX_DENOM.base)]), + mock_info("alicebob_d1", &[coin(8000_000000 * 2, TEST_COIN_DENOM)]), node_identity_3.clone(), ) .unwrap(); @@ -1214,7 +1287,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("alicebob_d2", &[coin(2000_000000 * 2, MIX_DENOM.base)]), + mock_info("alicebob_d2", &[coin(2000_000000 * 2, TEST_COIN_DENOM)]), node_identity_3.clone(), ) .unwrap(); @@ -1320,7 +1393,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), env.clone(), - mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]), + mock_info("alice_d1", &[coin(8000_000000, TEST_COIN_DENOM)]), node_identity_1.clone(), ) .unwrap(); @@ -1559,14 +1632,14 @@ pub mod tests { let node_owner: Addr = Addr::unchecked("alice"); let node_identity = test_helpers::add_mixnode( node_owner.as_str(), - coins(10_000_000_000, MIX_DENOM.base), + coins(10_000_000_000, TEST_COIN_DENOM), deps.as_mut(), ); try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]), + mock_info("alice_d1", &[coin(8000_000000, TEST_COIN_DENOM)]), node_identity.clone(), ) .unwrap(); @@ -1574,7 +1647,7 @@ pub mod tests { try_delegate_to_mixnode( deps.as_mut(), mock_env(), - mock_info("alice_d2", &[coin(2000_000000, MIX_DENOM.base)]), + mock_info("alice_d2", &[coin(2000_000000, TEST_COIN_DENOM)]), node_identity.clone(), ) .unwrap(); @@ -1750,7 +1823,7 @@ pub mod tests { #[allow(clippy::inconsistent_digit_grouping)] let node_identity = test_helpers::add_mixnode( node_owner.as_str(), - coins(10000_000_000, MIX_DENOM.base), + coins(10000_000_000, TEST_COIN_DENOM), deps.as_mut(), ); @@ -1777,7 +1850,7 @@ pub mod tests { #[allow(clippy::inconsistent_digit_grouping)] let node_identity = test_helpers::add_mixnode( node_owner.as_str(), - coins(10000_000_000, MIX_DENOM.base), + coins(10000_000_000, TEST_COIN_DENOM), deps.as_mut(), ); @@ -1787,7 +1860,7 @@ pub mod tests { env.clone(), mock_info( &*format!("delegator{:04}", i), - &[coin(2000_000000, MIX_DENOM.base)], + &[coin(2000_000000, TEST_COIN_DENOM)], ), node_identity.clone(), ) diff --git a/contracts/mixnet/src/support/tests/fixtures.rs b/contracts/mixnet/src/support/tests/fixtures.rs index f8c5513c32..813a68e537 100644 --- a/contracts/mixnet/src/support/tests/fixtures.rs +++ b/contracts/mixnet/src/support/tests/fixtures.rs @@ -1,11 +1,13 @@ use crate::contract::INITIAL_MIXNODE_PLEDGE; use crate::mixnodes::storage as mixnodes_storage; use crate::{mixnodes::storage::StoredMixnodeBond, support::tests}; -use config::defaults::MIX_DENOM; use cosmwasm_std::{coin, Addr, Coin}; use mixnet_contract_common::reward_params::NodeRewardParams; use mixnet_contract_common::{Gateway, GatewayBond, Layer, MixNode}; +pub const TEST_COIN_DENOM: &str = "unym"; +pub const TEST_REWARDING_VALIDATOR_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; + pub fn mix_node_fixture() -> MixNode { MixNode { host: "mix.node.org".to_string(), @@ -37,7 +39,7 @@ pub fn gateway_bond_fixture(owner: &str) -> GatewayBond { ..tests::fixtures::gateway_fixture() }; GatewayBond::new( - coin(50, MIX_DENOM.base), + coin(50, TEST_COIN_DENOM), Addr::unchecked(owner), 12_345, gateway, @@ -47,7 +49,7 @@ pub fn gateway_bond_fixture(owner: &str) -> GatewayBond { pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::StoredMixnodeBond { StoredMixnodeBond::new( - coin(50, MIX_DENOM.base), + coin(50, TEST_COIN_DENOM), Addr::unchecked(owner), Layer::One, 12_345, @@ -64,14 +66,14 @@ pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::Stor pub fn good_mixnode_pledge() -> Vec { vec![Coin { - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), amount: INITIAL_MIXNODE_PLEDGE, }] } pub fn good_gateway_pledge() -> Vec { vec![Coin { - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), amount: INITIAL_MIXNODE_PLEDGE, }] } diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index e8a1108317..19fe66dd35 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -17,7 +17,6 @@ pub mod test_helpers { use crate::mixnodes::storage as mixnodes_storage; use crate::mixnodes::transactions::try_add_mixnode; use crate::support::tests; - use config::defaults::{DEFAULT_NETWORK, MIX_DENOM}; use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::testing::mock_env; use cosmwasm_std::testing::mock_info; @@ -32,6 +31,9 @@ pub mod test_helpers { use mixnet_contract_common::{Delegation, Gateway, IdentityKeyRef, InstantiateMsg, MixNode}; use rand::thread_rng; + use super::fixtures::TEST_COIN_DENOM; + use super::fixtures::TEST_REWARDING_VALIDATOR_ADDRESS; + pub fn add_mixnode(sender: &str, stake: Vec, deps: DepsMut<'_>) -> String { let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng()); let owner_signature = keypair @@ -85,7 +87,8 @@ pub mod test_helpers { pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); let msg = InstantiateMsg { - rewarding_validator_address: DEFAULT_NETWORK.rewarding_validator_address().to_string(), + rewarding_validator_address: TEST_REWARDING_VALIDATOR_ADDRESS.to_string(), + mixnet_denom: TEST_COIN_DENOM.to_string(), }; let env = mock_env(); let info = mock_info("creator", &[]); @@ -111,7 +114,7 @@ pub mod test_helpers { let delegation = Delegation { owner: Addr::unchecked(owner.into()), node_identity: mix.into(), - amount: coin(12345, MIX_DENOM.base), + amount: coin(12345, TEST_COIN_DENOM), block_height: block_height, proxy: None, }; diff --git a/contracts/mixnet/src/support/tests/queries.rs b/contracts/mixnet/src/support/tests/queries.rs index c42dd9b267..e53caf148e 100644 --- a/contracts/mixnet/src/support/tests/queries.rs +++ b/contracts/mixnet/src/support/tests/queries.rs @@ -1,4 +1,3 @@ -use config::defaults::MIX_DENOM; use cosmwasm_std::{ from_binary, testing::{mock_env, MockApi, MockQuerier, MockStorage}, @@ -10,6 +9,8 @@ use mixnet_contract_common::{ use crate::contract::query; +use super::fixtures::TEST_COIN_DENOM; + pub fn get_mix_nodes(deps: &mut OwnedDeps) -> Vec { let result = query( deps.as_ref(), @@ -45,5 +46,5 @@ pub fn query_contract_balance( deps: OwnedDeps, ) -> Vec { let querier = deps.as_ref().querier; - vec![querier.query_balance(address, MIX_DENOM.base).unwrap()] + vec![querier.query_balance(address, TEST_COIN_DENOM).unwrap()] } diff --git a/contracts/multisig/cw3-flex-multisig/Cargo.toml b/contracts/multisig/cw3-flex-multisig/Cargo.toml index f1e76c09ac..0168eec05f 100644 --- a/contracts/multisig/cw3-flex-multisig/Cargo.toml +++ b/contracts/multisig/cw3-flex-multisig/Cargo.toml @@ -29,7 +29,6 @@ serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" } -network-defaults = { path = "../../../common/network-defaults" } [dev-dependencies] cosmwasm-schema = { version = "1.0.0" } diff --git a/contracts/multisig/cw3-flex-multisig/src/contract.rs b/contracts/multisig/cw3-flex-multisig/src/contract.rs index 3a9b7aadd0..c85d1362a7 100644 --- a/contracts/multisig/cw3-flex-multisig/src/contract.rs +++ b/contracts/multisig/cw3-flex-multisig/src/contract.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, Binary, BlockInfo, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, + to_binary, Binary, BlockInfo, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, Response, StdResult, }; @@ -16,7 +16,6 @@ use cw3_fixed_multisig::state::{next_id, Ballot, Proposal, Votes, BALLOTS, PROPO use cw4::{Cw4Contract, MemberChangedHookMsg, MemberDiff}; use cw_storage_plus::Bound; use cw_utils::{maybe_addr, Expiration, ThresholdResponse}; -use network_defaults::DEFAULT_NETWORK; use crate::error::ContractError; use crate::state::{Config, CONFIG}; @@ -40,11 +39,9 @@ pub fn instantiate( })?); // This might need to be changed via a migration, due to circular dependency // of deploying the two contracts - let coconut_bandwidth_addr = Addr::unchecked( - DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .ok_or(ContractError::InvalidCoconutBandwidth {})?, - ); + let coconut_bandwidth_addr = deps + .api + .addr_validate(&msg.coconut_bandwidth_contract_address)?; let total_weight = group_addr.total_weight(&deps.querier)?; msg.threshold.validate(total_weight)?; @@ -461,6 +458,8 @@ mod tests { const VOTER4: &str = "voter0004"; const VOTER5: &str = "voter0005"; const SOMEBODY: &str = "somebody"; + const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = + "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; fn member>(addr: T, weight: u64) -> Member { Member { @@ -513,9 +512,7 @@ mod tests { proposal: ExecuteMsg, voter: &str, ) -> AppResponse { - let proposer = DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .unwrap(); + let proposer = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(); let res = app .execute_contract(Addr::unchecked(proposer), flex_addr.clone(), &proposal, &[]) .unwrap(); @@ -541,6 +538,7 @@ mod tests { group_addr: group.to_string(), threshold, max_voting_period, + coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), }; app.instantiate_contract(flex_id, Addr::unchecked(OWNER), &msg, &[], "flex", None) .unwrap() @@ -576,9 +574,7 @@ mod tests { init_funds: Vec, multisig_as_group_admin: bool, ) -> (Addr, Addr) { - let coconut_bandwidth_contract = DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .unwrap(); + let coconut_bandwidth_contract = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(); // 1. Instantiate group contract with members (and OWNER as admin) and coconut bandwidth contract let members = vec![ member(coconut_bandwidth_contract, 0), @@ -658,6 +654,7 @@ mod tests { quorum: Decimal::percent(1), }, max_voting_period, + coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), }; let err = app .instantiate_contract( @@ -679,6 +676,7 @@ mod tests { group_addr: group_addr.to_string(), threshold: Threshold::AbsoluteCount { weight: 100 }, max_voting_period, + coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), }; let err = app .instantiate_contract( @@ -700,6 +698,7 @@ mod tests { group_addr: group_addr.to_string(), threshold: Threshold::AbsoluteCount { weight: 1 }, max_voting_period, + coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), }; let flex_addr = app .instantiate_contract( @@ -772,11 +771,7 @@ mod tests { }; let err = app .execute_contract( - Addr::unchecked( - DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .unwrap(), - ), + Addr::unchecked(TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string()), flex_addr.clone(), &proposal_wrong_exp, &[], @@ -851,9 +846,7 @@ mod tests { .unwrap_err(); assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - let proposer = DEFAULT_NETWORK - .coconut_bandwidth_contract_address() - .unwrap(); + let proposer = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(); let res = app .execute_contract( Addr::unchecked(&proposer), diff --git a/contracts/vesting/Cargo.toml b/contracts/vesting/Cargo.toml index 5d371cbf8b..3f29aa7e94 100644 --- a/contracts/vesting/Cargo.toml +++ b/contracts/vesting/Cargo.toml @@ -16,7 +16,6 @@ crate-type = ["cdylib", "rlib"] [dependencies] mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } -config = { path = "../../common/config" } cosmwasm-std = { version = "1.0.0 "} cw-storage-plus = { version = "0.13.4", features = ["iterator"] } diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index 15626909fd..b1dad8944c 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -1,13 +1,13 @@ use crate::errors::ContractError; +use crate::queued_migrations::migrate_config_from_env; use crate::storage::{ account_from_address, locked_pledge_cap, update_locked_pledge_cap, ADMIN, - MIXNET_CONTRACT_ADDRESS, + MIXNET_CONTRACT_ADDRESS, MIX_DENOM, }; use crate::traits::{ DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount, }; use crate::vesting::{populate_vesting_periods, Account}; -use config::defaults::MIX_DENOM; use cosmwasm_std::{ coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Timestamp, Uint128, @@ -36,11 +36,13 @@ pub fn instantiate( // ADMIN is set to the address that instantiated the contract, TODO: make this updatable ADMIN.save(deps.storage, &info.sender.to_string())?; MIXNET_CONTRACT_ADDRESS.save(deps.storage, &msg.mixnet_contract_address)?; + MIX_DENOM.save(deps.storage, &msg.mix_denom)?; Ok(Response::default()) } #[entry_point] pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { + migrate_config_from_env(_deps, _env, _msg)?; Ok(Response::default()) } @@ -167,11 +169,9 @@ pub fn try_withdraw_vested_coins( info: MessageInfo, deps: DepsMut<'_>, ) -> Result { - if amount.denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom( - amount.denom, - MIX_DENOM.base.to_string(), - )); + let mix_denom = MIX_DENOM.load(deps.storage)?; + if amount.denom != mix_denom { + return Err(ContractError::WrongDenom(amount.denom, mix_denom)); } let address = info.sender.clone(); @@ -245,7 +245,8 @@ pub fn try_bond_gateway( env: Env, deps: DepsMut<'_>, ) -> Result { - let pledge = validate_funds(&[amount])?; + let mix_denom = MIX_DENOM.load(deps.storage)?; + let pledge = validate_funds(&[amount], mix_denom)?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_bond_gateway(gateway, owner_signature, pledge, &env, deps.storage) } @@ -285,7 +286,8 @@ pub fn try_bond_mixnode( env: Env, deps: DepsMut<'_>, ) -> Result { - let pledge = validate_funds(&[amount])?; + let mix_denom = MIX_DENOM.load(deps.storage)?; + let pledge = validate_funds(&[amount], mix_denom)?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_bond_mixnode(mix_node, owner_signature, pledge, &env, deps.storage) } @@ -345,7 +347,8 @@ fn try_delegate_to_mixnode( env: Env, deps: DepsMut<'_>, ) -> Result { - let amount = validate_funds(&[amount])?; + let mix_denom = MIX_DENOM.load(deps.storage)?; + let amount = validate_funds(&[amount], mix_denom)?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_delegate_to_mixnode(mix_identity, amount, &env, deps.storage) } @@ -396,6 +399,7 @@ fn try_create_periodic_vesting_account( if info.sender != ADMIN.load(deps.storage)? { return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); } + let mix_denom = MIX_DENOM.load(deps.storage)?; let account_exists = account_from_address(owner_address, deps.storage, deps.api).is_ok(); if account_exists { @@ -406,7 +410,7 @@ fn try_create_periodic_vesting_account( let vesting_spec = vesting_spec.unwrap_or_default(); - let coin = validate_funds(&info.funds)?; + let coin = validate_funds(&info.funds, mix_denom)?; let owner_address = deps.api.addr_validate(owner_address)?; let staking_address = if let Some(staking_address) = staking_address { @@ -573,7 +577,7 @@ pub fn try_get_vested_coins( deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; - account.get_vested_coins(block_time, &env) + account.get_vested_coins(block_time, &env, deps.storage) } pub fn try_get_vesting_coins( @@ -583,7 +587,7 @@ pub fn try_get_vesting_coins( deps: Deps<'_>, ) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; - account.get_vesting_coins(block_time, &env) + account.get_vesting_coins(block_time, &env, deps.storage) } pub fn try_get_start_time( @@ -630,7 +634,7 @@ pub fn try_get_delegated_vesting( account.get_delegated_vesting(block_time, &env, deps.storage) } -fn validate_funds(funds: &[Coin]) -> Result { +fn validate_funds(funds: &[Coin], mix_denom: String) -> Result { if funds.is_empty() || funds[0].amount.is_zero() { return Err(ContractError::EmptyFunds); } @@ -639,11 +643,8 @@ fn validate_funds(funds: &[Coin]) -> Result { return Err(ContractError::MultipleDenoms); } - if funds[0].denom != MIX_DENOM.base { - return Err(ContractError::WrongDenom( - funds[0].denom.clone(), - MIX_DENOM.base.to_string(), - )); + if funds[0].denom != mix_denom { + return Err(ContractError::WrongDenom(funds[0].denom.clone(), mix_denom)); } Ok(funds[0].clone()) diff --git a/contracts/vesting/src/lib.rs b/contracts/vesting/src/lib.rs index 4e57232fe5..4e44a69be8 100644 --- a/contracts/vesting/src/lib.rs +++ b/contracts/vesting/src/lib.rs @@ -1,5 +1,6 @@ pub mod contract; mod errors; +mod queued_migrations; mod storage; mod support; mod traits; diff --git a/contracts/vesting/src/queued_migrations.rs b/contracts/vesting/src/queued_migrations.rs new file mode 100644 index 0000000000..75d63ba648 --- /dev/null +++ b/contracts/vesting/src/queued_migrations.rs @@ -0,0 +1,17 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::{DepsMut, Env, Response}; +use vesting_contract_common::MigrateMsg; + +use crate::{errors::ContractError, storage::MIX_DENOM}; + +pub fn migrate_config_from_env( + deps: DepsMut<'_>, + _env: Env, + msg: MigrateMsg, +) -> Result { + MIX_DENOM.save(deps.storage, &msg.mix_denom)?; + + Ok(Default::default()) +} diff --git a/contracts/vesting/src/storage.rs b/contracts/vesting/src/storage.rs index 9e845a58c6..400d90fc3e 100644 --- a/contracts/vesting/src/storage.rs +++ b/contracts/vesting/src/storage.rs @@ -17,6 +17,7 @@ const GATEWAY_PLEDGES: Map<'_, u32, PledgeData> = Map::new("gtw"); pub const DELEGATIONS: Map<'_, (u32, IdentityKey, BlockHeight), Uint128> = Map::new("dlg"); pub const ADMIN: Item<'_, String> = Item::new("adm"); pub const MIXNET_CONTRACT_ADDRESS: Item<'_, String> = Item::new("mix"); +pub const MIX_DENOM: Item<'_, String> = Item::new("den"); pub const LOCKED_PLEDGE_CAP: Item<'_, Uint128> = Item::new("lck"); pub fn locked_pledge_cap(storage: &dyn Storage) -> Uint128 { diff --git a/contracts/vesting/src/support/tests.rs b/contracts/vesting/src/support/tests.rs index eee85a9070..8084e48739 100644 --- a/contracts/vesting/src/support/tests.rs +++ b/contracts/vesting/src/support/tests.rs @@ -2,15 +2,17 @@ pub mod helpers { use crate::contract::instantiate; use crate::vesting::{populate_vesting_periods, Account}; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::{Addr, Coin, Empty, Env, MemoryStorage, OwnedDeps, Storage, Uint128}; use vesting_contract_common::messages::{InitMsg, VestingSpecification}; + pub const TEST_COIN_DENOM: &str = "unym"; + pub fn init_contract() -> OwnedDeps> { let mut deps = mock_dependencies(); let msg = InitMsg { mixnet_contract_address: "test".to_string(), + mix_denom: TEST_COIN_DENOM.to_string(), }; let env = mock_env(); let info = mock_info("admin", &[]); @@ -31,7 +33,7 @@ pub mod helpers { Some(Addr::unchecked("staking")), Coin { amount: Uint128::new(1_000_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, start_time_ts, periods, @@ -50,7 +52,7 @@ pub mod helpers { Some(Addr::unchecked("staking")), Coin { amount: Uint128::new(1_000_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, start_time, periods, diff --git a/contracts/vesting/src/traits/vesting_account.rs b/contracts/vesting/src/traits/vesting_account.rs index 840b5735f0..95f445114e 100644 --- a/contracts/vesting/src/traits/vesting_account.rs +++ b/contracts/vesting/src/traits/vesting_account.rs @@ -34,11 +34,13 @@ pub trait VestingAccount { &self, block_time: Option, env: &Env, + storage: &dyn Storage, ) -> Result; fn get_vesting_coins( &self, block_time: Option, env: &Env, + storage: &dyn Storage, ) -> Result; fn get_start_time(&self) -> Timestamp; diff --git a/contracts/vesting/src/vesting/account/delegating_account.rs b/contracts/vesting/src/vesting/account/delegating_account.rs index 8dede6014e..48f32207ac 100644 --- a/contracts/vesting/src/vesting/account/delegating_account.rs +++ b/contracts/vesting/src/vesting/account/delegating_account.rs @@ -2,6 +2,7 @@ use crate::errors::ContractError; use crate::storage::locked_pledge_cap; use crate::storage::save_delegation; use crate::storage::MIXNET_CONTRACT_ADDRESS; +use crate::storage::MIX_DENOM; use crate::traits::DelegatingAccount; use crate::traits::VestingAccount; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; @@ -28,7 +29,7 @@ impl DelegatingAccount for Account { let compound_delegator_reward_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new().add_message(compound_delegator_reward_msg)) @@ -46,7 +47,7 @@ impl DelegatingAccount for Account { let compound_delegator_reward_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new().add_message(compound_delegator_reward_msg)) @@ -118,7 +119,7 @@ impl DelegatingAccount for Account { let undelegate_from_mixnode = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new() diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index 05b27f8786..33af1d6506 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -2,6 +2,7 @@ use super::PledgeData; use crate::errors::ContractError; use crate::storage::locked_pledge_cap; use crate::storage::MIXNET_CONTRACT_ADDRESS; +use crate::storage::MIX_DENOM; use crate::traits::GatewayBondingAccount; use crate::traits::VestingAccount; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; @@ -76,7 +77,7 @@ impl GatewayBondingAccount for Account { let unbond_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new() diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index a031dc58f6..6312647a9a 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -1,6 +1,7 @@ use crate::errors::ContractError; use crate::storage::locked_pledge_cap; use crate::storage::MIXNET_CONTRACT_ADDRESS; +use crate::storage::MIX_DENOM; use crate::traits::MixnodeBondingAccount; use crate::traits::VestingAccount; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; @@ -24,7 +25,7 @@ impl MixnodeBondingAccount for Account { let compound_operator_reward_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new().add_message(compound_operator_reward_msg)) @@ -41,7 +42,7 @@ impl MixnodeBondingAccount for Account { let compound_operator_reward_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new().add_message(compound_operator_reward_msg)) @@ -60,7 +61,7 @@ impl MixnodeBondingAccount for Account { let update_mixnode_config_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new() @@ -130,7 +131,7 @@ impl MixnodeBondingAccount for Account { let unbond_msg = wasm_execute( MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, - vec![one_ucoin()], + vec![one_ucoin(MIX_DENOM.load(storage)?)], )?; Ok(Response::new() diff --git a/contracts/vesting/src/vesting/account/vesting_account.rs b/contracts/vesting/src/vesting/account/vesting_account.rs index a4ba2f062c..dc93627d1d 100644 --- a/contracts/vesting/src/vesting/account/vesting_account.rs +++ b/contracts/vesting/src/vesting/account/vesting_account.rs @@ -1,7 +1,6 @@ use crate::errors::ContractError; -use crate::storage::{delete_account, save_account, DELEGATIONS}; +use crate::storage::{delete_account, save_account, DELEGATIONS, MIX_DENOM}; use crate::traits::VestingAccount; -use config::defaults::MIX_DENOM; use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128}; use vesting_contract_common::{OriginalVestingResponse, Period}; @@ -33,7 +32,7 @@ impl VestingAccount for Account { // Returns 0 in case of underflow. Which is fine, as the amount of pledged and delegated tokens can be larger then vesting_coins due to rewards and vesting periods expiring Ok(Coin { amount: Uint128::new( - self.get_vesting_coins(block_time, env)? + self.get_vesting_coins(block_time, env, storage)? .amount .u128() .saturating_sub( @@ -47,7 +46,7 @@ impl VestingAccount for Account { .u128(), ), ), - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } @@ -63,7 +62,7 @@ impl VestingAccount for Account { .u128() .saturating_sub(self.locked_coins(block_time, env, storage)?.amount.u128()), ), - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } @@ -71,22 +70,24 @@ impl VestingAccount for Account { &self, block_time: Option, env: &Env, + storage: &dyn Storage, ) -> Result { let block_time = block_time.unwrap_or(env.block.time); let period = self.get_current_vesting_period(block_time); + let denom = MIX_DENOM.load(storage)?; let amount = match period { Period::Before => Coin { amount: Uint128::new(0), - denom: MIX_DENOM.base.to_string(), + denom, }, Period::In(idx) => Coin { amount: Uint128::new(self.tokens_per_period()? * idx as u128), - denom: MIX_DENOM.base.to_string(), + denom, }, Period::After => Coin { amount: self.coin.amount, - denom: MIX_DENOM.base.to_string(), + denom, }, }; Ok(amount) @@ -96,11 +97,12 @@ impl VestingAccount for Account { &self, block_time: Option, env: &Env, + storage: &dyn Storage, ) -> Result { Ok(Coin { amount: self.get_original_vesting().amount().amount - - self.get_vested_coins(block_time, env)?.amount, - denom: MIX_DENOM.base.to_string(), + - self.get_vested_coins(block_time, env, storage)?.amount, + denom: MIX_DENOM.load(storage)?, }) } @@ -130,7 +132,7 @@ impl VestingAccount for Account { let period = self.get_current_vesting_period(block_time); let withdrawn = self.load_withdrawn(storage)?; let max_available = self - .get_vested_coins(Some(block_time), env)? + .get_vested_coins(Some(block_time), env, storage)? .amount .saturating_sub(withdrawn); let start_time = match period { @@ -152,7 +154,7 @@ impl VestingAccount for Account { Ok(Coin { amount, - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } @@ -170,7 +172,7 @@ impl VestingAccount for Account { Ok(Coin { amount, - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } @@ -182,7 +184,7 @@ impl VestingAccount for Account { ) -> Result { let block_time = block_time.unwrap_or(env.block.time); let period = self.get_current_vesting_period(block_time); - let max_vested = self.get_vested_coins(Some(block_time), env)?; + let max_vested = self.get_vested_coins(Some(block_time), env, storage)?; let start_time = match period { Period::Before => 0, Period::After => u64::MAX, @@ -206,7 +208,7 @@ impl VestingAccount for Account { Ok(Coin { amount, - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } @@ -226,12 +228,12 @@ impl VestingAccount for Account { let amount = bond.amount().amount - bonded_free.amount; Ok(Coin { amount, - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } else { Ok(Coin { amount: Uint128::zero(), - denom: MIX_DENOM.base.to_string(), + denom: MIX_DENOM.load(storage)?, }) } } diff --git a/contracts/vesting/src/vesting/mod.rs b/contracts/vesting/src/vesting/mod.rs index 0ac5ba7562..9dc389864d 100644 --- a/contracts/vesting/src/vesting/mod.rs +++ b/contracts/vesting/src/vesting/mod.rs @@ -39,12 +39,11 @@ mod tests { use crate::contract::execute; use crate::storage::load_account; use crate::support::tests::helpers::{ - init_contract, vesting_account_mid_fixture, vesting_account_new_fixture, + init_contract, vesting_account_mid_fixture, vesting_account_new_fixture, TEST_COIN_DENOM, }; use crate::traits::DelegatingAccount; use crate::traits::VestingAccount; use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount}; - use config::defaults::MIX_DENOM; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128}; use mixnet_contract_common::{Gateway, MixNode}; @@ -55,7 +54,7 @@ mod tests { fn test_account_creation() { let mut deps = init_contract(); let env = mock_env(); - let info = mock_info("not_admin", &coins(1_000_000_000_000, MIX_DENOM.base)); + let info = mock_info("not_admin", &coins(1_000_000_000_000, TEST_COIN_DENOM)); let msg = ExecuteMsg::CreateAccount { owner_address: "owner".to_string(), staking_address: Some("staking".to_string()), @@ -65,7 +64,7 @@ mod tests { let response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); assert!(response.is_err()); - let info = mock_info("admin", &coins(1_000_000_000_000, MIX_DENOM.base)); + let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM)); let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); let created_account = load_account(&Addr::unchecked("owner"), &deps.storage) .unwrap() @@ -131,7 +130,7 @@ mod tests { let msg = ExecuteMsg::WithdrawVestedCoins { amount: Coin { amount: Uint128::new(1), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, }; let info = mock_info("new_owner", &[]); @@ -186,8 +185,12 @@ mod tests { Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1); let current_period = account.get_current_vesting_period(block_time); assert_eq!(current_period, Period::In(1)); - let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap(); - let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap(); + let vested_coins = account + .get_vested_coins(Some(block_time), &env, &deps.storage) + .unwrap(); + let vesting_coins = account + .get_vesting_coins(Some(block_time), &env, &deps.storage) + .unwrap(); assert_eq!( vested_coins.amount, Uint128::new( @@ -207,8 +210,12 @@ mod tests { Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1); let current_period = account.get_current_vesting_period(block_time); assert_eq!(current_period, Period::In(5)); - let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap(); - let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap(); + let vested_coins = account + .get_vested_coins(Some(block_time), &env, &deps.storage) + .unwrap(); + let vesting_coins = account + .get_vesting_coins(Some(block_time), &env, &deps.storage) + .unwrap(); assert_eq!( vested_coins.amount, Uint128::new( @@ -230,8 +237,12 @@ mod tests { ); let current_period = account.get_current_vesting_period(block_time); assert_eq!(current_period, Period::After); - let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap(); - let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap(); + let vested_coins = account + .get_vested_coins(Some(block_time), &env, &deps.storage) + .unwrap(); + let vesting_coins = account + .get_vesting_coins(Some(block_time), &env, &deps.storage) + .unwrap(); assert_eq!( vested_coins.amount, Uint128::new(account.get_original_vesting().amount().amount.u128()) @@ -245,8 +256,10 @@ mod tests { let env = mock_env(); let account = vesting_account_mid_fixture(&mut deps.storage, &env); - let vested_coins = account.get_vested_coins(None, &env).unwrap(); - let vesting_coins = account.get_vesting_coins(None, &env).unwrap(); + let vested_coins = account.get_vested_coins(None, &env, &deps.storage).unwrap(); + let vesting_coins = account + .get_vesting_coins(None, &env, &deps.storage) + .unwrap(); let locked_coins = account.locked_coins(None, &env, &mut deps.storage).unwrap(); assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000)); assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000)); @@ -262,7 +275,7 @@ mod tests { let delegation = Coin { amount: Uint128::new(90_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }; let ok = account.try_delegate_to_mixnode( @@ -273,8 +286,10 @@ mod tests { ); assert!(ok.is_ok()); - let vested_coins = account.get_vested_coins(None, &env).unwrap(); - let vesting_coins = account.get_vesting_coins(None, &env).unwrap(); + let vested_coins = account.get_vested_coins(None, &env, &deps.storage).unwrap(); + let vesting_coins = account + .get_vesting_coins(None, &env, &deps.storage) + .unwrap(); assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000)); assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000)); @@ -333,8 +348,10 @@ mod tests { let withdrawn = account.load_withdrawn(&deps.storage).unwrap(); assert_eq!(withdrawn, Uint128::new(250_000_000_000)); - let vested_coins = account.get_vested_coins(None, &env).unwrap(); - let vesting_coins = account.get_vesting_coins(None, &env).unwrap(); + let vested_coins = account.get_vested_coins(None, &env, &deps.storage).unwrap(); + let vesting_coins = account + .get_vesting_coins(None, &env, &deps.storage) + .unwrap(); let locked_coins = account.locked_coins(None, &env, &mut deps.storage).unwrap(); assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000)); assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000)); @@ -352,8 +369,10 @@ mod tests { ); assert!(ok.is_ok()); - let vested_coins = account.get_vested_coins(None, &env).unwrap(); - let vesting_coins = account.get_vesting_coins(None, &env).unwrap(); + let vested_coins = account.get_vested_coins(None, &env, &deps.storage).unwrap(); + let vesting_coins = account + .get_vesting_coins(None, &env, &deps.storage) + .unwrap(); let locked_coins = account.locked_coins(None, &env, &mut deps.storage).unwrap(); assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000)); assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000)); @@ -388,7 +407,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(1_000_000_000_001), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -399,7 +418,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(90_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -411,7 +430,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(20_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -426,7 +445,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(500_000_000_001), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -522,7 +541,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(1_000_000_000_001), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -534,7 +553,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(90_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -550,7 +569,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(10_000_000_001), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -642,7 +661,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(1_000_000_000_001), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -654,7 +673,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(90_000_000_000), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, @@ -670,7 +689,7 @@ mod tests { "alice".to_string(), Coin { amount: Uint128::new(500_000_000_001), - denom: MIX_DENOM.base.to_string(), + denom: TEST_COIN_DENOM.to_string(), }, &env, &mut deps.storage, diff --git a/envs/mainnet.env b/envs/mainnet.env new file mode 100644 index 0000000000..dce1c3c9c0 --- /dev/null +++ b/envs/mainnet.env @@ -0,0 +1,20 @@ +CONFIGURED=true + +RUST_LOG=info +RUST_BACKTRACE=1 + +BECH32_PREFIX=n +MIX_DENOM=unym +MIX_DENOM_DISPLAY=nym +STAKE_DENOM=unyx +STAKE_DENOM_DISPLAY=nyx +DENOMS_EXPONENT=6 +MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g +VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw +BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy +STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090" +NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/" +API_VALIDATOR="https://validator.nymtech.net/api/" diff --git a/envs/qa.env b/envs/qa.env new file mode 100644 index 0000000000..842fb54c4d --- /dev/null +++ b/envs/qa.env @@ -0,0 +1,20 @@ +CONFIGURED=true + +RUST_LOG=info +RUST_BACKTRACE=1 + +BECH32_PREFIX=n +MIX_DENOM=unym +MIX_DENOM_DISPLAY=nym +STAKE_DENOM=unyx +STAKE_DENOM_DISPLAY=nyx +DENOMS_EXPONENT=6 +MIXNET_CONTRACT_ADDRESS=n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep +VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav +BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw +MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs +REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq +STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" +NYMD_VALIDATOR="https://qa-validator.nymtech.net" +API_VALIDATOR="https://qa-validator-api.nymtech.net/api" diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index 7e0400693f..f65a46db03 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] chrono = { version = "0.4.19", features = ["serde"] } +clap = { version = "3.2.8", features = ["cargo", "derive"] } humantime-serde = "1.0" isocountry = "0.3.2" itertools = "0.10.3" @@ -25,4 +26,5 @@ tokio = {version = "1.19.1", features = ["full"] } mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" } network-defaults = { path = "../common/network-defaults" } +task = { path = "../common/task" } validator-client = { path = "../common/client-libs/validator-client", features=["nymd-client"] } diff --git a/explorer-api/src/client.rs b/explorer-api/src/client.rs index 1f15891c16..129a394d31 100644 --- a/explorer-api/src/client.rs +++ b/explorer-api/src/client.rs @@ -1,6 +1,9 @@ -use network_defaults::{default_api_endpoints, default_nymd_endpoints, DEFAULT_NETWORK}; +use network_defaults::{ + var_names::{API_VALIDATOR, NYMD_VALIDATOR}, + NymNetworkDetails, +}; use reqwest::Url; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; use validator_client::nymd::QueryNymdClient; // since this is just a query client, we don't need any locking mechanism to keep sequence numbers in check @@ -23,16 +26,17 @@ impl ThreadsafeValidatorClient { } pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient { - let nymd_url = default_nymd_endpoints()[0].clone(); - let api_url = default_api_endpoints()[0].clone(); + let nymd_url = Url::from_str(&std::env::var(NYMD_VALIDATOR).expect("nymd validator not set")) + .expect("nymd validator not in url format"); + let api_url = Url::from_str(&std::env::var(API_VALIDATOR).expect("nymd validator not set")) + .expect("nymd validator not in url format"); - let client_config = - validator_client::Config::try_from_nym_network_details(&DEFAULT_NETWORK.details()) - .expect("failed to construct valid validator client config with the provided network") - .with_urls(nymd_url, api_url); + let details = NymNetworkDetails::new_from_env(); + let client_config = validator_client::Config::try_from_nym_network_details(&details) + .expect("failed to construct valid validator client config with the provided network") + .with_urls(nymd_url, api_url); ThreadsafeValidatorClient(Arc::new( - validator_client::Client::new_query(client_config, DEFAULT_NETWORK) - .expect("Failed to connect to nymd!"), + validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"), )) } diff --git a/explorer-api/src/commands/mod.rs b/explorer-api/src/commands/mod.rs new file mode 100644 index 0000000000..3c63e989cf --- /dev/null +++ b/explorer-api/src/commands/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; + +#[derive(Parser)] +#[clap(author = "Nymtech", version, about)] +pub(crate) struct Cli { + /// Path pointing to an env file that configures the explorer api. + #[clap(long)] + pub(crate) config_env_file: Option, +} diff --git a/explorer-api/src/country_statistics/distribution.rs b/explorer-api/src/country_statistics/distribution.rs index ebbc1eca9b..f48e87eebb 100644 --- a/explorer-api/src/country_statistics/distribution.rs +++ b/explorer-api/src/country_statistics/distribution.rs @@ -1,4 +1,5 @@ use log::info; +use task::ShutdownListener; use crate::country_statistics::country_nodes_distribution::CountryNodesDistribution; use crate::COUNTRY_DATA_REFRESH_INTERVAL; @@ -7,11 +8,12 @@ use crate::state::ExplorerApiStateContext; pub(crate) struct CountryStatisticsDistributionTask { state: ExplorerApiStateContext, + shutdown: ShutdownListener, } impl CountryStatisticsDistributionTask { - pub(crate) fn new(state: ExplorerApiStateContext) -> Self { - CountryStatisticsDistributionTask { state } + pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self { + CountryStatisticsDistributionTask { state, shutdown } } pub(crate) fn start(mut self) { @@ -20,10 +22,15 @@ impl CountryStatisticsDistributionTask { let mut interval_timer = tokio::time::interval(std::time::Duration::from_secs( COUNTRY_DATA_REFRESH_INTERVAL, )); - loop { - // wait for the next interval tick - interval_timer.tick().await; - self.calculate_nodes_per_country().await; + while !self.shutdown.is_shutdown() { + tokio::select! { + _ = interval_timer.tick() => { + self.calculate_nodes_per_country().await; + } + _ = self.shutdown.recv() => { + trace!("Listener: Received shutdown"); + } + } } }); } diff --git a/explorer-api/src/country_statistics/geolocate.rs b/explorer-api/src/country_statistics/geolocate.rs index 41b7eb6f3a..fd84a8c380 100644 --- a/explorer-api/src/country_statistics/geolocate.rs +++ b/explorer-api/src/country_statistics/geolocate.rs @@ -5,15 +5,17 @@ use crate::mix_nodes::location::{GeoLocation, Location}; use crate::state::ExplorerApiStateContext; use log::{info, warn}; use reqwest::Error as ReqwestError; +use task::ShutdownListener; use thiserror::Error; pub(crate) struct GeoLocateTask { state: ExplorerApiStateContext, + shutdown: ShutdownListener, } impl GeoLocateTask { - pub(crate) fn new(state: ExplorerApiStateContext) -> Self { - GeoLocateTask { state } + pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self { + GeoLocateTask { state, shutdown } } pub(crate) fn start(mut self) { @@ -27,10 +29,15 @@ impl GeoLocateTask { info!("Spawning mix node locator task runner..."); tokio::spawn(async move { let mut interval_timer = tokio::time::interval(std::time::Duration::from_millis(50)); - loop { - // wait for the next interval tick - interval_timer.tick().await; - self.locate_mix_nodes().await; + while !self.shutdown.is_shutdown() { + tokio::select! { + _ = interval_timer.tick() => { + self.locate_mix_nodes().await; + } + _ = self.shutdown.recv() => { + trace!("Listener: Received shutdown"); + } + } } }); } diff --git a/explorer-api/src/main.rs b/explorer-api/src/main.rs index ed96dd4fff..7e6779794b 100644 --- a/explorer-api/src/main.rs +++ b/explorer-api/src/main.rs @@ -3,10 +3,14 @@ extern crate rocket; #[macro_use] extern crate rocket_okapi; +use clap::Parser; use log::info; +use network_defaults::setup_env; +use task::ShutdownNotifier; pub(crate) mod cache; mod client; +pub(crate) mod commands; mod country_statistics; mod gateways; mod http; @@ -24,6 +28,8 @@ const COUNTRY_DATA_REFRESH_INTERVAL: u64 = 60 * 15; // every 15 minutes #[tokio::main] async fn main() { setup_logging(); + let args = commands::Cli::parse(); + setup_env(args.config_env_file); let mut explorer_api = ExplorerApi::new(); explorer_api.run().await; } @@ -45,29 +51,63 @@ impl ExplorerApi { let validator_api_url = self.state.inner.validator_client.api_endpoint(); info!("Using validator API - {}", validator_api_url); + let shutdown = ShutdownNotifier::default(); + // spawn concurrent tasks - crate::tasks::ExplorerApiTasks::new(self.state.clone()).start(); + crate::tasks::ExplorerApiTasks::new(self.state.clone(), shutdown.subscribe()).start(); country_statistics::distribution::CountryStatisticsDistributionTask::new( self.state.clone(), + shutdown.subscribe(), ) .start(); - country_statistics::geolocate::GeoLocateTask::new(self.state.clone()).start(); + country_statistics::geolocate::GeoLocateTask::new(self.state.clone(), shutdown.subscribe()) + .start(); + + // Rocket handles shutdown on it's own, but its shutdown handling should be incorporated + // with that of the rest of the tasks. + // Currently it's runtime is forcefully terminated once the explorer-api exits. http::start(self.state.clone()); // wait for user to press ctrl+C - self.wait_for_interrupt().await + self.wait_for_interrupt(shutdown).await } - async fn wait_for_interrupt(&self) { - if let Err(e) = tokio::signal::ctrl_c().await { - error!( - "There was an error while capturing SIGINT - {:?}. We will terminate regardless", - e - ); + async fn wait_for_interrupt(&self, mut shutdown: ShutdownNotifier) { + wait_for_signal().await; + + log::info!("Sending shutdown"); + shutdown.signal_shutdown().ok(); + log::info!("Waiting for tasks to finish... (Press ctrl-c to force)"); + shutdown.wait_for_shutdown().await; + log::info!("Stopping explorer API"); + } +} + +#[cfg(unix)] +async fn wait_for_signal() { + use tokio::signal::unix::{signal, SignalKind}; + let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel"); + let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel"); + + tokio::select! { + _ = tokio::signal::ctrl_c() => { + log::info!("Received SIGINT"); + }, + _ = sigterm.recv() => { + log::info!("Received SIGTERM"); } - info!( - "Received SIGINT - the mixnode will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)." - ); + _ = sigquit.recv() => { + log::info!("Received SIGQUIT"); + } + } +} + +#[cfg(not(unix))] +async fn wait_for_signal() { + tokio::select! { + _ = tokio::signal::ctrl_c() => { + log::info!("Received SIGINT"); + }, } } diff --git a/explorer-api/src/tasks.rs b/explorer-api/src/tasks.rs index 83ed0d7d6a..bac19b7ee9 100644 --- a/explorer-api/src/tasks.rs +++ b/explorer-api/src/tasks.rs @@ -4,6 +4,7 @@ use std::future::Future; use mixnet_contract_common::GatewayBond; +use task::ShutdownListener; use validator_client::models::MixNodeBondAnnotated; use validator_client::nymd::error::NymdError; use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse}; @@ -14,11 +15,12 @@ use crate::state::ExplorerApiStateContext; pub(crate) struct ExplorerApiTasks { state: ExplorerApiStateContext, + shutdown: ShutdownListener, } impl ExplorerApiTasks { - pub(crate) fn new(state: ExplorerApiStateContext) -> Self { - ExplorerApiTasks { state } + pub(crate) fn new(state: ExplorerApiStateContext, shutdown: ShutdownListener) -> Self { + ExplorerApiTasks { state, shutdown } } // a helper to remove duplicate code when grabbing active/rewarded/all mixnodes @@ -128,24 +130,28 @@ impl ExplorerApiTasks { } } - pub(crate) fn start(self) { + pub(crate) fn start(mut self) { info!("Spawning mix nodes task runner..."); tokio::spawn(async move { let mut interval_timer = tokio::time::interval(CACHE_REFRESH_RATE); - loop { - // wait for the next interval tick - interval_timer.tick().await; + while !self.shutdown.is_shutdown() { + tokio::select! { + _ = interval_timer.tick() => { + info!("Updating validator cache..."); + self.update_validators_cache().await; + info!("Done"); - info!("Updating validator cache..."); - self.update_validators_cache().await; - info!("Done"); + info!("Updating gateway cache..."); + self.update_gateways_cache().await; + info!("Done"); - info!("Updating gateway cache..."); - self.update_gateways_cache().await; - info!("Done"); - - info!("Updating mix node cache..."); - self.update_mixnode_cache().await; + info!("Updating mix node cache..."); + self.update_mixnode_cache().await; + } + _ = self.shutdown.recv() => { + trace!("Listener: Received shutdown"); + } + } } }); } diff --git a/explorer/.env.prod b/explorer/.env.prod index 17c51ea14e..c6d0a35f6d 100644 --- a/explorer/.env.prod +++ b/explorer/.env.prod @@ -1,5 +1,5 @@ -EXPLORER_API_URL=https://sandbox-explorer.nymtech.net/api/v1 -VALIDATOR_API_URL=https://sandbox-validator.nymtech.net -BIG_DIPPER_URL=https://sandbox-blocks.nymtech.net -CURRENCY_DENOM=unymt -CURRENCY_STAKING_DENOM=unyxt +EXPLORER_API_URL=https://explorer.nymtech.net/api/v1 +VALIDATOR_API_URL=https://validator.nymtech.net +BIG_DIPPER_URL=https://blocks.nymtech.net +CURRENCY_DENOM=unym +CURRENCY_STAKING_DENOM=unyx diff --git a/explorer/src/components/Filters/Filters.tsx b/explorer/src/components/Filters/Filters.tsx index c62b09a53f..1a04688428 100644 --- a/explorer/src/components/Filters/Filters.tsx +++ b/explorer/src/components/Filters/Filters.tsx @@ -6,7 +6,6 @@ import { DialogContent, DialogActions, DialogTitle, - IconButton, Slider, Typography, Box, @@ -25,6 +24,7 @@ import { useIsMobile } from '../../hooks/useIsMobile'; const FilterItem = ({ label, id, + tooltipInfo, value, marks, scale, @@ -36,6 +36,7 @@ const FilterItem = ({ }) => ( {label} + {tooltipInfo} onChange(id, newValue as number[])} @@ -50,7 +51,7 @@ const FilterItem = ({ ); export const Filters = () => { - const { filterMixnodes, fetchMixnodes } = useMainContext(); + const { filterMixnodes, fetchMixnodes, mixnodes } = useMainContext(); const { status } = useParams<{ status: MixnodeStatusWithAll | undefined }>(); const isMobile = useIsMobile(); @@ -129,17 +130,23 @@ export const Filters = () => { variant={isMobile ? 'standard' : 'outlined'} action={ } - sx={{ width: 300 }} > - Filters applied + {mixnodes?.data?.length} mixnodes matched your criteria - - - + Mixnode filters diff --git a/explorer/src/components/Filters/filterSchema.ts b/explorer/src/components/Filters/filterSchema.ts index 1d6b742f90..a994b698a4 100644 --- a/explorer/src/components/Filters/filterSchema.ts +++ b/explorer/src/components/Filters/filterSchema.ts @@ -18,6 +18,8 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({ { label: '90', value: 90 }, { label: '100', value: 100 }, ], + tooltipInfo: + 'As a delegator you want to chose nodes with lower profit margin, meaning more payout for their delegators', }, stakeSaturation: { label: 'Stake saturation (%)', @@ -43,65 +45,29 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({ }, ], max: upperSaturationValue, + tooltipInfo: "Select nodes with <100% saturation. Any additional stake above 100% saturation won't get rewards", }, - stake: { - label: 'Stake (NYM)', - id: EnumFilterKey.stake, - value: [20, 90], - min: 20, - max: 90, + routingScore: { + label: 'Routing score (%)', + id: EnumFilterKey.routingScore, + value: [0, 100], marks: [ - { - value: 0, - label: '1', - }, - { - value: 10, - label: '10', - }, - { - value: 20, - label: '100', - }, - { - value: 30, - label: '1k', - }, - { - value: 40, - label: '10k', - }, - { - value: 50, - label: '100k', - }, - { - value: 60, - label: '1M', - }, - { - value: 70, - label: '10M', - }, - { - value: 80, - label: '100M', - }, - { - value: 90, - label: '1B', - }, + { label: '0', value: 0 }, + { label: '10', value: 10 }, + { label: '20', value: 20 }, + { label: '30', value: 30 }, + { label: '40', value: 40 }, + { label: '50', value: 50 }, + { label: '60', value: 60 }, + { label: '70', value: 70 }, + { label: '80', value: 80 }, + { label: '90', value: 90 }, + { label: '100', value: 100 }, ], + tooltipInfo: 'The higher the routing score the better the performance of the node and so its rewards', }, }); -const formatStakeValuesToMinorDenom = ([value_1, value_2]: number[]) => { - const lowerValue = 10 ** (value_1 / 10) * 1_000_000; - const upperValue = 10 ** (value_2 / 10) * 1_000_000; - - return [lowerValue, upperValue]; -}; - const formatStakeSaturationValues = ([value_1, value_2]: number[]) => { const lowerValue = value_1 / 100; const upperValue = value_2 / 100; @@ -110,7 +76,7 @@ const formatStakeSaturationValues = ([value_1, value_2]: number[]) => { }; export const formatOnSave = (filters: TFilters) => ({ - stake: formatStakeValuesToMinorDenom(filters.stake.value), + routingScore: filters.routingScore.value, profitMargin: filters.profitMargin.value, stakeSaturation: formatStakeSaturationValues(filters.stakeSaturation.value), }); diff --git a/explorer/src/context/main.tsx b/explorer/src/context/main.tsx index 7fca785209..9255eb20e3 100644 --- a/explorer/src/context/main.tsx +++ b/explorer/src/context/main.tsx @@ -102,8 +102,8 @@ export const MainContextProvider: React.FC = ({ children }) => { m.mix_node.profit_margin_percent <= filters.profitMargin[1] && m.stake_saturation >= filters.stakeSaturation[0] && m.stake_saturation <= filters.stakeSaturation[1] && - +m.pledge_amount.amount + +m.total_delegation.amount >= filters.stake[0] && - +m.pledge_amount.amount + +m.total_delegation.amount <= filters.stake[1], + m.avg_uptime >= filters.routingScore[0] && + m.avg_uptime <= filters.routingScore[1], ); setMixnodes({ data: filtered, isLoading: false }); }; diff --git a/explorer/src/typeDefs/filters.ts b/explorer/src/typeDefs/filters.ts index 8ec2a918c9..2152f3a35f 100644 --- a/explorer/src/typeDefs/filters.ts +++ b/explorer/src/typeDefs/filters.ts @@ -4,7 +4,7 @@ import { Mark } from '@mui/base'; export enum EnumFilterKey { profitMargin = 'profitMargin', stakeSaturation = 'stakeSaturation', - stake = 'stake', + routingScore = 'routingScore', } export type TFilterItem = { @@ -15,6 +15,7 @@ export type TFilterItem = { min?: number; max?: number; scale?: (value: number) => number; + tooltipInfo?: string; }; export type TFilters = { [key in EnumFilterKey]: TFilterItem }; diff --git a/gateway/src/commands/init.rs b/gateway/src/commands/init.rs index 680e2770e7..eee994fbf2 100644 --- a/gateway/src/commands/init.rs +++ b/gateway/src/commands/init.rs @@ -156,6 +156,8 @@ pub async fn execute(args: &Init) { #[cfg(test)] mod tests { + use network_defaults::var_names::BECH32_PREFIX; + use crate::node::{storage::InMemStorage, Gateway}; use super::*; @@ -180,6 +182,7 @@ mod tests { #[cfg(all(feature = "eth", not(feature = "coconut")))] eth_endpoint: "".to_string(), }; + std::env::set_var(BECH32_PREFIX, "n"); let config = Config::new(&args.id); let config = override_config(config, OverrideConfig::from(args.clone())); diff --git a/gateway/src/commands/mod.rs b/gateway/src/commands/mod.rs index 2e3272674d..e0bedb8976 100644 --- a/gateway/src/commands/mod.rs +++ b/gateway/src/commands/mod.rs @@ -7,6 +7,9 @@ use crate::{config::Config, Cli}; use clap::Subcommand; use colored::Colorize; use crypto::bech32_address_validation; +use network_defaults::var_names::{ + API_VALIDATOR, BECH32_PREFIX, CONFIGURED, NYMD_VALIDATOR, STATISTICS_SERVICE_DOMAIN_ADDRESS, +}; use url::Url; pub(crate) mod init; @@ -109,14 +112,28 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi .parse() .expect("the provided statistics service url is invalid!"), ); + } else if std::env::var(CONFIGURED).is_ok() { + let raw_url = std::env::var(STATISTICS_SERVICE_DOMAIN_ADDRESS) + .expect("statistics service url not set"); + config = config.with_custom_statistics_service_url( + raw_url + .parse() + .expect("the provided statistics service url is invalid"), + ) } if let Some(raw_validators) = args.validator_apis { config = config.with_custom_validator_apis(parse_validators(&raw_validators)); + } else if std::env::var(CONFIGURED).is_ok() { + let raw_validators = std::env::var(API_VALIDATOR).expect("api validator not set"); + config = config.with_custom_validator_apis(parse_validators(&raw_validators)) } if let Some(raw_validators) = args.validators { config = config.with_custom_validator_nymd(parse_validators(&raw_validators)); + } else if std::env::var(CONFIGURED).is_ok() { + let raw_validators = std::env::var(NYMD_VALIDATOR).expect("nymd validator not set"); + config = config.with_custom_validator_nymd(parse_validators(&raw_validators)) } if let Some(wallet_address) = args.wallet_address { @@ -166,6 +183,7 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi /// Ensures that a given bech32 address is valid, or exits pub(crate) fn validate_bech32_address_or_exit(address: &str) { + let prefix = std::env::var(BECH32_PREFIX).expect("bech32 prefix not set"); if let Err(bech32_address_validation::Bech32Error::DecodeFailed(err)) = bech32_address_validation::try_bech32_decode(address) { @@ -176,7 +194,7 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) { } if let Err(bech32_address_validation::Bech32Error::WrongPrefix(err)) = - bech32_address_validation::validate_bech32_prefix(address) + bech32_address_validation::validate_bech32_prefix(&prefix, address) { let error_message = format!("Error: wallet address type is wrong, {}", err).red(); println!("{}", error_message); diff --git a/gateway/src/commands/upgrade.rs b/gateway/src/commands/upgrade.rs index d7f3d6e947..cc62c5219e 100644 --- a/gateway/src/commands/upgrade.rs +++ b/gateway/src/commands/upgrade.rs @@ -3,7 +3,6 @@ use crate::config::{Config, MISSING_VALUE}; use clap::Args; -use config::defaults::default_api_endpoints; use config::NymConfig; use std::fmt::Display; use std::process; @@ -104,14 +103,7 @@ fn minor_0_12_upgrade( print_start_upgrade(&config_version, &to_version); - println!( - "Setting validator API endpoints to {:?}", - default_api_endpoints() - ); - - let upgraded_config = config - .with_custom_version(to_version.to_string().as_ref()) - .with_custom_validator_apis(default_api_endpoints()); + let upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); upgraded_config.save_to_file(None).unwrap_or_else(|err| { eprintln!("failed to overwrite config file! - {:?}", err); diff --git a/gateway/src/config/mod.rs b/gateway/src/config/mod.rs index 6922cbe75d..d3acfacf52 100644 --- a/gateway/src/config/mod.rs +++ b/gateway/src/config/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::config::template::config_template; -use config::defaults::*; +use config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; use config::NymConfig; use log::error; use serde::{Deserialize, Serialize}; @@ -422,9 +422,9 @@ impl Default for Gateway { #[cfg(not(feature = "coconut"))] eth_endpoint: "".to_string(), enabled_statistics: false, - statistics_service_url: default_statistics_service_url(), - validator_api_urls: default_api_endpoints(), - validator_nymd_urls: default_nymd_endpoints(), + statistics_service_url: Url::from_str("http://127.0.0.1").unwrap(), + validator_api_urls: vec![], + validator_nymd_urls: vec![], cosmos_mnemonic: bip39::Mnemonic::from_str("exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day").unwrap(), nym_root_directory: Config::default_root_directory(), persistent_storage: Default::default(), diff --git a/gateway/src/main.rs b/gateway/src/main.rs index a7ea4ff1da..0651b37237 100644 --- a/gateway/src/main.rs +++ b/gateway/src/main.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::{crate_version, Parser}; -use network_defaults::DEFAULT_NETWORK; +use network_defaults::setup_env; use once_cell::sync::OnceCell; mod commands; @@ -19,13 +19,16 @@ fn long_version_static() -> &'static str { #[derive(Parser)] #[clap(author = "Nymtech", version, about, long_version = long_version_static())] struct Cli { + /// Path pointing to an env file that configures the gateway. + #[clap(long)] + pub(crate) config_env_file: Option, + #[clap(subcommand)] command: commands::Commands, } #[tokio::main] async fn main() { - dotenv::dotenv().ok(); setup_logging(); println!("{}", banner()); LONG_VERSION @@ -33,6 +36,7 @@ async fn main() { .expect("Failed to set long about text"); let args = Cli::parse(); + setup_env(args.config_env_file.clone()); commands::execute(args).await; } @@ -64,7 +68,6 @@ fn long_version() -> String { {:<20}{} {:<20}{} {:<20}{} -{:<20}{} "#, "Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP"), @@ -81,9 +84,7 @@ fn long_version() -> String { "rustc Channel:", env!("VERGEN_RUSTC_CHANNEL"), "cargo Profile:", - env!("VERGEN_CARGO_PROFILE"), - "Network:", - DEFAULT_NETWORK + env!("VERGEN_CARGO_PROFILE") ) } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index 04f6fa4ac5..8f0ffcd098 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -6,7 +6,6 @@ use std::time::{Duration, SystemTime}; use log::*; use coconut_interface::{Credential, VerificationKey}; -use network_defaults::MIX_DENOM; use validator_client::{ nymd::{ cosmwasm_client::logs::find_attribute, @@ -24,6 +23,7 @@ const MAX_FEEGRANT_UNYM: u128 = 10000; pub(crate) struct CoconutVerifier { api_clients: Vec, nymd_client: NymdClient, + mix_denom_base: String, aggregated_verification_key: VerificationKey, } @@ -31,6 +31,7 @@ impl CoconutVerifier { pub fn new( api_clients: Vec, nymd_client: NymdClient, + mix_denom_base: String, aggregated_verification_key: VerificationKey, ) -> Result { if api_clients.is_empty() { @@ -42,6 +43,7 @@ impl CoconutVerifier { Ok(CoconutVerifier { api_clients, nymd_client, + mix_denom_base, aggregated_verification_key, }) } @@ -58,7 +60,10 @@ impl CoconutVerifier { let res = self .nymd_client .spend_credential( - Coin::new(credential.voucher_value().into(), MIX_DENOM.base), + Coin::new( + credential.voucher_value().into(), + self.mix_denom_base.clone(), + ), credential.blinded_serial_number(), self.nymd_client.address().to_string(), None, @@ -91,7 +96,7 @@ impl CoconutVerifier { self.nymd_client .grant_allowance( &api_cosmos_addr, - vec![Coin::new(MAX_FEEGRANT_UNYM, MIX_DENOM.base)], + vec![Coin::new(MAX_FEEGRANT_UNYM, self.mix_denom_base.clone())], SystemTime::now().checked_add(Duration::from_secs(ONE_HOUR_SEC)), // It would be nice to be able to filter deeper, but for now only the msg type filter is avaialable vec![String::from("/cosmwasm.wasm.v1.MsgExecuteContract")], diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index b2354e7a3b..379aae5fe2 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -9,10 +9,10 @@ use crate::node::client_handling::websocket; use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler; use crate::node::statistics::collector::GatewayStatisticsCollector; use crate::node::storage::Storage; -use config::defaults::DEFAULT_NETWORK; use crypto::asymmetric::{encryption, identity}; use log::*; use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder}; +use network_defaults::NymNetworkDetails; use rand::seq::SliceRandom; use rand::thread_rng; use statistics_common::collector::StatisticsSender; @@ -249,7 +249,8 @@ where .choose(&mut thread_rng()) .expect("The list of validators is empty"); - let client_config = nymd::Config::try_from_nym_network_details(&DEFAULT_NETWORK.details()) + let network_details = NymNetworkDetails::new_from_env(); + let client_config = nymd::Config::try_from_nym_network_details(&network_details) .expect("failed to construct valid validator client config with the provided network"); validator_client::nymd::NymdClient::connect_with_mnemonic( @@ -316,6 +317,7 @@ where let coconut_verifier = CoconutVerifier::new( self.all_api_clients(), nymd_client, + std::env::var(network_defaults::var_names::MIX_DENOM).expect("mix denom base not set"), validators_verification_key, ) .expect("Could not create coconut verifier"); @@ -334,6 +336,7 @@ where if self.config.get_enabled_statistics() { let statistics_service_url = self.config.get_statistics_service_url(); let stats_collector = GatewayStatisticsCollector::new( + self.identity_keypair.public_key().to_base58_string(), active_clients_store.clone(), statistics_service_url, ); diff --git a/gateway/src/node/statistics/collector.rs b/gateway/src/node/statistics/collector.rs index 18f290d006..62014d0d16 100644 --- a/gateway/src/node/statistics/collector.rs +++ b/gateway/src/node/statistics/collector.rs @@ -14,13 +14,19 @@ use statistics_common::{ use crate::node::client_handling::active_clients::ActiveClientsStore; pub(crate) struct GatewayStatisticsCollector { + gateway_id: String, active_clients_store: ActiveClientsStore, statistics_service_url: Url, } impl GatewayStatisticsCollector { - pub fn new(active_clients_store: ActiveClientsStore, statistics_service_url: Url) -> Self { + pub fn new( + gateway_id: String, + active_clients_store: ActiveClientsStore, + statistics_service_url: Url, + ) -> Self { GatewayStatisticsCollector { + gateway_id, active_clients_store, statistics_service_url, } @@ -35,7 +41,10 @@ impl StatisticsCollector for GatewayStatisticsCollector { timestamp: DateTime, ) -> StatsMessage { let inbox_count = self.active_clients_store.size() as u32; - let stats_data = vec![StatsData::Gateway(StatsGatewayData { inbox_count })]; + let stats_data = vec![StatsData::Gateway(StatsGatewayData::new( + self.gateway_id.clone(), + inbox_count, + ))]; StatsMessage { stats_data, interval_seconds: interval.as_secs() as u32, diff --git a/mixnode/src/commands/mod.rs b/mixnode/src/commands/mod.rs index 1d924bcf63..07d8302d73 100644 --- a/mixnode/src/commands/mod.rs +++ b/mixnode/src/commands/mod.rs @@ -6,6 +6,7 @@ use std::process; use crate::{config::Config, Cli}; use clap::Subcommand; use colored::Colorize; +use config::defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED}; use crypto::bech32_address_validation; use url::Url; @@ -92,6 +93,9 @@ fn override_config(mut config: Config, args: OverrideConfig) -> Config { if let Some(ref raw_validators) = args.validators { config = config.with_custom_validator_apis(parse_validators(raw_validators)); + } else if std::env::var(CONFIGURED).is_ok() { + let raw_validators = std::env::var(API_VALIDATOR).expect("api validator not set"); + config = config.with_custom_validator_apis(parse_validators(&raw_validators)) } if let Some(ref announce_host) = args.announce_host { @@ -112,6 +116,7 @@ fn override_config(mut config: Config, args: OverrideConfig) -> Config { /// Ensures that a given bech32 address is valid, or exits pub(crate) fn validate_bech32_address_or_exit(address: &str) { + let prefix = std::env::var(BECH32_PREFIX).expect("bech32 prefix not set"); if let Err(bech32_address_validation::Bech32Error::DecodeFailed(err)) = bech32_address_validation::try_bech32_decode(address) { @@ -122,7 +127,7 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) { } if let Err(bech32_address_validation::Bech32Error::WrongPrefix(err)) = - bech32_address_validation::validate_bech32_prefix(address) + bech32_address_validation::validate_bech32_prefix(&prefix, address) { let error_message = format!("Error: wallet address type is wrong, {}", err).red(); println!("{}", error_message); diff --git a/mixnode/src/commands/upgrade.rs b/mixnode/src/commands/upgrade.rs index 0b0241e03c..85352e77c3 100644 --- a/mixnode/src/commands/upgrade.rs +++ b/mixnode/src/commands/upgrade.rs @@ -3,7 +3,6 @@ use crate::config::{missing_string_value, Config}; use clap::Args; -use config::defaults::default_api_endpoints; use config::NymConfig; use std::fmt::Display; use std::process; @@ -104,14 +103,7 @@ fn minor_0_12_upgrade( print_start_upgrade(&config_version, &to_version); - println!( - "Setting validator API endpoints to {:?}", - default_api_endpoints() - ); - - let upgraded_config = config - .with_custom_version(to_version.to_string().as_ref()) - .with_custom_validator_apis(default_api_endpoints()); + let upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); upgraded_config.save_to_file(None).unwrap_or_else(|err| { eprintln!("failed to overwrite config file! - {:?}", err); diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 7d3b520da9..14279ccdc2 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::config::template::config_template; -use config::defaults::*; +use config::defaults::{ + DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, +}; use config::NymConfig; use serde::{Deserialize, Deserializer, Serialize}; use std::net::{IpAddr, SocketAddr}; @@ -401,7 +403,7 @@ impl Default for MixNode { public_identity_key_file: Default::default(), private_sphinx_key_file: Default::default(), public_sphinx_key_file: Default::default(), - validator_api_urls: default_api_endpoints(), + validator_api_urls: vec![], nym_root_directory: Config::default_root_directory(), wallet_address: "nymXXXXXXXX".to_string(), } diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index 44637eace1..2e30e00976 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -4,7 +4,7 @@ extern crate rocket; // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use ::config::defaults::DEFAULT_NETWORK; +use ::config::defaults::setup_env; use clap::{crate_version, Parser}; use lazy_static::lazy_static; @@ -24,17 +24,21 @@ fn long_version_static() -> &'static str { #[derive(Parser)] #[clap(author = "Nymtech", version, about, long_version = long_version_static())] struct Cli { + /// Path pointing to an env file that configures the mixnode. + #[clap(long)] + pub(crate) config_env_file: Option, + #[clap(subcommand)] command: commands::Commands, } #[tokio::main] async fn main() { - dotenv::dotenv().ok(); setup_logging(); println!("{}", banner()); let args = Cli::parse(); + setup_env(args.config_env_file.clone()); commands::execute(args).await; } @@ -66,7 +70,6 @@ fn long_version() -> String { {:<20}{} {:<20}{} {:<20}{} -{:<20}{} "#, "Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP"), @@ -83,9 +86,7 @@ fn long_version() -> String { "rustc Channel:", env!("VERGEN_RUSTC_CHANNEL"), "cargo Profile:", - env!("VERGEN_CARGO_PROFILE"), - "Network:", - DEFAULT_NETWORK + env!("VERGEN_CARGO_PROFILE") ) } diff --git a/nym-chitchat/Cargo.lock b/nym-chitchat/Cargo.lock new file mode 100644 index 0000000000..408a2c30f8 --- /dev/null +++ b/nym-chitchat/Cargo.lock @@ -0,0 +1,1983 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" + +[[package]] +name = "assert_cmd" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" +dependencies = [ + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chitchat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e16bc6492a6744d6e322e22562c3ab89404ba326aedb65a570799d2e042687" +dependencies = [ + "anyhow", + "async-trait", + "bytes 1.2.0", + "rand", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "chitchat-test" +version = "0.3.0" +dependencies = [ + "anyhow", + "assert_cmd", + "chitchat", + "cool-id-generator", + "env_logger", + "once_cell", + "poem", + "poem-openapi", + "predicates", + "reqwest", + "serde", + "serde_json", + "structopt", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +dependencies = [ + "aes-gcm", + "base64 0.13.0", + "hkdf", + "hmac", + "percent-encoding", + "rand", + "sha2", + "subtle", + "time 0.3.11", + "version_check", +] + +[[package]] +name = "cool-id-generator" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "946cf88fb171128bc025588b9b408982814ace1a1fb31ffb9bfa63d955f0b43d" +dependencies = [ + "rand", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes 1.2.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64 0.13.0", + "bitflags", + "bytes 1.2.0", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes 1.2.0", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.2.0", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes 1.2.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "multer" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a30ba6d97eb198c5e8a35d67d5779d6680cca35652a60ee90fc23dc431d4fde8" +dependencies = [ + "bytes 1.2.0", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "tokio", + "version_check", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poem" +version = "1.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e479ed477ea11939bea754109eaa024e55bf1639e19c1753fb250026351da8" +dependencies = [ + "async-trait", + "bytes 1.2.0", + "chrono", + "cookie", + "futures-util", + "headers", + "http", + "hyper", + "mime", + "multer", + "parking_lot", + "percent-encoding", + "pin-project-lite", + "poem-derive", + "regex", + "rfc7239", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "tempfile", + "thiserror", + "time 0.3.11", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "typed-headers", +] + +[[package]] +name = "poem-derive" +version = "1.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d454ac05b5299eb8e6261214fcb823f0c3c1b02f47167f8809c9dc2db0ee033" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "poem-openapi" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27cd1c97d45a9552b0c80bab1d51f93a1050c3f7f91e9312239b3b87c336b0f" +dependencies = [ + "base64 0.13.0", + "bytes 1.2.0", + "derive_more", + "futures-util", + "mime", + "num-traits", + "poem", + "poem-openapi-derive", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "serde_yaml", + "thiserror", + "tokio", +] + +[[package]] +name = "poem-openapi-derive" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5105e426a8a747fefc4be06ab7705cb63bc3ab1b2db373867e1d83b1d95877a" +dependencies = [ + "Inflector", + "darling", + "http", + "indexmap", + "mime", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn", + "thiserror", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "predicates" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" + +[[package]] +name = "predicates-tree" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64 0.13.0", + "bytes 1.2.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc7239" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "087317b3cf7eb481f13bd9025d729324b7cd068d6f470e2d76d049e191f5ba47" +dependencies = [ + "uncased", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" + +[[package]] +name = "serde" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termtree" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e" +dependencies = [ + "autocfg", + "bytes 1.2.0", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes 1.2.0", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" +dependencies = [ + "ansi_term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typed-headers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3179a61e9eccceead5f1574fd173cf2e162ac42638b9bf214c6ad0baf7efa24a" +dependencies = [ + "base64 0.11.0", + "bytes 0.5.6", + "chrono", + "http", + "mime", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "uncased" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/nym-chitchat/Cargo.toml b/nym-chitchat/Cargo.toml new file mode 100644 index 0000000000..eae29e5738 --- /dev/null +++ b/nym-chitchat/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "chitchat-test" +version = "0.3.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chitchat = "0.4" +poem = "1" +poem-openapi = {version="2", features = ["swagger-ui"] } +structopt = "0.3" +tokio = { version = "1.14.0", features = ["net", "sync", "rt-multi-thread", "macros", "time"] } +serde = { version="1", features=["derive"] } +serde_json = "1" +anyhow = "1" +once_cell = "1" +tracing = "0.1" +tracing-subscriber = "0.3" +cool-id-generator = "1" +env_logger = "0.9" + +[dev-dependencies] +assert_cmd = "2" +predicates = "2" +reqwest = { version = "0.11", default-features=false, features = ["blocking", "json"] } + +[workspace] diff --git a/nym-chitchat/README.md b/nym-chitchat/README.md new file mode 100644 index 0000000000..2fd61cd169 --- /dev/null +++ b/nym-chitchat/README.md @@ -0,0 +1,15 @@ +# Chitchat test + +Runs simple chitchat servers, mostly copied over from https://github.com/quickwit-oss/chitchat + +## Example + +```bash +# Starts 5 servers and joins them into a cluster on localhost ports 10000-10004 +# All servers print cluster state on `/` ie 127.0.0.1:10000 +# `/docs` endpoint has an open api with a key value setter, set it on one node and observe how the state propagates to the other nodes +# NodeState is a regular BTreeMap +./run-servers.sh + +# run killall chitchat-test after you're done, as the servers will continue to run forever in the background +``` diff --git a/nym-chitchat/run-servers.sh b/nym-chitchat/run-servers.sh new file mode 100755 index 0000000000..9e08abac3c --- /dev/null +++ b/nym-chitchat/run-servers.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +killall chitchat-test + +cargo build --release + +for i in $(seq 10000 10004) +do + listen_addr="127.0.0.1:$i"; + echo ${listen_addr}; + cargo run --release -- --listen_addr ${listen_addr} --seed 127.0.0.1:10000 --node_id node_$i & +done; + +read +kill 0 diff --git a/nym-chitchat/src/main.rs b/nym-chitchat/src/main.rs new file mode 100644 index 0000000000..dc0899913d --- /dev/null +++ b/nym-chitchat/src/main.rs @@ -0,0 +1,123 @@ +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::Duration; + +use chitchat::transport::UdpTransport; +use chitchat::{spawn_chitchat, Chitchat, ChitchatConfig, FailureDetectorConfig, NodeId}; +use cool_id_generator::Size; +use poem::listener::TcpListener; +use poem::{Route, Server}; +use poem_openapi::param::Query; +use poem_openapi::payload::Json; +use poem_openapi::{OpenApi, OpenApiService}; +use structopt::StructOpt; +use tokio::sync::Mutex; + +use chitchat::ClusterStateSnapshot; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct ApiResponse { + pub cluster_id: String, + pub cluster_state: ClusterStateSnapshot, + pub live_nodes: Vec, + pub dead_nodes: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SetKeyValueResponse { + pub status: bool, +} + +struct Api { + chitchat: Arc>, +} + +#[OpenApi] +impl Api { + /// Chitchat state + #[oai(path = "/", method = "get")] + async fn index(&self) -> Json { + let chitchat_guard = self.chitchat.lock().await; + let response = ApiResponse { + cluster_id: chitchat_guard.cluster_id().to_string(), + cluster_state: chitchat_guard.state_snapshot(), + live_nodes: chitchat_guard.live_nodes().cloned().collect::>(), + dead_nodes: chitchat_guard.dead_nodes().cloned().collect::>(), + }; + Json(serde_json::to_value(&response).unwrap()) + } + + /// Set a key & value on this node (with no validation). + #[oai(path = "/set_kv/", method = "get")] + async fn set_kv(&self, key: Query, value: Query) -> Json { + let mut chitchat_guard = self.chitchat.lock().await; + + let cc_state = chitchat_guard.self_node_state(); + cc_state.set(key.as_str(), value.as_str()); + + Json(serde_json::to_value(&SetKeyValueResponse { status: true }).unwrap()) + } +} + +#[derive(Debug, StructOpt)] +#[structopt(name = "chitchat", about = "Chitchat test server.")] +struct Opt { + /// Defines the socket addr on which we should listen to. + #[structopt(long = "listen_addr", default_value = "127.0.0.1:10000")] + listen_addr: SocketAddr, + /// Defines the socket_address (host:port) other servers should use to + /// reach this server. + /// + /// It defaults to the listen address, but this is only valid + /// when all server are running on the same server. + #[structopt(long = "public_addr")] + public_addr: Option, + + /// Node id. Has to be unique. If None, the node_id will be generated from + /// the public_addr and a random suffix. + #[structopt(long = "node_id")] + node_id: Option, + + #[structopt(long = "seed")] + seeds: Vec, + + #[structopt(long = "interval_ms", default_value = "500")] + interval: u64, +} + +fn generate_server_id(public_addr: SocketAddr) -> String { + let cool_id = cool_id_generator::get_id(Size::Medium); + format!("server:{}-{}", public_addr, cool_id) +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt::init(); + let opt = Opt::from_args(); + println!("{:?}", opt); + let public_addr = opt.public_addr.unwrap_or(opt.listen_addr); + let node_id_str = opt + .node_id + .unwrap_or_else(|| generate_server_id(public_addr)); + let node_id = NodeId::new(node_id_str, public_addr); + let config = ChitchatConfig { + node_id, + cluster_id: "testing".to_string(), + gossip_interval: Duration::from_millis(opt.interval), + listen_addr: opt.listen_addr, + seed_nodes: opt.seeds.clone(), + failure_detector_config: FailureDetectorConfig::default(), + }; + let chitchat_handler = spawn_chitchat(config, Vec::new(), &UdpTransport).await?; + let chitchat = chitchat_handler.chitchat(); + let api = Api { chitchat }; + let api_service = OpenApiService::new(api, "Hello World", "1.0") + .server(&format!("http://{}/", opt.listen_addr)); + let docs = api_service.swagger_ui(); + let app = Route::new().nest("/", api_service).nest("/docs", docs); + Server::new(TcpListener::bind(&opt.listen_addr)) + .run(app) + .await?; + Ok(()) +} diff --git a/nym-connect/Cargo.lock b/nym-connect/Cargo.lock index 3c331dab3e..a94e7ab621 100644 --- a/nym-connect/Cargo.lock +++ b/nym-connect/Cargo.lock @@ -680,6 +680,7 @@ name = "coconut-bandwidth-contract-common" version = "0.1.0" dependencies = [ "cosmwasm-std", + "multisig-contract-common", "schemars", "serde", ] @@ -805,8 +806,7 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ca04d3795c18023c221a2143b29de9c70668ecb22d17783bc02ee780c6c404" +source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" dependencies = [ "prost", "prost-types", @@ -816,8 +816,7 @@ dependencies = [ [[package]] name = "cosmrs" version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6989fdb6267eccb52762530b79ce0b385f4eaeb8b786522a95512e9bebb268c2" +source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" dependencies = [ "bip32", "cosmos-sdk-proto", @@ -927,7 +926,6 @@ dependencies = [ "bls12_381", "coconut-interface", "crypto", - "network-defaults", "thiserror", "url", "validator-api-requests", @@ -3201,7 +3199,6 @@ dependencies = [ "cosmwasm-std", "fixed", "log", - "network-defaults", "schemars", "serde", "serde_repr", @@ -3273,6 +3270,7 @@ name = "network-defaults" version = "0.1.0" dependencies = [ "cfg-if", + "dotenv", "hex-literal", "once_cell", "serde", @@ -3406,7 +3404,7 @@ dependencies = [ [[package]] name = "nym-connect" -version = "1.0.0" +version = "1.0.1" dependencies = [ "bip39", "client-core", @@ -3446,7 +3444,6 @@ dependencies = [ "credential-storage", "crypto", "dirs", - "dotenv", "futures", "gateway-client", "gateway-requests", @@ -6485,7 +6482,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "vesting-contract" version = "1.0.1" dependencies = [ - "config", "cosmwasm-std", "cw-storage-plus", "mixnet-contract-common", diff --git a/nym-connect/README.md b/nym-connect/README.md index 5433a366d6..5a247235ab 100644 --- a/nym-connect/README.md +++ b/nym-connect/README.md @@ -32,7 +32,7 @@ yarn install ## Development mode -You can compile nym-connectin development mode by running the following command inside the `nym-connect` directory: +You can compile nym-connect in development mode by running the following command inside the `nym-connect` directory: ``` yarn dev diff --git a/nym-connect/src-tauri/Cargo.toml b/nym-connect/src-tauri/Cargo.toml index 23bfeed153..a529005681 100644 --- a/nym-connect/src-tauri/Cargo.toml +++ b/nym-connect/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-connect" -version = "1.0.0" +version = "1.0.1" description = "nym-connect" authors = ["Nym Technologies SA"] license = "" @@ -39,7 +39,7 @@ tokio = { version = "1.19.1", features = ["sync", "time"] } url = "2.2" client-core = { path = "../../clients/client-core" } -config = { path = "../../common/config" } +config-common = { path = "../../common/config", package = "config" } nym-socks5-client = { path = "../../clients/socks5" } topology = { path = "../../common/topology" } diff --git a/nym-connect/src-tauri/src/config/mod.rs b/nym-connect/src-tauri/src/config/mod.rs index 5f4ce22e21..f1fb18bc5e 100644 --- a/nym-connect/src-tauri/src/config/mod.rs +++ b/nym-connect/src-tauri/src/config/mod.rs @@ -6,8 +6,8 @@ use tap::TapFallible; use tokio::sync::RwLock; use client_core::config::Config as BaseConfig; -use config::NymConfig; -use nym_socks5::client::config::Config as Socks5Config; +use config_common::NymConfig; +use nym_socks5::{client::config::Config as Socks5Config, commands::parse_validators}; use crate::{ error::{BackendError, Result}, @@ -134,6 +134,11 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str config .get_base_mut() .with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY); + if let Ok(raw_validators) = std::env::var(config_common::defaults::var_names::API_VALIDATOR) { + config + .get_base_mut() + .set_custom_validator_apis(parse_validators(&raw_validators)); + } let gateway = setup_gateway( &id, diff --git a/nym-connect/src-tauri/src/main.rs b/nym-connect/src-tauri/src/main.rs index 795533287c..e1117c7516 100644 --- a/nym-connect/src-tauri/src/main.rs +++ b/nym-connect/src-tauri/src/main.rs @@ -5,6 +5,7 @@ use std::sync::Arc; +use config_common::defaults::setup_env; use tauri::Menu; use tokio::sync::RwLock; @@ -24,6 +25,7 @@ mod window; fn main() { setup_logging(); + setup_env(None); println!("Starting up..."); // As per breaking change description here diff --git a/nym-connect/src-tauri/src/state.rs b/nym-connect/src-tauri/src/state.rs index d9a14dc9d0..2fce640487 100644 --- a/nym-connect/src-tauri/src/state.rs +++ b/nym-connect/src-tauri/src/state.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use ::config::NymConfig; +use ::config_common::NymConfig; use futures::SinkExt; use tap::TapFallible; use tauri::Manager; diff --git a/nym-connect/src-tauri/src/tasks.rs b/nym-connect/src-tauri/src/tasks.rs index 8e44f1bb40..3729d4b4da 100644 --- a/nym-connect/src-tauri/src/tasks.rs +++ b/nym-connect/src-tauri/src/tasks.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use tap::TapFallible; use tokio::sync::RwLock; -use config::NymConfig; +use config_common::NymConfig; #[cfg(not(feature = "coconut"))] use nym_socks5::client::NymClient as Socks5NymClient; use nym_socks5::client::{config::Config as Socks5Config, Socks5ControlMessageSender}; diff --git a/nym-connect/src-tauri/tauri.conf.json b/nym-connect/src-tauri/tauri.conf.json index b2699a20b7..97ea3f1af8 100644 --- a/nym-connect/src-tauri/tauri.conf.json +++ b/nym-connect/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "package": { "productName": "nym-connect", - "version": "1.0.0" + "version": "1.0.1" }, "build": { "distDir": "../dist", diff --git a/nym-connect/src/layouts/DefaultLayout.tsx b/nym-connect/src/layouts/DefaultLayout.tsx index 42b6357442..9c329206d3 100644 --- a/nym-connect/src/layouts/DefaultLayout.tsx +++ b/nym-connect/src/layouts/DefaultLayout.tsx @@ -22,11 +22,14 @@ export const DefaultLayout: React.FC<{ }; return ( - - Connect, your privacy will be 100% protected thanks to the Nym Mixnet + + This is experimental software.
+ Do not rely on it for strong anonymity (yet).
- - You are not protected now + + Connect to the +
+ Nym mixnet for privacy.
Ok(Account::new( + Some((_, client)) => Ok(Account::new( client.nymd.address().to_string(), default_network.mix_denom(), )), - None => Err(BackendError::NetworkNotSupported( - config::defaults::DEFAULT_NETWORK, - )), + None => Err(BackendError::NetworkNotSupported), }; // Register all the clients @@ -161,8 +159,7 @@ async fn _connect_with_mnemonic( let mut w_state = state.write().await; w_state.logout(); } - for client in clients { - let network: WalletNetwork = client.network.clone().into(); + for (network, client) in clients { let mut w_state = state.write().await; w_state.add_client(network, client); w_state.register_default_denoms(network); @@ -206,7 +203,7 @@ fn create_clients( default_api_urls: &HashMap, config: &Config, mnemonic: &Mnemonic, -) -> Result>, BackendError> { +) -> Result)>, BackendError> { let mut clients = Vec::new(); for network in WalletNetwork::iter() { let nymd_url = if let Some(url) = config.get_selected_validator_nymd_url(network) { @@ -253,10 +250,9 @@ fn create_clients( let config = validator_client::Config::try_from_nym_network_details(&network_details)? .with_urls(nymd_url, api_url); - let mut client = - validator_client::Client::new_signing(config, network.into(), mnemonic.clone())?; + let mut client = validator_client::Client::new_signing(config, mnemonic.clone())?; client.set_nymd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); - clients.push(client); + clients.push((network, client)); } Ok(clients) } diff --git a/nym-wallet/src/components/ConfirmTX.stories.tsx b/nym-wallet/src/components/ConfirmTX.stories.tsx index 2eec07b7e3..77bbf4fdcf 100644 --- a/nym-wallet/src/components/ConfirmTX.stories.tsx +++ b/nym-wallet/src/components/ConfirmTX.stories.tsx @@ -10,9 +10,9 @@ export default { const Template: ComponentStory = (args) => ( - - - + + + ); diff --git a/nym-wallet/src/components/Delegation/DelegateModal.tsx b/nym-wallet/src/components/Delegation/DelegateModal.tsx index f9503e25e8..be32256409 100644 --- a/nym-wallet/src/components/Delegation/DelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegateModal.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; -import { Box, Typography } from '@mui/material'; -import { SxProps } from '@mui/system'; +import { Box, Typography, SxProps } from '@mui/material'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; import { CurrencyDenom, FeeDetails, DecCoin } from '@nymproject/types'; @@ -31,7 +30,7 @@ export const DelegateModal: React.FC<{ estimatedReward?: number; profitMarginPercentage?: number | null; nodeUptimePercentage?: number | null; - currency: string; + denom: CurrencyDenom; initialAmount?: string; hasVestingContract: boolean; sx?: SxProps; @@ -48,7 +47,7 @@ export const DelegateModal: React.FC<{ rewardInterval, accountBalance, estimatedReward, - currency, + denom, profitMarginPercentage, nodeUptimePercentage, initialAmount, @@ -103,7 +102,7 @@ export const DelegateModal: React.FC<{ } if (amount && Number(amount) < MIN_AMOUNT_TO_DELEGATE) { - errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${currency}`; + errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`; newValidatedValue = false; } @@ -118,7 +117,7 @@ export const DelegateModal: React.FC<{ const handleOk = async () => { if (onOk && amount && identityKey) { - onOk(identityKey, { amount, denom: currency as CurrencyDenom }, tokenPool, fee); + onOk(identityKey, { amount, denom }, tokenPool, fee); } }; @@ -169,8 +168,8 @@ export const DelegateModal: React.FC<{ onPrev={resetFeeState} onConfirm={handleOk} > - - + + ); } @@ -181,27 +180,28 @@ export const DelegateModal: React.FC<{ onClose={onClose} onOk={async () => { if (identityKey && amount) { - handleConfirm({ identity: identityKey, value: { amount, denom: currency as CurrencyDenom } }); + handleConfirm({ identity: identityKey, value: { amount, denom } }); } }} header={header || 'Delegate'} - subHeader="Delegate to mixnode" okLabel={buttonText || 'Delegate stake'} okDisabled={!isValidated} sx={sx} backdropProps={backdropProps} > - + + +
- + - } onClick={() => handleActionSelect?.('redeem')} disabled={disableRedeemingRewards} diff --git a/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx b/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx index 635c48f153..8694938a63 100644 --- a/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx +++ b/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx @@ -29,9 +29,6 @@ const transactionForDarkTheme = { url: 'https://sandbox-blocks.nymtech.net/transactions/11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CFO', hash: '11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CF0', }; -const balance = '104 NYMT'; -const balanceVested = '12 NYMT'; -const recipient = 'nymt1923pujepxfnv8dqyxqrl078s4ysf3xn2p7z2xa'; const Content: React.FC<{ children: React.ReactElement; handleClick: () => void }> = ({ children, @@ -78,8 +75,6 @@ export const DelegateSuccess = () => { status="success" action="delegate" message="You delegated 5 NYM" - recipient={recipient} - balance={balance} transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]} {...storybookStyles(theme)} /> @@ -99,8 +94,6 @@ export const UndelegateSuccess = () => { status="success" action="undelegate" message="You undelegated 5 NYM" - recipient={recipient} - balance={balance} transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]} {...storybookStyles(theme)} /> @@ -120,8 +113,6 @@ export const RedeemSuccess = () => { status="success" action="redeem" message="42 NYM" - recipient={recipient} - balance={balance} transactions={ theme.palette.mode === 'light' ? [transaction, transaction] @@ -145,9 +136,6 @@ export const RedeemWithVestedSuccess = () => { status="success" action="redeem" message="42 NYM" - recipient={recipient} - balance={balance} - balanceVested={balanceVested} transactions={ theme.palette.mode === 'light' ? [transaction, transaction] @@ -171,8 +159,6 @@ export const RedeemAllSuccess = () => { status="success" action="redeem-all" message="42 NYM" - recipient={recipient} - balance={balance} transactions={ theme.palette.mode === 'light' ? [transaction, transaction] @@ -196,8 +182,6 @@ export const Error = () => { status="error" action="redeem-all" message="Minim esse veniam Lorem id velit Lorem eu eu est. Excepteur labore sunt do proident proident sint aliquip consequat Lorem sint non nulla ad excepteur." - recipient={recipient} - balance={balance} transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]} {...storybookStyles(theme)} /> diff --git a/nym-wallet/src/components/Delegation/DelegationModal.tsx b/nym-wallet/src/components/Delegation/DelegationModal.tsx index 13846a67a2..ea48817f4f 100644 --- a/nym-wallet/src/components/Delegation/DelegationModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegationModal.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { Box, Button, Modal, Typography, SxProps } from '@mui/material'; +import { Box, Button, Modal, Typography, SxProps, Stack } from '@mui/material'; import { Link } from '@nymproject/react/link/Link'; import { Console } from 'src/utils/console'; import { modalStyle } from '../Modals/styles'; import { LoadingModal } from '../Modals/LoadingModal'; +import { ConfirmationModal } from '../Modals/ConfirmationModal'; export type ActionType = 'delegate' | 'undelegate' | 'redeem' | 'redeem-all' | 'compound'; @@ -15,9 +16,9 @@ const actionToHeader = (action: ActionType): string => { case 'redeem-all': return 'All rewards redeemed successfully'; case 'delegate': - return 'Delegation complete'; + return 'Delegation successful'; case 'undelegate': - return 'Undelegation complete'; + return 'Undelegation successful'; case 'compound': return 'Rewards compounded successfully'; default: @@ -29,9 +30,6 @@ export type DelegationModalProps = { status: 'loading' | 'success' | 'error'; action: ActionType; message?: string; - recipient?: string; - balance?: string; - balanceVested?: string; transactions?: { url: string; hash: string; @@ -45,20 +43,7 @@ export const DelegationModal: React.FC< sx?: SxProps; backdropProps?: object; } -> = ({ - status, - action, - message, - recipient, - balance, - balanceVested, - transactions, - open, - onClose, - children, - sx, - backdropProps, -}) => { +> = ({ status, action, message, transactions, open, onClose, children, sx, backdropProps }) => { if (status === 'loading') return ; if (status === 'error') { @@ -81,54 +66,28 @@ export const DelegationModal: React.FC< } transactions?.map((transaction) => Console.log('action', action, 'status', status, 'key', transaction.hash)); - return ( - - - theme.palette.success.main} mb={1}> - {actionToHeader(action)} - - - {message} - - {recipient && ( - theme.palette.text.secondary}> - Recipient: {recipient} - + return ( + {})} + title={actionToHeader(action)} + confirmButton="Done" + > + + {message && {message}} + {transactions?.length === 1 && ( + )} - {balanceVested ? ( - <> - theme.palette.text.secondary}> - Your current balance: {balance?.toUpperCase()} - - theme.palette.text.secondary}> - ({balanceVested.toUpperCase()} is unlocked in your vesting account) - - - ) : ( - theme.palette.text.secondary}> - Your current balance: {balance?.toUpperCase()} - - )} - {transactions && ( - theme.palette.text.secondary}> - Check the transaction {transactions.length > 1 ? 'hashes' : 'hash'}: - {transactions.map((transaction) => ( - + {transactions && transactions.length > 1 && ( + + View the transactions on blockchain: + {transactions.map(({ url, hash }) => ( + ))} - + )} - {children} - - - + + ); }; diff --git a/nym-wallet/src/components/Delegation/Modals.stories.tsx b/nym-wallet/src/components/Delegation/Modals.stories.tsx index a45a0cfd7e..0ca52166b7 100644 --- a/nym-wallet/src/components/Delegation/Modals.stories.tsx +++ b/nym-wallet/src/components/Delegation/Modals.stories.tsx @@ -61,7 +61,7 @@ export const Delegate = () => { open={open} onClose={() => setOpen(false)} onOk={async () => setOpen(false)} - currency="nym" + denom="nym" estimatedReward={50.423} accountBalance="425.2345053" nodeUptimePercentage={99.28394} @@ -84,7 +84,7 @@ export const DelegateBelowMinimum = () => { open={open} onClose={() => setOpen(false)} onOk={async () => setOpen(false)} - currency="nym" + denom="nym" estimatedReward={425.2345053} nodeUptimePercentage={99.28394} profitMarginPercentage={11.12334234} @@ -109,7 +109,7 @@ export const DelegateMore = () => { onOk={async () => setOpen(false)} header="Delegate more" buttonText="Delegate more" - currency="nym" + denom="nym" estimatedReward={50.423} accountBalance="425.2345053" nodeUptimePercentage={99.28394} diff --git a/nym-wallet/src/components/Delegation/UndelegateModal.tsx b/nym-wallet/src/components/Delegation/UndelegateModal.tsx index 1c05390090..227cc7b9f5 100644 --- a/nym-wallet/src/components/Delegation/UndelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/UndelegateModal.tsx @@ -55,7 +55,7 @@ export const UndelegateModal: React.FC<{ /> - + diff --git a/nym-wallet/src/components/Modals/LoadingModal.tsx b/nym-wallet/src/components/Modals/LoadingModal.tsx index 8422390e3b..a50bec77eb 100644 --- a/nym-wallet/src/components/Modals/LoadingModal.tsx +++ b/nym-wallet/src/components/Modals/LoadingModal.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { Box, CircularProgress, Modal, Stack, Typography } from '@mui/material'; -import { SxProps } from '@mui/system'; +import { Box, CircularProgress, Modal, Stack, Typography, SxProps } from '@mui/material'; const modalStyle: SxProps = { position: 'absolute', diff --git a/nym-wallet/src/components/Modals/ModalFee.tsx b/nym-wallet/src/components/Modals/ModalFee.tsx index c64fa0286e..31458a5356 100644 --- a/nym-wallet/src/components/Modals/ModalFee.tsx +++ b/nym-wallet/src/components/Modals/ModalFee.tsx @@ -13,5 +13,5 @@ const getValue = ({ fee, isLoading, error }: TFeeProps) => { }; export const ModalFee = ({ fee, isLoading, error }: TFeeProps) => ( - + ); diff --git a/nym-wallet/src/components/Modals/ModalListItem.tsx b/nym-wallet/src/components/Modals/ModalListItem.tsx index f0458a05ec..955e61089b 100644 --- a/nym-wallet/src/components/Modals/ModalListItem.tsx +++ b/nym-wallet/src/components/Modals/ModalListItem.tsx @@ -7,20 +7,22 @@ export const ModalListItem: React.FC<{ divider?: boolean; hidden?: boolean; strong?: boolean; - value: React.ReactNode; + value?: React.ReactNode; }> = ({ label, value, hidden, divider, strong }) => ( diff --git a/nym-wallet/src/components/Modals/SimpleModal.tsx b/nym-wallet/src/components/Modals/SimpleModal.tsx index 37b02c9adf..986087c724 100644 --- a/nym-wallet/src/components/Modals/SimpleModal.tsx +++ b/nym-wallet/src/components/Modals/SimpleModal.tsx @@ -60,12 +60,16 @@ export const SimpleModal: React.FC<{ {children} - - {onBack && } - - + {(onOk || onBack) && ( + + {onBack && } + {onOk && ( + + )} + + )} ); diff --git a/nym-wallet/src/components/Nav.tsx b/nym-wallet/src/components/Nav.tsx index b8119316d9..f450f17bbd 100644 --- a/nym-wallet/src/components/Nav.tsx +++ b/nym-wallet/src/components/Nav.tsx @@ -2,14 +2,14 @@ import React, { useState, useContext } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'; import { AccountBalanceWalletOutlined, ArrowBack, ArrowForward, Description, Settings } from '@mui/icons-material'; -import { AppContext } from '../context/main'; +import { AppContext } from '../context'; import { Bond, Delegate, Unbond } from '../svg-icons'; export const Nav = () => { const location = useLocation(); const navigate = useNavigate(); - const { isAdminAddress, handleShowSendModal } = useContext(AppContext); + const { isAdminAddress, handleShowSendModal, handleShowReceiveModal } = useContext(AppContext); const [routesSchema] = useState([ { @@ -25,9 +25,8 @@ export const Nav = () => { }, { label: 'Receive', - route: '/receive', Icon: ArrowBack, - onClick: () => navigate('/receive'), + onClick: handleShowReceiveModal, }, { label: 'Bond', @@ -69,9 +68,16 @@ export const Nav = () => { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', + marginLeft: 12, + marginRight: 12, }} > - + {routesSchema .filter(({ mode }) => { if (!mode) { @@ -87,17 +93,35 @@ export const Nav = () => { } }) .map(({ Icon, onClick, label, route }) => ( - + theme.palette.nym.nymWallet.hover.background }, + }} + > - + void; + open: boolean; + sx?: SxProps; + backdropProps?: object; +}) => { + const { clientDetails } = useContext(AppContext); + return ( + + + + Your address: + + + + `1px solid ${t.palette.nym.highlight}`, borderRadius: 2, p: 2 }}> + {clientDetails && } + + + + + ); +}; diff --git a/nym-wallet/src/components/Receive/index.tsx b/nym-wallet/src/components/Receive/index.tsx new file mode 100644 index 0000000000..1dd7e38aba --- /dev/null +++ b/nym-wallet/src/components/Receive/index.tsx @@ -0,0 +1,12 @@ +import React, { useContext } from 'react'; +import { AppContext } from 'src/context'; +import { ReceiveModal } from './ReceiveModal'; + +export const Receive = ({ hasStorybookStyles }: { hasStorybookStyles?: {} }) => { + const { showReceiveModal, handleShowReceiveModal } = useContext(AppContext); + + if (showReceiveModal) + return ; + + return null; +}; diff --git a/nym-wallet/src/components/Rewards/CompoundModal.tsx b/nym-wallet/src/components/Rewards/CompoundModal.tsx index 81a9e182ce..bcd67c00f6 100644 --- a/nym-wallet/src/components/Rewards/CompoundModal.tsx +++ b/nym-wallet/src/components/Rewards/CompoundModal.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { Stack, Typography } from '@mui/material'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; -import { FeeDetails } from '@nymproject/types'; +import { CurrencyDenom, FeeDetails } from '@nymproject/types'; import { simulateCompoundDelgatorReward, simulateVestingCompoundDelgatorReward } from 'src/requests'; import { useGetFee } from 'src/hooks/useGetFee'; import { SimpleModal } from '../Modals/SimpleModal'; @@ -14,10 +14,10 @@ export const CompoundModal: React.FC<{ onOk?: (identityKey: string, fee?: FeeDetails) => void; identityKey: string; amount: number; - currency: string; + denom: CurrencyDenom; message: string; usesVestingTokens: boolean; -}> = ({ open, onClose, onOk, identityKey, amount, currency, message, usesVestingTokens }) => { +}> = ({ open, onClose, onOk, identityKey, amount, denom, message, usesVestingTokens }) => { const { fee, isFeeLoading, feeError, getFee } = useGetFee(); const handleOk = async () => { @@ -47,7 +47,7 @@ export const CompoundModal: React.FC<{ Rewards amount: - {amount} {currency} + {amount} {denom.toUpperCase()} diff --git a/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx b/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx index 81d65fb978..cd20d7f8c5 100644 --- a/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx +++ b/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx @@ -61,7 +61,7 @@ export const RedeemAllRewards = () => { onClose={() => setOpen(false)} onOk={async () => setOpen(false)} message="Redeem all rewards" - currency="nym" + denom="nym" identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" amount={425.65843} {...storybookStyles(theme)} @@ -82,7 +82,7 @@ export const RedeemRewardForMixnode = () => { onClose={() => setOpen(false)} onOk={async () => setOpen(false)} message="Redeem rewards" - currency="nym" + denom="nym" identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" amount={425.65843} {...storybookStyles(theme)} @@ -103,7 +103,7 @@ export const FeeIsMoreThanAllRewards = () => { onClose={() => setOpen(false)} onOk={() => setOpen(false)} message="Redeem all rewards" - currency="nym" + denom="nym" identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" amount={0.001} {...storybookStyles(theme)} @@ -125,7 +125,7 @@ export const FeeIsMoreThanMixnodeReward = () => { onOk={async () => setOpen(false)} identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" message="Redeem rewards" - currency="nym" + denom="nym" amount={0.001} {...storybookStyles(theme)} usesVestingTokens={false} diff --git a/nym-wallet/src/components/Rewards/RedeemModal.tsx b/nym-wallet/src/components/Rewards/RedeemModal.tsx index a0543e3ea3..285e8a7721 100644 --- a/nym-wallet/src/components/Rewards/RedeemModal.tsx +++ b/nym-wallet/src/components/Rewards/RedeemModal.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { Stack, Typography, SxProps } from '@mui/material'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; -import { FeeDetails } from '@nymproject/types'; +import { CurrencyDenom, FeeDetails } from '@nymproject/types'; import { useGetFee } from 'src/hooks/useGetFee'; import { simulateClaimDelgatorReward, simulateVestingClaimDelgatorReward } from 'src/requests'; import { ModalFee } from '../Modals/ModalFee'; @@ -14,12 +14,12 @@ export const RedeemModal: React.FC<{ onOk?: (identityKey: string, fee?: FeeDetails) => void; identityKey: string; amount: number; - currency: string; + denom: CurrencyDenom; message: string; sx?: SxProps; backdropProps?: Object; usesVestingTokens: boolean; -}> = ({ open, onClose, onOk, identityKey, amount, currency, message, usesVestingTokens, sx, backdropProps }) => { +}> = ({ open, onClose, onOk, identityKey, amount, denom, message, usesVestingTokens, sx, backdropProps }) => { const { fee, isFeeLoading, feeError, getFee } = useGetFee(); const handleOk = async () => { @@ -52,7 +52,7 @@ export const RedeemModal: React.FC<{ Rewards amount: - {amount} {currency} + {amount} {denom.toUpperCase()} diff --git a/nym-wallet/src/components/Send/SendDetails.stories.tsx b/nym-wallet/src/components/Send/SendDetails.stories.tsx index a9434aa47c..f71b3709ef 100644 --- a/nym-wallet/src/components/Send/SendDetails.stories.tsx +++ b/nym-wallet/src/components/Send/SendDetails.stories.tsx @@ -46,6 +46,7 @@ export const SendDetails = () => { fromAddress="nymt1w8qp7zsxggvtxhpqpt6e329j42wtv07dm5ts8u" toAddress="nymt1w8qp7zsxggvtxhpqpt6e329j42wtv07dm5ts8u" fee={{ amount: { amount: '0.01', denom: 'nym' }, fee: { Auto: null } }} + denom="nym" amount={{ amount: '100', denom: 'nym' }} onPrev={() => {}} onSend={() => {}} diff --git a/nym-wallet/src/components/Send/SendDetailsModal.tsx b/nym-wallet/src/components/Send/SendDetailsModal.tsx index 38da2fd143..6523b5ed91 100644 --- a/nym-wallet/src/components/Send/SendDetailsModal.tsx +++ b/nym-wallet/src/components/Send/SendDetailsModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Stack } from '@mui/material'; -import { SxProps } from '@mui/system'; +import { Stack, SxProps } from '@mui/material'; +import { CurrencyDenom } from '@nymproject/types'; import { FeeDetails, DecCoin } from '@nymproject/types'; import { SimpleModal } from '../Modals/SimpleModal'; import { ModalListItem } from '../Modals/ModalListItem'; @@ -10,6 +10,7 @@ export const SendDetailsModal = ({ toAddress, fromAddress, fee, + denom, onClose, onPrev, onSend, @@ -20,6 +21,7 @@ export const SendDetailsModal = ({ toAddress: string; fee?: FeeDetails; amount?: DecCoin; + denom: CurrencyDenom; onClose: () => void; onPrev: () => void; onSend: (data: { val: DecCoin; to: string }) => void; @@ -37,11 +39,11 @@ export const SendDetailsModal = ({ backdropProps={backdropProps} > - - - + + + diff --git a/nym-wallet/src/components/Send/SendErrorModal.tsx b/nym-wallet/src/components/Send/SendErrorModal.tsx index 890b8cdf16..18fb5b4b74 100644 --- a/nym-wallet/src/components/Send/SendErrorModal.tsx +++ b/nym-wallet/src/components/Send/SendErrorModal.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { SxProps } from '@mui/system'; +import { SxProps } from '@mui/material'; import { SimpleModal } from '../Modals/SimpleModal'; export const SendErrorModal = ({ diff --git a/nym-wallet/src/components/Send/SendInputModal.tsx b/nym-wallet/src/components/Send/SendInputModal.tsx index 55c72519fe..f84e248141 100644 --- a/nym-wallet/src/components/Send/SendInputModal.tsx +++ b/nym-wallet/src/components/Send/SendInputModal.tsx @@ -1,8 +1,7 @@ import React, { useEffect, useState } from 'react'; -import { Stack, TextField, Typography } from '@mui/material'; -import { SxProps } from '@mui/system'; +import { Stack, TextField, Typography, SxProps } from '@mui/material'; import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; -import { DecCoin } from '@nymproject/types'; +import { CurrencyDenom, DecCoin } from '@nymproject/types'; import { validateAmount } from 'src/utils'; import { SimpleModal } from '../Modals/SimpleModal'; import { ModalListItem } from '../Modals/ModalListItem'; @@ -12,6 +11,7 @@ export const SendInputModal = ({ toAddress, amount, balance, + denom, error, onNext, onClose, @@ -24,6 +24,7 @@ export const SendInputModal = ({ toAddress: string; amount?: DecCoin; balance?: string; + denom?: CurrencyDenom; error?: string; onNext: () => void; onClose: () => void; @@ -69,14 +70,15 @@ export const SendInputModal = ({ validate(value); }} initialValue={amount?.amount} + denom={denom} /> {error} - - + + Est. fee for this transaction will be show on the next page diff --git a/nym-wallet/src/components/Send/SendModal.tsx b/nym-wallet/src/components/Send/SendModal.tsx index ec7f83dc45..c1792c7354 100644 --- a/nym-wallet/src/components/Send/SendModal.tsx +++ b/nym-wallet/src/components/Send/SendModal.tsx @@ -21,7 +21,7 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void const [isLoading, setIsLoading] = useState(false); const [txDetails, setTxDetails] = useState(); - const { clientDetails, userBalance, network, denom } = useContext(AppContext); + const { clientDetails, userBalance, network } = useContext(AppContext); const { fee, getFee } = useGetFee(); const handleOnNext = async () => { @@ -47,7 +47,7 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void try { const txResponse = await send({ amount: val, address: to, memo: '', fee: fee?.fee }); setTxDetails({ - amount: `${amount?.amount} ${denom}`, + amount: `${amount?.amount} ${clientDetails?.display_mix_denom.toUpperCase()}`, txUrl: `${urls(network).blockExplorer}/transaction/${txResponse.tx_hash}`, }); } catch (e) { @@ -74,6 +74,7 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void onClose={onClose} onPrev={() => setModal('send')} onSend={handleSend} + denom={clientDetails?.display_mix_denom || 'nym'} {...hasStorybookStyles} /> ); @@ -87,6 +88,7 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void onClose={onClose} onNext={handleOnNext} error={error} + denom={clientDetails?.display_mix_denom} onAmountChange={(value) => setAmount(value)} onAddressChange={(value) => setToAddress(value)} {...hasStorybookStyles} diff --git a/nym-wallet/src/components/Send/SendSuccessModal.tsx b/nym-wallet/src/components/Send/SendSuccessModal.tsx index 5c93d4cc05..29f3ffa1e9 100644 --- a/nym-wallet/src/components/Send/SendSuccessModal.tsx +++ b/nym-wallet/src/components/Send/SendSuccessModal.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { Stack, Typography } from '@mui/material'; -import { SxProps } from '@mui/system'; +import { Stack, Typography, SxProps } from '@mui/material'; import { Link } from '@nymproject/react/link/Link'; import { TTransactionDetails } from './types'; import { ConfirmationModal } from '../Modals/ConfirmationModal'; diff --git a/nym-wallet/src/components/TokenPoolSelector.tsx b/nym-wallet/src/components/TokenPoolSelector.tsx index 4b19ec2fb9..1509e0c983 100644 --- a/nym-wallet/src/components/TokenPoolSelector.tsx +++ b/nym-wallet/src/components/TokenPoolSelector.tsx @@ -11,7 +11,7 @@ export const TokenPoolSelector: React.FC<{ disabled: boolean; onSelect: (pool: T const [value, setValue] = useState('balance'); const { userBalance: { tokenAllocation, balance, fetchBalance, fetchTokenAllocation }, - denom, + clientDetails, } = useContext(AppContext); useEffect(() => { @@ -48,7 +48,9 @@ export const TokenPoolSelector: React.FC<{ disabled: boolean; onSelect: (pool: T {tokenAllocation && ( )} diff --git a/nym-wallet/src/context/delegations.tsx b/nym-wallet/src/context/delegations.tsx index cb1110db16..009d3c0ecd 100644 --- a/nym-wallet/src/context/delegations.tsx +++ b/nym-wallet/src/context/delegations.tsx @@ -74,7 +74,6 @@ export const DelegationContextProvider: FC<{ }; const resetState = () => { - setIsLoading(true); setError(undefined); setTotalDelegations(undefined); setTotalRewards(undefined); @@ -82,6 +81,7 @@ export const DelegationContextProvider: FC<{ }; const refresh = useCallback(async () => { + setIsLoading(true); try { const data = await getDelegationSummary(); const pending = await getAllPendingDelegations(); diff --git a/nym-wallet/src/context/main.tsx b/nym-wallet/src/context/main.tsx index b03318ba0c..47d98bd058 100644 --- a/nym-wallet/src/context/main.tsx +++ b/nym-wallet/src/context/main.tsx @@ -2,7 +2,7 @@ import React, { createContext, useEffect, useMemo, useState } from 'react'; import { forage } from '@tauri-apps/tauri-forage'; import { useNavigate } from 'react-router-dom'; import { useSnackbar } from 'notistack'; -import { Account, AccountEntry, CurrencyDenom, MixNodeBond } from '@nymproject/types'; +import { Account, AccountEntry, MixNodeBond } from '@nymproject/types'; import { getVersion } from '@tauri-apps/api/app'; import { AppEnv, Network } from '../types'; import { TUseuserBalance, useGetBalance } from '../hooks/useGetBalance'; @@ -49,9 +49,10 @@ export type TAppContext = { loginType?: TLoginType; showSettings: boolean; showSendModal: boolean; - denom: Uppercase; + showReceiveModal: boolean; handleShowSettings: () => void; handleShowSendModal: () => void; + handleShowReceiveModal: () => void; setIsLoading: (isLoading: boolean) => void; setError: (value?: string) => void; switchNetwork: (network: Network) => void; @@ -68,7 +69,6 @@ export const AppContext = createContext({} as TAppContext); export const AppProvider = ({ children }: { children: React.ReactNode }) => { const [clientDetails, setClientDetails] = useState(); - const [denom, setDenom] = useState>('NYM'); const [storedAccounts, setStoredAccounts] = useState(); const [mixnodeDetails, setMixnodeDetails] = useState(null); const [network, setNetwork] = useState(); @@ -83,6 +83,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { const [isAdminAddress, setIsAdminAddress] = useState(false); const [showSettings, setShowSettings] = useState(false); const [showSendModal, setShowSendModal] = useState(false); + const [showReceiveModal, setShowReceiveModal] = useState(false); const userBalance = useGetBalance(clientDetails); const navigate = useNavigate(); @@ -101,7 +102,6 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { try { const client = await selectNetwork(n); setClientDetails(client); - setDenom(client.display_mix_denom.toUpperCase() as Uppercase); } catch (e) { enqueueSnackbar('Error loading account', { variant: 'error' }); Console.error(e as string); @@ -237,6 +237,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { const switchNetwork = (_network: Network) => setNetwork(_network); const handleShowSettings = () => setShowSettings((show) => !show); const handleShowSendModal = () => setShowSendModal((show) => !show); + const handleShowReceiveModal = () => setShowReceiveModal((show) => !show); const handleSwitchMode = () => setMode((currentMode) => { const newMode = currentMode === 'light' ? 'dark' : 'light'; @@ -256,7 +257,6 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { storedAccounts, mixnodeDetails, userBalance, - denom, showAdmin, showTerminal, showSettings, @@ -274,7 +274,9 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { onAccountChange, handleShowSettings, showSendModal, + showReceiveModal, handleShowSendModal, + handleShowReceiveModal, handleSwitchMode, }), [ @@ -294,7 +296,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { showTerminal, showSettings, showSendModal, - denom, + showReceiveModal, ], ); diff --git a/nym-wallet/src/context/mocks/main.tsx b/nym-wallet/src/context/mocks/main.tsx index 62b53da136..c9f32dedd8 100644 --- a/nym-wallet/src/context/mocks/main.tsx +++ b/nym-wallet/src/context/mocks/main.tsx @@ -35,11 +35,12 @@ export const MockMainContextProvider: FC<{}> = ({ children }) => { fetchTokenAllocation: async () => undefined, refreshBalances: async () => {}, }, - denom: 'NYM', + displayDenom: 'NYM', showAdmin: false, showTerminal: false, showSettings: false, showSendModal: true, + showReceiveModal: false, network: 'SANDBOX', loginType: 'mnemonic', setIsLoading: () => undefined, @@ -54,6 +55,7 @@ export const MockMainContextProvider: FC<{}> = ({ children }) => { onAccountChange: () => undefined, handleShowSettings: () => undefined, handleShowSendModal: () => undefined, + handleShowReceiveModal: () => undefined, }), [], ); diff --git a/nym-wallet/src/layouts/AppLayout.tsx b/nym-wallet/src/layouts/AppLayout.tsx index 157678143f..ead44f0ae1 100644 --- a/nym-wallet/src/layouts/AppLayout.tsx +++ b/nym-wallet/src/layouts/AppLayout.tsx @@ -26,21 +26,20 @@ export const ApplicationLayout: React.FC = ({ children }) => { sx={{ background: (t) => t.palette.nym.nymWallet.nav.background, overflow: 'auto', - py: 3, - px: 5, + py: 5, }} display="flex" flexDirection="column" justifyContent="space-between" > - +