Compare commits

..

27 Commits

Author SHA1 Message Date
fmtabbara e68a2c2ae3 create reuseable ActionMenu component 2022-07-21 11:31:03 +01:00
fmtabbara e945c94afc rebuild BondedNodeCard using existing shared components 2022-07-20 22:01:14 +01:00
fmtabbara 641b8179ba fix displayed denom 2022-07-20 12:22:36 +01:00
fmtabbara df4385ab71 update coin types in new bonding page 2022-07-18 11:35:59 +01:00
pierre 777166b93d feat(wallet-bonding): fetch mixnode status 2022-07-18 11:10:08 +01:00
pierre ccd889fc38 chore(wallet-bonding): add todo 2022-07-18 11:09:58 +01:00
pierre d9291df347 fix(wallet-bonding): bonding context mock 2022-07-18 11:09:33 +01:00
pierre 5fcce2de48 fix(wallet-bonding): bonding context mock 2022-07-18 11:09:13 +01:00
pierre abf9ccb823 refactor(wallet-bonding): switch to simpledialog component to keep modals consistency 2022-07-18 11:09:11 +01:00
pierre 352527a098 feat(wallet-bonding): unbond with gasFee and request 2022-07-18 11:08:45 +01:00
pierre 724888e790 feat(wallet-bonding): unbond with gasFee and request 2022-07-18 11:08:38 +01:00
pierre 4b552db19f refactor(wallet-bonding): bonding flow with new gasFee estimation 2022-07-18 11:08:38 +01:00
pierre 75aa2579a0 feat(wallet-bonding): node menu ui 2022-07-18 11:08:38 +01:00
pierre 2493abcdff various ui adjustments 2022-07-18 11:08:23 +01:00
pierre 508f8324f9 feat(wallet): use confirmation modal component 2022-07-18 11:08:14 +01:00
pierre 468d0f38e9 feat(wallet-bonding): bond more flow (done) 2022-07-18 11:07:07 +01:00
pierre 3382642d70 feat(wallet-bonding): node settings flow 2022-07-18 11:07:07 +01:00
pierre 8d3f1a3c38 feat(wallet-bonding): new dialog component 2022-07-18 11:07:07 +01:00
pierre c5e695f8b5 refactor(wallet-bonding): code structure 2022-07-18 11:07:07 +01:00
pierre 80cfe83f9d refactor(wallet-bonding): code structure 2022-07-18 11:06:58 +01:00
pierre 76a22035be feat(wallet-bonding): node settings wip 2022-07-18 11:06:51 +01:00
pierre 79bc2ab493 feat(wallet-bonding): add node table component 2022-07-18 11:06:39 +01:00
pierre 8c2c0f8033 fix(wallet-bonding): post merge 2022-07-18 11:06:39 +01:00
pierre 5deb0875e2 feat(wallet-bonding): bonding page, new bond form wip 2022-07-18 11:06:39 +01:00
pierre d1ee6faca8 feat(wallet-bonding): bonding page, new bond form wip 2022-07-18 11:06:24 +01:00
pierre 756bad977f feat(wallet-bonding): add context mock 2022-07-18 11:04:12 +01:00
Mark Sinclair b6448691ce feat(wallet-bonding): create context wip 2022-07-18 11:04:02 +01:00
228 changed files with 4352 additions and 19233 deletions
-7
View File
@@ -27,8 +27,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- mixnode: Added basic mixnode hardware reporting to the HTTP API ([#1308]).
- validator-api: endpoint, in coconut mode, for returning the validator-api cosmos address ([#1404]).
- validator-client: add `denom` argument and add simple test for querying an account balance
- gateway, validator-api: Checks for coconut credential double spending attempts, taking the coconut bandwidth contract as source of truth ([#1457])
- coconut-bandwidth-contract: Record the state of a coconut credential; create specific proposal for releasing funds ([#1457])
### Fixed
@@ -51,9 +49,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- gateway & mixnode: move detailed build info back to `--version` from `--help`.
- 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
@@ -75,8 +70,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#1404]: https://github.com/nymtech/nym/pull/1404
[#1419]: https://github.com/nymtech/nym/pull/1419
[#1427]: https://github.com/nymtech/nym/pull/1427
[#1457]: https://github.com/nymtech/nym/pull/1457
[#1463]: https://github.com/nymtech/nym/pull/1463
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
Generated
+6 -5
View File
@@ -644,7 +644,6 @@ name = "coconut-bandwidth-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"multisig-contract-common",
"schemars",
"serde",
]
@@ -928,6 +927,7 @@ dependencies = [
"coconut-interface",
"cosmrs",
"crypto",
"network-defaults",
"rand 0.7.3",
"thiserror",
"url",
@@ -1590,7 +1590,6 @@ name = "explorer-api"
version = "1.0.1"
dependencies = [
"chrono",
"clap 3.2.8",
"humantime-serde",
"isocountry",
"itertools",
@@ -2871,6 +2870,7 @@ dependencies = [
"cosmwasm-std",
"fixed",
"log",
"network-defaults",
"schemars",
"serde",
"serde_repr",
@@ -2967,7 +2967,6 @@ name = "network-defaults"
version = "0.1.0"
dependencies = [
"cfg-if 1.0.0",
"dotenv",
"hex-literal",
"once_cell",
"serde",
@@ -3065,6 +3064,7 @@ dependencies = [
"credentials",
"crypto",
"dirs",
"dotenv",
"futures",
"gateway-client",
"gateway-requests",
@@ -3231,6 +3231,7 @@ dependencies = [
"credentials",
"crypto",
"dirs",
"dotenv",
"futures",
"gateway-client",
"gateway-requests",
@@ -3297,8 +3298,6 @@ dependencies = [
"credential-storage",
"credentials",
"crypto",
"cw-utils",
"cw3",
"dirs",
"dotenv",
"futures",
@@ -5388,6 +5387,7 @@ version = "1.0.1"
dependencies = [
"async-trait",
"log",
"network-defaults",
"reqwest",
"serde",
"serde_json",
@@ -6335,6 +6335,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
name = "vesting-contract"
version = "1.0.1"
dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"mixnet-contract-common",
+2 -1
View File
@@ -1,6 +1,7 @@
// 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;
@@ -365,7 +366,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: vec![],
validator_api_urls: default_api_endpoints(),
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(),
+4 -9
View File
@@ -4,7 +4,7 @@
use crate::error::Result;
use crate::{MNEMONIC, NYMD_URL};
use bip39::Mnemonic;
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use network_defaults::{DEFAULT_NETWORK, MIX_DENOM, VOUCHER_INFO};
use std::str::FromStr;
use url::Url;
use validator_client::nymd;
@@ -13,23 +13,18 @@ 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 network_details = NymNetworkDetails::new_from_env();
let config = nymd::Config::try_from_nym_network_details(&network_details)
let config = nymd::Config::try_from_nym_network_details(&DEFAULT_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,
mix_denom_base: network_details.chain_details.mix_denom.base,
}
Client { nymd_client }
}
pub async fn deposit(
@@ -39,7 +34,7 @@ impl Client {
encryption_key: String,
fee: Option<Fee>,
) -> Result<String> {
let amount = Coin::new(amount as u128, self.mix_denom_base.clone());
let amount = Coin::new(amount as u128, MIX_DENOM.base.to_string());
Ok(self
.nymd_client
.deposit(
+1
View File
@@ -22,6 +22,7 @@ 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
+4 -10
View File
@@ -3,6 +3,7 @@
use crate::client::config::{Config, SocketType};
use clap::{Parser, Subcommand};
use network_defaults::DEFAULT_NETWORK;
use url::Url;
#[cfg(not(feature = "coconut"))]
@@ -27,6 +28,7 @@ fn long_version() -> String {
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
"#,
"Build Timestamp:",
env!("VERGEN_BUILD_TIMESTAMP"),
@@ -44,6 +46,8 @@ fn long_version() -> String {
env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:",
env!("VERGEN_CARGO_PROFILE"),
"Network:",
DEFAULT_NETWORK
)
}
@@ -54,10 +58,6 @@ 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,12 +113,6 @@ 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 {
+10 -1
View File
@@ -3,7 +3,7 @@
use crate::client::config::{Config, MISSING_VALUE};
use config::NymConfig;
use config::{defaults::default_api_endpoints, NymConfig};
use version_checker::Version;
use clap::Args;
@@ -105,6 +105,15 @@ 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());
+1 -2
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, Parser};
use network_defaults::setup_env;
pub mod client;
pub mod commands;
@@ -10,11 +9,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;
}
+1
View File
@@ -13,6 +13,7 @@ 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"
+4 -8
View File
@@ -3,6 +3,7 @@
use crate::client::config::Config;
use clap::{Parser, Subcommand};
use network_defaults::DEFAULT_NETWORK;
use url::Url;
pub mod init;
@@ -27,6 +28,7 @@ fn long_version() -> String {
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
"#,
"Build Timestamp:",
env!("VERGEN_BUILD_TIMESTAMP"),
@@ -44,6 +46,8 @@ fn long_version() -> String {
env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:",
env!("VERGEN_CARGO_PROFILE"),
"Network:",
DEFAULT_NETWORK
)
}
@@ -54,10 +58,6 @@ 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,10 +112,6 @@ 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 {
+10 -1
View File
@@ -3,7 +3,7 @@
use crate::client::config::{Config, MISSING_VALUE};
use config::NymConfig;
use config::{defaults::default_api_endpoints, NymConfig};
use version_checker::Version;
use clap::Args;
@@ -104,6 +104,15 @@ 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());
+1 -2
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, Parser};
use network_defaults::setup_env;
pub mod client;
mod commands;
@@ -10,11 +9,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;
}
@@ -5,7 +5,8 @@ use crate::{validator_api, ValidatorClientError};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use url::Url;
use validator_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse,
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
@@ -28,7 +29,7 @@ use mixnet_contract_common::{
RewardedSetUpdateDetails,
};
#[cfg(feature = "nymd-client")]
use network_defaults::NymNetworkDetails;
use network_defaults::{all::Network, NymNetworkDetails};
#[cfg(feature = "nymd-client")]
use std::collections::{HashMap, HashSet};
@@ -113,6 +114,9 @@ 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>,
@@ -131,6 +135,9 @@ 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());
@@ -142,6 +149,7 @@ impl Client<SigningNymdClient> {
)?;
Ok(Client {
network,
mnemonic: Some(mnemonic),
mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit,
@@ -169,12 +177,18 @@ impl Client<SigningNymdClient> {
#[cfg(feature = "nymd-client")]
impl Client<QueryNymdClient> {
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
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> {
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,
@@ -733,4 +747,14 @@ impl ApiClient {
.verify_bandwidth_credential(request_body)
.await?)
}
pub async fn propose_release_funds(
&self,
request_body: &ProposeReleaseFundsRequestBody,
) -> Result<ProposeReleaseFundsResponse, ValidatorClientError> {
Ok(self
.validator_api
.propose_release_funds(request_body)
.await?)
}
}
@@ -1,33 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::error::NymdError;
use crate::nymd::{CosmWasmClient, NymdClient};
use coconut_bandwidth_contract_common::msg::QueryMsg;
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
use async_trait::async_trait;
#[async_trait]
pub trait CoconutBandwidthQueryClient {
async fn get_spent_credential(
&self,
blinded_serial_number: String,
) -> Result<SpendCredentialResponse, NymdError>;
}
#[async_trait]
impl<C: CosmWasmClient + Sync + Send> CoconutBandwidthQueryClient for NymdClient<C> {
async fn get_spent_credential(
&self,
blinded_serial_number: String,
) -> Result<SpendCredentialResponse, NymdError> {
let request = QueryMsg::GetSpentCredential {
blinded_serial_number,
};
self.client
.query_contract_smart(self.coconut_bandwidth_contract_address(), &request)
.await
}
}
@@ -5,7 +5,6 @@ pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
use crate::nymd::cosmwasm_client::types::ExecuteResult;
use crate::nymd::error::NymdError;
use crate::nymd::{Coin, Fee, NymdClient};
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialData;
use coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg};
use async_trait::async_trait;
@@ -20,13 +19,6 @@ pub trait CoconutBandwidthSigningClient {
encryption_key: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn spend_credential(
&self,
funds: Coin,
blinded_serial_number: String,
gateway_cosmos_address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
}
#[async_trait]
@@ -54,30 +46,4 @@ impl<C: SigningCosmWasmClient + Sync + Send> CoconutBandwidthSigningClient for N
)
.await
}
async fn spend_credential(
&self,
funds: Coin,
blinded_serial_number: String,
gateway_cosmos_address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
let req = ExecuteMsg::SpendCredential {
data: SpendCredentialData::new(
funds.into(),
blinded_serial_number,
gateway_cosmos_address,
),
};
self.client
.execute(
self.address(),
self.coconut_bandwidth_contract_address(),
&req,
fee,
"CoconutBandwidth::SpendCredential",
vec![],
)
.await
}
}
@@ -1,16 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod coconut_bandwidth_query_client;
mod coconut_bandwidth_signing_client;
mod multisig_query_client;
mod multisig_signing_client;
mod vesting_query_client;
mod vesting_signing_client;
pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient;
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
pub use multisig_query_client::MultisigQueryClient;
pub use multisig_query_client::QueryClient;
pub use multisig_signing_client::MultisigSigningClient;
pub use vesting_query_client::VestingQueryClient;
pub use vesting_signing_client::VestingSigningClient;
@@ -9,12 +9,12 @@ use multisig_contract_common::msg::{ProposalResponse, QueryMsg};
use async_trait::async_trait;
#[async_trait]
pub trait MultisigQueryClient {
pub trait QueryClient {
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError>;
}
#[async_trait]
impl<C: CosmWasmClient + Sync + Send> MultisigQueryClient for NymdClient<C> {
impl<C: CosmWasmClient + Sync + Send> QueryClient for NymdClient<C> {
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError> {
let request = QueryMsg::Proposal { proposal_id };
self.client
@@ -12,6 +12,7 @@ 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 {
@@ -48,10 +49,7 @@ 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,
self.config.chain_details.mix_denom.base.clone(),
),
funds: Coin::new(voucher_value, DEFAULT_NETWORK.mix_denom().base),
};
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: self.coconut_bandwidth_contract_address().to_string(),
@@ -207,20 +207,35 @@ 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 prefix = MAINNET.bech32_prefix();
let addrs = vec![
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
"n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es",
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
let prefixes = vec![
MAINNET.bech32_prefix(),
SANDBOX.bech32_prefix(),
QA.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()
)
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()
)
}
}
}
}
@@ -8,7 +8,8 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use url::Url;
use validator_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse,
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
@@ -404,6 +405,23 @@ impl Client {
)
.await
}
pub async fn propose_release_funds(
&self,
request_body: &ProposeReleaseFundsRequestBody,
) -> Result<ProposeReleaseFundsResponse, ValidatorAPIError> {
self.post_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_PROPOSE_RELEASE_FUNDS,
],
NO_PARAMS,
request_body,
)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
@@ -19,6 +19,7 @@ pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-creden
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const COCONUT_COSMOS_ADDRESS: &str = "cosmos-address";
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
pub const COCONUT_PROPOSE_RELEASE_FUNDS: &str = "propose-release-funds";
pub const STATUS_ROUTES: &str = "status";
pub const MIXNODE: &str = "mixnode";
@@ -9,4 +9,3 @@ edition = "2021"
cosmwasm-std = "1.0.0"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
multisig-contract-common = { path = "../multisig-contract" }
@@ -1,4 +1,3 @@
pub mod deposit;
pub mod events;
pub mod msg;
pub mod spend_credential;
@@ -5,34 +5,24 @@ use cosmwasm_std::Coin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{deposit::DepositData, spend_credential::SpendCredentialData};
use crate::deposit::DepositData;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct InstantiateMsg {
pub multisig_addr: String,
pub pool_addr: String,
pub mix_denom: String,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
DepositFunds { data: DepositData },
SpendCredential { data: SpendCredentialData },
ReleaseFunds { funds: Coin },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetSpentCredential {
blinded_serial_number: String,
},
GetAllSpentCredentials {
limit: Option<u32>,
start_after: Option<String>,
},
}
pub enum QueryMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
@@ -1,148 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{from_binary, to_binary, Addr, Coin, CosmosMsg, StdResult, WasmMsg};
use multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::msg::ExecuteMsg;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct SpendCredentialData {
funds: Coin,
blinded_serial_number: String,
gateway_cosmos_address: String,
}
impl SpendCredentialData {
pub fn new(funds: Coin, blinded_serial_number: String, gateway_cosmos_address: String) -> Self {
SpendCredentialData {
funds,
blinded_serial_number,
gateway_cosmos_address,
}
}
pub fn funds(&self) -> &Coin {
&self.funds
}
pub fn blinded_serial_number(&self) -> &str {
&self.blinded_serial_number
}
pub fn gateway_cosmos_address(&self) -> &str {
&self.gateway_cosmos_address
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub enum SpendCredentialStatus {
InProgress,
Spent,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct SpendCredential {
funds: Coin,
blinded_serial_number: String,
gateway_cosmos_address: Addr,
status: SpendCredentialStatus,
}
impl SpendCredential {
pub fn new(funds: Coin, blinded_serial_number: String, gateway_cosmos_address: Addr) -> Self {
SpendCredential {
funds,
blinded_serial_number,
gateway_cosmos_address,
status: SpendCredentialStatus::InProgress,
}
}
pub fn blinded_serial_number(&self) -> &str {
&self.blinded_serial_number
}
pub fn status(&self) -> SpendCredentialStatus {
self.status
}
pub fn mark_as_spent(&mut self) {
self.status = SpendCredentialStatus::Spent;
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedSpendCredentialResponse {
pub spend_credentials: Vec<SpendCredential>,
pub per_page: usize,
pub start_next_after: Option<String>,
}
impl PagedSpendCredentialResponse {
pub fn new(
spend_credentials: Vec<SpendCredential>,
per_page: usize,
start_next_after: Option<String>,
) -> Self {
PagedSpendCredentialResponse {
spend_credentials,
per_page,
start_next_after,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct SpendCredentialResponse {
pub spend_credential: Option<SpendCredential>,
}
impl SpendCredentialResponse {
pub fn new(spend_credential: Option<SpendCredential>) -> Self {
SpendCredentialResponse { spend_credential }
}
}
pub fn to_cosmos_msg(
funds: Coin,
blinded_serial_number: String,
coconut_bandwidth_addr: String,
multisig_addr: String,
) -> StdResult<CosmosMsg> {
let release_funds_req = ExecuteMsg::ReleaseFunds { funds };
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: coconut_bandwidth_addr,
msg: to_binary(&release_funds_req)?,
funds: vec![],
});
let req = MultisigExecuteMsg::Propose {
title: String::from("Release funds, as ordered by Coconut Bandwidth Contract"),
description: blinded_serial_number,
msgs: vec![release_funds_msg],
latest: None,
};
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: multisig_addr,
msg: to_binary(&req)?,
funds: vec![],
});
Ok(msg)
}
pub fn funds_from_cosmos_msgs(msgs: Vec<CosmosMsg>) -> Option<Coin> {
if let Some(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: _,
msg,
funds: _,
})) = msgs.get(0)
{
if let Ok(ExecuteMsg::ReleaseFunds { funds }) = from_binary::<ExecuteMsg>(msg) {
return Some(funds);
}
}
None
}
@@ -14,6 +14,7 @@ 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,7 +10,6 @@ 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,7 +14,6 @@ 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,
}
@@ -78,8 +77,3 @@ pub enum QueryMsg {
limit: Option<u32>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {
pub coconut_bandwidth_address: String,
}
@@ -1,5 +1,6 @@
// 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};
@@ -9,8 +10,8 @@ pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
pub mod events;
pub mod messages;
pub fn one_ucoin(denom: String) -> Coin {
Coin::new(1, denom)
pub fn one_ucoin() -> Coin {
Coin::new(1, MIX_DENOM.base)
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
@@ -7,7 +7,6 @@ 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)]
+1
View File
@@ -14,6 +14,7 @@ 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" }
+7 -11
View File
@@ -1,3 +1,4 @@
use config::defaults::DEFAULT_NETWORK;
use subtle_encoding::bech32;
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -14,15 +15,16 @@ pub fn try_bech32_decode(address: &str) -> Result<String, Bech32Error> {
}
}
pub fn validate_bech32_prefix(bech32_prefix: &str, address: &str) -> Result<(), Bech32Error> {
pub fn validate_bech32_prefix(address: &str) -> Result<(), Bech32Error> {
let prefix = try_bech32_decode(address)?;
if prefix == bech32_prefix {
if prefix == DEFAULT_NETWORK.bech32_prefix() {
Ok(())
} else {
Err(Bech32Error::WrongPrefix(format!(
"your bech32 address prefix should be {}, not {}",
bech32_prefix, prefix
DEFAULT_NETWORK.bech32_prefix(),
prefix
)))
}
}
@@ -31,8 +33,6 @@ pub fn validate_bech32_prefix(bech32_prefix: &str, address: &str) -> Result<(),
mod tests {
use super::*;
const TEST_BECH32_PREFIX: &str = "n";
mod decoding_bech32_addresses {
use super::*;
@@ -71,12 +71,9 @@ mod tests {
assert_eq!(
Err(Bech32Error::WrongPrefix(format!(
"your bech32 address prefix should be {}, not punk",
TEST_BECH32_PREFIX
DEFAULT_NETWORK.bech32_prefix()
))),
validate_bech32_prefix(
TEST_BECH32_PREFIX,
"punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0"
)
validate_bech32_prefix("punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0")
)
}
@@ -85,7 +82,6 @@ mod tests {
assert_eq!(
Ok(()),
validate_bech32_prefix(
TEST_BECH32_PREFIX,
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g"
)
)
-1
View File
@@ -8,7 +8,6 @@ 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"]}
+11
View File
@@ -0,0 +1,11 @@
// 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"),
}
}
+61 -63
View File
@@ -3,7 +3,6 @@
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::{env::var, path::PathBuf};
use url::Url;
pub mod all;
@@ -11,10 +10,36 @@ pub mod eth_contract;
pub mod mainnet;
pub mod qa;
pub mod sandbox;
pub mod var_names;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
// 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;
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ChainDetails {
@@ -57,56 +82,23 @@ impl NymNetworkDetails {
NymNetworkDetails::default()
}
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_qa() -> Self {
(&*QA_DEFAULTS).into()
}
pub fn new_sandbox() -> Self {
(&*SANDBOX_DEFAULTS).into()
}
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
@@ -341,21 +333,27 @@ impl ValidatorDetails {
}
}
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(),
_ => {}
}
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()
}
// Name of the event triggered by the eth contract. If the event name is changed,
+4 -44
View File
@@ -1,7 +1,6 @@
// 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";
@@ -25,48 +24,9 @@ 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(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);
vec![ValidatorDetails::new(
"https://rpc.nyx.nodes.guru/",
Some("https://validator.nymtech.net/api/"),
)]
}
-21
View File
@@ -1,21 +0,0 @@
// 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";
+2 -1
View File
@@ -34,7 +34,8 @@ mod error;
mod impls;
mod proofs;
mod scheme;
pub mod tests;
#[cfg(test)]
mod tests;
mod traits;
mod utils;
+63 -3
View File
@@ -1,13 +1,23 @@
use crate::{
aggregate_verification_keys, setup, tests::helpers::theta_from_keys_and_attributes, ttp_keygen,
verify_credential, CoconutError, VerificationKey,
aggregate_signature_shares, aggregate_verification_keys, blind_sign, prepare_blind_sign,
prove_bandwidth_credential, setup, ttp_keygen, verify_credential, CoconutError, Signature,
SignatureShare, VerificationKey,
};
use itertools::izip;
#[test]
fn main() -> Result<(), CoconutError> {
let params = setup(5)?;
let public_attributes = params.n_random_scalars(2);
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
// generate commitment
let (commitments_openings, blind_sign_request) =
prepare_blind_sign(&params, &private_attributes, &public_attributes)?;
// generate_keys
let coconut_keypairs = ttp_keygen(&params, 2, 3)?;
@@ -20,8 +30,58 @@ fn main() -> Result<(), CoconutError> {
// aggregate verification keys
let verification_key = aggregate_verification_keys(&verification_keys, Some(&[1, 2, 3]))?;
// generate blinded signatures
let mut blinded_signatures = Vec::new();
for keypair in coconut_keypairs {
let blinded_signature = blind_sign(
&params,
&keypair.secret_key(),
&blind_sign_request,
&public_attributes,
)?;
blinded_signatures.push(blinded_signature)
}
// Unblind
let unblinded_signatures: Vec<Signature> =
izip!(blinded_signatures.iter(), verification_keys.iter())
.map(|(s, vk)| {
s.unblind(
&params,
vk,
&private_attributes,
&public_attributes,
&blind_sign_request.get_commitment_hash(),
&commitments_openings,
)
.unwrap()
})
.collect();
// Aggregate signatures
let signature_shares: Vec<SignatureShare> = unblinded_signatures
.iter()
.enumerate()
.map(|(idx, signature)| SignatureShare::new(*signature, (idx + 1) as u64))
.collect();
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
attributes.extend_from_slice(&private_attributes);
attributes.extend_from_slice(&public_attributes);
// Randomize credentials and generate any cryptographic material to verify them
let signature =
aggregate_signature_shares(&params, &verification_key, &attributes, &signature_shares)?;
// Generate cryptographic material to verify them
let theta = theta_from_keys_and_attributes(&params, &coconut_keypairs, &public_attributes)?;
let theta = prove_bandwidth_credential(
&params,
&verification_key,
&signature,
serial_number,
binding_number,
)?;
// Verify credentials
assert!(verify_credential(
-87
View File
@@ -1,87 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::*;
use itertools::izip;
pub fn theta_from_keys_and_attributes(
params: &Parameters,
coconut_keypairs: &Vec<KeyPair>,
public_attributes: &Vec<PublicAttribute>,
) -> Result<Theta, CoconutError> {
let serial_number = params.random_scalar();
let binding_number = params.random_scalar();
let private_attributes = vec![serial_number, binding_number];
// generate commitment
let (commitments_openings, blind_sign_request) =
prepare_blind_sign(params, &private_attributes, public_attributes)?;
let verification_keys: Vec<VerificationKey> = coconut_keypairs
.iter()
.map(|keypair| keypair.verification_key())
.collect();
// aggregate verification keys
let indices: Vec<u64> = coconut_keypairs
.iter()
.enumerate()
.map(|(idx, _)| (idx + 1) as u64)
.collect();
let verification_key = aggregate_verification_keys(&verification_keys, Some(&indices))?;
// generate blinded signatures
let mut blinded_signatures = Vec::new();
for keypair in coconut_keypairs {
let blinded_signature = blind_sign(
params,
&keypair.secret_key(),
&blind_sign_request,
public_attributes,
)?;
blinded_signatures.push(blinded_signature)
}
// Unblind
let unblinded_signatures: Vec<Signature> =
izip!(blinded_signatures.iter(), verification_keys.iter())
.map(|(s, vk)| {
s.unblind(
params,
vk,
&private_attributes,
public_attributes,
&blind_sign_request.get_commitment_hash(),
&commitments_openings,
)
.unwrap()
})
.collect();
// Aggregate signatures
let signature_shares: Vec<SignatureShare> = unblinded_signatures
.iter()
.enumerate()
.map(|(idx, signature)| SignatureShare::new(*signature, (idx + 1) as u64))
.collect();
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
attributes.extend_from_slice(&private_attributes);
attributes.extend_from_slice(public_attributes);
// Randomize credentials and generate any cryptographic material to verify them
let signature =
aggregate_signature_shares(params, &verification_key, &attributes, &signature_shares)?;
// Generate cryptographic material to verify them
let theta = prove_bandwidth_credential(
params,
&verification_key,
&signature,
serial_number,
binding_number,
)?;
Ok(theta)
}
-2
View File
@@ -1,3 +1 @@
#[cfg(test)]
mod e2e;
pub mod helpers;
+2
View File
@@ -17,3 +17,5 @@ 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" }
+5 -30
View File
@@ -50,6 +50,7 @@ name = "bandwidth-claim"
version = "1.0.0"
dependencies = [
"bandwidth-claim-contract",
"config",
"cosmwasm-std",
"cosmwasm-storage",
"schemars",
@@ -220,11 +221,12 @@ version = "0.1.0"
dependencies = [
"bandwidth-claim-contract",
"coconut-bandwidth-contract-common",
"config",
"cosmwasm-std",
"cosmwasm-storage",
"cw-controllers",
"cw-multi-test",
"cw-storage-plus",
"multisig-contract-common",
"schemars",
"serde",
"thiserror",
@@ -235,32 +237,10 @@ name = "coconut-bandwidth-contract-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"multisig-contract-common",
"schemars",
"serde",
]
[[package]]
name = "coconut-test"
version = "0.1.0"
dependencies = [
"bandwidth-claim-contract",
"coconut-bandwidth",
"coconut-bandwidth-contract-common",
"cosmwasm-std",
"cosmwasm-storage",
"cw-controllers",
"cw-multi-test",
"cw-storage-plus",
"cw-utils",
"cw3-flex-multisig",
"cw4-group",
"multisig-contract-common",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "config"
version = "0.1.0"
@@ -612,12 +592,6 @@ 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"
@@ -1082,6 +1056,7 @@ dependencies = [
"cosmwasm-std",
"fixed",
"log",
"network-defaults",
"schemars",
"serde",
"serde_repr",
@@ -1107,7 +1082,6 @@ name = "network-defaults"
version = "0.1.0"
dependencies = [
"cfg-if",
"dotenv",
"hex-literal",
"once_cell",
"serde",
@@ -1854,6 +1828,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
name = "vesting-contract"
version = "1.0.1"
dependencies = [
"config",
"cosmwasm-std",
"cw-storage-plus",
"mixnet-contract-common",
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
members = ["bandwidth-claim", "coconut-bandwidth", "mixnet", "vesting", "multisig/cw3-flex-multisig", "multisig/cw4-group", "coconut-test"]
members = ["bandwidth-claim", "coconut-bandwidth", "mixnet", "vesting", "multisig/cw3-flex-multisig", "multisig/cw4-group"]
[profile.release]
opt-level = 3
+2
View File
@@ -9,6 +9,8 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dev-dependencies]
config = { path = "../../common/config"}
[dependencies]
bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
+3 -4
View File
@@ -62,11 +62,10 @@ 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();
@@ -92,11 +91,11 @@ pub mod tests {
// Contract balance should match what we initialized it as
assert_eq!(
coins(0, TEST_MIX_DENOM),
coins(0, MIX_DENOM.base),
vec![deps
.as_ref()
.querier
.query_balance(env.contract.address, TEST_MIX_DENOM)
.query_balance(env.contract.address, MIX_DENOM.base)
.unwrap()]
);
}
+4 -1
View File
@@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
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"
@@ -21,3 +21,6 @@ cw-controllers = "0.13.4"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
[dev-dependencies]
cw-multi-test = { version = "0.13.2" }
+77 -23
View File
@@ -1,14 +1,11 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
};
use cosmwasm_std::{entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use coconut_bandwidth_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use crate::error::ContractError;
use crate::queries::{query_all_spent_credentials_paged, query_spent_credential};
use crate::state::{Config, ADMIN, CONFIG};
use crate::transactions;
@@ -25,14 +22,12 @@ 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)?;
@@ -49,23 +44,13 @@ pub fn execute(
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::DepositFunds { data } => transactions::deposit_funds(deps, env, info, data),
ExecuteMsg::SpendCredential { data } => {
transactions::spend_credential(deps, env, info, data)
}
ExecuteMsg::ReleaseFunds { funds } => transactions::release_funds(deps, env, info, funds),
}
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetAllSpentCredentials { limit, start_after } => to_binary(
&query_all_spent_credentials_paged(deps, start_after, limit)?,
),
QueryMsg::GetSpentCredential {
blinded_serial_number,
} => to_binary(&query_spent_credential(deps, blinded_serial_number)?),
}
pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult<Binary> {
unimplemented!();
}
#[entry_point]
@@ -76,10 +61,12 @@ 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 cosmwasm_std::coins;
use coconut_bandwidth_contract_common::deposit::DepositData;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, Addr};
use cw_multi_test::Executor;
#[test]
fn initialize_contract() {
@@ -88,7 +75,6 @@ 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", &[]);
@@ -97,12 +83,80 @@ mod tests {
// Contract balance should be 0
assert_eq!(
coins(0, TEST_MIX_DENOM),
coins(0, MIX_DENOM.base),
vec![deps
.as_ref()
.querier
.query_balance(env.contract.address, TEST_MIX_DENOM)
.query_balance(env.contract.address, MIX_DENOM.base)
.unwrap()]
);
}
#[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 mut app = mock_app(&init_funds);
let multisig_addr = String::from(MULTISIG_CONTRACT);
let pool_addr = String::from(POOL_CONTRACT);
let code_id = app.store_code(contract_bandwidth());
let msg = InstantiateMsg {
multisig_addr: multisig_addr.clone(),
pool_addr: pool_addr.clone(),
};
let contract_addr = app
.instantiate_contract(
code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"bandwidth",
None,
)
.unwrap();
let msg = ExecuteMsg::DepositFunds {
data: DepositData::new(
String::from("info"),
String::from("id"),
String::from("enc"),
),
};
app.execute_contract(
Addr::unchecked(OWNER),
contract_addr.clone(),
&msg,
&deposit_funds,
)
.unwrap();
// try to release more then it's in the contract
let msg = ExecuteMsg::ReleaseFunds {
funds: release_funds[0].clone(),
};
let err = app
.execute_contract(
Addr::unchecked(multisig_addr.clone()),
contract_addr.clone(),
&msg,
&[],
)
.unwrap_err();
assert_eq!(ContractError::NotEnoughFunds, err.downcast().unwrap());
let msg = ExecuteMsg::ReleaseFunds {
funds: deposit_funds[0].clone(),
};
app.execute_contract(
Addr::unchecked(multisig_addr),
contract_addr.clone(),
&msg,
&[],
)
.unwrap();
let pool_bal = app.wrap().query_balance(pool_addr, MIX_DENOM.base).unwrap();
assert_eq!(pool_bal, deposit_funds[0]);
}
}
+4 -5
View File
@@ -5,6 +5,8 @@ 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.
@@ -20,15 +22,12 @@ pub enum ContractError {
#[error("No coin was sent for voucher")]
NoCoin,
#[error("Wrong coin denomination, you must send {mix_denom}")]
WrongDenom { mix_denom: String },
#[error("Wrong coin denomination, you must send {}", MIX_DENOM.base)]
WrongDenom,
#[error("There aren't enough funds in the contract")]
NotEnoughFunds,
#[error("Credential already spent or in process of spending")]
DuplicateBlindedSerialNumber,
#[error("{0}")]
Admin(#[from] AdminError),
}
+1 -3
View File
@@ -2,9 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
pub mod contract;
pub mod error;
mod queries;
mod error;
mod state;
mod storage;
mod support;
mod transactions;
-178
View File
@@ -1,178 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_bandwidth_contract_common::spend_credential::{
PagedSpendCredentialResponse, SpendCredential, SpendCredentialResponse,
};
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use crate::storage::{self, SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT, SPEND_CREDENTIAL_PAGE_MAX_LIMIT};
pub(crate) fn query_all_spent_credentials_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedSpendCredentialResponse> {
let limit = limit
.unwrap_or(SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT)
.min(SPEND_CREDENTIAL_PAGE_MAX_LIMIT) as usize;
let start = start_after.as_deref().map(Bound::exclusive);
let nodes = storage::spent_credentials()
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<SpendCredential>>>()?;
let start_next_after = nodes
.last()
.map(|spend_credential| spend_credential.blinded_serial_number().to_string());
Ok(PagedSpendCredentialResponse::new(
nodes,
limit,
start_next_after,
))
}
pub(crate) fn query_spent_credential(
deps: Deps<'_>,
blinded_serial_number: String,
) -> StdResult<SpendCredentialResponse> {
let spend_credential =
storage::spent_credentials().may_load(deps.storage, &blinded_serial_number)?;
Ok(SpendCredentialResponse::new(spend_credential))
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::support::tests::fixtures::spend_credential_data_fixture;
use crate::support::tests::helpers::init_contract;
use crate::transactions::spend_credential;
use cosmwasm_std::testing::{mock_env, mock_info};
#[test]
fn spent_credentials_empty_on_init() {
let deps = init_contract();
let response =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(2)).unwrap();
assert_eq!(0, response.spend_credentials.len());
}
#[test]
fn spent_credentials_paged_retrieval_obeys_limits() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let limit = 2;
for n in 0..1000 {
let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n));
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
}
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
assert_eq!(limit, page1.spend_credentials.len() as u32);
}
#[test]
fn spent_credentials_paged_retrieval_has_default_limit() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
for n in 0..1000 {
let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n));
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
}
// query without explicitly setting a limit
let page1 = query_all_spent_credentials_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(
SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT,
page1.spend_credentials.len() as u32
);
}
#[test]
fn spent_credentials_paged_retrieval_has_max_limit() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
for n in 0..1000 {
let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n));
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
}
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * SPEND_CREDENTIAL_PAGE_MAX_LIMIT;
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(crazy_limit))
.unwrap();
// we default to a decent sized upper bound instead
let expected_limit = SPEND_CREDENTIAL_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.spend_credentials.len() as u32);
}
#[test]
fn spent_credentials_pagination_works() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let data = spend_credential_data_fixture("blinded_serial_number1");
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
let per_page = 2;
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.spend_credentials.len());
// save another
let data = spend_credential_data_fixture("blinded_serial_number2");
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
// page1 should have 2 results on it
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.spend_credentials.len());
let data = spend_credential_data_fixture("blinded_serial_number3");
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
// page1 still has 2 results
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.spend_credentials.len());
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_all_spent_credentials_paged(
deps.as_ref(),
Option::from(start_after.clone()),
Option::from(per_page),
)
.unwrap();
assert_eq!(1, page2.spend_credentials.len());
let data = spend_credential_data_fixture("blinded_serial_number4");
spend_credential(deps.as_mut(), env, info, data).unwrap();
let page2 = query_all_spent_credentials_paged(
deps.as_ref(),
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.spend_credentials.len());
}
}
-1
View File
@@ -13,7 +13,6 @@ 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");
-106
View File
@@ -1,106 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_bandwidth_contract_common::spend_credential::SpendCredential;
use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex};
// storage prefixes
const SPEND_CREDENTIAL_PK_NAMESPACE: &str = "sc";
const SPEND_CREDENTIAL_BLINDED_SERIAL_NO_IDX_NAMESPACE: &str = "scn";
// paged retrieval limits for all queries and transactions
pub(crate) const SPEND_CREDENTIAL_PAGE_MAX_LIMIT: u32 = 75;
pub(crate) const SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT: u32 = 50;
pub(crate) struct SpendCredentialIndex<'a> {
pub(crate) blinded_serial_number: UniqueIndex<'a, String, SpendCredential>,
}
// IndexList is just boilerplate code for fetching a struct's indexes
// note that from my understanding this will be converted into a macro at some point in the future
impl<'a> IndexList<SpendCredential> for SpendCredentialIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<SpendCredential>> + '_> {
let v: Vec<&dyn Index<SpendCredential>> = vec![&self.blinded_serial_number];
Box::new(v.into_iter())
}
}
// gateways() is the storage access function.
pub(crate) fn spent_credentials<'a>(
) -> IndexedMap<'a, &'a str, SpendCredential, SpendCredentialIndex<'a>> {
let indexes = SpendCredentialIndex {
blinded_serial_number: UniqueIndex::new(
|d| d.blinded_serial_number().to_string(),
SPEND_CREDENTIAL_BLINDED_SERIAL_NO_IDX_NAMESPACE,
),
};
IndexedMap::new(SPEND_CREDENTIAL_PK_NAMESPACE, indexes)
}
// currently not used outside tests
#[cfg(test)]
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 cosmwasm_std::testing::MockStorage;
use cosmwasm_std::Addr;
use cosmwasm_std::Coin;
#[test]
fn spend_credential_single_read_retrieval() {
let mut storage = MockStorage::new();
let blind_serial_number1 = "number1";
let blind_serial_number2 = "number2";
let spend1 = fixtures::spend_credential_fixture(blind_serial_number1);
let spend2 = fixtures::spend_credential_fixture(blind_serial_number2);
storage::spent_credentials()
.save(&mut storage, blind_serial_number1, &spend1)
.unwrap();
storage::spent_credentials()
.save(&mut storage, blind_serial_number2, &spend2)
.unwrap();
let res1 = storage::spent_credentials()
.load(&storage, blind_serial_number1)
.unwrap();
let res2 = storage::spent_credentials()
.load(&storage, blind_serial_number2)
.unwrap();
assert_eq!(spend1, res1);
assert_eq!(spend2, res2);
}
#[test]
fn mark_as_spent_credential() {
let mut mock_storage = MockStorage::new();
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");
let res = storage::spent_credentials()
.may_load(&mock_storage, blind_serial_number)
.unwrap();
assert!(res.is_none());
let mut spend_credential = SpendCredential::new(
funds.clone(),
blind_serial_number.to_string(),
gateway_cosmos_address.clone(),
);
spend_credential.mark_as_spent();
storage::spent_credentials()
.save(&mut mock_storage, blind_serial_number, &spend_credential)
.unwrap();
let ret = storage::spent_credentials()
.load(&mock_storage, blind_serial_number)
.unwrap();
assert_eq!(ret, spend_credential);
assert_eq!(ret.status(), SpendCredentialStatus::Spent);
}
}
@@ -1,5 +1,3 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub mod tests;
@@ -0,0 +1,45 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub mod helpers {
pub const OWNER: &str = "admin0001";
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const POOL_CONTRACT: &str = "mix pool contract address";
use crate::contract::instantiate;
use coconut_bandwidth_contract_common::msg::InstantiateMsg;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{Addr, Coin, Empty, MemoryStorage, OwnedDeps};
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper};
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),
};
let env = mock_env();
let info = mock_info("creator", &[]);
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
deps
}
pub fn mock_app(init_funds: &[Coin]) -> App {
AppBuilder::new().build(|router, _, storage| {
router
.bank
.init_balance(storage, &Addr::unchecked(OWNER), init_funds.to_vec())
.unwrap();
})
}
pub fn contract_bandwidth() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
crate::contract::execute,
crate::contract::instantiate,
crate::contract::query,
);
Box::new(contract)
}
}
@@ -1,23 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_bandwidth_contract_common::spend_credential::{SpendCredential, SpendCredentialData};
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, TEST_MIX_DENOM),
blinded_serial_number.to_string(),
Addr::unchecked("gateway_owner_addr"),
)
}
pub fn spend_credential_data_fixture(blinded_serial_number: &str) -> SpendCredentialData {
SpendCredentialData::new(
Coin::new(100, TEST_MIX_DENOM),
blinded_serial_number.to_string(),
"gateway_owner_addr".to_string(),
)
}
@@ -1,25 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const POOL_CONTRACT: &str = "mix pool contract address";
use crate::contract::instantiate;
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", &[]);
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
deps
}
@@ -1,5 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod fixtures;
pub mod helpers;
+16 -161
View File
@@ -1,23 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_bandwidth_contract_common::spend_credential::{
to_cosmos_msg, SpendCredential, SpendCredentialData,
};
use cosmwasm_std::{BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Response};
use crate::error::ContractError;
use crate::state::{ADMIN, CONFIG};
use crate::storage;
use coconut_bandwidth_contract_common::deposit::DepositData;
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,
@@ -28,9 +25,8 @@ pub(crate) fn deposit_funds(
if info.funds.len() > 1 {
return Err(ContractError::MultipleDenoms);
}
let mix_denom = CONFIG.load(deps.storage)?.mix_denom;
if info.funds[0].denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
if info.funds[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom);
}
let voucher_value = info.funds.last().unwrap();
@@ -43,55 +39,18 @@ pub(crate) fn deposit_funds(
Ok(Response::new().add_event(event))
}
pub(crate) fn spend_credential(
deps: DepsMut<'_>,
env: Env,
_info: MessageInfo,
data: SpendCredentialData,
) -> Result<Response, ContractError> {
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);
}
let cfg = CONFIG.load(deps.storage)?;
let gateway_cosmos_address = deps.api.addr_validate(data.gateway_cosmos_address())?;
storage::spent_credentials().save(
deps.storage,
data.blinded_serial_number(),
&SpendCredential::new(
data.funds().to_owned(),
data.blinded_serial_number().to_owned(),
gateway_cosmos_address,
),
)?;
let msg = to_cosmos_msg(
data.funds().clone(),
data.blinded_serial_number().to_string(),
env.contract.address.into_string(),
cfg.multisig_addr.into_string(),
)?;
Ok(Response::new().add_message(msg))
}
pub(crate) fn release_funds(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
funds: Coin,
) -> Result<Response, ContractError> {
let mix_denom = CONFIG.load(deps.storage)?.mix_denom;
if funds.denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
if funds.denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom);
}
let current_balance = deps
.querier
.query_balance(env.contract.address, mix_denom)?;
.query_balance(env.contract.address, MIX_DENOM.base)?;
if funds.amount > current_balance.amount {
return Err(ContractError::NotEnoughFunds);
}
@@ -111,13 +70,11 @@ pub(crate) fn release_funds(
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::fixtures::spend_credential_data_fixture;
use crate::support::tests::helpers::{self, MULTISIG_CONTRACT, POOL_CONTRACT};
use coconut_bandwidth_contract_common::msg::ExecuteMsg;
use crate::support::tests::helpers;
use crate::support::tests::helpers::{MULTISIG_CONTRACT, POOL_CONTRACT};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{from_binary, Coin, CosmosMsg, WasmMsg};
use cosmwasm_std::{Coin, CosmosMsg};
use cw_controllers::AdminError;
use multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
#[test]
fn invalid_deposit() {
@@ -135,7 +92,7 @@ mod tests {
Err(ContractError::NoCoin)
);
let coin = Coin::new(1000000, crate::support::tests::fixtures::TEST_MIX_DENOM);
let coin = Coin::new(1000000, MIX_DENOM.base);
let second_coin = Coin::new(1000000, "some_denom");
let info = mock_info("requester", &[coin, second_coin.clone()]);
@@ -147,9 +104,7 @@ mod tests {
let info = mock_info("requester", &[second_coin]);
assert_eq!(
deposit_funds(deps.as_mut(), env, info, data),
Err(ContractError::WrongDenom {
mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string()
})
Err(ContractError::WrongDenom)
);
}
@@ -167,10 +122,7 @@ mod tests {
verification_key.clone(),
encryption_key.clone(),
);
let coin = Coin::new(
deposit_value,
crate::support::tests::fixtures::TEST_MIX_DENOM,
);
let coin = Coin::new(deposit_value, MIX_DENOM.base);
let info = mock_info("requester", &[coin]);
let tx = deposit_funds(deps.as_mut(), env.clone(), info, data).unwrap();
@@ -219,7 +171,7 @@ mod tests {
let mut deps = helpers::init_contract();
let env = mock_env();
let invalid_admin = "invalid admin";
let funds = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM);
let funds = Coin::new(1, MIX_DENOM.base);
let err = release_funds(
deps.as_mut(),
@@ -228,12 +180,7 @@ mod tests {
Coin::new(1, "invalid denom"),
)
.unwrap_err();
assert_eq!(
err,
ContractError::WrongDenom {
mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string()
}
);
assert_eq!(err, ContractError::WrongDenom);
let err = release_funds(
deps.as_mut(),
@@ -260,7 +207,7 @@ mod tests {
fn valid_release() {
let mut deps = helpers::init_contract();
let env = mock_env();
let coin = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM);
let coin = Coin::new(1, MIX_DENOM.base);
deps.querier
.update_balance(env.contract.address.clone(), vec![coin.clone()]);
@@ -279,96 +226,4 @@ mod tests {
})
);
}
#[test]
fn valid_spend() {
let mut deps = helpers::init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let data = spend_credential_data_fixture("blinded_serial_number");
let res = spend_credential(deps.as_mut(), env.clone(), info, data.clone()).unwrap();
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &res.messages[0].msg
{
assert_eq!(contract_addr, MULTISIG_CONTRACT);
assert!(funds.is_empty());
let multisig_msg: MultisigExecuteMsg = from_binary(&msg).unwrap();
if let MultisigExecuteMsg::Propose {
title: _,
description,
msgs,
latest,
} = multisig_msg
{
assert_eq!(description, data.blinded_serial_number().to_string());
assert!(latest.is_none());
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msgs[0]
{
assert_eq!(*contract_addr, env.contract.address.into_string());
assert!(funds.is_empty());
let release_funds_req: ExecuteMsg = from_binary(&msg).unwrap();
if let ExecuteMsg::ReleaseFunds { funds } = release_funds_req {
assert_eq!(funds, *data.funds());
} else {
panic!("Could not extract release funds message from proposal");
}
}
} else {
panic!("Could not extract proposal from binary blob");
}
} else {
panic!("Wasm execute message not found");
}
}
#[test]
fn invalid_spend_attempts() {
let mut deps = helpers::init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let invalid_data = SpendCredentialData::new(
Coin::new(1, "invalid_denom".to_string()),
String::new(),
String::new(),
);
let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data);
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, crate::support::tests::fixtures::TEST_MIX_DENOM),
String::new(),
"Blinded Serial Number".to_string(),
);
let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data);
assert_eq!(
ret.unwrap_err().to_string(),
"Generic error: Invalid input: address not normalized".to_string()
);
let invalid_data = spend_credential_data_fixture("blined_serial_number");
spend_credential(
deps.as_mut(),
env.clone(),
info.clone(),
invalid_data.clone(),
)
.unwrap();
let ret = spend_credential(deps.as_mut(), env, info, invalid_data);
assert_eq!(
ret.unwrap_err(),
ContractError::DuplicateBlindedSerialNumber
);
}
}
-30
View File
@@ -1,30 +0,0 @@
[package]
name = "coconut-test"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
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" }
cosmwasm-std = "1.0.0"
cosmwasm-storage = "1.0.0"
cw-storage-plus = "0.13.4"
cw-controllers = "0.13.4"
cw-utils = "0.13.4"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
coconut-bandwidth = { path = "../coconut-bandwidth" }
cw-multi-test = { version = "0.13.2" }
cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" }
cw4-group = { path = "../multisig/cw4-group" }
[[test]]
name = "coconut-test"
path = "src/tests.rs"
@@ -1,101 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::*;
use coconut_bandwidth::error::ContractError;
use coconut_bandwidth_contract_common::{
deposit::DepositData,
msg::{ExecuteMsg, InstantiateMsg},
};
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, 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);
let random_addr = String::from(RANDOM_ADDRESS);
let code_id = app.store_code(contract_bandwidth());
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(
code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"bandwidth",
None,
)
.unwrap();
let msg = ExecuteMsg::DepositFunds {
data: DepositData::new(
String::from("info"),
String::from("id"),
String::from("enc"),
),
};
app.execute_contract(
Addr::unchecked(OWNER),
contract_addr.clone(),
&msg,
&deposit_funds,
)
.unwrap();
// try to release more then it's in the contract
let msg = ExecuteMsg::ReleaseFunds {
funds: release_funds[0].clone(),
};
let err = app
.execute_contract(
Addr::unchecked(multisig_addr.clone()),
contract_addr.clone(),
&msg,
&[],
)
.unwrap_err();
assert_eq!(ContractError::NotEnoughFunds, err.downcast().unwrap());
// try to call release from non-admin
let msg = ExecuteMsg::ReleaseFunds {
funds: deposit_funds[0].clone(),
};
let err = app
.execute_contract(
Addr::unchecked(random_addr),
contract_addr.clone(),
&msg,
&[],
)
.unwrap_err();
assert_eq!(
ContractError::Admin(AdminError::NotAdmin {}),
err.downcast().unwrap()
);
let msg = ExecuteMsg::ReleaseFunds {
funds: deposit_funds[0].clone(),
};
app.execute_contract(
Addr::unchecked(multisig_addr),
contract_addr.clone(),
&msg,
&[],
)
.unwrap();
let pool_bal = app.wrap().query_balance(pool_addr, TEST_MIX_DENOM).unwrap();
assert_eq!(pool_bal, deposit_funds[0]);
}
-64
View File
@@ -1,64 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{entry_point, Addr, Coin, DepsMut, Empty, Env, Response};
use cw3_flex_multisig::{state::CONFIG, ContractError};
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub const OWNER: &str = "admin0001";
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const POOL_CONTRACT: &str = "mix pool contract address";
pub const RANDOM_ADDRESS: &str = "random address";
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {
pub coconut_bandwidth_address: String,
}
#[entry_point]
pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
let mut cfg = CONFIG.load(deps.storage)?;
cfg.coconut_bandwidth_addr = deps.api.addr_validate(&msg.coconut_bandwidth_address)?;
CONFIG.save(deps.storage, &cfg)?;
Ok(Default::default())
}
pub fn mock_app(init_funds: &[Coin]) -> App {
AppBuilder::new().build(|router, _, storage| {
router
.bank
.init_balance(storage, &Addr::unchecked(OWNER), init_funds.to_vec())
.unwrap();
})
}
pub fn contract_bandwidth() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
coconut_bandwidth::contract::execute,
coconut_bandwidth::contract::instantiate,
coconut_bandwidth::contract::query,
);
Box::new(contract)
}
pub fn contract_multisig() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
cw3_flex_multisig::contract::execute,
cw3_flex_multisig::contract::instantiate,
cw3_flex_multisig::contract::query,
)
.with_migrate(migrate);
Box::new(contract)
}
pub fn contract_group() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
cw4_group::contract::execute,
cw4_group::contract::instantiate,
cw4_group::contract::query,
);
Box::new(contract)
}
@@ -1,160 +0,0 @@
use crate::helpers::*;
use coconut_bandwidth::error::ContractError;
use coconut_bandwidth_contract_common::{
msg::{
ExecuteMsg as CoconutBandwidthExecuteMsg, InstantiateMsg as CoconutBandwidthInstantiateMsg,
},
spend_credential::SpendCredentialData,
};
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, TEST_COIN_DENOM);
let mut app = mock_app(&init_funds);
let pool_addr = String::from(POOL_CONTRACT);
let group_code_id = app.store_code(contract_group());
let msg = GroupInstantiateMsg {
admin: Some(OWNER.to_string()),
members: vec![],
};
let group_contract_addr = app
.instantiate_contract(
group_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"group",
None,
)
.unwrap();
let multisig_code_id = app.store_code(contract_multisig());
let msg = MultisigInstantiateMsg {
group_addr: group_contract_addr.into_string(),
threshold: Threshold::AbsolutePercentage {
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(
multisig_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"multisig",
Some(OWNER.to_string()),
)
.unwrap();
let coconut_bandwidth_code_id = app.store_code(contract_bandwidth());
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(
coconut_bandwidth_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"coconut bandwidth",
None,
)
.unwrap();
let msg = MigrateMsg {
coconut_bandwidth_address: coconut_bandwidth_contract_addr.to_string(),
};
app.migrate_contract(
Addr::unchecked(OWNER),
multisig_contract_addr,
&msg,
multisig_code_id,
)
.unwrap();
let msg = CoconutBandwidthExecuteMsg::SpendCredential {
data: SpendCredentialData::new(
Coin::new(1, TEST_COIN_DENOM),
String::from("blinded_serial_number"),
String::from("gateway_cosmos_address"),
),
};
let res = app
.execute_contract(
Addr::unchecked(OWNER),
coconut_bandwidth_contract_addr.clone(),
&msg,
&vec![],
)
.unwrap();
let proposal_id = res
.events
.into_iter()
.find(|e| &e.ty == "wasm")
.unwrap()
.attributes
.into_iter()
.find(|attr| &attr.key == "proposal_id")
.unwrap()
.value
.parse::<u64>()
.unwrap();
assert_eq!(1, proposal_id);
// Trying with the same blinded serial number will detect the double spend attempt
let err = app
.execute_contract(
Addr::unchecked(OWNER),
coconut_bandwidth_contract_addr.clone(),
&msg,
&vec![],
)
.unwrap_err();
assert_eq!(
ContractError::DuplicateBlindedSerialNumber,
err.downcast().unwrap()
);
let msg = CoconutBandwidthExecuteMsg::SpendCredential {
data: SpendCredentialData::new(
Coin::new(1, TEST_COIN_DENOM),
String::from("blinded_serial_number2"),
String::from("gateway_cosmos_address"),
),
};
let res = app
.execute_contract(
Addr::unchecked(OWNER),
coconut_bandwidth_contract_addr.clone(),
&msg,
&vec![],
)
.unwrap();
let proposal_id = res
.events
.into_iter()
.find(|e| &e.ty == "wasm")
.unwrap()
.attributes
.into_iter()
.find(|attr| &attr.key == "proposal_id")
.unwrap()
.value
.parse::<u64>()
.unwrap();
assert_eq!(2, proposal_id);
}
-6
View File
@@ -1,6 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod deposit_and_release;
mod helpers;
mod spend_credential_creates_proposal;
+5 -11
View File
@@ -61,14 +61,9 @@ 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,
mix_denom: String,
rewarding_validator_address: Addr,
) -> ContractState {
fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState {
ContractState {
owner,
mix_denom,
rewarding_validator_address,
params: ContractStateParams {
minimum_mixnode_pledge: INITIAL_MIXNODE_PLEDGE,
@@ -93,7 +88,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, msg.mixnet_denom, rewarding_validator_address);
let state = default_initial_state(info.sender, rewarding_validator_address);
init_epoch(deps.storage, env)?;
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?;
@@ -457,7 +452,7 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respon
pub mod tests {
use super::*;
use crate::support::tests;
use crate::support::tests::fixtures::{TEST_COIN_DENOM, TEST_REWARDING_VALIDATOR_ADDRESS};
use config::defaults::{DEFAULT_NETWORK, MIX_DENOM};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
use mixnet_contract_common::PagedMixnodeResponse;
@@ -467,8 +462,7 @@ pub mod tests {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {
rewarding_validator_address: TEST_REWARDING_VALIDATOR_ADDRESS.to_string(),
mixnet_denom: TEST_COIN_DENOM.to_string(),
rewarding_validator_address: DEFAULT_NETWORK.rewarding_validator_address().to_string(),
};
let info = mock_info("creator", &[]);
@@ -490,7 +484,7 @@ pub mod tests {
// Contract balance should match what we initialized it as
assert_eq!(
coins(0, TEST_COIN_DENOM),
coins(0, MIX_DENOM.base),
tests::queries::query_contract_balance(env.contract.address, deps)
);
}
+5 -4
View File
@@ -199,7 +199,8 @@ pub(crate) fn query_mixnode_delegations_paged(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::support::tests::{fixtures::TEST_COIN_DENOM, test_helpers};
use crate::support::tests::test_helpers;
use config::defaults::MIX_DENOM;
use cosmwasm_std::{coin, Addr, Storage};
use rand::Rng;
@@ -384,7 +385,7 @@ pub(crate) mod tests {
let delegation = Delegation::new(
delegation_owner.clone(),
node_identity.clone(),
coin(1234, TEST_COIN_DENOM),
coin(1234, MIX_DENOM.base),
1234,
None,
);
@@ -432,7 +433,7 @@ pub(crate) mod tests {
let delegation = Delegation::new(
delegation_owner2,
node_identity1.clone(),
coin(1234, TEST_COIN_DENOM),
coin(1234, MIX_DENOM.base),
1234,
None,
);
@@ -459,7 +460,7 @@ pub(crate) mod tests {
let delegation = Delegation::new(
delegation_owner1.clone(),
node_identity2,
coin(1234, TEST_COIN_DENOM),
coin(1234, MIX_DENOM.base),
1234,
None,
);
+3 -3
View File
@@ -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, TEST_COIN_DENOM);
let delegation = coin(12345, MIX_DENOM.base);
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, TEST_COIN_DENOM);
let delegation = coin(12345, MIX_DENOM.base);
assert!(test_helpers::read_delegation(
deps.as_ref().storage,
@@ -4,8 +4,9 @@ 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::{self as mixnet_params_storage};
use crate::mixnet_contract_settings::storage 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,
@@ -73,10 +74,7 @@ pub(crate) fn _try_reconcile_all_delegation_events(
Ok(response)
}
fn validate_delegation_stake(
mut delegation: Vec<Coin>,
mix_denom: String,
) -> Result<Coin, ContractError> {
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
// check if anything was put as delegation
if delegation.is_empty() {
return Err(ContractError::EmptyDelegation);
@@ -87,8 +85,8 @@ fn validate_delegation_stake(
}
// check that the denomination is correct
if delegation[0].denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
if delegation[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom {});
}
// check that we have provided a non-zero amount in the delegation
@@ -106,8 +104,7 @@ 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, mixnet_params_storage::mix_denom(deps.storage)?)?;
let amount = validate_delegation_stake(info.funds)?;
_try_delegate_to_mixnode(
deps,
@@ -127,8 +124,7 @@ 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, mixnet_params_storage::mix_denom(deps.storage)?)?;
let amount = validate_delegation_stake(info.funds)?;
_try_delegate_to_mixnode(
deps,
@@ -264,7 +260,6 @@ 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");
@@ -394,7 +389,7 @@ pub(crate) fn try_reconcile_undelegation(
.as_ref()
.unwrap_or(&pending_undelegate.delegate())
.to_string(),
amount: coins(total_funds.u128(), mix_denom.clone()),
amount: coins(total_funds.u128(), MIX_DENOM.base),
})
} else {
None
@@ -406,10 +401,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.clone()),
amount: Coin::new(total_funds.u128(), MIX_DENOM.base),
});
wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?);
wasm_msg = Some(wasm_execute(proxy, &msg, vec![one_ucoin()])?);
}
let event = new_undelegation_event(
@@ -473,9 +468,9 @@ 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::storage;
use super::*;
#[cfg(test)]
@@ -488,7 +483,7 @@ mod tests {
fn stake_cant_be_empty() {
assert_eq!(
Err(ContractError::EmptyDelegation),
validate_delegation_stake(vec![], TEST_COIN_DENOM.to_string())
validate_delegation_stake(vec![])
)
}
@@ -496,24 +491,19 @@ mod tests {
fn stake_must_have_single_coin_type() {
assert_eq!(
Err(ContractError::MultipleDenoms),
validate_delegation_stake(
vec![
coin(123, TEST_COIN_DENOM),
coin(123, "BTC"),
coin(123, "DOGE")
],
TEST_COIN_DENOM.to_string()
)
validate_delegation_stake(vec![
coin(123, MIX_DENOM.base),
coin(123, "BTC"),
coin(123, "DOGE")
])
)
}
#[test]
fn stake_coin_must_be_of_correct_type() {
assert_eq!(
Err(ContractError::WrongDenom {
mix_denom: TEST_COIN_DENOM.to_string()
}),
validate_delegation_stake(coins(123, "DOGE"), TEST_COIN_DENOM.to_string())
Err(ContractError::WrongDenom {}),
validate_delegation_stake(coins(123, "DOGE"))
)
}
@@ -521,28 +511,16 @@ mod tests {
fn stake_coin_must_have_value_greater_than_zero() {
assert_eq!(
Err(ContractError::EmptyDelegation),
validate_delegation_stake(coins(0, TEST_COIN_DENOM), TEST_COIN_DENOM.to_string())
validate_delegation_stake(coins(0, MIX_DENOM.base))
)
}
#[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, 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());
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());
}
}
@@ -565,7 +543,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("sender", &coins(123, TEST_COIN_DENOM)),
mock_info("sender", &coins(123, MIX_DENOM.base)),
"non-existent-mix-identity".into(),
)
);
@@ -581,7 +559,7 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation = coin(123, TEST_COIN_DENOM);
let delegation = coin(123, MIX_DENOM.base);
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
@@ -638,7 +616,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(123, TEST_COIN_DENOM)),
mock_info(delegation_owner.as_str(), &coins(123, MIX_DENOM.base)),
identity,
)
);
@@ -659,7 +637,7 @@ mod tests {
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
let delegation = coin(123, TEST_COIN_DENOM);
let delegation = coin(123, MIX_DENOM.base);
let delegation_owner = Addr::unchecked("sender");
assert!(try_delegate_to_mixnode(
deps.as_mut(),
@@ -709,8 +687,8 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation1 = coin(100, TEST_COIN_DENOM);
let delegation2 = coin(50, TEST_COIN_DENOM);
let delegation1 = coin(100, MIX_DENOM.base);
let delegation2 = coin(50, MIX_DENOM.base);
let mut env = mock_env();
@@ -737,7 +715,7 @@ mod tests {
// let expected = Delegation::new(
// delegation_owner.clone(),
// identity.clone(),
// coin(delegation1.amount.u128() + delegation2.amount.u128(), TEST_COIN_DENOM),
// coin(delegation1.amount.u128() + delegation2.amount.u128(), MIX_DENOM.base),
// mock_env().block.height,
// None,
// );
@@ -772,7 +750,7 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation = coin(100, TEST_COIN_DENOM);
let delegation = coin(100, MIX_DENOM.base);
let env1 = mock_env();
let mut env2 = mock_env();
let initial_height = env1.block.height;
@@ -837,8 +815,8 @@ mod tests {
);
let delegation_owner1 = Addr::unchecked("sender1");
let delegation_owner2 = Addr::unchecked("sender2");
let delegation1 = coin(100, TEST_COIN_DENOM);
let delegation2 = coin(120, TEST_COIN_DENOM);
let delegation1 = coin(100, MIX_DENOM.base);
let delegation2 = coin(120, MIX_DENOM.base);
let env1 = mock_env();
let mut env2 = mock_env();
let initial_height = env1.block.height;
@@ -913,7 +891,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)),
identity.clone(),
)
.unwrap();
@@ -925,7 +903,7 @@ mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(50, TEST_COIN_DENOM)),
mock_info(delegation_owner.as_str(), &coins(50, MIX_DENOM.base)),
identity,
)
);
@@ -950,14 +928,14 @@ mod tests {
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(123, TEST_COIN_DENOM)),
mock_info(delegation_owner.as_str(), &coins(123, MIX_DENOM.base)),
identity1.clone(),
)
.is_ok());
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info(delegation_owner.as_str(), &coins(42, TEST_COIN_DENOM)),
mock_info(delegation_owner.as_str(), &coins(42, MIX_DENOM.base)),
identity2.clone(),
)
.is_ok());
@@ -967,7 +945,7 @@ mod tests {
let expected1 = Delegation::new(
delegation_owner.clone(),
identity1.clone(),
coin(123, TEST_COIN_DENOM),
coin(123, MIX_DENOM.base),
mock_env().block.height,
None,
);
@@ -975,7 +953,7 @@ mod tests {
let expected2 = Delegation::new(
delegation_owner.clone(),
identity2.clone(),
coin(42, TEST_COIN_DENOM),
coin(42, MIX_DENOM.base),
mock_env().block.height,
None,
);
@@ -1011,8 +989,8 @@ mod tests {
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
let delegation1 = coin(123, TEST_COIN_DENOM);
let delegation2 = coin(234, TEST_COIN_DENOM);
let delegation1 = coin(123, MIX_DENOM.base);
let delegation2 = coin(234, MIX_DENOM.base);
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
@@ -1048,7 +1026,7 @@ mod tests {
deps.as_mut(),
);
let delegation_owner = Addr::unchecked("sender");
let delegation_amount = coin(100, TEST_COIN_DENOM);
let delegation_amount = coin(100, MIX_DENOM.base);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
@@ -1084,13 +1062,17 @@ mod tests {
#[cfg(test)]
mod removing_mix_stake_delegation {
use crate::delegations::queries::query_mixnode_delegation;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Addr;
use cosmwasm_std::Uint128;
use crate::mixnodes::transactions::try_remove_mixnode;
use crate::support::tests;
use super::storage;
use super::*;
// TODO: Probably delete due to reconciliation logic
@@ -1136,7 +1118,7 @@ mod tests {
// try_delegate_to_mixnode(
// deps.as_mut(),
// mock_env(),
// mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
// mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)),
// identity.clone(),
// )
// .unwrap();
@@ -1155,7 +1137,7 @@ mod tests {
// let expected_response = Response::new()
// .add_message(BankMsg::Send {
// to_address: delegation_owner.clone().into(),
// amount: coins(100, TEST_COIN_DENOM),
// amount: coins(100, MIX_DENOM.base),
// })
// .add_event(new_undelegation_event(
// &delegation_owner,
@@ -1206,7 +1188,7 @@ mod tests {
// try_delegate_to_mixnode(
// deps.as_mut(),
// mock_env(),
// mock_info(delegation_owner.as_str(), &coins(100, TEST_COIN_DENOM)),
// mock_info(delegation_owner.as_str(), &coins(100, MIX_DENOM.base)),
// identity.clone(),
// )
// .unwrap();
@@ -1225,7 +1207,7 @@ mod tests {
// let expected_response = Response::new()
// .add_message(BankMsg::Send {
// to_address: delegation_owner.clone().into(),
// amount: coins(100, TEST_COIN_DENOM),
// amount: coins(100, MIX_DENOM.base),
// })
// .add_event(new_undelegation_event(
// &delegation_owner,
@@ -1269,8 +1251,8 @@ mod tests {
);
let delegation_owner1 = Addr::unchecked("sender1");
let delegation_owner2 = Addr::unchecked("sender2");
let delegation1 = coin(123, TEST_COIN_DENOM);
let delegation2 = coin(234, TEST_COIN_DENOM);
let delegation1 = coin(123, MIX_DENOM.base);
let delegation2 = coin(234, MIX_DENOM.base);
assert!(try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
+5 -4
View File
@@ -1,6 +1,7 @@
// 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;
@@ -32,14 +33,14 @@ pub enum ContractError {
#[error("MIXNET ({}): Unauthorized", line!())]
Unauthorized,
#[error("MIXNET ({}): Wrong coin denomination, you must send {mix_denom}", line!())]
WrongDenom { mix_denom: String },
#[error("MIXNET ({}): Wrong coin denomination, you must send {}", line!(), MIX_DENOM.base)]
WrongDenom,
#[error("MIXNET ({}): Received multiple coin types during staking", line!())]
MultipleDenoms,
#[error("MIXNET ({}): No coin was sent for the bonding, you must send {mix_denom}", line!())]
NoBondFound { mix_denom: String },
#[error("MIXNET ({}): No coin was sent for the bonding, you must send {}", line!(), MIX_DENOM.base)]
NoBondFound,
#[error("MIXNET ({}): Provided active set size is bigger than the rewarded set", line!())]
InvalidActiveSetSize,
+2 -2
View File
@@ -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 crate::support::tests::fixtures::TEST_COIN_DENOM;
use config::defaults::MIX_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, TEST_COIN_DENOM),
pledge_amount: coin(pledge_amount, MIX_DENOM.base),
owner: node_owner,
block_height: 12_345,
gateway: Gateway {
+17 -55
View File
@@ -5,6 +5,7 @@ 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,
};
@@ -25,8 +26,7 @@ pub fn try_add_gateway(
.load(deps.storage)?
.params
.minimum_mixnode_pledge;
let mix_denom = mixnet_params_storage::mix_denom(deps.storage)?;
let pledge = validate_gateway_pledge(info.funds, minimum_pledge, mix_denom)?;
let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?;
_try_add_gateway(
deps,
@@ -52,8 +52,7 @@ pub fn try_add_gateway_on_behalf(
.load(deps.storage)?
.params
.minimum_mixnode_pledge;
let mix_denom = mixnet_params_storage::mix_denom(deps.storage)?;
let pledge = validate_gateway_pledge(info.funds, minimum_pledge, mix_denom)?;
let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?;
let proxy = info.sender;
_try_add_gateway(
@@ -139,7 +138,6 @@ 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
@@ -179,7 +177,7 @@ pub(crate) fn _try_remove_gateway(
amount: gateway_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?;
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(track_unbond_message);
}
@@ -194,11 +192,10 @@ 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 { mix_denom });
return Err(ContractError::NoBondFound);
}
if pledge.len() > 1 {
@@ -206,8 +203,8 @@ fn validate_gateway_pledge(
}
// check that the denomination is correct
if pledge[0].denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
if pledge[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom {});
}
// check that we have at least 100 coins in our pledge
@@ -228,8 +225,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};
@@ -241,7 +238,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, TEST_COIN_DENOM));
let info = mock_info("anyone", &coins(insufficient_bond, MIX_DENOM.base));
let (msg, _) = tests::messages::valid_bond_gateway_msg("anyone");
// we are informed that we didn't send enough funds
@@ -547,26 +544,13 @@ pub mod tests {
#[test]
fn validating_gateway_bond() {
// you must send SOME funds
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()
})
);
let result = validate_gateway_pledge(Vec::new(), INITIAL_GATEWAY_PLEDGE);
assert_eq!(result, Err(ContractError::NoBondFound));
// 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,
TEST_COIN_DENOM.to_string(),
);
let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE);
assert_eq!(
result,
Err(ContractError::InsufficientGatewayBond {
@@ -578,40 +562,18 @@ 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,
TEST_COIN_DENOM.to_string(),
);
let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE);
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,
TEST_COIN_DENOM.to_string(),
);
assert_eq!(
result,
Err(ContractError::WrongDenom {
mix_denom: TEST_COIN_DENOM.to_string()
})
);
let result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE);
assert_eq!(result, Err(ContractError::WrongDenom {}));
let mut bond = tests::fixtures::good_gateway_pledge();
bond[0].denom = "foomp".to_string();
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 result = validate_gateway_pledge(bond.clone(), INITIAL_GATEWAY_PLEDGE);
assert_eq!(result, Err(ContractError::WrongDenom {}));
}
}
@@ -9,7 +9,6 @@ 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,7 +45,6 @@ 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,6 +1,7 @@
// 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;
@@ -10,14 +11,10 @@ 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) -> StdResult<String> {
CONTRACT_STATE
pub fn rewarding_validator_address(storage: &dyn Storage) -> Result<String, ContractError> {
Ok(CONTRACT_STATE
.load(storage)
.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)
.map(|state| state.rewarding_validator_address.to_string())?)
}
pub fn increment_layer_count(storage: &mut dyn Storage, layer: Layer) -> StdResult<()> {
+5 -7
View File
@@ -1,6 +1,7 @@
// 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::{
@@ -10,8 +11,6 @@ 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";
@@ -172,7 +171,7 @@ pub(crate) fn read_full_mixnode_bond(
Ok(Some(MixNodeBond {
pledge_amount: stored_bond.pledge_amount,
total_delegation: Coin {
denom: mix_denom(storage)?,
denom: MIX_DENOM.base.to_owned(),
amount: total_delegation.unwrap_or_default(),
},
owner: stored_bond.owner,
@@ -191,8 +190,7 @@ mod tests {
use super::super::storage;
use super::*;
use crate::support::tests;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::init_contract;
use config::defaults::MIX_DENOM;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{coin, Addr, Uint128};
use mixnet_contract_common::{IdentityKey, MixNode};
@@ -213,7 +211,7 @@ mod tests {
#[test]
fn reading_mixnode_bond() {
let mut mock_storage = init_contract().storage;
let mut mock_storage = MockStorage::new();
let node_owner: Addr = Addr::unchecked("node-owner");
let node_identity: IdentityKey = "nodeidentity".into();
@@ -225,7 +223,7 @@ mod tests {
let pledge_value = 1000000000;
let mixnode_bond = StoredMixnodeBond {
pledge_amount: coin(pledge_value, TEST_COIN_DENOM),
pledge_amount: coin(pledge_value, MIX_DENOM.base),
owner: node_owner,
layer: Layer::One,
block_height: 12_345,
+20 -59
View File
@@ -3,10 +3,11 @@
use super::storage::{self, LAST_PM_UPDATE_TIME};
use crate::error::ContractError;
use crate::mixnet_contract_settings::storage::{self as mixnet_params_storage, mix_denom};
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
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,
};
@@ -47,7 +48,7 @@ pub fn try_add_mixnode(
.load(deps.storage)?
.params
.minimum_mixnode_pledge;
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge, mix_denom(deps.storage)?)?;
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?;
_try_add_mixnode(
deps,
@@ -73,7 +74,7 @@ pub fn try_add_mixnode_on_behalf(
.load(deps.storage)?
.params
.minimum_mixnode_pledge;
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge, mix_denom(deps.storage)?)?;
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?;
let proxy = info.sender;
_try_add_mixnode(
@@ -238,8 +239,7 @@ pub(crate) fn _try_remove_mixnode(
amount: mixnode_bond.pledge_amount(),
};
let track_unbond_message =
wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom(deps.storage)?)])?;
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(track_unbond_message);
}
@@ -288,7 +288,6 @@ 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 {
@@ -330,9 +329,7 @@ pub(crate) fn _try_update_mixnode_config(
mixnode_bond.block_height = env.block.height;
mixnode_bond
})
.ok_or(ContractError::NoBondFound {
mix_denom: mix_denom.clone(),
})
.ok_or(ContractError::NoBondFound)
},
)?;
@@ -345,7 +342,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(mix_denom)],
amount: vec![one_ucoin()],
};
response = response.add_message(return_one_ucoint);
}
@@ -356,11 +353,10 @@ 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 { mix_denom });
return Err(ContractError::NoBondFound);
}
if pledge.len() > 1 {
@@ -368,8 +364,8 @@ fn validate_mixnode_pledge(
}
// check that the denomination is correct
if pledge[0].denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
if pledge[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom {});
}
// check that we have at least MIXNODE_BOND coins in our pledge
@@ -390,8 +386,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};
@@ -406,7 +402,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, TEST_COIN_DENOM));
let info = mock_info("anyone", &coins(insufficient_bond, MIX_DENOM.base));
let (msg, _) = tests::messages::valid_bond_mixnode_msg("anyone");
// we are informed that we didn't send enough funds
@@ -812,26 +808,13 @@ pub mod tests {
#[test]
fn validating_mixnode_bond() {
// you must send SOME funds
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()
})
);
let result = validate_mixnode_pledge(Vec::new(), INITIAL_MIXNODE_PLEDGE);
assert_eq!(result, Err(ContractError::NoBondFound));
// 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,
TEST_COIN_DENOM.to_string(),
);
let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE);
assert_eq!(
result,
Err(ContractError::InsufficientMixNodeBond {
@@ -843,41 +826,19 @@ 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,
TEST_COIN_DENOM.to_string(),
);
let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE);
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,
TEST_COIN_DENOM.to_string(),
);
assert_eq!(
result,
Err(ContractError::WrongDenom {
mix_denom: TEST_COIN_DENOM.to_string()
})
);
let result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE);
assert_eq!(result, Err(ContractError::WrongDenom {}));
let mut bond = tests::fixtures::good_mixnode_pledge();
bond[0].denom = "foomp".to_string();
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 result = validate_mixnode_pledge(bond.clone(), INITIAL_MIXNODE_PLEDGE);
assert_eq!(result, Err(ContractError::WrongDenom {}));
}
#[test]
+2 -2
View File
@@ -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 crate::support::tests::fixtures::TEST_COIN_DENOM;
use config::defaults::MIX_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, TEST_COIN_DENOM)],
&[coin(200_000000, MIX_DENOM.base)],
),
node_identity.clone(),
)
+28 -31
View File
@@ -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,7 +69,6 @@ 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
@@ -106,7 +105,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.clone()),
amount: coins(reward.u128(), MIX_DENOM.base),
};
let mut response = Response::default()
@@ -116,10 +115,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.clone()),
amount: Coin::new(reward.u128(), MIX_DENOM.base),
});
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?;
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(wasm_msg);
}
@@ -135,7 +134,6 @@ 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)?;
@@ -155,7 +153,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.clone()),
amount: coins(reward.u128(), MIX_DENOM.base),
};
let mut response =
@@ -171,10 +169,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.clone()),
amount: Coin::new(reward.u128(), MIX_DENOM.base),
});
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin(mix_denom)])?;
let wasm_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
response = response.add_message(wasm_msg);
}
@@ -405,7 +403,6 @@ 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)?,
@@ -441,7 +438,7 @@ pub fn _try_compound_delegator_reward(
owner_address,
Coin {
amount: compounded_delegation,
denom: mix_denom,
denom: MIX_DENOM.base.to_string(),
},
proxy,
)?;
@@ -727,9 +724,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::{
@@ -747,7 +744,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, TEST_COIN_DENOM));
let info = mock_info(&sender, &coins(1000, MIX_DENOM.base));
crate::interval::transactions::init_epoch(&mut deps.storage, env.clone()).unwrap();
// bond the node
@@ -888,7 +885,7 @@ pub mod tests {
let initial_bond = 10000_000000;
let initial_delegation = 20000_000000;
let mixnode_bond = StoredMixnodeBond {
pledge_amount: coin(initial_bond, TEST_COIN_DENOM),
pledge_amount: coin(initial_bond, MIX_DENOM.base),
owner: node_owner,
layer: Layer::One,
block_height: env.block.height,
@@ -928,7 +925,7 @@ pub mod tests {
&Delegation::new(
Addr::unchecked("delegator"),
node_identity.clone(),
coin(initial_delegation, TEST_COIN_DENOM),
coin(initial_delegation, MIX_DENOM.base),
env.block.height,
None,
),
@@ -1112,7 +1109,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, TEST_COIN_DENOM);
let stake = coins(10_000_000_000, MIX_DENOM.base);
let keypair = crypto::asymmetric::identity::KeyPair::new(&mut thread_rng());
let owner_signature = keypair
@@ -1163,14 +1160,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, TEST_COIN_DENOM),
coins(10_000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d1", &[coin(8000_000000, TEST_COIN_DENOM)]),
mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity_1.clone(),
)
.unwrap();
@@ -1178,7 +1175,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d2", &[coin(2000_000000, TEST_COIN_DENOM)]),
mock_info("alice_d2", &[coin(2000_000000, MIX_DENOM.base)]),
node_identity_1.clone(),
)
.unwrap();
@@ -1186,7 +1183,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("bob_d1", &[coin(8000_000000, TEST_COIN_DENOM)]),
mock_info("bob_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity_2.clone(),
)
.unwrap();
@@ -1194,7 +1191,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("bob_d2", &[coin(2000_000000, TEST_COIN_DENOM)]),
mock_info("bob_d2", &[coin(2000_000000, MIX_DENOM.base)]),
node_identity_2.clone(),
)
.unwrap();
@@ -1202,14 +1199,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, TEST_COIN_DENOM),
coins(10_000_000_000 * 2, MIX_DENOM.base),
deps.as_mut(),
);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alicebob_d1", &[coin(8000_000000 * 2, TEST_COIN_DENOM)]),
mock_info("alicebob_d1", &[coin(8000_000000 * 2, MIX_DENOM.base)]),
node_identity_3.clone(),
)
.unwrap();
@@ -1217,7 +1214,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alicebob_d2", &[coin(2000_000000 * 2, TEST_COIN_DENOM)]),
mock_info("alicebob_d2", &[coin(2000_000000 * 2, MIX_DENOM.base)]),
node_identity_3.clone(),
)
.unwrap();
@@ -1323,7 +1320,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
env.clone(),
mock_info("alice_d1", &[coin(8000_000000, TEST_COIN_DENOM)]),
mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity_1.clone(),
)
.unwrap();
@@ -1562,14 +1559,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, TEST_COIN_DENOM),
coins(10_000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d1", &[coin(8000_000000, TEST_COIN_DENOM)]),
mock_info("alice_d1", &[coin(8000_000000, MIX_DENOM.base)]),
node_identity.clone(),
)
.unwrap();
@@ -1577,7 +1574,7 @@ pub mod tests {
try_delegate_to_mixnode(
deps.as_mut(),
mock_env(),
mock_info("alice_d2", &[coin(2000_000000, TEST_COIN_DENOM)]),
mock_info("alice_d2", &[coin(2000_000000, MIX_DENOM.base)]),
node_identity.clone(),
)
.unwrap();
@@ -1753,7 +1750,7 @@ pub mod tests {
#[allow(clippy::inconsistent_digit_grouping)]
let node_identity = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10000_000_000, TEST_COIN_DENOM),
coins(10000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
@@ -1780,7 +1777,7 @@ pub mod tests {
#[allow(clippy::inconsistent_digit_grouping)]
let node_identity = test_helpers::add_mixnode(
node_owner.as_str(),
coins(10000_000_000, TEST_COIN_DENOM),
coins(10000_000_000, MIX_DENOM.base),
deps.as_mut(),
);
@@ -1790,7 +1787,7 @@ pub mod tests {
env.clone(),
mock_info(
&*format!("delegator{:04}", i),
&[coin(2000_000000, TEST_COIN_DENOM)],
&[coin(2000_000000, MIX_DENOM.base)],
),
node_identity.clone(),
)
@@ -1,13 +1,11 @@
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(),
@@ -39,7 +37,7 @@ pub fn gateway_bond_fixture(owner: &str) -> GatewayBond {
..tests::fixtures::gateway_fixture()
};
GatewayBond::new(
coin(50, TEST_COIN_DENOM),
coin(50, MIX_DENOM.base),
Addr::unchecked(owner),
12_345,
gateway,
@@ -49,7 +47,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, TEST_COIN_DENOM),
coin(50, MIX_DENOM.base),
Addr::unchecked(owner),
Layer::One,
12_345,
@@ -66,14 +64,14 @@ pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::Stor
pub fn good_mixnode_pledge() -> Vec<Coin> {
vec![Coin {
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
amount: INITIAL_MIXNODE_PLEDGE,
}]
}
pub fn good_gateway_pledge() -> Vec<Coin> {
vec![Coin {
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
amount: INITIAL_MIXNODE_PLEDGE,
}]
}
+3 -6
View File
@@ -17,6 +17,7 @@ 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;
@@ -31,9 +32,6 @@ 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
@@ -87,8 +85,7 @@ pub mod test_helpers {
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
rewarding_validator_address: TEST_REWARDING_VALIDATOR_ADDRESS.to_string(),
mixnet_denom: TEST_COIN_DENOM.to_string(),
rewarding_validator_address: DEFAULT_NETWORK.rewarding_validator_address().to_string(),
};
let env = mock_env();
let info = mock_info("creator", &[]);
@@ -114,7 +111,7 @@ pub mod test_helpers {
let delegation = Delegation {
owner: Addr::unchecked(owner.into()),
node_identity: mix.into(),
amount: coin(12345, TEST_COIN_DENOM),
amount: coin(12345, MIX_DENOM.base),
block_height: block_height,
proxy: None,
};
@@ -1,3 +1,4 @@
use config::defaults::MIX_DENOM;
use cosmwasm_std::{
from_binary,
testing::{mock_env, MockApi, MockQuerier, MockStorage},
@@ -9,8 +10,6 @@ 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(),
@@ -46,5 +45,5 @@ pub fn query_contract_balance(
deps: OwnedDeps<MockStorage, MockApi, MockQuerier>,
) -> Vec<Coin> {
let querier = deps.as_ref().querier;
vec![querier.query_balance(address, TEST_COIN_DENOM).unwrap()]
vec![querier.query_balance(address, MIX_DENOM.base).unwrap()]
}
@@ -19,7 +19,7 @@ use cw_utils::{maybe_addr, Expiration, ThresholdResponse};
use crate::error::ContractError;
use crate::state::{Config, CONFIG};
use multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
// version info for migration info
const CONTRACT_NAME: &str = "crates.io:cw3-flex-multisig";
@@ -37,11 +37,6 @@ pub fn instantiate(
addr: msg.group_addr.clone(),
}
})?);
// This might need to be changed via a migration, due to circular dependency
// of deploying the two contracts
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)?;
@@ -51,21 +46,12 @@ pub fn instantiate(
threshold: msg.threshold,
max_voting_period: msg.max_voting_period,
group_addr,
coconut_bandwidth_addr,
};
CONFIG.save(deps.storage, &cfg)?;
Ok(Response::default())
}
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
let mut cfg = CONFIG.load(deps.storage)?;
cfg.coconut_bandwidth_addr = deps.api.addr_validate(&msg.coconut_bandwidth_address)?;
CONFIG.save(deps.storage, &cfg)?;
Ok(Default::default())
}
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
@@ -102,12 +88,15 @@ pub fn execute_propose(
// only members of the multisig can create a proposal
let cfg = CONFIG.load(deps.storage)?;
// Only the coconut bandwidth contract can create proposals
if info.sender != cfg.coconut_bandwidth_addr {
return Err(ContractError::Unauthorized {});
}
// The contract doesn't have any say in the voting outcome
let vote_power = 0;
// Only members of the multisig can create a proposal
// Non-voting members are special - they are allowed to create a proposal and
// therefore "vote", but they aren't allowed to vote otherwise.
// Such vote is also special, because despite having 0 weight it still counts when
// counting threshold passing
let vote_power = cfg
.group_addr
.is_member(&deps.querier, &info.sender, None)?
.ok_or(ContractError::Unauthorized {})?;
// max expires also used as default
let max_expires = cfg.max_voting_period.after(&env.block);
@@ -444,9 +433,7 @@ mod tests {
use cw2::{query_contract_info, ContractVersion};
use cw4::{Cw4ExecuteMsg, Member};
use cw4_group::helpers::Cw4GroupContract;
use cw_multi_test::{
next_block, App, AppBuilder, AppResponse, Contract, ContractWrapper, Executor,
};
use cw_multi_test::{next_block, App, AppBuilder, Contract, ContractWrapper, Executor};
use cw_utils::{Duration, Threshold};
use super::*;
@@ -458,8 +445,6 @@ 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 {
@@ -506,26 +491,6 @@ mod tests {
.unwrap()
}
fn propose_and_vote(
app: &mut App,
flex_addr: Addr,
proposal: ExecuteMsg,
voter: &str,
) -> AppResponse {
let proposer = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string();
let res = app
.execute_contract(Addr::unchecked(proposer), flex_addr.clone(), &proposal, &[])
.unwrap();
let yes_vote = ExecuteMsg::Vote {
proposal_id: res.custom_attrs(1)[2].value.parse().unwrap(),
vote: Vote::Yes,
};
let _ = app.execute_contract(Addr::unchecked(voter), flex_addr, &yes_vote, &[]);
res
}
#[track_caller]
fn instantiate_flex(
app: &mut App,
@@ -538,7 +503,6 @@ 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()
@@ -574,10 +538,8 @@ mod tests {
init_funds: Vec<Coin>,
multisig_as_group_admin: bool,
) -> (Addr, Addr) {
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
// 1. Instantiate group contract with members (and OWNER as admin)
let members = vec![
member(coconut_bandwidth_contract, 0),
member(OWNER, 0),
member(VOTER1, 1),
member(VOTER2, 2),
@@ -654,7 +616,6 @@ 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(
@@ -676,7 +637,6 @@ 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(
@@ -698,7 +658,6 @@ 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(
@@ -771,13 +730,41 @@ mod tests {
};
let err = app
.execute_contract(
Addr::unchecked(TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string()),
Addr::unchecked(OWNER),
flex_addr.clone(),
&proposal_wrong_exp,
&[],
)
.unwrap_err();
assert_eq!(ContractError::WrongExpiration {}, err.downcast().unwrap());
// Proposal from voter works
let res = app
.execute_contract(Addr::unchecked(VOTER3), flex_addr.clone(), &proposal, &[])
.unwrap();
assert_eq!(
res.custom_attrs(1),
[
("action", "propose"),
("sender", VOTER3),
("proposal_id", "1"),
("status", "Open"),
],
);
// Proposal from voter with enough vote power directly passes
let res = app
.execute_contract(Addr::unchecked(VOTER4), flex_addr, &proposal, &[])
.unwrap();
assert_eq!(
res.custom_attrs(1),
[
("action", "propose"),
("sender", VOTER4),
("proposal_id", "2"),
("status", "Passed"),
],
);
}
fn get_tally(app: &App, flex_addr: &str, proposal_id: u64) -> u64 {
@@ -818,54 +805,6 @@ mod tests {
}
}
#[test]
fn test_proposer_limited_to_coconut_bandwidth() {
let init_funds = coins(10, "BTC");
let mut app = mock_app(&init_funds);
let voting_period = Duration::Time(2000000);
let threshold = Threshold::ThresholdQuorum {
threshold: Decimal::percent(80),
quorum: Decimal::percent(20),
};
let (flex_addr, _) = setup_test_case(&mut app, threshold, voting_period, init_funds, false);
let proposal = pay_somebody_proposal();
let err = app
.execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[])
.unwrap_err();
assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap());
let err = app
.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &proposal, &[])
.unwrap_err();
assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap());
let err = app
.execute_contract(Addr::unchecked(SOMEBODY), flex_addr.clone(), &proposal, &[])
.unwrap_err();
assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap());
let proposer = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string();
let res = app
.execute_contract(
Addr::unchecked(&proposer),
flex_addr.clone(),
&proposal,
&[],
)
.unwrap();
assert_eq!(
res.custom_attrs(1),
[
("action", "propose"),
("sender", &proposer),
("proposal_id", "1"),
("status", "Open"),
],
);
}
#[test]
fn test_proposal_queries() {
let init_funds = coins(10, "BTC");
@@ -880,13 +819,17 @@ mod tests {
// create proposal with 1 vote power
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER1);
let res = app
.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &proposal, &[])
.unwrap();
let proposal_id1: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
// another proposal immediately passes
app.update_block(next_block);
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER4);
let res = app
.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &proposal, &[])
.unwrap();
let proposal_id2: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
// expire them both
@@ -894,7 +837,9 @@ mod tests {
// add one more open proposal, 2 votes
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER2);
let res = app
.execute_contract(Addr::unchecked(VOTER2), flex_addr.clone(), &proposal, &[])
.unwrap();
let proposal_id3: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
let proposed_at = app.block_info();
@@ -969,7 +914,9 @@ mod tests {
// create proposal with 0 vote power
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER);
let res = app
.execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1070,6 +1017,22 @@ mod tests {
assert_eq!(ContractError::NotOpen {}, err.downcast().unwrap());
// query individual votes
// initial (with 0 weight)
let voter = OWNER.into();
let vote: VoteResponse = app
.wrap()
.query_wasm_smart(&flex_addr, &QueryMsg::Vote { proposal_id, voter })
.unwrap();
assert_eq!(
vote.vote.unwrap(),
VoteInfo {
proposal_id,
voter: OWNER.into(),
vote: Vote::Yes,
weight: 0
}
);
// nay sayer
let voter = VOTER2.into();
let vote: VoteResponse = app
@@ -1096,7 +1059,9 @@ mod tests {
// create proposal with 0 vote power
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER);
let res = app
.execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1144,7 +1109,9 @@ mod tests {
// create proposal with 0 vote power
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER);
let res = app
.execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1248,7 +1215,9 @@ mod tests {
// create proposal with 0 vote power
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER);
let res = app
.execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1317,7 +1286,9 @@ mod tests {
// create proposal with 0 vote power
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER);
let res = app
.execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1367,7 +1338,9 @@ mod tests {
// VOTER1 starts a proposal to send some tokens (1/4 votes)
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER1);
let res = app
.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
let prop_status = |app: &App, proposal_id: u64| -> Status {
@@ -1427,7 +1400,9 @@ mod tests {
// make a second proposal
let proposal2 = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal2, VOTER1);
let res = app
.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &proposal2, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id2: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1497,7 +1472,14 @@ mod tests {
msgs: vec![update_msg],
latest: None,
};
let res = propose_and_vote(&mut app, flex_addr.clone(), update_proposal, VOTER1);
let res = app
.execute_contract(
Addr::unchecked(VOTER1),
flex_addr.clone(),
&update_proposal,
&[],
)
.unwrap();
// Get the proposal id from the logs
let update_proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1506,7 +1488,14 @@ mod tests {
// VOTER1 starts a proposal to send some tokens
let cash_proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), cash_proposal, VOTER1);
let res = app
.execute_contract(
Addr::unchecked(VOTER1),
flex_addr.clone(),
&cash_proposal,
&[],
)
.unwrap();
// Get the proposal id from the logs
let cash_proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
assert_ne!(cash_proposal_id, update_proposal_id);
@@ -1595,7 +1584,9 @@ mod tests {
// VOTER3 starts a proposal to send some tokens (3/12 votes)
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER3);
let res = app
.execute_contract(Addr::unchecked(VOTER3), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
let prop_status = |app: &App| -> Status {
@@ -1637,7 +1628,9 @@ mod tests {
// new proposal can be passed single-handedly by newbie
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, newbie);
let res = app
.execute_contract(Addr::unchecked(newbie), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id2: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
@@ -1674,7 +1667,9 @@ mod tests {
// VOTER3 starts a proposal to send some tokens (3 votes)
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER3);
let res = app
.execute_contract(Addr::unchecked(VOTER3), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
let prop_status = |app: &App| -> Status {
@@ -1742,7 +1737,9 @@ mod tests {
// create proposal
let proposal = pay_somebody_proposal();
let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER5);
let res = app
.execute_contract(Addr::unchecked(VOTER5), flex_addr.clone(), &proposal, &[])
.unwrap();
// Get the proposal id from the logs
let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap();
let prop_status = |app: &App| -> Status {
@@ -14,9 +14,6 @@ pub enum ContractError {
#[error("Group contract invalid address '{addr}'")]
InvalidGroup { addr: String },
#[error("Coconut bandwidth contract address not found")]
InvalidCoconutBandwidth {},
#[error("Unauthorized")]
Unauthorized {},
@@ -1,4 +1,3 @@
use cosmwasm_std::Addr;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -12,7 +11,6 @@ pub struct Config {
pub max_voting_period: Duration,
// Total weight and voters are queried from this contract
pub group_addr: Cw4Contract,
pub coconut_bandwidth_addr: Addr,
}
// unique items
+1
View File
@@ -16,6 +16,7 @@ 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"] }
+19 -18
View File
@@ -1,12 +1,13 @@
use crate::errors::ContractError;
use crate::storage::{
account_from_address, locked_pledge_cap, update_locked_pledge_cap, ADMIN,
MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
MIXNET_CONTRACT_ADDRESS,
};
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,
@@ -35,7 +36,6 @@ 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,9 +167,11 @@ pub fn try_withdraw_vested_coins(
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
let mix_denom = MIX_DENOM.load(deps.storage)?;
if amount.denom != mix_denom {
return Err(ContractError::WrongDenom(amount.denom, mix_denom));
if amount.denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom(
amount.denom,
MIX_DENOM.base.to_string(),
));
}
let address = info.sender.clone();
@@ -243,8 +245,7 @@ pub fn try_bond_gateway(
env: Env,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
let mix_denom = MIX_DENOM.load(deps.storage)?;
let pledge = validate_funds(&[amount], mix_denom)?;
let pledge = validate_funds(&[amount])?;
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_bond_gateway(gateway, owner_signature, pledge, &env, deps.storage)
}
@@ -284,8 +285,7 @@ pub fn try_bond_mixnode(
env: Env,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
let mix_denom = MIX_DENOM.load(deps.storage)?;
let pledge = validate_funds(&[amount], mix_denom)?;
let pledge = validate_funds(&[amount])?;
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,8 +345,7 @@ fn try_delegate_to_mixnode(
env: Env,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
let mix_denom = MIX_DENOM.load(deps.storage)?;
let amount = validate_funds(&[amount], mix_denom)?;
let amount = validate_funds(&[amount])?;
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_delegate_to_mixnode(mix_identity, amount, &env, deps.storage)
}
@@ -397,7 +396,6 @@ 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 {
@@ -408,7 +406,7 @@ fn try_create_periodic_vesting_account(
let vesting_spec = vesting_spec.unwrap_or_default();
let coin = validate_funds(&info.funds, mix_denom)?;
let coin = validate_funds(&info.funds)?;
let owner_address = deps.api.addr_validate(owner_address)?;
let staking_address = if let Some(staking_address) = staking_address {
@@ -575,7 +573,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, deps.storage)
account.get_vested_coins(block_time, &env)
}
pub fn try_get_vesting_coins(
@@ -585,7 +583,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, deps.storage)
account.get_vesting_coins(block_time, &env)
}
pub fn try_get_start_time(
@@ -632,7 +630,7 @@ pub fn try_get_delegated_vesting(
account.get_delegated_vesting(block_time, &env, deps.storage)
}
fn validate_funds(funds: &[Coin], mix_denom: String) -> Result<Coin, ContractError> {
fn validate_funds(funds: &[Coin]) -> Result<Coin, ContractError> {
if funds.is_empty() || funds[0].amount.is_zero() {
return Err(ContractError::EmptyFunds);
}
@@ -641,8 +639,11 @@ fn validate_funds(funds: &[Coin], mix_denom: String) -> Result<Coin, ContractErr
return Err(ContractError::MultipleDenoms);
}
if funds[0].denom != mix_denom {
return Err(ContractError::WrongDenom(funds[0].denom.clone(), mix_denom));
if funds[0].denom != MIX_DENOM.base {
return Err(ContractError::WrongDenom(
funds[0].denom.clone(),
MIX_DENOM.base.to_string(),
));
}
Ok(funds[0].clone())
-1
View File
@@ -17,7 +17,6 @@ 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 {
+3 -5
View File
@@ -2,17 +2,15 @@
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", &[]);
@@ -33,7 +31,7 @@ pub mod helpers {
Some(Addr::unchecked("staking")),
Coin {
amount: Uint128::new(1_000_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
start_time_ts,
periods,
@@ -52,7 +50,7 @@ pub mod helpers {
Some(Addr::unchecked("staking")),
Coin {
amount: Uint128::new(1_000_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
start_time,
periods,
@@ -34,13 +34,11 @@ 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,7 +2,6 @@ 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};
@@ -29,7 +28,7 @@ impl DelegatingAccount for Account {
let compound_delegator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_delegator_reward_msg))
@@ -47,7 +46,7 @@ impl DelegatingAccount for Account {
let compound_delegator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_delegator_reward_msg))
@@ -119,7 +118,7 @@ impl DelegatingAccount for Account {
let undelegate_from_mixnode = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new()
@@ -2,7 +2,6 @@ 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};
@@ -77,7 +76,7 @@ impl GatewayBondingAccount for Account {
let unbond_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new()
@@ -1,7 +1,6 @@
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};
@@ -25,7 +24,7 @@ impl MixnodeBondingAccount for Account {
let compound_operator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_operator_reward_msg))
@@ -42,7 +41,7 @@ impl MixnodeBondingAccount for Account {
let compound_operator_reward_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new().add_message(compound_operator_reward_msg))
@@ -61,7 +60,7 @@ impl MixnodeBondingAccount for Account {
let update_mixnode_config_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new()
@@ -131,7 +130,7 @@ impl MixnodeBondingAccount for Account {
let unbond_msg = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![one_ucoin(MIX_DENOM.load(storage)?)],
vec![one_ucoin()],
)?;
Ok(Response::new()
@@ -1,6 +1,7 @@
use crate::errors::ContractError;
use crate::storage::{delete_account, save_account, DELEGATIONS, MIX_DENOM};
use crate::storage::{delete_account, save_account, DELEGATIONS};
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};
@@ -32,7 +33,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, storage)?
self.get_vesting_coins(block_time, env)?
.amount
.u128()
.saturating_sub(
@@ -46,7 +47,7 @@ impl VestingAccount for Account {
.u128(),
),
),
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
}
@@ -62,7 +63,7 @@ impl VestingAccount for Account {
.u128()
.saturating_sub(self.locked_coins(block_time, env, storage)?.amount.u128()),
),
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
}
@@ -70,24 +71,22 @@ 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,
denom: MIX_DENOM.base.to_string(),
},
Period::In(idx) => Coin {
amount: Uint128::new(self.tokens_per_period()? * idx as u128),
denom,
denom: MIX_DENOM.base.to_string(),
},
Period::After => Coin {
amount: self.coin.amount,
denom,
denom: MIX_DENOM.base.to_string(),
},
};
Ok(amount)
@@ -97,12 +96,11 @@ 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, storage)?.amount,
denom: MIX_DENOM.load(storage)?,
- self.get_vested_coins(block_time, env)?.amount,
denom: MIX_DENOM.base.to_string(),
})
}
@@ -132,7 +130,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, storage)?
.get_vested_coins(Some(block_time), env)?
.amount
.saturating_sub(withdrawn);
let start_time = match period {
@@ -154,7 +152,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount,
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
}
@@ -172,7 +170,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount,
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
}
@@ -184,7 +182,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, storage)?;
let max_vested = self.get_vested_coins(Some(block_time), env)?;
let start_time = match period {
Period::Before => 0,
Period::After => u64::MAX,
@@ -208,7 +206,7 @@ impl VestingAccount for Account {
Ok(Coin {
amount,
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
}
@@ -228,12 +226,12 @@ impl VestingAccount for Account {
let amount = bond.amount().amount - bonded_free.amount;
Ok(Coin {
amount,
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
} else {
Ok(Coin {
amount: Uint128::zero(),
denom: MIX_DENOM.load(storage)?,
denom: MIX_DENOM.base.to_string(),
})
}
}
+30 -49
View File
@@ -39,11 +39,12 @@ 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, TEST_COIN_DENOM,
init_contract, vesting_account_mid_fixture, vesting_account_new_fixture,
};
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};
@@ -54,7 +55,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, TEST_COIN_DENOM));
let info = mock_info("not_admin", &coins(1_000_000_000_000, MIX_DENOM.base));
let msg = ExecuteMsg::CreateAccount {
owner_address: "owner".to_string(),
staking_address: Some("staking".to_string()),
@@ -64,7 +65,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, TEST_COIN_DENOM));
let info = mock_info("admin", &coins(1_000_000_000_000, MIX_DENOM.base));
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
let created_account = load_account(&Addr::unchecked("owner"), &deps.storage)
.unwrap()
@@ -130,7 +131,7 @@ mod tests {
let msg = ExecuteMsg::WithdrawVestedCoins {
amount: Coin {
amount: Uint128::new(1),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
};
let info = mock_info("new_owner", &[]);
@@ -185,12 +186,8 @@ 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, &deps.storage)
.unwrap();
let vesting_coins = account
.get_vesting_coins(Some(block_time), &env, &deps.storage)
.unwrap();
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(
@@ -210,12 +207,8 @@ 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, &deps.storage)
.unwrap();
let vesting_coins = account
.get_vesting_coins(Some(block_time), &env, &deps.storage)
.unwrap();
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(
@@ -237,12 +230,8 @@ 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, &deps.storage)
.unwrap();
let vesting_coins = account
.get_vesting_coins(Some(block_time), &env, &deps.storage)
.unwrap();
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
assert_eq!(
vested_coins.amount,
Uint128::new(account.get_original_vesting().amount().amount.u128())
@@ -256,10 +245,8 @@ 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, &deps.storage).unwrap();
let vesting_coins = account
.get_vesting_coins(None, &env, &deps.storage)
.unwrap();
let vested_coins = account.get_vested_coins(None, &env).unwrap();
let vesting_coins = account.get_vesting_coins(None, &env).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));
@@ -275,7 +262,7 @@ mod tests {
let delegation = Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
};
let ok = account.try_delegate_to_mixnode(
@@ -286,10 +273,8 @@ mod tests {
);
assert!(ok.is_ok());
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 vested_coins = account.get_vested_coins(None, &env).unwrap();
let vesting_coins = account.get_vesting_coins(None, &env).unwrap();
assert_eq!(vested_coins.amount, Uint128::new(250_000_000_000));
assert_eq!(vesting_coins.amount, Uint128::new(750_000_000_000));
@@ -348,10 +333,8 @@ 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, &deps.storage).unwrap();
let vesting_coins = account
.get_vesting_coins(None, &env, &deps.storage)
.unwrap();
let vested_coins = account.get_vested_coins(None, &env).unwrap();
let vesting_coins = account.get_vesting_coins(None, &env).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));
@@ -369,10 +352,8 @@ mod tests {
);
assert!(ok.is_ok());
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 vested_coins = account.get_vested_coins(None, &env).unwrap();
let vesting_coins = account.get_vesting_coins(None, &env).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));
@@ -407,7 +388,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -418,7 +399,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -430,7 +411,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(20_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -445,7 +426,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -541,7 +522,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -553,7 +534,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -569,7 +550,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(10_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -661,7 +642,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -673,7 +654,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
@@ -689,7 +670,7 @@ mod tests {
"alice".to_string(),
Coin {
amount: Uint128::new(500_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
denom: MIX_DENOM.base.to_string(),
},
&env,
&mut deps.storage,
-20
View File
@@ -1,20 +0,0 @@
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
View File
@@ -1,20 +0,0 @@
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"
-1
View File
@@ -7,7 +7,6 @@ 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"
+10 -14
View File
@@ -1,9 +1,6 @@
use network_defaults::{
var_names::{API_VALIDATOR, NYMD_VALIDATOR},
NymNetworkDetails,
};
use network_defaults::{default_api_endpoints, default_nymd_endpoints, DEFAULT_NETWORK};
use reqwest::Url;
use std::{str::FromStr, sync::Arc};
use std::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
@@ -26,17 +23,16 @@ impl ThreadsafeValidatorClient {
}
pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient {
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 nymd_url = default_nymd_endpoints()[0].clone();
let api_url = default_api_endpoints()[0].clone();
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);
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);
ThreadsafeValidatorClient(Arc::new(
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"),
validator_client::Client::new_query(client_config, DEFAULT_NETWORK)
.expect("Failed to connect to nymd!"),
))
}
-12
View File
@@ -1,12 +0,0 @@
// 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>,
}
-5
View File
@@ -3,13 +3,10 @@ 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;
@@ -27,8 +24,6 @@ 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;
}

Some files were not shown because too many files have changed in this diff Show More