Feature/config from env (#1463)
* Clients use env * Explorer api uses env * Mainnet and qa env files * Set CONFIGURED on the mainnet defaulting * Gateway uses env * Mixnode uses env * Wallet error simplification * Network requester takes only mainnet client address * Validator api uses env * Mixnet contract uses denom from instantiate * Vesting contract uses denom from instantiate * More contract test refactoring * Coconut bandwidth contract uses denom from instantiate * Bandwidth claim contract uses denom from instantiate and remove from Cargos * More remove from Cargos and one missed DEFAULT_NETWORK * Refactor some other missed places * Minor fixes * Test and clippy fixes * Update CHANGELOG
This commit is contained in:
committed by
GitHub
parent
c485934b06
commit
9ca3f69aa8
@@ -52,6 +52,8 @@ 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])
|
||||
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
@@ -74,6 +76,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#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
|
||||
|
||||
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
||||
|
||||
|
||||
Generated
+2
-6
@@ -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",
|
||||
@@ -2871,7 +2871,6 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
@@ -2968,6 +2967,7 @@ name = "network-defaults"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"dotenv",
|
||||
"hex-literal",
|
||||
"once_cell",
|
||||
"serde",
|
||||
@@ -3065,7 +3065,6 @@ dependencies = [
|
||||
"credentials",
|
||||
"crypto",
|
||||
"dirs",
|
||||
"dotenv",
|
||||
"futures",
|
||||
"gateway-client",
|
||||
"gateway-requests",
|
||||
@@ -3232,7 +3231,6 @@ dependencies = [
|
||||
"credentials",
|
||||
"crypto",
|
||||
"dirs",
|
||||
"dotenv",
|
||||
"futures",
|
||||
"gateway-client",
|
||||
"gateway-requests",
|
||||
@@ -5390,7 +5388,6 @@ version = "1.0.1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -6338,7 +6335,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<T: NymConfig> Default for Client<T> {
|
||||
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(),
|
||||
|
||||
@@ -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<SigningNymdClient>,
|
||||
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<Fee>,
|
||||
) -> Result<String> {
|
||||
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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<std::path::PathBuf>,
|
||||
|
||||
#[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 {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<std::path::PathBuf>,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<C> {
|
||||
// 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<bip39::Mnemonic>,
|
||||
@@ -134,9 +131,6 @@ pub struct Client<C> {
|
||||
impl Client<SigningNymdClient> {
|
||||
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<Client<SigningNymdClient>, ValidatorClientError> {
|
||||
let validator_api_client = validator_api::Client::new(config.api_url.clone());
|
||||
@@ -148,7 +142,6 @@ impl Client<SigningNymdClient> {
|
||||
)?;
|
||||
|
||||
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<SigningNymdClient> {
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
impl Client<QueryNymdClient> {
|
||||
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<Client<QueryNymdClient>, ValidatorClientError> {
|
||||
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, 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,
|
||||
|
||||
@@ -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<C: SigningCosmWasmClient + Sync + Send> MultisigSigningClient for NymdClien
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
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(),
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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))]
|
||||
|
||||
@@ -7,6 +7,7 @@ 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)]
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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<String, Bech32Error> {
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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"]}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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"),
|
||||
}
|
||||
}
|
||||
@@ -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<S: Into<String>>(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<Url> {
|
||||
DEFAULT_NETWORK
|
||||
.validators()
|
||||
.iter()
|
||||
.map(ValidatorDetails::nymd_url)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn default_api_endpoints() -> Vec<Url> {
|
||||
DEFAULT_NETWORK
|
||||
.validators()
|
||||
.iter()
|
||||
.filter_map(ValidatorDetails::api_url)
|
||||
.collect()
|
||||
pub fn setup_env(config_env_file: Option<PathBuf>) {
|
||||
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,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<ValidatorDetails> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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";
|
||||
@@ -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" }
|
||||
|
||||
Generated
+7
-6
@@ -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",
|
||||
@@ -1852,7 +1854,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -62,10 +62,11 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respon
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use bandwidth_claim_contract::payment::PagedPaymentResponse;
|
||||
use config::defaults::MIX_DENOM;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, from_binary};
|
||||
|
||||
const TEST_MIX_DENOM: &str = "unym";
|
||||
|
||||
#[test]
|
||||
fn initialize_contract() {
|
||||
let mut deps = mock_dependencies();
|
||||
@@ -91,11 +92,11 @@ pub mod tests {
|
||||
|
||||
// Contract balance should match what we initialized it as
|
||||
assert_eq!(
|
||||
coins(0, MIX_DENOM.base),
|
||||
coins(0, TEST_MIX_DENOM),
|
||||
vec![deps
|
||||
.as_ref()
|
||||
.querier
|
||||
.query_balance(env.contract.address, MIX_DENOM.base)
|
||||
.query_balance(env.contract.address, TEST_MIX_DENOM)
|
||||
.unwrap()]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ crate-type = ["cdylib", "rlib"]
|
||||
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"
|
||||
|
||||
@@ -25,12 +25,14 @@ pub fn instantiate(
|
||||
) -> Result<Response, ContractError> {
|
||||
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<Respon
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::TEST_MIX_DENOM;
|
||||
use crate::support::tests::helpers::*;
|
||||
use config::defaults::MIX_DENOM;
|
||||
use cosmwasm_std::coins;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
|
||||
@@ -86,6 +88,7 @@ mod tests {
|
||||
let msg = InstantiateMsg {
|
||||
multisig_addr: String::from(MULTISIG_CONTRACT),
|
||||
pool_addr: String::from(POOL_CONTRACT),
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
@@ -94,11 +97,11 @@ mod tests {
|
||||
|
||||
// Contract balance should be 0
|
||||
assert_eq!(
|
||||
coins(0, MIX_DENOM.base),
|
||||
coins(0, TEST_MIX_DENOM),
|
||||
vec![deps
|
||||
.as_ref()
|
||||
.querier
|
||||
.query_balance(env.contract.address, MIX_DENOM.base)
|
||||
.query_balance(env.contract.address, TEST_MIX_DENOM)
|
||||
.unwrap()]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ use cosmwasm_std::StdError;
|
||||
use cw_controllers::AdminError;
|
||||
use thiserror::Error;
|
||||
|
||||
use config::defaults::MIX_DENOM;
|
||||
|
||||
/// Custom errors for contract failure conditions.
|
||||
///
|
||||
/// Add any other custom errors you like here.
|
||||
@@ -22,8 +20,8 @@ pub enum ContractError {
|
||||
#[error("No coin was sent for voucher")]
|
||||
NoCoin,
|
||||
|
||||
#[error("Wrong coin denomination, you must send {}", MIX_DENOM.base)]
|
||||
WrongDenom,
|
||||
#[error("Wrong coin denomination, you must send {mix_denom}")]
|
||||
WrongDenom { mix_denom: String },
|
||||
|
||||
#[error("There aren't enough funds in the contract")]
|
||||
NotEnoughFunds,
|
||||
|
||||
@@ -13,6 +13,7 @@ pub const ADMIN: Admin = Admin::new("admin");
|
||||
pub struct Config {
|
||||
pub multisig_addr: Addr,
|
||||
pub pool_addr: Addr,
|
||||
pub mix_denom: String,
|
||||
}
|
||||
|
||||
pub const CONFIG: Item<Config> = Item::new("config");
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -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<MemoryStorage, MockApi, MockQuerier<Empty>> {
|
||||
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", &[]);
|
||||
|
||||
@@ -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<Response, ContractError> {
|
||||
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<Response, ContractError> {
|
||||
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(),
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
),
|
||||
|
||||
@@ -61,9 +61,14 @@ pub fn debug_with_visibility<S: Into<String>>(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 +93,7 @@ pub fn instantiate(
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
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)?;
|
||||
@@ -452,7 +457,7 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respon
|
||||
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 +467,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 +490,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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
@@ -74,7 +73,10 @@ pub(crate) fn _try_reconcile_all_delegation_events(
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
|
||||
fn validate_delegation_stake(
|
||||
mut delegation: Vec<Coin>,
|
||||
mix_denom: String,
|
||||
) -> Result<Coin, ContractError> {
|
||||
// check if anything was put as delegation
|
||||
if delegation.is_empty() {
|
||||
return Err(ContractError::EmptyDelegation);
|
||||
@@ -85,8 +87,8 @@ fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, Contract
|
||||
}
|
||||
|
||||
// check that the denomination is correct
|
||||
if delegation[0].denom != MIX_DENOM.base {
|
||||
return Err(ContractError::WrongDenom {});
|
||||
if delegation[0].denom != mix_denom {
|
||||
return Err(ContractError::WrongDenom { mix_denom });
|
||||
}
|
||||
|
||||
// check that we have provided a non-zero amount in the delegation
|
||||
@@ -104,7 +106,8 @@ pub(crate) fn try_delegate_to_mixnode(
|
||||
mix_identity: IdentityKey,
|
||||
) -> Result<Response, ContractError> {
|
||||
// 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 +127,8 @@ pub(crate) fn try_delegate_to_mixnode_on_behalf(
|
||||
delegate: String,
|
||||
) -> Result<Response, ContractError> {
|
||||
// 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 +264,7 @@ pub(crate) fn try_reconcile_undelegation(
|
||||
pending_undelegate: &PendingUndelegate,
|
||||
) -> Result<ReconcileUndelegateResponse, ContractError> {
|
||||
let delegation_map = storage::delegations();
|
||||
let mix_denom = mixnet_params_storage::mix_denom(storage)?;
|
||||
|
||||
// debug_with_visibility(api, "Reconciling undelegations");
|
||||
|
||||
@@ -389,7 +394,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 +406,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 +473,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 +488,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 +496,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 +521,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 +565,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 +581,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(),
|
||||
@@ -615,7 +638,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,
|
||||
)
|
||||
);
|
||||
@@ -636,7 +659,7 @@ mod tests {
|
||||
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 +709,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 +737,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 +772,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 +837,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,7 +913,7 @@ 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();
|
||||
@@ -902,7 +925,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 +950,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 +967,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 +975,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 +1011,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 +1048,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(),
|
||||
@@ -1113,7 +1136,7 @@ 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();
|
||||
@@ -1132,7 +1155,7 @@ mod tests {
|
||||
// let expected_response = Response::new()
|
||||
// .add_message(BankMsg::Send {
|
||||
// to_address: delegation_owner.clone().into(),
|
||||
// amount: coins(100, MIX_DENOM.base),
|
||||
// amount: coins(100, TEST_COIN_DENOM),
|
||||
// })
|
||||
// .add_event(new_undelegation_event(
|
||||
// &delegation_owner,
|
||||
@@ -1183,7 +1206,7 @@ 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();
|
||||
@@ -1202,7 +1225,7 @@ mod tests {
|
||||
// let expected_response = Response::new()
|
||||
// .add_message(BankMsg::Send {
|
||||
// to_address: delegation_owner.clone().into(),
|
||||
// amount: coins(100, MIX_DENOM.base),
|
||||
// amount: coins(100, TEST_COIN_DENOM),
|
||||
// })
|
||||
// .add_event(new_undelegation_event(
|
||||
// &delegation_owner,
|
||||
@@ -1246,8 +1269,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(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Addr>,
|
||||
) -> Result<Response, ContractError> {
|
||||
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<Coin>,
|
||||
minimum_pledge: Uint128,
|
||||
mix_denom: String,
|
||||
) -> Result<Coin, ContractError> {
|
||||
// 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::<u128>::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()
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<String, ContractError> {
|
||||
Ok(CONTRACT_STATE
|
||||
pub fn rewarding_validator_address(storage: &dyn Storage) -> StdResult<String> {
|
||||
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<String> {
|
||||
CONTRACT_STATE.load(storage).map(|state| state.mix_denom)
|
||||
}
|
||||
|
||||
pub fn increment_layer_count(storage: &mut dyn Storage, layer: Layer) -> StdResult<()> {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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";
|
||||
@@ -171,7 +172,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 +191,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 +213,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 +225,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,
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -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(
|
||||
@@ -239,7 +238,8 @@ 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(deps.storage)?)])?;
|
||||
response = response.add_message(track_unbond_message);
|
||||
}
|
||||
|
||||
@@ -288,6 +288,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 +330,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 +345,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 +356,11 @@ pub(crate) fn _try_update_mixnode_config(
|
||||
fn validate_mixnode_pledge(
|
||||
mut pledge: Vec<Coin>,
|
||||
minimum_pledge: Uint128,
|
||||
mix_denom: String,
|
||||
) -> Result<Coin, ContractError> {
|
||||
// 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 +368,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 +390,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 +406,7 @@ pub mod tests {
|
||||
|
||||
// if we don't send enough funds
|
||||
let insufficient_bond = Into::<u128>::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 +812,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 +843,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]
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -10,11 +10,11 @@ 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::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 +69,7 @@ fn _try_claim_operator_reward(
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = api.addr_validate(owner)?;
|
||||
let mix_denom = mix_denom(storage)?;
|
||||
|
||||
let bond = match crate::mixnodes::storage::mixnodes()
|
||||
.idx
|
||||
@@ -105,7 +106,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 +116,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 +135,7 @@ pub fn _try_claim_delegator_reward(
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, ContractError> {
|
||||
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 +155,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 +171,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);
|
||||
}
|
||||
|
||||
@@ -403,6 +405,7 @@ pub fn _try_compound_delegator_reward(
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
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)?,
|
||||
@@ -438,7 +441,7 @@ pub fn _try_compound_delegator_reward(
|
||||
owner_address,
|
||||
Coin {
|
||||
amount: compounded_delegation,
|
||||
denom: MIX_DENOM.base.to_string(),
|
||||
denom: mix_denom,
|
||||
},
|
||||
proxy,
|
||||
)?;
|
||||
@@ -724,9 +727,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 +747,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 +888,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 +928,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 +1112,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 +1163,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 +1178,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 +1186,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 +1194,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 +1202,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 +1217,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 +1323,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 +1562,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 +1577,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 +1753,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 +1780,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 +1790,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(),
|
||||
)
|
||||
|
||||
@@ -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<Coin> {
|
||||
vec![Coin {
|
||||
denom: MIX_DENOM.base.to_string(),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
amount: INITIAL_MIXNODE_PLEDGE,
|
||||
}]
|
||||
}
|
||||
|
||||
pub fn good_gateway_pledge() -> Vec<Coin> {
|
||||
vec![Coin {
|
||||
denom: MIX_DENOM.base.to_string(),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
amount: INITIAL_MIXNODE_PLEDGE,
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -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<Coin>, 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<MemoryStorage, MockApi, MockQuerier<Empty>> {
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -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<MockStorage, MockApi, MockQuerier>) -> Vec<MixNodeBond> {
|
||||
let result = query(
|
||||
deps.as_ref(),
|
||||
@@ -45,5 +46,5 @@ pub fn query_contract_balance(
|
||||
deps: OwnedDeps<MockStorage, MockApi, MockQuerier>,
|
||||
) -> Vec<Coin> {
|
||||
let querier = deps.as_ref().querier;
|
||||
vec![querier.query_balance(address, MIX_DENOM.base).unwrap()]
|
||||
vec![querier.query_balance(address, TEST_COIN_DENOM).unwrap()]
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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<T: Into<String>>(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<Coin>,
|
||||
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),
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use crate::errors::ContractError;
|
||||
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,6 +35,7 @@ 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())
|
||||
}
|
||||
|
||||
@@ -167,11 +167,9 @@ pub fn try_withdraw_vested_coins(
|
||||
info: MessageInfo,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
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 +243,8 @@ pub fn try_bond_gateway(
|
||||
env: Env,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
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 +284,8 @@ pub fn try_bond_mixnode(
|
||||
env: Env,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
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 +345,8 @@ fn try_delegate_to_mixnode(
|
||||
env: Env,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
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 +397,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 +408,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 +575,7 @@ pub fn try_get_vested_coins(
|
||||
deps: Deps<'_>,
|
||||
) -> Result<Coin, ContractError> {
|
||||
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 +585,7 @@ pub fn try_get_vesting_coins(
|
||||
deps: Deps<'_>,
|
||||
) -> Result<Coin, ContractError> {
|
||||
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 +632,7 @@ pub fn try_get_delegated_vesting(
|
||||
account.get_delegated_vesting(block_time, &env, deps.storage)
|
||||
}
|
||||
|
||||
fn validate_funds(funds: &[Coin]) -> Result<Coin, ContractError> {
|
||||
fn validate_funds(funds: &[Coin], mix_denom: String) -> Result<Coin, ContractError> {
|
||||
if funds.is_empty() || funds[0].amount.is_zero() {
|
||||
return Err(ContractError::EmptyFunds);
|
||||
}
|
||||
@@ -639,11 +641,8 @@ fn validate_funds(funds: &[Coin]) -> Result<Coin, ContractError> {
|
||||
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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<MemoryStorage, MockApi, MockQuerier<Empty>> {
|
||||
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,
|
||||
|
||||
@@ -34,11 +34,13 @@ pub trait VestingAccount {
|
||||
&self,
|
||||
block_time: Option<Timestamp>,
|
||||
env: &Env,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Coin, ContractError>;
|
||||
fn get_vesting_coins(
|
||||
&self,
|
||||
block_time: Option<Timestamp>,
|
||||
env: &Env,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Coin, ContractError>;
|
||||
|
||||
fn get_start_time(&self) -> Timestamp;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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<Timestamp>,
|
||||
env: &Env,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Coin, ContractError> {
|
||||
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<Timestamp>,
|
||||
env: &Env,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Coin, ContractError> {
|
||||
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<Coin, ContractError> {
|
||||
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)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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/"
|
||||
+20
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
+14
-10
@@ -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!"),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<std::path::PathBuf>,
|
||||
}
|
||||
@@ -3,10 +3,13 @@ extern crate rocket;
|
||||
#[macro_use]
|
||||
extern crate rocket_okapi;
|
||||
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use network_defaults::setup_env;
|
||||
|
||||
pub(crate) mod cache;
|
||||
mod client;
|
||||
pub(crate) mod commands;
|
||||
mod country_statistics;
|
||||
mod gateways;
|
||||
mod http;
|
||||
@@ -24,6 +27,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;
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
+7
-6
@@ -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<std::path::PathBuf>,
|
||||
|
||||
#[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")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ApiClient>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
mix_denom_base: String,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ impl CoconutVerifier {
|
||||
pub fn new(
|
||||
api_clients: Vec<ApiClient>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
mix_denom_base: String,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
) -> Result<Self, RequestHandlingError> {
|
||||
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")],
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
+7
-6
@@ -4,7 +4,7 @@ extern crate rocket;
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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 gateway.
|
||||
#[clap(long)]
|
||||
pub(crate) config_env_file: Option<std::path::PathBuf>,
|
||||
|
||||
#[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")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Generated
+4
-7
@@ -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",
|
||||
@@ -3273,6 +3271,7 @@ name = "network-defaults"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dotenv",
|
||||
"hex-literal",
|
||||
"once_cell",
|
||||
"serde",
|
||||
@@ -3446,7 +3445,6 @@ dependencies = [
|
||||
"credential-storage",
|
||||
"crypto",
|
||||
"dirs",
|
||||
"dotenv",
|
||||
"futures",
|
||||
"gateway-client",
|
||||
"gateway-requests",
|
||||
@@ -6485,7 +6483,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
|
||||
Generated
+2
-2
@@ -727,6 +727,7 @@ name = "coconut-bandwidth-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"multisig-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -2790,7 +2791,6 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
@@ -2887,6 +2887,7 @@ name = "network-defaults"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dotenv",
|
||||
"hex-literal",
|
||||
"once_cell",
|
||||
"serde",
|
||||
@@ -5585,7 +5586,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
|
||||
@@ -78,7 +78,7 @@ pub enum BackendError {
|
||||
#[error("No balance available for address {0}")]
|
||||
NoBalance(String),
|
||||
#[error("The provided network is not supported (yet)")]
|
||||
NetworkNotSupported(config::defaults::all::Network),
|
||||
NetworkNotSupported,
|
||||
#[error("Could not access the local data storage directory")]
|
||||
UnknownStorageDirectory,
|
||||
#[error("The wallet file already exists")]
|
||||
|
||||
@@ -142,18 +142,16 @@ async fn _connect_with_mnemonic(
|
||||
)?;
|
||||
|
||||
// Set the default account
|
||||
let default_network: WalletNetwork = config::defaults::DEFAULT_NETWORK.into();
|
||||
let default_network = WalletNetwork::MAINNET;
|
||||
let client_for_default_network = clients
|
||||
.iter()
|
||||
.find(|client| WalletNetwork::from(client.network.clone()) == default_network);
|
||||
.find(|(network, _)| *network == default_network);
|
||||
let account_for_default_network = match client_for_default_network {
|
||||
Some(client) => 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<WalletNetwork, Url>,
|
||||
config: &Config,
|
||||
mnemonic: &Mnemonic,
|
||||
) -> Result<Vec<Client<SigningNymdClient>>, BackendError> {
|
||||
) -> Result<Vec<(WalletNetwork, Client<SigningNymdClient>)>, 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)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use ordered_buffer::OrderedMessageSender;
|
||||
use socks5_requests::{ConnectionId, Message as Socks5Message, RemoteAddress, Request};
|
||||
@@ -59,6 +58,7 @@ pub struct StatsProviderConfigEntry {
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[allow(dead_code)]
|
||||
pub struct OptionalStatsProviderConfig {
|
||||
mainnet: Option<StatsProviderConfigEntry>,
|
||||
sandbox: Option<StatsProviderConfigEntry>,
|
||||
@@ -67,15 +67,7 @@ pub struct OptionalStatsProviderConfig {
|
||||
|
||||
impl OptionalStatsProviderConfig {
|
||||
pub fn stats_client_address(&self) -> Option<String> {
|
||||
let entry_config = match DEFAULT_NETWORK {
|
||||
network_defaults::all::Network::MAINNET => self.mainnet.clone(),
|
||||
network_defaults::all::Network::SANDBOX => self.sandbox.clone(),
|
||||
network_defaults::all::Network::QA => self.qa.clone(),
|
||||
network_defaults::all::Network::CUSTOM { .. } => {
|
||||
panic!("custom network is not supported")
|
||||
}
|
||||
};
|
||||
entry_config.map(|e| e.stats_client_address)
|
||||
self.mainnet.clone().map(|e| e.stats_client_address)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use coconut_bandwidth_contract_common::spend_credential::{
|
||||
use coconut_interface::{
|
||||
Attribute, BlindSignRequest, BlindedSignature, KeyPair, Parameters, VerificationKey,
|
||||
};
|
||||
use config::defaults::{MIX_DENOM, VALIDATOR_API_VERSION};
|
||||
use config::defaults::VALIDATOR_API_VERSION;
|
||||
use credentials::coconut::params::{
|
||||
ValidatorApiCredentialEncryptionAlgorithm, ValidatorApiCredentialHkdfAlgorithm,
|
||||
};
|
||||
@@ -45,6 +45,7 @@ use self::comm::APICommunicationChannel;
|
||||
|
||||
pub struct State {
|
||||
client: Arc<dyn LocalClient + Send + Sync>,
|
||||
mix_denom: String,
|
||||
key_pair: KeyPair,
|
||||
comm_channel: Arc<dyn APICommunicationChannel + Send + Sync>,
|
||||
storage: ValidatorApiStorage,
|
||||
@@ -54,6 +55,7 @@ pub struct State {
|
||||
impl State {
|
||||
pub(crate) fn new<C, D>(
|
||||
client: C,
|
||||
mix_denom: String,
|
||||
key_pair: KeyPair,
|
||||
comm_channel: D,
|
||||
storage: ValidatorApiStorage,
|
||||
@@ -67,6 +69,7 @@ impl State {
|
||||
let rng = Arc::new(Mutex::new(OsRng));
|
||||
Self {
|
||||
client,
|
||||
mix_denom,
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage,
|
||||
@@ -160,6 +163,7 @@ impl InternalSignRequest {
|
||||
|
||||
pub fn stage<C, D>(
|
||||
client: C,
|
||||
mix_denom: String,
|
||||
key_pair: KeyPair,
|
||||
comm_channel: D,
|
||||
storage: ValidatorApiStorage,
|
||||
@@ -168,7 +172,7 @@ impl InternalSignRequest {
|
||||
C: LocalClient + Send + Sync + 'static,
|
||||
D: APICommunicationChannel + Send + Sync + 'static,
|
||||
{
|
||||
let state = State::new(client, key_pair, comm_channel, storage);
|
||||
let state = State::new(client, mix_denom, key_pair, comm_channel, storage);
|
||||
AdHoc::on_ignite("Internal Sign Request Stage", |rocket| async {
|
||||
rocket.manage(state).mount(
|
||||
// this format! is so ugly...
|
||||
@@ -306,7 +310,7 @@ pub async fn verify_bandwidth_credential(
|
||||
vote_yes &= Coin::from(proposed_release_funds)
|
||||
== Coin::new(
|
||||
verify_credential_body.credential().voucher_value() as u128,
|
||||
MIX_DENOM.base,
|
||||
state.mix_denom.clone(),
|
||||
);
|
||||
|
||||
// Vote yes or no on the proposal based on the verification result
|
||||
|
||||
@@ -11,7 +11,7 @@ use coconut_bandwidth_contract_common::spend_credential::{
|
||||
SpendCredential, SpendCredentialResponse,
|
||||
};
|
||||
use coconut_interface::{hash_to_scalar, Credential, VerificationKey};
|
||||
use config::defaults::{DEFAULT_NETWORK, MIX_DENOM, VOUCHER_INFO};
|
||||
use config::defaults::VOUCHER_INFO;
|
||||
use cosmwasm_std::{to_binary, Addr, CosmosMsg, Decimal, WasmMsg};
|
||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use credentials::coconut::params::{
|
||||
@@ -47,6 +47,9 @@ use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
const TEST_COIN_DENOM: &str = "unym";
|
||||
const TEST_REWARDING_VALIDATOR_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DummyClient {
|
||||
validator_address: AccountId,
|
||||
@@ -178,7 +181,7 @@ async fn check_signer_verif_key(key_pair: KeyPair) {
|
||||
db_dir.push(&verification_key.to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap(),
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -187,6 +190,7 @@ async fn check_signer_verif_key(key_pair: KeyPair) {
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage,
|
||||
@@ -271,7 +275,7 @@ async fn signed_before() {
|
||||
.unwrap()
|
||||
.insert(tx_hash.to_string(), tx_entry.clone());
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap(),
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&tx_db,
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -280,6 +284,7 @@ async fn signed_before() {
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
@@ -335,7 +340,7 @@ async fn signed_before() {
|
||||
#[tokio::test]
|
||||
async fn state_functions() {
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap(),
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -346,7 +351,13 @@ async fn state_functions() {
|
||||
db_dir.push(&key_pair.verification_key().to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let state = State::new(nymd_client, key_pair, comm_channel, storage.clone());
|
||||
let state = State::new(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
);
|
||||
|
||||
let tx_hash = String::from("6B27412050B823E58BB38447D7870BBC8CBE3C51C905BEA89D459ACCDA80A00E");
|
||||
assert!(state.signed_before(&tx_hash).await.unwrap().is_none());
|
||||
@@ -504,7 +515,7 @@ async fn blind_sign_correct() {
|
||||
.unwrap()
|
||||
.insert(tx_hash.to_string(), tx_entry.clone());
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap(),
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&tx_db,
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -513,6 +524,7 @@ async fn blind_sign_correct() {
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
@@ -582,7 +594,7 @@ async fn signature_test() {
|
||||
db_dir.push(&key_pair.verification_key().to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap(),
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -591,6 +603,7 @@ async fn signature_test() {
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
@@ -645,8 +658,7 @@ async fn signature_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_cosmos_address() {
|
||||
let validator_address =
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap();
|
||||
let validator_address = AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap();
|
||||
let nymd_client = DummyClient::new(
|
||||
validator_address.clone(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -662,6 +674,7 @@ async fn get_cosmos_address() {
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
@@ -687,8 +700,7 @@ async fn get_cosmos_address() {
|
||||
#[tokio::test]
|
||||
async fn verification_of_bandwidth_credential() {
|
||||
// Setup variables
|
||||
let validator_address =
|
||||
AccountId::from_str(DEFAULT_NETWORK.rewarding_validator_address()).unwrap();
|
||||
let validator_address = AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap();
|
||||
let proposal_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let spent_credential_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let nymd_client = DummyClient::new(
|
||||
@@ -713,6 +725,7 @@ async fn verification_of_bandwidth_credential() {
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client.clone(),
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
key_pair,
|
||||
comm_channel.clone(),
|
||||
storage1.clone(),
|
||||
@@ -806,7 +819,7 @@ async fn verification_of_bandwidth_credential() {
|
||||
);
|
||||
|
||||
// Test the endpoint without any credential recorded in the Coconut Bandwidth Contract
|
||||
let funds = Coin::new(voucher_value as u128, MIX_DENOM.base);
|
||||
let funds = Coin::new(voucher_value as u128, TEST_COIN_DENOM);
|
||||
let msg = coconut_bandwidth_contract_common::msg::ExecuteMsg::ReleaseFunds {
|
||||
funds: funds.clone().into(),
|
||||
};
|
||||
@@ -901,7 +914,7 @@ async fn verification_of_bandwidth_credential() {
|
||||
|
||||
// Test the endpoint with a proposal that has a different value for the funds to be released
|
||||
// then what's in the credential
|
||||
let funds = Coin::new((voucher_value + 10) as u128, MIX_DENOM.base);
|
||||
let funds = Coin::new((voucher_value + 10) as u128, TEST_COIN_DENOM);
|
||||
let msg = coconut_bandwidth_contract_common::msg::ExecuteMsg::ReleaseFunds {
|
||||
funds: funds.clone().into(),
|
||||
};
|
||||
@@ -939,7 +952,7 @@ async fn verification_of_bandwidth_credential() {
|
||||
);
|
||||
|
||||
// Test the endpoint with every dependency met
|
||||
let funds = Coin::new(voucher_value as u128, MIX_DENOM.base);
|
||||
let funds = Coin::new(voucher_value as u128, TEST_COIN_DENOM);
|
||||
let msg = coconut_bandwidth_contract_common::msg::ExecuteMsg::ReleaseFunds {
|
||||
funds: funds.clone().into(),
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::template::config_template;
|
||||
use config::defaults::DEFAULT_NETWORK;
|
||||
use config::NymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
@@ -108,9 +107,7 @@ impl Default for Base {
|
||||
local_validator: DEFAULT_LOCAL_VALIDATOR
|
||||
.parse()
|
||||
.expect("default local validator is malformed!"),
|
||||
mixnet_contract_address: DEFAULT_NETWORK
|
||||
.mixnet_contract_address()
|
||||
.expect("mixnet contract address is unavailable"),
|
||||
mixnet_contract_address: String::default(),
|
||||
mnemonic: "exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day".to_string(),
|
||||
}
|
||||
}
|
||||
@@ -279,7 +276,7 @@ impl Default for Rewarding {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct CoconutSigner {
|
||||
/// Specifies whether rewarding service is enabled in this process.
|
||||
@@ -294,16 +291,6 @@ pub struct CoconutSigner {
|
||||
all_validator_apis: Vec<Url>,
|
||||
}
|
||||
|
||||
impl Default for CoconutSigner {
|
||||
fn default() -> Self {
|
||||
CoconutSigner {
|
||||
enabled: false,
|
||||
keypair_path: PathBuf::default(),
|
||||
all_validator_apis: config::defaults::default_api_endpoints(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Self {
|
||||
Config::default()
|
||||
|
||||
@@ -10,7 +10,10 @@ use crate::network_monitor::NetworkMonitorBuilder;
|
||||
use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater;
|
||||
use crate::nymd_client::Client;
|
||||
use crate::storage::ValidatorApiStorage;
|
||||
use ::config::defaults::DEFAULT_NETWORK;
|
||||
use ::config::defaults::setup_env;
|
||||
#[cfg(feature = "coconut")]
|
||||
use ::config::defaults::var_names::API_VALIDATOR;
|
||||
use ::config::defaults::var_names::{CONFIGURED, MIXNET_CONTRACT_ADDRESS, MIX_DENOM};
|
||||
use ::config::NymConfig;
|
||||
use anyhow::Result;
|
||||
use clap::{crate_version, App, Arg, ArgMatches};
|
||||
@@ -23,6 +26,8 @@ use rocket::{Ignite, Rocket};
|
||||
use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors};
|
||||
use rocket_okapi::mount_endpoints_and_merged_docs;
|
||||
use rocket_okapi::swagger_ui::make_swagger_ui;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{fs, process};
|
||||
@@ -50,6 +55,7 @@ mod swagger;
|
||||
mod coconut;
|
||||
|
||||
const ID: &str = "id";
|
||||
const CONFIG_ENV_FILE: &str = "config-env-file";
|
||||
const MONITORING_ENABLED: &str = "enable-monitor";
|
||||
const REWARDING_ENABLED: &str = "enable-rewarding";
|
||||
const MIXNET_CONTRACT_ARG: &str = "mixnet-contract";
|
||||
@@ -98,7 +104,6 @@ fn long_version() -> String {
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
"#,
|
||||
"Build Timestamp:",
|
||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||
@@ -115,9 +120,7 @@ fn long_version() -> String {
|
||||
"rustc Channel:",
|
||||
env!("VERGEN_RUSTC_CHANNEL"),
|
||||
"cargo Profile:",
|
||||
env!("VERGEN_CARGO_PROFILE"),
|
||||
"Network:",
|
||||
DEFAULT_NETWORK
|
||||
env!("VERGEN_CARGO_PROFILE")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -127,6 +130,12 @@ fn parse_args<'a>() -> ArgMatches<'a> {
|
||||
.version(crate_version!())
|
||||
.long_version(&*build_details)
|
||||
.author("Nymtech")
|
||||
.arg(
|
||||
Arg::with_name(CONFIG_ENV_FILE)
|
||||
.help("Path pointing to an env file that configures the validator API")
|
||||
.long(CONFIG_ENV_FILE)
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ID)
|
||||
.help("Id of the validator-api we want to run")
|
||||
@@ -273,6 +282,9 @@ fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
#[cfg(feature = "coconut")]
|
||||
if let Some(raw_validators) = matches.value_of(API_VALIDATORS_ARG) {
|
||||
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_validator) = matches.value_of(NYMD_VALIDATOR_ARG) {
|
||||
@@ -288,6 +300,10 @@ fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
|
||||
if let Some(mixnet_contract) = matches.value_of(MIXNET_CONTRACT_ARG) {
|
||||
config = config.with_custom_mixnet_contract(mixnet_contract)
|
||||
} else if std::env::var(CONFIGURED).is_ok() {
|
||||
let mixnet_contract =
|
||||
std::env::var(MIXNET_CONTRACT_ADDRESS).expect("mixnet contract not set");
|
||||
config = config.with_custom_mixnet_contract(mixnet_contract)
|
||||
}
|
||||
|
||||
if let Some(mnemonic) = matches.value_of(MNEMONIC_ARG) {
|
||||
@@ -413,6 +429,7 @@ fn expected_monitor_test_runs(config: &Config, interval_length: Duration) -> usi
|
||||
|
||||
async fn setup_rocket(
|
||||
config: &Config,
|
||||
_mix_denom: String,
|
||||
liftoff_notify: Arc<Notify>,
|
||||
_nymd_client: Client<SigningNymdClient>,
|
||||
) -> Result<Rocket<Ignite>> {
|
||||
@@ -452,6 +469,7 @@ async fn setup_rocket(
|
||||
let keypair = KeyPair::try_from_bs58(keypair_bs58)?;
|
||||
rocket.attach(InternalSignRequest::stage(
|
||||
_nymd_client,
|
||||
_mix_denom,
|
||||
keypair,
|
||||
QueryCommunicationChannel::new(config.get_all_validator_api_endpoints()),
|
||||
storage.clone().unwrap(),
|
||||
@@ -524,6 +542,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
if matches.is_present(WRITE_CONFIG_ARG) {
|
||||
return Ok(());
|
||||
}
|
||||
let mix_denom = std::env::var(MIX_DENOM).expect("mix denom not set");
|
||||
|
||||
let signing_nymd_client = Client::new_signing(&config);
|
||||
|
||||
@@ -532,6 +551,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
// let's build our rocket!
|
||||
let rocket = setup_rocket(
|
||||
&config,
|
||||
mix_denom,
|
||||
Arc::clone(&liftoff_notify),
|
||||
signing_nymd_client.clone(),
|
||||
)
|
||||
@@ -605,8 +625,6 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
|
||||
async fn main() -> Result<()> {
|
||||
println!("Starting validator api...");
|
||||
|
||||
let _ = dotenv::dotenv();
|
||||
|
||||
cfg_if::cfg_if! {if #[cfg(feature = "console-subscriber")] {
|
||||
// instriment tokio console subscriber needs RUSTFLAGS="--cfg tokio_unstable" at build time
|
||||
console_subscriber::init();
|
||||
@@ -614,5 +632,9 @@ async fn main() -> Result<()> {
|
||||
|
||||
setup_logging();
|
||||
let args = parse_args();
|
||||
let config_env_file = args
|
||||
.value_of(CONFIG_ENV_FILE)
|
||||
.map(|s| PathBuf::from_str(s).expect("invalid env config file"));
|
||||
setup_env(config_env_file);
|
||||
run_validator_api(args).await
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use config::defaults::{DEFAULT_NETWORK, DEFAULT_VALIDATOR_API_PORT};
|
||||
use config::defaults::{NymNetworkDetails, DEFAULT_VALIDATOR_API_PORT};
|
||||
use mixnet_contract_common::{
|
||||
reward_params::EpochRewardParams, ContractStateParams, Delegation, ExecuteMsg, GatewayBond,
|
||||
IdentityKey, Interval, MixNodeBond, MixnodeRewardingStatusResponse, RewardedSetNodeStatus,
|
||||
@@ -51,17 +51,16 @@ impl Client<QueryNymdClient> {
|
||||
.parse()
|
||||
.unwrap();
|
||||
let nymd_url = config.get_nymd_validator_url();
|
||||
let network = DEFAULT_NETWORK;
|
||||
let details = network
|
||||
.details()
|
||||
|
||||
let details = NymNetworkDetails::new_from_env()
|
||||
.with_mixnet_contract(Some(config.get_mixnet_contract_address()));
|
||||
|
||||
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);
|
||||
|
||||
let inner = validator_client::Client::new_query(client_config, network)
|
||||
.expect("Failed to connect to nymd!");
|
||||
let inner =
|
||||
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!");
|
||||
|
||||
Client(Arc::new(RwLock::new(inner)))
|
||||
}
|
||||
@@ -76,9 +75,7 @@ impl Client<SigningNymdClient> {
|
||||
.unwrap();
|
||||
let nymd_url = config.get_nymd_validator_url();
|
||||
|
||||
let network = DEFAULT_NETWORK;
|
||||
let details = network
|
||||
.details()
|
||||
let details = NymNetworkDetails::new_from_env()
|
||||
.with_mixnet_contract(Some(config.get_mixnet_contract_address()));
|
||||
|
||||
let client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
@@ -90,7 +87,7 @@ impl Client<SigningNymdClient> {
|
||||
.parse()
|
||||
.expect("the mnemonic is invalid!");
|
||||
|
||||
let inner = validator_client::Client::new_signing(client_config, network, mnemonic)
|
||||
let inner = validator_client::Client::new_signing(client_config, mnemonic)
|
||||
.expect("Failed to connect to nymd!");
|
||||
|
||||
Client(Arc::new(RwLock::new(inner)))
|
||||
|
||||
Reference in New Issue
Block a user