Allow proxy gateway bonding (#936)

* Allow proxy gateway bonding

* Update msg text

Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
This commit is contained in:
Drazen Urch
2021-12-02 18:16:26 +01:00
committed by GitHub
parent 231fba34bc
commit 1f42ce57e3
17 changed files with 481 additions and 71 deletions
+14 -1
View File
@@ -27,15 +27,23 @@ pub struct GatewayBond {
pub owner: Addr,
pub block_height: u64,
pub gateway: Gateway,
pub proxy: Option<Addr>,
}
impl GatewayBond {
pub fn new(bond_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
pub fn new(
bond_amount: Coin,
owner: Addr,
block_height: u64,
gateway: Gateway,
proxy: Option<Addr>,
) -> Self {
GatewayBond {
bond_amount,
owner,
block_height,
gateway,
proxy,
}
}
@@ -154,6 +162,7 @@ mod tests {
owner: Addr::unchecked("foo1"),
block_height: 100,
gateway: gateway_fixture(),
proxy: None,
};
let gate2 = GatewayBond {
@@ -161,6 +170,7 @@ mod tests {
owner: Addr::unchecked("foo2"),
block_height: 120,
gateway: gateway_fixture(),
proxy: None,
};
let gate3 = GatewayBond {
@@ -168,6 +178,7 @@ mod tests {
owner: Addr::unchecked("foo3"),
block_height: 120,
gateway: gateway_fixture(),
proxy: None,
};
let gate4 = GatewayBond {
@@ -175,6 +186,7 @@ mod tests {
owner: Addr::unchecked("foo4"),
block_height: 120,
gateway: gateway_fixture(),
proxy: None,
};
let gate5 = GatewayBond {
@@ -182,6 +194,7 @@ mod tests {
owner: Addr::unchecked("foo5"),
block_height: 120,
gateway: gateway_fixture(),
proxy: None,
};
// summary:
+7
View File
@@ -71,6 +71,13 @@ pub enum ExecuteMsg {
UnbondMixnodeOnBehalf {
owner: String,
},
BondGatewayOnBehalf {
gateway: Gateway,
owner: String,
},
UnbondGatewayOnBehalf {
owner: String,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
+8
View File
@@ -184,6 +184,14 @@ pub fn execute(
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(deps, info, owner)
}
ExecuteMsg::BondGatewayOnBehalf { gateway, owner } => {
crate::gateways::transactions::try_add_gateway_on_behalf(
deps, env, info, gateway, owner,
)
}
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
}
}
}
+1
View File
@@ -93,6 +93,7 @@ mod tests {
identity_key: node_identity.clone(),
..test_helpers::gateway_fixture()
},
proxy: None,
};
storage::gateways()
+89 -18
View File
@@ -6,36 +6,71 @@ use crate::error::ContractError;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::support::helpers::ensure_no_existing_bond;
use config::defaults::DENOM;
use cosmwasm_std::{BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128};
use cosmwasm_std::{
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
};
use mixnet_contract::{Gateway, GatewayBond, Layer};
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
pub(crate) fn try_add_gateway(
pub fn try_add_gateway(
deps: DepsMut,
env: Env,
info: MessageInfo,
gateway: Gateway,
) -> Result<Response, ContractError> {
// check if the bond contains any funds of the appropriate denomination
let minimum_bond = mixnet_params_storage::CONTRACT_STATE
.load(deps.storage)?
.params
.minimum_mixnode_bond;
let bond = validate_gateway_bond(info.funds, minimum_bond)?;
_try_add_gateway(deps, env, gateway, bond, info.sender.as_str(), None)
}
pub fn try_add_gateway_on_behalf(
deps: DepsMut,
env: Env,
info: MessageInfo,
gateway: Gateway,
owner: String,
) -> Result<Response, ContractError> {
// check if the bond contains any funds of the appropriate denomination
let minimum_bond = mixnet_params_storage::CONTRACT_STATE
.load(deps.storage)?
.params
.minimum_mixnode_bond;
let bond = validate_gateway_bond(info.funds, minimum_bond)?;
let proxy = info.sender;
_try_add_gateway(deps, env, gateway, bond, &owner, Some(proxy))
}
pub(crate) fn _try_add_gateway(
deps: DepsMut,
env: Env,
gateway: Gateway,
bond: Coin,
owner: &str,
proxy: Option<Addr>,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(owner)?;
// if the client has an active bonded mixnode or gateway, don't allow bonding
ensure_no_existing_bond(deps.storage, &info.sender)?;
ensure_no_existing_bond(deps.storage, &owner)?;
// check if somebody else has already bonded a gateway with this identity
if let Some(existing_bond) =
storage::gateways().may_load(deps.storage, &gateway.identity_key)?
{
if existing_bond.owner != info.sender {
if existing_bond.owner != owner {
return Err(ContractError::DuplicateGateway {
owner: existing_bond.owner,
});
}
}
let minimum_bond = mixnet_params_storage::CONTRACT_STATE
.load(deps.storage)?
.params
.minimum_gateway_bond;
let bond_amount = validate_gateway_bond(info.funds, minimum_bond)?;
let bond = GatewayBond::new(bond_amount, info.sender, env.block.height, gateway);
let bond = GatewayBond::new(bond, owner, env.block.height, gateway, proxy);
storage::gateways().save(deps.storage, bond.identity(), &bond)?;
mixnet_params_storage::increment_layer_count(deps.storage, Layer::Gateway)?;
@@ -43,23 +78,47 @@ pub(crate) fn try_add_gateway(
Ok(Response::new())
}
pub(crate) fn try_remove_gateway(
pub fn try_remove_gateway_on_behalf(
deps: DepsMut,
info: MessageInfo,
owner: String,
) -> Result<Response, ContractError> {
let proxy = info.sender;
_try_remove_gateway(deps, &owner, Some(proxy))
}
pub fn try_remove_gateway(deps: DepsMut, info: MessageInfo) -> Result<Response, ContractError> {
_try_remove_gateway(deps, info.sender.as_ref(), None)
}
pub(crate) fn _try_remove_gateway(
deps: DepsMut,
owner: &str,
proxy: Option<Addr>,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(owner)?;
// try to find the node of the sender
let gateway_bond = match storage::gateways()
.idx
.owner
.item(deps.storage, info.sender.clone())?
.item(deps.storage, owner.clone())?
{
Some(record) => record.1,
None => return Err(ContractError::NoAssociatedGatewayBond { owner: info.sender }),
None => return Err(ContractError::NoAssociatedGatewayBond { owner }),
};
if proxy != gateway_bond.proxy {
return Err(ContractError::ProxyMismatch {
existing: gateway_bond
.proxy
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
// send bonded funds back to the bond owner
let return_tokens = BankMsg::Send {
to_address: info.sender.as_str().to_owned(),
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
amount: vec![gateway_bond.bond_amount()],
};
@@ -69,11 +128,23 @@ pub(crate) fn try_remove_gateway(
// decrement layer count
mixnet_params_storage::decrement_layer_count(deps.storage, Layer::Gateway)?;
Ok(Response::new()
let mut response = Response::new()
.add_message(return_tokens)
.add_attribute("action", "unbond")
.add_attribute("address", info.sender)
.add_attribute("gateway_bond", gateway_bond.to_string()))
.add_attribute("address", owner.clone())
.add_attribute("gateway_bond", gateway_bond.to_string());
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondGateway {
owner: owner.as_str().to_string(),
amount: gateway_bond.bond_amount,
};
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
response = response.add_message(track_unbond_message);
}
Ok(response)
}
fn validate_gateway_bond(
@@ -150,7 +150,7 @@ pub(crate) fn _try_remove_mixnode(
.add_attribute("mixnode_bond", mixnode_bond.to_string());
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbond {
let msg = VestingContractExecuteMsg::TrackUnbondMixnode {
owner: owner.as_str().to_string(),
amount: mixnode_bond.bond_amount,
};
+7 -1
View File
@@ -154,7 +154,13 @@ pub mod test_helpers {
identity_key: format!("id-{}", owner),
..gateway_fixture()
};
GatewayBond::new(coin(50, DENOM), Addr::unchecked(owner), 12_345, gateway)
GatewayBond::new(
coin(50, DENOM),
Addr::unchecked(owner),
12_345,
gateway,
None,
)
}
pub fn query_contract_balance(
+44 -5
View File
@@ -1,14 +1,16 @@
use crate::errors::ContractError;
use crate::messages::{ExecuteMsg, InitMsg, QueryMsg};
use crate::storage::account_from_address;
use crate::traits::{BondingAccount, DelegatingAccount, VestingAccount};
use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM};
use cosmwasm_std::{
entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Timestamp, Uint128,
};
use mixnet_contract::{IdentityKey, MixNode};
use mixnet_contract::{Gateway, IdentityKey, MixNode};
// We're using a 24 month vesting period with 3 months sub-periods.
// There are 8 three month periods in two years
@@ -56,10 +58,47 @@ pub fn execute(
} => try_track_undelegation(&owner, mix_identity, amount, info, deps),
ExecuteMsg::BondMixnode { mix_node } => try_bond_mixnode(mix_node, info, env, deps),
ExecuteMsg::UnbondMixnode {} => try_unbond_mixnode(info, deps),
ExecuteMsg::TrackUnbond { owner, amount } => try_track_unbond(&owner, amount, info, deps),
ExecuteMsg::TrackUnbondMixnode { owner, amount } => {
try_track_unbond_mixnode(&owner, amount, info, deps)
}
ExecuteMsg::BondGateway { gateway } => try_bond_gateway(gateway, info, env, deps),
ExecuteMsg::UnbondGateway {} => try_unbond_gateway(info, deps),
ExecuteMsg::TrackUnbondGateway { owner, amount } => {
try_track_unbond_gateway(&owner, amount, info, deps)
}
}
}
pub fn try_bond_gateway(
gateway: Gateway,
info: MessageInfo,
env: Env,
deps: DepsMut,
) -> Result<Response, ContractError> {
let bond = validate_funds(&info.funds)?;
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_bond_gateway(gateway, bond, &env, deps.storage)
}
pub fn try_unbond_gateway(info: MessageInfo, deps: DepsMut) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_unbond_gateway(deps.storage)
}
pub fn try_track_unbond_gateway(
owner: &str,
amount: Coin,
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS {
return Err(ContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.try_track_unbond_gateway(amount, deps.storage)?;
Ok(Response::default())
}
pub fn try_bond_mixnode(
mix_node: MixNode,
info: MessageInfo,
@@ -76,7 +115,7 @@ pub fn try_unbond_mixnode(info: MessageInfo, deps: DepsMut) -> Result<Response,
account.try_unbond_mixnode(deps.storage)
}
pub fn try_track_unbond(
pub fn try_track_unbond_mixnode(
owner: &str,
amount: Coin,
info: MessageInfo,
@@ -86,7 +125,7 @@ pub fn try_track_unbond(
return Err(ContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.track_unbond(amount, deps.storage)?;
account.try_track_unbond_mixnode(amount, deps.storage)?;
Ok(Response::default())
}
+10 -2
View File
@@ -1,6 +1,6 @@
use cosmwasm_std::{Coin, Timestamp};
use mixnet_contract::IdentityKey;
use mixnet_contract::MixNode;
use mixnet_contract::{Gateway, MixNode};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -33,7 +33,15 @@ pub enum ExecuteMsg {
mix_node: MixNode,
},
UnbondMixnode {},
TrackUnbond {
TrackUnbondMixnode {
owner: String,
amount: Coin,
},
BondGateway {
gateway: Gateway,
},
UnbondGateway {},
TrackUnbondGateway {
owner: String,
amount: Coin,
},
+39
View File
@@ -0,0 +1,39 @@
use crate::errors::ContractError;
use cosmwasm_std::{Coin, Env, Response, Storage};
use mixnet_contract::{Gateway, MixNode};
pub trait MixnodeBondingAccount {
fn try_bond_mixnode(
&self,
mix_node: MixNode,
amount: Coin,
env: &Env,
storage: &mut dyn Storage,
) -> Result<Response, ContractError>;
fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result<Response, ContractError>;
fn try_track_unbond_mixnode(
&self,
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError>;
}
pub trait GatewayBondingAccount {
fn try_bond_gateway(
&self,
gateway: Gateway,
amount: Coin,
env: &Env,
storage: &mut dyn Storage,
) -> Result<Response, ContractError>;
fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result<Response, ContractError>;
fn try_track_unbond_gateway(
&self,
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError>;
}
@@ -1,17 +0,0 @@
use crate::errors::ContractError;
use cosmwasm_std::{Coin, Env, Response, Storage};
use mixnet_contract::MixNode;
pub trait BondingAccount {
fn try_bond_mixnode(
&self,
mix_node: MixNode,
amount: Coin,
env: &Env,
storage: &mut dyn Storage,
) -> Result<Response, ContractError>;
fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result<Response, ContractError>;
fn track_unbond(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError>;
}
+2 -2
View File
@@ -1,7 +1,7 @@
pub mod bonding_account;
pub mod bonding;
pub mod delegating_account;
pub mod vesting_account;
pub use self::bonding_account::BondingAccount;
pub use self::bonding::{GatewayBondingAccount, MixnodeBondingAccount};
pub use self::delegating_account::DelegatingAccount;
pub use self::vesting_account::VestingAccount;
@@ -0,0 +1,84 @@
use super::BondData;
use crate::errors::ContractError;
use crate::traits::GatewayBondingAccount;
use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract::{ExecuteMsg as MixnetExecuteMsg, Gateway};
use super::Account;
impl GatewayBondingAccount for Account {
fn try_bond_gateway(
&self,
gateway: Gateway,
bond: Coin,
env: &Env,
storage: &mut dyn Storage,
) -> Result<Response, ContractError> {
let current_balance = self.load_balance(storage)?;
if current_balance < bond.amount {
return Err(ContractError::InsufficientBalance(
self.address.as_str().to_string(),
current_balance.u128(),
));
}
let bond_data = if let Some(_bond) = self.load_gateway_bond(storage)? {
return Err(ContractError::AlreadyBonded(
self.address.as_str().to_string(),
));
} else {
BondData {
block_time: env.block.time,
amount: bond.amount,
}
};
let msg = MixnetExecuteMsg::BondGatewayOnBehalf {
gateway,
owner: self.address().into_string(),
};
let new_balance = Uint128::new(current_balance.u128() - bond.amount.u128());
let bond_mixnode_mag = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![bond])?;
self.save_balance(new_balance, storage)?;
self.save_gateway_bond(bond_data, storage)?;
Ok(Response::new()
.add_attribute("action", "bond gateway on behalf")
.add_message(bond_mixnode_mag))
}
fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::UnbondGatewayOnBehalf {
owner: self.address().into_string(),
};
if let Some(_bond) = self.load_gateway_bond(storage)? {
let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?;
Ok(Response::new()
.add_attribute("action", "unbond gateway on behalf")
.add_message(unbond_msg))
} else {
Err(ContractError::NoBondFound(
self.address.as_str().to_string(),
))
}
}
fn try_track_unbond_gateway(
&self,
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128());
self.save_balance(new_balance, storage)?;
self.remove_gateway_bond(storage)?;
Ok(())
}
}
@@ -1,13 +1,13 @@
use super::BondData;
use crate::errors::ContractError;
use crate::traits::BondingAccount;
use crate::traits::MixnodeBondingAccount;
use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract::{ExecuteMsg as MixnetExecuteMsg, MixNode};
use super::Account;
impl BondingAccount for Account {
impl MixnodeBondingAccount for Account {
fn try_bond_mixnode(
&self,
mix_node: MixNode,
@@ -24,7 +24,7 @@ impl BondingAccount for Account {
));
}
let bond_data = if let Some(_bond) = self.load_bond(storage)? {
let bond_data = if let Some(_bond) = self.load_mixnode_bond(storage)? {
return Err(ContractError::AlreadyBonded(
self.address.as_str().to_string(),
));
@@ -45,7 +45,7 @@ impl BondingAccount for Account {
let bond_mixnode_mag = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![bond])?;
self.save_balance(new_balance, storage)?;
self.save_bond(bond_data, storage)?;
self.save_mixnode_bond(bond_data, storage)?;
Ok(Response::new()
.add_attribute("action", "bond mixnode on behalf")
@@ -57,7 +57,7 @@ impl BondingAccount for Account {
owner: self.address().into_string(),
};
if let Some(_bond) = self.load_bond(storage)? {
if let Some(_bond) = self.load_mixnode_bond(storage)? {
let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?;
Ok(Response::new()
@@ -70,11 +70,15 @@ impl BondingAccount for Account {
}
}
fn track_unbond(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> {
fn try_track_unbond_mixnode(
&self,
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128());
self.save_balance(new_balance, storage)?;
self.remove_bond(storage)?;
self.remove_mixnode_bond(storage)?;
Ok(())
}
}
+42 -11
View File
@@ -8,13 +8,15 @@ use mixnet_contract::IdentityKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
mod bonding_account;
mod delegating_account;
mod gateway_bonding_account;
mod mixnode_bonding_account;
mod vesting_account;
const DELEGATIONS_SUFFIX: &str = "de";
const BALANCE_SUFFIX: &str = "ba";
const BOND_SUFFIX: &str = "bo";
const GATEWAY_SUFFIX: &str = "ga";
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Account {
@@ -24,7 +26,8 @@ pub struct Account {
coin: Coin,
delegations_key: String,
balance_key: String,
bond_key: String,
mixnode_bond_key: String,
gateway_bond_key: String,
}
impl Account {
@@ -43,7 +46,8 @@ impl Account {
coin,
delegations_key: format!("{}_{}", address, DELEGATIONS_SUFFIX),
balance_key: format!("{}_{}", address, BALANCE_SUFFIX),
bond_key: format!("{}_{}", address, BOND_SUFFIX),
mixnode_bond_key: format!("{}_{}", address, BOND_SUFFIX),
gateway_bond_key: format!("{}_{}", address, GATEWAY_SUFFIX),
};
save_account(&account, storage)?;
account.save_balance(amount, storage)?;
@@ -114,25 +118,52 @@ impl Account {
Item::new(self.balance_key.as_ref())
}
pub fn load_bond(&self, storage: &dyn Storage) -> Result<Option<BondData>, ContractError> {
Ok(self.bond().may_load(storage)?)
pub fn load_mixnode_bond(
&self,
storage: &dyn Storage,
) -> Result<Option<BondData>, ContractError> {
Ok(self.mixnode_bond().may_load(storage)?)
}
pub fn save_bond(
pub fn save_mixnode_bond(
&self,
bond: BondData,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
Ok(self.bond().save(storage, &bond)?)
Ok(self.mixnode_bond().save(storage, &bond)?)
}
pub fn remove_bond(&self, storage: &mut dyn Storage) -> Result<(), ContractError> {
self.bond().remove(storage);
pub fn remove_mixnode_bond(&self, storage: &mut dyn Storage) -> Result<(), ContractError> {
self.mixnode_bond().remove(storage);
Ok(())
}
fn bond(&self) -> Item<BondData> {
Item::new(self.bond_key.as_ref())
fn mixnode_bond(&self) -> Item<BondData> {
Item::new(self.mixnode_bond_key.as_ref())
}
pub fn load_gateway_bond(
&self,
storage: &dyn Storage,
) -> Result<Option<BondData>, ContractError> {
Ok(self.gateway_bond().may_load(storage)?)
}
pub fn save_gateway_bond(
&self,
bond: BondData,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
Ok(self.gateway_bond().save(storage, &bond)?)
}
pub fn remove_gateway_bond(&self, storage: &mut dyn Storage) -> Result<(), ContractError> {
self.gateway_bond().remove(storage);
Ok(())
}
fn gateway_bond(&self) -> Item<BondData> {
Item::new(self.gateway_bond_key.as_ref())
}
fn delegations(&self) -> Map<(&[u8], u64), Uint128> {
@@ -163,7 +163,10 @@ impl VestingAccount for Account {
let max_vested = self.tokens_per_period()? * period as u128;
let start_time = self.periods[period].start_time;
let amount = if let Some(bond) = self.load_bond(storage)? {
let amount = if let Some(bond) = self
.load_mixnode_bond(storage)?
.or(self.load_gateway_bond(storage)?)
{
if bond.block_time.seconds() < start_time {
bond.amount
} else {
@@ -190,7 +193,10 @@ impl VestingAccount for Account {
let block_time = block_time.unwrap_or(env.block.time);
let bonded_free = self.get_bonded_free(Some(block_time), env, storage)?;
if let Some(bond) = self.load_bond(storage)? {
if let Some(bond) = self
.load_mixnode_bond(storage)?
.or(self.load_gateway_bond(storage)?)
{
let amount = bond.amount - bonded_free.amount;
Ok(Coin {
amount,
+114 -4
View File
@@ -39,13 +39,13 @@ mod tests {
use crate::contract::{NUM_VESTING_PERIODS, VESTING_PERIOD};
use crate::storage::load_account;
use crate::support::tests::helpers::{init_contract, vesting_account_fixture};
use crate::traits::BondingAccount;
use crate::traits::DelegatingAccount;
use crate::traits::VestingAccount;
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
use config::defaults::DENOM;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::{Addr, Coin, Timestamp, Uint128};
use mixnet_contract::MixNode;
use mixnet_contract::{Gateway, MixNode};
#[test]
fn test_account_creation() {
@@ -234,7 +234,7 @@ mod tests {
}
#[test]
fn test_bonds() {
fn test_mixnode_bonds() {
let mut deps = init_contract();
let env = mock_env();
@@ -287,7 +287,117 @@ mod tests {
);
assert!(err.is_err());
let bond = account.load_bond(&deps.storage).unwrap().unwrap();
let bond = account.load_mixnode_bond(&deps.storage).unwrap().unwrap();
assert_eq!(Uint128::new(500_000_000_000), bond.amount);
// Current period -> block_time: None
let bonded_free = account.get_bonded_free(None, &env, &deps.storage).unwrap();
assert_eq!(Uint128::new(0), bonded_free.amount);
let bonded_vesting = account
.get_bonded_vesting(None, &env, &deps.storage)
.unwrap();
assert_eq!(bond.amount - bonded_free.amount, bonded_vesting.amount);
// All periods
for (i, period) in account.periods().iter().enumerate() {
let bonded_free = account
.get_bonded_free(
Some(Timestamp::from_seconds(period.start_time + 1)),
&env,
&deps.storage,
)
.unwrap();
assert_eq!(
(account.tokens_per_period().unwrap() * i as u128).min(bond.amount.u128()),
bonded_free.amount.u128()
);
let bonded_vesting = account
.get_bonded_vesting(
Some(Timestamp::from_seconds(period.start_time + 1)),
&env,
&deps.storage,
)
.unwrap();
assert_eq!(bond.amount - bonded_free.amount, bonded_vesting.amount);
}
let bonded_free = account
.get_bonded_free(
Some(Timestamp::from_seconds(1764416964)),
&env,
&deps.storage,
)
.unwrap();
assert_eq!(bond.amount, bonded_free.amount);
let bonded_vesting = account
.get_bonded_vesting(
Some(Timestamp::from_seconds(1764416964)),
&env,
&deps.storage,
)
.unwrap();
assert_eq!(Uint128::zero(), bonded_vesting.amount);
}
#[test]
fn test_gateway_bonds() {
let mut deps = init_contract();
let env = mock_env();
let account = vesting_account_fixture(&mut deps.storage, &env);
let gateway = Gateway {
host: "1.1.1.1".to_string(),
mix_port: 1789,
clients_port: 9000,
location: "Sweden".to_string(),
sphinx_key: "sphinx".to_string(),
identity_key: "identity".to_string(),
version: "0.10.0".to_string(),
};
// Try delegating too much
let err = account.try_bond_gateway(
gateway.clone(),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(err.is_err());
let ok = account.try_bond_gateway(
gateway.clone(),
Coin {
amount: Uint128::new(500_000_000_000),
denom: DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(ok.is_ok());
let balance = account.load_balance(&deps.storage).unwrap();
assert_eq!(balance, Uint128::new(500_000_000_000));
// Try delegating too much again
let err = account.try_bond_gateway(
gateway,
Coin {
amount: Uint128::new(500_000_000_001),
denom: DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(err.is_err());
let bond = account.load_gateway_bond(&deps.storage).unwrap().unwrap();
assert_eq!(Uint128::new(500_000_000_000), bond.amount);
// Current period -> block_time: None