Rework vesting contract storage (#1061)

* Rework storage

* Handle may_load failures

* Set ADMIN on init

* Use strings for storage keys

* Add GetAccount

* Move amount to msg

* Use iterators everywhere

* mock test properly

* Configure mixnet contract on init

* Updated Cargo.lock
This commit is contained in:
Drazen Urch
2022-01-27 21:47:28 +01:00
committed by GitHub
parent 390e58f603
commit 5f4d56c1d8
16 changed files with 291 additions and 208 deletions
Generated
+8 -8
View File
@@ -1002,9 +1002,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a"
checksum = "f903ebbabc0d4880dbc76148efb8be8fc29fa4bf294c440c3d70da1c8bcafff7"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -1015,18 +1015,18 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866713b2fe13f23038c7d8824c3059d1f28dd94685fb406d1533c4eeeefeefae"
checksum = "832bebef577ecb394603de8e2bf0de429b74aa364e17dec18e15ce37e71b0cae"
dependencies = [
"syn",
]
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dbb9939b31441dfa9af3ec9740c8a24d585688401eff1b6b386abb7ad0d10a8"
checksum = "6238c45840cc9de5a39f0f619e3a4f7c38c5d2c6ac9e3e4d72751ee045e6d7da"
dependencies = [
"base64",
"cosmwasm-crypto",
@@ -5819,9 +5819,9 @@ dependencies = [
[[package]]
name = "serde-json-wasm"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50eef3672ec8fa45f3457fd423ba131117786784a895548021976117c1ded449"
checksum = "042ac496d97e5885149d34139bad1d617192770d7eb8f1866da2317ff4501853"
dependencies = [
"serde",
]
@@ -83,6 +83,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
let req = VestingExecuteMsg::BondGateway {
gateway,
owner_signature: owner_signature.to_string(),
amount: pledge,
};
self.client
.execute(
@@ -91,7 +92,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
&req,
fee,
"VestingContract::BondGateway",
vec![cosmwasm_coin_to_cosmos_coin(pledge)],
vec![],
)
.await
}
@@ -143,6 +144,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
let req = VestingExecuteMsg::BondMixnode {
mix_node,
owner_signature: owner_signature.to_string(),
amount: pledge,
};
self.client
.execute(
@@ -151,7 +153,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
&req,
fee,
"VestingContract::BondMixnode",
vec![cosmwasm_coin_to_cosmos_coin(pledge)],
vec![],
)
.await
}
@@ -239,6 +241,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
let fee = self.operation_fee(Operation::DelegateToMixnode);
let req = VestingExecuteMsg::DelegateToMixnode {
mix_identity: mix_identity.into(),
amount: amount.clone(),
};
self.client
.execute(
@@ -247,7 +250,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
&req,
fee,
"VestingContract::DeledateToMixnode",
vec![cosmwasm_coin_to_cosmos_coin(amount.to_owned())],
vec![],
)
.await
}
+6 -6
View File
@@ -245,9 +245,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a"
checksum = "f903ebbabc0d4880dbc76148efb8be8fc29fa4bf294c440c3d70da1c8bcafff7"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -258,9 +258,9 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866713b2fe13f23038c7d8824c3059d1f28dd94685fb406d1533c4eeeefeefae"
checksum = "832bebef577ecb394603de8e2bf0de429b74aa364e17dec18e15ce37e71b0cae"
dependencies = [
"syn",
]
@@ -277,9 +277,9 @@ dependencies = [
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dbb9939b31441dfa9af3ec9740c8a24d585688401eff1b6b386abb7ad0d10a8"
checksum = "6238c45840cc9de5a39f0f619e3a4f7c38c5d2c6ac9e3e4d72751ee045e6d7da"
dependencies = [
"base64",
"cosmwasm-crypto",
+3 -3
View File
@@ -18,9 +18,9 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
config = { path = "../../common/config" }
cosmwasm-std = { version = "1.0.0-beta3"}
cosmwasm-std = { version = "1.0.0-beta4"}
cw-storage-plus = { version = "0.11.1", features = ["iterator"] }
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }
serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = { version = "1.0" }
+39 -20
View File
@@ -1,11 +1,11 @@
use crate::errors::ContractError;
use crate::messages::{ExecuteMsg, InitMsg, QueryMsg};
use crate::storage::account_from_address;
use crate::messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg};
use crate::storage::{account_from_address, ADMIN, MIXNET_CONTRACT_ADDRESS};
use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
};
use crate::vesting::{populate_vesting_periods, Account};
use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM};
use config::defaults::DENOM;
use cosmwasm_std::{
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Timestamp, Uint128,
@@ -22,16 +22,22 @@ use vesting_contract_common::events::{
// and duration of a single period is 30 days.
pub const NUM_VESTING_PERIODS: usize = 8;
pub const VESTING_PERIOD: u64 = 3 * 30 * 86400;
// Address of the account set to be contract admin
pub const ADMIN_ADDRESS: &str = "admin";
#[entry_point]
pub fn instantiate(
_deps: DepsMut,
deps: DepsMut,
_env: Env,
_info: MessageInfo,
_msg: InitMsg,
info: MessageInfo,
msg: InitMsg,
) -> Result<Response, ContractError> {
// 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)?;
Ok(Response::default())
}
#[entry_point]
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
Ok(Response::default())
}
@@ -43,9 +49,10 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::DelegateToMixnode { mix_identity } => {
try_delegate_to_mixnode(mix_identity, info, env, deps)
}
ExecuteMsg::DelegateToMixnode {
mix_identity,
amount,
} => try_delegate_to_mixnode(mix_identity, amount, info, env, deps),
ExecuteMsg::UndelegateFromMixnode { mix_identity } => {
try_undelegate_from_mixnode(mix_identity, info, deps)
}
@@ -72,7 +79,8 @@ pub fn execute(
ExecuteMsg::BondMixnode {
mix_node,
owner_signature,
} => try_bond_mixnode(mix_node, owner_signature, info, env, deps),
amount,
} => try_bond_mixnode(mix_node, owner_signature, amount, info, env, deps),
ExecuteMsg::UnbondMixnode {} => try_unbond_mixnode(info, deps),
ExecuteMsg::TrackUnbondMixnode { owner, amount } => {
try_track_unbond_mixnode(&owner, amount, info, deps)
@@ -80,7 +88,8 @@ pub fn execute(
ExecuteMsg::BondGateway {
gateway,
owner_signature,
} => try_bond_gateway(gateway, owner_signature, info, env, deps),
amount,
} => try_bond_gateway(gateway, owner_signature, amount, info, env, deps),
ExecuteMsg::UnbondGateway {} => try_unbond_gateway(info, deps),
ExecuteMsg::TrackUnbondGateway { owner, amount } => {
try_track_unbond_gateway(&owner, amount, info, deps)
@@ -175,11 +184,12 @@ fn try_update_staking_address(
pub fn try_bond_gateway(
gateway: Gateway,
owner_signature: String,
amount: Coin,
info: MessageInfo,
env: Env,
deps: DepsMut,
) -> Result<Response, ContractError> {
let pledge = validate_funds(&info.funds)?;
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)
}
@@ -195,7 +205,7 @@ pub fn try_track_unbond_gateway(
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS {
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
return Err(ContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(owner, deps.storage, deps.api)?;
@@ -206,11 +216,12 @@ pub fn try_track_unbond_gateway(
pub fn try_bond_mixnode(
mix_node: MixNode,
owner_signature: String,
amount: Coin,
info: MessageInfo,
env: Env,
deps: DepsMut,
) -> Result<Response, ContractError> {
let pledge = validate_funds(&info.funds)?;
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)
}
@@ -226,7 +237,7 @@ pub fn try_track_unbond_mixnode(
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS {
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
return Err(ContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(owner, deps.storage, deps.api)?;
@@ -241,7 +252,7 @@ fn try_track_undelegation(
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
if info.sender != DEFAULT_MIXNET_CONTRACT_ADDRESS {
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
return Err(ContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(address, deps.storage, deps.api)?;
@@ -251,11 +262,12 @@ fn try_track_undelegation(
fn try_delegate_to_mixnode(
mix_identity: IdentityKey,
amount: Coin,
info: MessageInfo,
env: Env,
deps: DepsMut,
) -> Result<Response, ContractError> {
let amount = validate_funds(&info.funds)?;
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)
}
@@ -277,9 +289,10 @@ fn try_create_periodic_vesting_account(
env: Env,
deps: DepsMut,
) -> Result<Response, ContractError> {
if info.sender != ADMIN_ADDRESS {
if info.sender != ADMIN.load(deps.storage)? {
return Err(ContractError::NotAdmin(info.sender.as_str().to_string()));
}
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 {
@@ -375,11 +388,17 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<QueryResponse, Contr
env,
deps,
)?),
QueryMsg::GetAccount { address } => to_binary(&try_get_account(&address, deps)?),
};
Ok(query_res?)
}
pub fn try_get_account(address: &str, deps: Deps) -> Result<Option<Account>, ContractError> {
let account = account_from_address(address, deps.storage, deps.api)?;
Ok(Some(account))
}
pub fn try_get_locked_coins(
vesting_account_address: &str,
block_time: Option<Timestamp>,
+13 -1
View File
@@ -5,13 +5,20 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct InitMsg {}
pub struct InitMsg {
pub(crate) mixnet_contract_address: String,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
DelegateToMixnode {
mix_identity: IdentityKey,
amount: Coin,
},
UndelegateFromMixnode {
mix_identity: IdentityKey,
@@ -32,6 +39,7 @@ pub enum ExecuteMsg {
BondMixnode {
mix_node: MixNode,
owner_signature: String,
amount: Coin,
},
UnbondMixnode {},
TrackUnbondMixnode {
@@ -41,6 +49,7 @@ pub enum ExecuteMsg {
BondGateway {
gateway: Gateway,
owner_signature: String,
amount: Coin,
},
UnbondGateway {},
TrackUnbondGateway {
@@ -91,4 +100,7 @@ pub enum QueryMsg {
block_time: Option<Timestamp>,
vesting_account_address: String,
},
GetAccount {
address: String,
},
}
+94 -6
View File
@@ -1,22 +1,108 @@
use crate::errors::ContractError;
use crate::vesting::Account;
use cosmwasm_std::{Addr, Api, Storage};
use crate::vesting::PledgeData;
use cosmwasm_std::{Addr, Api, Storage, Uint128};
use cw_storage_plus::{Item, Map};
use mixnet_contract_common::IdentityKey;
type BlockHeight = u64;
pub const KEY: Item<u32> = Item::new("key");
const ACCOUNTS: Map<Addr, Account> = Map::new("acc");
const ACCOUNTS: Map<String, Account> = Map::new("acc");
// Holds data related to individual accounts
const BALANCES: Map<u32, Uint128> = Map::new("blc");
const BOND_PLEDGES: Map<u32, PledgeData> = Map::new("bnd");
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 fn save_delegation(
key: (u32, IdentityKey, BlockHeight),
amount: Uint128,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
DELEGATIONS.save(storage, key, &amount)?;
Ok(())
}
pub fn remove_delegation(
key: (u32, IdentityKey, BlockHeight),
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
DELEGATIONS.remove(storage, key);
Ok(())
}
pub fn delete_account(address: &Addr, storage: &mut dyn Storage) -> Result<(), ContractError> {
ACCOUNTS.remove(storage, address.to_owned());
ACCOUNTS.remove(storage, address.to_owned().to_string());
Ok(())
}
pub fn load_balance(key: u32, storage: &dyn Storage) -> Result<Uint128, ContractError> {
Ok(BALANCES
.may_load(storage, key)
.unwrap_or(None)
.unwrap_or_else(Uint128::zero))
}
pub fn save_balance(
key: u32,
value: Uint128,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
BALANCES.save(storage, key, &value)?;
Ok(())
}
pub fn load_bond_pledge(
key: u32,
storage: &dyn Storage,
) -> Result<Option<PledgeData>, ContractError> {
Ok(BOND_PLEDGES.may_load(storage, key).unwrap_or(None))
}
pub fn remove_bond_pledge(key: u32, storage: &mut dyn Storage) -> Result<(), ContractError> {
BOND_PLEDGES.remove(storage, key);
Ok(())
}
pub fn save_bond_pledge(
key: u32,
value: &PledgeData,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
BOND_PLEDGES.save(storage, key, value)?;
Ok(())
}
pub fn load_gateway_pledge(
key: u32,
storage: &dyn Storage,
) -> Result<Option<PledgeData>, ContractError> {
Ok(GATEWAY_PLEDGES.may_load(storage, key).unwrap_or(None))
}
pub fn save_gateway_pledge(
key: u32,
value: &PledgeData,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
GATEWAY_PLEDGES.save(storage, key, value)?;
Ok(())
}
pub fn remove_gateway_pledge(key: u32, storage: &mut dyn Storage) -> Result<(), ContractError> {
GATEWAY_PLEDGES.remove(storage, key);
Ok(())
}
pub fn save_account(account: &Account, storage: &mut dyn Storage) -> Result<(), ContractError> {
// This is a bit dirty, but its a simple way to allow for both staking account and owner to load it from storage
if let Some(staking_address) = account.staking_address() {
ACCOUNTS.save(storage, staking_address.to_owned(), account)?;
ACCOUNTS.save(storage, staking_address.to_owned().to_string(), account)?;
}
ACCOUNTS.save(storage, account.owner_address(), account)?;
ACCOUNTS.save(storage, account.owner_address().to_string(), account)?;
Ok(())
}
@@ -24,7 +110,9 @@ pub fn load_account(
address: &Addr,
storage: &dyn Storage,
) -> Result<Option<Account>, ContractError> {
Ok(ACCOUNTS.may_load(storage, address.to_owned())?)
Ok(ACCOUNTS
.may_load(storage, address.to_owned().to_string())
.unwrap_or(None))
}
fn validate_account(address: &Addr, storage: &dyn Storage) -> Result<Account, ContractError> {
+4 -2
View File
@@ -9,9 +9,11 @@ pub mod helpers {
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
let msg = InitMsg {};
let msg = InitMsg {
mixnet_contract_address: "test".to_string(),
};
let env = mock_env();
let info = mock_info("creator", &[]);
let info = mock_info("admin", &[]);
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
return deps;
}
@@ -1,5 +1,5 @@
use crate::errors::ContractError;
use cosmwasm_std::{Coin, Env, Response, Storage, Timestamp, Uint128};
use cosmwasm_std::{Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::IdentityKey;
pub trait DelegatingAccount {
@@ -18,12 +18,12 @@ pub trait DelegatingAccount {
) -> Result<Response, ContractError>;
// track_delegation performs internal vesting accounting necessary when
// delegating from a vesting account. It accepts the current block time, the
// delegating from a vesting account. It accepts the current block height, the
// delegation amount and balance of all coins whose denomination exists in
// the account's original vesting balance.
fn track_delegation(
&self,
block_time: Timestamp,
block_height: u64,
mix_identity: IdentityKey,
// Save some gas by passing it in
current_balance: Uint128,
@@ -1,8 +1,9 @@
use crate::errors::ContractError;
use crate::storage::save_delegation;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::DelegatingAccount;
use config::defaults::{DEFAULT_MIXNET_CONTRACT_ADDRESS, DENOM};
use cosmwasm_std::{wasm_execute, Coin, Env, Order, Response, Storage, Timestamp, Uint128};
use cw_storage_plus::Map;
use config::defaults::DENOM;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::ExecuteMsg as MixnetExecuteMsg;
use mixnet_contract_common::IdentityKey;
use vesting_contract_common::events::{
@@ -32,9 +33,18 @@ impl DelegatingAccount for Account {
mix_identity: mix_identity.clone(),
delegate: self.owner_address().into_string(),
};
let delegate_to_mixnode =
wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![coin.clone()])?;
self.track_delegation(env.block.time, mix_identity, current_balance, coin, storage)?;
let delegate_to_mixnode = wasm_execute(
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![coin.clone()],
)?;
self.track_delegation(
env.block.height,
mix_identity,
current_balance,
coin,
storage,
)?;
Ok(Response::new()
.add_message(delegate_to_mixnode)
@@ -58,7 +68,7 @@ impl DelegatingAccount for Account {
delegate: self.owner_address().into_string(),
};
let undelegate_from_mixnode = wasm_execute(
DEFAULT_MIXNET_CONTRACT_ADDRESS,
MIXNET_CONTRACT_ADDRESS.load(storage)?,
&msg,
vec![Coin {
amount: Uint128::new(0),
@@ -73,29 +83,19 @@ impl DelegatingAccount for Account {
fn track_delegation(
&self,
block_time: Timestamp,
block_height: u64,
mix_identity: IdentityKey,
current_balance: Uint128,
delegation: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let delegation_key = (mix_identity.as_bytes(), block_time.seconds());
let delegations_storage_key = self.delegations_key();
let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_storage_key);
let new_delegation =
if let Some(existing_delegation) = delegations.may_load(storage, delegation_key)? {
existing_delegation + delegation.amount
} else {
delegation.amount
};
delegations.save(storage, delegation_key, &new_delegation)?;
save_delegation(
(self.storage_key(), mix_identity, block_height),
delegation.amount,
storage,
)?;
let new_balance = Uint128::new(current_balance.u128() - delegation.amount.u128());
self.save_balance(new_balance, storage)?;
Ok(())
}
@@ -105,26 +105,9 @@ impl DelegatingAccount for Account {
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let mix_bytes = mix_identity.as_bytes();
let delegations_key = self.delegations_key();
let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key);
// Iterate over keys matching the prefix and remove them from the map
let block_times = delegations
.prefix(mix_bytes)
.keys(storage, None, None, Order::Ascending)
// Scan will blow up on first error
.scan((), |_, x| x.ok())
.collect::<Vec<u64>>();
for t in block_times {
delegations.remove(storage, (mix_bytes, t))
}
self.remove_delegations_for_mix(&mix_identity, storage)?;
let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128());
self.save_balance(new_balance, storage)?;
Ok(())
}
}
@@ -1,7 +1,7 @@
use super::PledgeData;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::GatewayBondingAccount;
use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
use vesting_contract_common::events::{
@@ -47,7 +47,8 @@ impl GatewayBondingAccount for Account {
let new_balance = Uint128::new(current_balance.u128() - pledge.amount.u128());
let bond_gateway_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![pledge])?;
let bond_gateway_msg =
wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![pledge])?;
self.save_balance(new_balance, storage)?;
self.save_gateway_pledge(pledge_data, storage)?;
@@ -63,7 +64,7 @@ impl GatewayBondingAccount for Account {
};
if let Some(_bond) = self.load_gateway_pledge(storage)? {
let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?;
let unbond_msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
Ok(Response::new()
.add_message(unbond_msg)
@@ -1,7 +1,7 @@
use super::PledgeData;
use crate::errors::ContractError;
use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::MixnodeBondingAccount;
use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode};
use vesting_contract_common::events::{
@@ -47,7 +47,8 @@ impl MixnodeBondingAccount for Account {
let new_balance = Uint128::new(current_balance.u128() - pledge.amount.u128());
let bond_mixnode_mag = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![pledge])?;
let bond_mixnode_mag =
wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![pledge])?;
self.save_balance(new_balance, storage)?;
self.save_mixnode_pledge(pledge_data, storage)?;
@@ -63,7 +64,7 @@ impl MixnodeBondingAccount for Account {
};
if self.load_mixnode_pledge(storage)?.is_some() {
let unbond_msg = wasm_execute(DEFAULT_MIXNET_CONTRACT_ADDRESS, &msg, vec![])?;
let unbond_msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
Ok(Response::new()
.add_message(unbond_msg)
+63 -84
View File
@@ -1,9 +1,13 @@
use super::{PledgeData, VestingPeriod};
use crate::contract::NUM_VESTING_PERIODS;
use crate::errors::ContractError;
use crate::storage::{save_account, KEY};
use crate::storage::{
load_balance, load_bond_pledge, load_gateway_pledge, remove_bond_pledge, remove_delegation,
remove_gateway_pledge, save_account, save_balance, save_bond_pledge, save_gateway_pledge,
DELEGATIONS, KEY,
};
use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128};
use cw_storage_plus::{Item, Map};
use cw_storage_plus::Bound;
use mixnet_contract_common::IdentityKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -13,11 +17,6 @@ mod gateway_bonding_account;
mod mixnode_bonding_account;
mod vesting_account;
const DELEGATIONS_SUFFIX: &str = "de";
const BALANCE_SUFFIX: &str = "ba";
const PLEDGE_SUFFIX: &str = "bo";
const GATEWAY_SUFFIX: &str = "ga";
fn generate_storage_key(storage: &mut dyn Storage) -> Result<u32, ContractError> {
let key = KEY.may_load(storage)?.unwrap_or(0) + 1;
KEY.save(storage, &key)?;
@@ -31,7 +30,7 @@ pub struct Account {
start_time: Timestamp,
periods: Vec<VestingPeriod>,
coin: Coin,
storage_key: String,
storage_key: u32,
}
impl Account {
@@ -43,7 +42,7 @@ impl Account {
periods: Vec<VestingPeriod>,
storage: &mut dyn Storage,
) -> Result<Self, ContractError> {
let storage_key = generate_storage_key(storage)?.to_string();
let storage_key = generate_storage_key(storage)?;
let amount = coin.amount;
let account = Account {
owner_address,
@@ -58,20 +57,8 @@ impl Account {
Ok(account)
}
pub fn delegations_key(&self) -> String {
format!("{}{}", self.storage_key, DELEGATIONS_SUFFIX)
}
pub fn balance_key(&self) -> String {
format!("{}{}", self.storage_key, BALANCE_SUFFIX)
}
pub fn mixnode_pledge_key(&self) -> String {
format!("{}{}", self.storage_key, PLEDGE_SUFFIX)
}
pub fn gateway_pledge_key(&self) -> String {
format!("{}{}", self.storage_key, GATEWAY_SUFFIX)
pub fn storage_key(&self) -> u32 {
self.storage_key
}
pub fn owner_address(&self) -> Addr {
@@ -124,9 +111,7 @@ impl Account {
}
pub fn load_balance(&self, storage: &dyn Storage) -> Result<Uint128, ContractError> {
let key = self.balance_key();
let balance = Item::new(&key);
Ok(balance.may_load(storage)?.unwrap_or_else(Uint128::zero))
load_balance(self.storage_key(), storage)
}
pub fn save_balance(
@@ -134,18 +119,14 @@ impl Account {
amount: Uint128,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let key = self.balance_key();
let balance = Item::new(&key);
Ok(balance.save(storage, &amount)?)
save_balance(self.storage_key(), amount, storage)
}
pub fn load_mixnode_pledge(
&self,
storage: &dyn Storage,
) -> Result<Option<PledgeData>, ContractError> {
let key = self.mixnode_pledge_key();
let mixnode_pledge = Item::new(&key);
Ok(mixnode_pledge.may_load(storage)?)
load_bond_pledge(self.storage_key(), storage)
}
pub fn save_mixnode_pledge(
@@ -153,25 +134,18 @@ impl Account {
pledge: PledgeData,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let key = self.mixnode_pledge_key();
let mixnode_pledge = Item::new(&key);
Ok(mixnode_pledge.save(storage, &pledge)?)
save_bond_pledge(self.storage_key(), &pledge, storage)
}
pub fn remove_mixnode_pledge(&self, storage: &mut dyn Storage) -> Result<(), ContractError> {
let key = self.mixnode_pledge_key();
let mixnode_pledge: Item<PledgeData> = Item::new(&key);
mixnode_pledge.remove(storage);
Ok(())
remove_bond_pledge(self.storage_key(), storage)
}
pub fn load_gateway_pledge(
&self,
storage: &dyn Storage,
) -> Result<Option<PledgeData>, ContractError> {
let key = self.gateway_pledge_key();
let gateway_pledge = Item::new(&key);
Ok(gateway_pledge.may_load(storage)?)
load_gateway_pledge(self.storage_key(), storage)
}
pub fn save_gateway_pledge(
@@ -179,70 +153,75 @@ impl Account {
pledge: PledgeData,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let key = self.gateway_pledge_key();
let gateway_pledge = Item::new(&key);
Ok(gateway_pledge.save(storage, &pledge)?)
save_gateway_pledge(self.storage_key(), &pledge, storage)
}
pub fn remove_gateway_pledge(&self, storage: &mut dyn Storage) -> Result<(), ContractError> {
let key = self.gateway_pledge_key();
let gateway_pledge: Item<PledgeData> = Item::new(&key);
gateway_pledge.remove(storage);
Ok(())
}
// Returns block_time part of the delegation key
pub fn delegation_keys_for_mix(&self, mix: &str, storage: &dyn Storage) -> Vec<u64> {
let key = self.delegations_key();
let delegations: Map<(&[u8], u64), Uint128> = Map::new(&key);
delegations
.prefix(mix.as_bytes())
.keys(storage, None, None, Order::Ascending)
// Scan will blow up on first error
.scan((), |_, x| x.ok())
.collect::<Vec<u64>>()
remove_gateway_pledge(self.storage_key(), storage)
}
pub fn any_delegation_for_mix(&self, mix: &str, storage: &dyn Storage) -> bool {
!self.delegation_keys_for_mix(mix, storage).is_empty()
DELEGATIONS
.prefix((self.storage_key(), mix.to_string()))
.range(storage, None, None, Order::Ascending)
.next()
.is_some()
}
pub fn delegations_for_mix(
pub fn remove_delegations_for_mix(
&self,
mix: IdentityKey,
storage: &dyn Storage,
) -> Result<Vec<Uint128>, ContractError> {
let mix_bytes = mix.as_bytes();
let keys = self.delegation_keys_for_mix(&mix, storage);
let delegations_key = self.delegations_key();
let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key);
mix: &str,
storage: &mut dyn Storage,
) -> Result<(), ContractError> {
let limit = 50;
let mut start_after = None;
let mut block_heights = Vec::new();
let mut prev_len = 0;
// TODO: Test this
loop {
block_heights.extend(
DELEGATIONS
.prefix((self.storage_key(), mix.to_string()))
.keys(storage, start_after, None, Order::Ascending)
.take(limit)
.filter_map(|key| key.ok()),
);
let mut delegation_amounts = Vec::new();
for key in keys {
delegation_amounts.push(delegations.load(storage, (mix_bytes, key))?)
if prev_len == block_heights.len() {
break;
}
prev_len = block_heights.len();
start_after = block_heights.last().map(|last| Bound::exclusive_int(*last));
if start_after.is_none() {
break;
}
}
Ok(delegation_amounts)
for block_height in block_heights {
remove_delegation((self.storage_key(), mix.to_string(), block_height), storage)?;
}
Ok(())
}
#[allow(dead_code)]
pub fn total_delegations_for_mix(
&self,
mix: IdentityKey,
storage: &dyn Storage,
) -> Result<Uint128, ContractError> {
Ok(self
.delegations_for_mix(mix, storage)?
.iter()
.fold(Uint128::zero(), |acc, x| acc + *x))
Ok(DELEGATIONS
.prefix((self.storage_key(), mix))
.range(storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.fold(Uint128::zero(), |acc, (_key, val)| acc + val))
}
pub fn total_delegations(&self, storage: &dyn Storage) -> Result<Uint128, ContractError> {
let delegations_key = self.delegations_key();
let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key);
Ok(delegations
Ok(DELEGATIONS
.sub_prefix(self.storage_key())
.range(storage, None, None, Order::Ascending)
.scan((), |_, x| x.ok())
.fold(Uint128::zero(), |acc, (_, x)| acc + x))
.filter_map(|x| x.ok())
.fold(Uint128::zero(), |acc, (_key, val)| acc + val))
}
}
@@ -1,10 +1,9 @@
use crate::contract::NUM_VESTING_PERIODS;
use crate::errors::ContractError;
use crate::storage::{delete_account, save_account};
use crate::storage::{delete_account, save_account, DELEGATIONS};
use crate::traits::VestingAccount;
use config::defaults::DENOM;
use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128};
use cw_storage_plus::Map;
use super::Account;
@@ -115,21 +114,17 @@ impl VestingAccount for Account {
let period = self.get_current_vesting_period(block_time);
let max_vested = self.tokens_per_period()? * period as u128;
let start_time = self.periods[period].start_time;
let delegations_key = self.delegations_key();
let delegations: Map<(&[u8], u64), Uint128> = Map::new(&delegations_key);
let delegations_keys = delegations
.keys(storage, None, None, Order::Ascending)
.scan((), |_, x| x.ok())
.filter(|(_mix, block_time)| *block_time < start_time)
.map(|(mix, block_time)| (mix, block_time))
.collect::<Vec<(Vec<u8>, u64)>>();
let coin = DELEGATIONS
.sub_prefix(self.storage_key())
.range(storage, None, None, Order::Ascending)
.filter_map(|x| x.ok())
.filter(|((_mix, block_time), _amount)| *block_time < start_time)
.fold(Uint128::zero(), |acc, ((_mix, _block_time), amount)| {
acc + amount
});
let mut amount = Uint128::zero();
for (mix, block_time) in delegations_keys {
amount += delegations.load(storage, (&mix, block_time))?
}
amount = Uint128::new(amount.u128().min(max_vested));
let amount = Uint128::new(coin.u128().min(max_vested));
Ok(Coin {
amount,
+4 -4
View File
@@ -36,7 +36,7 @@ pub fn populate_vesting_periods(start_time: u64, n: usize) -> Vec<VestingPeriod>
#[cfg(test)]
mod tests {
use crate::contract::{execute, ADMIN_ADDRESS, NUM_VESTING_PERIODS, VESTING_PERIOD};
use crate::contract::{execute, NUM_VESTING_PERIODS, VESTING_PERIOD};
use crate::messages::ExecuteMsg;
use crate::storage::load_account;
use crate::support::tests::helpers::{init_contract, vesting_account_fixture};
@@ -61,7 +61,7 @@ mod tests {
let response = execute(deps.as_mut(), env.clone(), info, msg.clone());
assert!(response.is_err());
let info = mock_info(ADMIN_ADDRESS, &coins(1_000_000_000_000, DENOM));
let info = mock_info("admin", &coins(1_000_000_000_000, DENOM));
let _response = execute(deps.as_mut(), env.clone(), info, msg.clone());
let created_account = load_account(&Addr::unchecked("owner"), &deps.storage)
.unwrap()
@@ -78,8 +78,8 @@ mod tests {
// Test key collision avoidance
let account_again = vesting_account_fixture(&mut deps.storage, &env);
assert_eq!(created_account.balance_key(), "1ba".to_string());
assert_ne!(created_account.balance_key(), account_again.balance_key());
assert_eq!(created_account.storage_key(), 1);
assert_ne!(created_account.storage_key(), account_again.storage_key());
}
#[test]
+6 -6
View File
@@ -706,9 +706,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a"
checksum = "f903ebbabc0d4880dbc76148efb8be8fc29fa4bf294c440c3d70da1c8bcafff7"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -719,18 +719,18 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866713b2fe13f23038c7d8824c3059d1f28dd94685fb406d1533c4eeeefeefae"
checksum = "832bebef577ecb394603de8e2bf0de429b74aa364e17dec18e15ce37e71b0cae"
dependencies = [
"syn",
]
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta3"
version = "1.0.0-beta4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dbb9939b31441dfa9af3ec9740c8a24d585688401eff1b6b386abb7ad0d10a8"
checksum = "6238c45840cc9de5a39f0f619e3a4f7c38c5d2c6ac9e3e4d72751ee045e6d7da"
dependencies = [
"base64",
"cosmwasm-crypto",