Feature/adjusting epoch events (#1696)

* Added (not yet using) source height for pending events

* Fixed existing unit tests

* Emitting source height for resolved pending events

* Removed unused attribute keys

* Emitting initial total unit reward at time of delegation

* Updated ts types

* regenerated corresponding typescript types

* missed changes

* Piggybacking on breaking change to remove serde aliases
This commit is contained in:
Jędrzej Stuczyński
2022-10-24 09:30:51 +01:00
committed by GitHub
parent fc44f2fe1c
commit f1624e658e
24 changed files with 501 additions and 259 deletions
@@ -9,7 +9,7 @@ use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
use comfy_table::Table;
use cosmwasm_std::Addr;
use mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventData};
use mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventKind};
#[derive(Debug, Parser)]
pub struct Args {}
@@ -90,8 +90,8 @@ async fn print_delegation_events(
]);
for event in events {
match event.event {
PendingEpochEventData::Delegate {
match event.event.kind {
PendingEpochEventKind::Delegate {
owner,
mix_id,
amount,
@@ -107,7 +107,7 @@ async fn print_delegation_events(
]);
}
}
PendingEpochEventData::Undelegate {
PendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy,
@@ -34,13 +34,11 @@ pub struct Delegation {
pub owner: Addr,
/// Id of the MixNode that this delegation was performed against.
#[serde(alias = "node_id")]
pub mix_id: MixId,
// Note to UI/UX devs: there's absolutely no point in displaying this value to the users,
// it would serve them no purpose. It's only used for calculating rewards
/// Value of the "unit delegation" associated with the mixnode at the time of delegation.
#[serde(alias = "crr")]
pub cumulative_reward_ratio: Decimal,
/// Original delegation amount. Note that it is never mutated as delegation accumulates rewards.
@@ -4,7 +4,7 @@
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use crate::reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate};
use crate::rewarding::RewardDistribution;
use crate::{ContractStateParams, IdentityKeyRef, Interval, Layer, MixId};
use crate::{BlockHeight, ContractStateParams, IdentityKeyRef, Interval, Layer, MixId};
pub use contracts_common::events::*;
use cosmwasm_std::{Addr, Coin, Decimal, Event};
@@ -96,7 +96,7 @@ pub const PROXY_KEY: &str = "proxy";
// delegation/undelegation
pub const DELEGATOR_KEY: &str = "delegator";
pub const DELEGATION_TARGET_KEY: &str = "delegation_target";
pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height";
pub const UNIT_REWARD_KEY: &str = "unit_reward";
// bonding/unbonding
pub const MIX_ID_KEY: &str = "mix_id";
@@ -120,52 +120,45 @@ pub const UPDATED_MIXNODE_COST_PARAMS_KEY: &str = "updated_mixnode_cost_params";
// rewarding
pub const INTERVAL_KEY: &str = "interval_details";
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
pub const TOTAL_PLEDGE_KEY: &str = "pledge";
pub const TOTAL_DELEGATIONS_KEY: &str = "delegated";
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
pub const DELEGATES_REWARD_KEY: &str = "delegates_reward";
pub const APPROXIMATE_TIME_LEFT_SECS_KEY: &str = "approximate_time_left_secs";
pub const INTERVAL_REWARDING_PARAMS_UPDATE_KEY: &str = "interval_rewarding_params_update";
pub const UPDATED_INTERVAL_REWARDING_PARAMS_KEY: &str = "updated_interval_rewarding_params";
pub const PRIOR_DELEGATES_KEY: &str = "prior_delegates";
pub const PRIOR_UNIT_REWARD: &str = "prior_unit_reward";
pub const PRIOR_UNIT_REWARD_KEY: &str = "prior_unit_reward";
pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations";
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh";
pub const ZERO_PERFORMANCE_VALUE: &str = "zero_performance";
// rewarded set update
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
pub const REWARDED_SET_SIZE_KEY: &str = "rewarded_set_size";
pub const NODES_IN_REWARDED_SET_KEY: &str = "nodes_in_rewarded_set";
pub const CURRENT_INTERVAL_ID_KEY: &str = "current_interval";
pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval";
pub const NEW_CURRENT_EPOCH_KEY: &str = "new_current_epoch";
pub const BLOCK_HEIGHT_KEY: &str = "block_height";
pub const RECONCILIATION_ERROR_EVENT: &str = "reconciliation_error";
// interval
pub const EVENTS_EXECUTED_KEY: &str = "number_of_events_executed";
pub const EVENT_CREATION_HEIGHT_KEY: &str = "created_at";
pub const REWARDED_SET_NODES_KEY: &str = "rewarded_set_nodes";
pub const NEW_EPOCHS_DURATION_SECS_KEY: &str = "new_epoch_durations_secs";
pub const NEW_EPOCHS_IN_INTERVAL: &str = "new_epochs_in_interval";
pub fn new_delegation_event(
created_at: BlockHeight,
delegator: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
mix_id: MixId,
unit_reward: Decimal,
) -> Event {
Event::new(MixnetEventType::Delegation)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
.add_attribute(UNIT_REWARD_KEY, unit_reward.to_string())
}
pub fn new_delegation_on_unbonded_node_event(
@@ -218,8 +211,9 @@ pub fn new_withdraw_delegator_reward_event(
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
pub fn new_active_set_update_event(new_size: u32) -> Event {
pub fn new_active_set_update_event(created_at: BlockHeight, new_size: u32) -> Event {
Event::new(MixnetEventType::ActiveSetUpdate)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(ACTIVE_SET_SIZE_KEY, new_size.to_string())
}
@@ -236,10 +230,13 @@ pub fn new_pending_active_set_update_event(
}
pub fn new_rewarding_params_update_event(
created_at: BlockHeight,
update: IntervalRewardingParamsUpdate,
updated: IntervalRewardParams,
) -> Event {
Event::new(MixnetEventType::IntervalRewardingParamsUpdate)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(
INTERVAL_REWARDING_PARAMS_UPDATE_KEY,
update.to_inline_json(),
@@ -265,8 +262,14 @@ pub fn new_pending_rewarding_params_update_event(
)
}
pub fn new_undelegation_event(delegator: &Addr, proxy: &Option<Addr>, mix_id: MixId) -> Event {
pub fn new_undelegation_event(
created_at: BlockHeight,
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
Event::new(MixnetEventType::Undelegation)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
@@ -327,8 +330,10 @@ pub fn new_mixnode_bonding_event(
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_unbonding_event(mix_id: MixId) -> Event {
Event::new(MixnetEventType::MixnodeUnbonding).add_attribute(MIX_ID_KEY, mix_id.to_string())
pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Event {
Event::new(MixnetEventType::MixnodeUnbonding)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_pending_mixnode_unbonding_event(
@@ -370,8 +375,13 @@ pub fn new_mixnode_pending_cost_params_update_event(
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
}
pub fn new_mixnode_cost_params_update_event(mix_id: MixId, new_costs: &MixNodeCostParams) -> Event {
pub fn new_mixnode_cost_params_update_event(
created_at: BlockHeight,
mix_id: MixId,
new_costs: &MixNodeCostParams,
) -> Event {
Event::new(MixnetEventType::MixnodeCostParamsUpdate)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
}
@@ -461,7 +471,7 @@ pub fn new_mix_rewarding_event(
interval.current_epoch_absolute_id().to_string(),
)
.add_attribute(PRIOR_DELEGATES_KEY, prior_delegates.to_string())
.add_attribute(PRIOR_UNIT_REWARD, prior_unit_reward.to_string())
.add_attribute(PRIOR_UNIT_REWARD_KEY, prior_unit_reward.to_string())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(
OPERATOR_REWARD_KEY,
@@ -497,11 +507,13 @@ pub fn new_reconcile_pending_events() -> Event {
}
pub fn new_interval_config_update_event(
created_at: BlockHeight,
epochs_in_interval: u32,
epoch_duration_secs: u64,
updated_rewarding_params: IntervalRewardParams,
) -> Event {
Event::new(MixnetEventType::IntervalConfigUpdate)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(
NEW_EPOCHS_DURATION_SECS_KEY,
epoch_duration_secs.to_string(),
@@ -34,7 +34,8 @@ pub use mixnode::{
};
pub use msg::*;
pub use pending_events::{
PendingEpochEvent, PendingEpochEventData, PendingIntervalEvent, PendingIntervalEventData,
PendingEpochEvent, PendingEpochEventData, PendingEpochEventKind, PendingIntervalEvent,
PendingIntervalEventData, PendingIntervalEventKind,
};
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
pub use types::*;
@@ -78,34 +78,27 @@ impl MixNodeDetails {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNodeRewarding {
/// Information provided by the operator that influence the cost function.
#[serde(alias = "cp")]
pub cost_params: MixNodeCostParams,
/// Total pledge and compounded reward earned by the node operator.
#[serde(alias = "op")]
pub operator: Decimal,
/// Total delegation and compounded reward earned by all node delegators.
#[serde(alias = "dg")]
pub delegates: Decimal,
/// Cumulative reward earned by the "unit delegation" since the block 0.
#[serde(alias = "tur")]
pub total_unit_reward: Decimal,
/// Value of the theoretical "unit delegation" that has delegated to this mixnode at block 0.
#[serde(alias = "ud")]
pub unit_delegation: Decimal,
/// Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt
/// to reward it multiple times in the same epoch.
#[serde(alias = "le")]
pub last_rewarded_epoch: EpochId,
// technically we don't need that field to determine reward magnitude or anything
// but it saves on extra queries to determine if we're removing the final delegation
// (so that we could zero the field correctly)
#[serde(alias = "uqd")]
pub unique_delegations: u32,
}
@@ -428,7 +421,6 @@ impl MixNodeRewarding {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNodeBond {
/// Unique id assigned to the bonded mixnode.
#[serde(alias = "id")]
pub mix_id: MixId,
/// Address of the owner of this mixnode.
@@ -3,7 +3,7 @@
use crate::mixnode::MixNodeCostParams;
use crate::reward_params::IntervalRewardingParamsUpdate;
use crate::{EpochEventId, IntervalEventId, MixId};
use crate::{BlockHeight, EpochEventId, IntervalEventId, MixId};
use cosmwasm_std::{Addr, Coin};
use serde::{Deserialize, Serialize};
@@ -14,7 +14,13 @@ pub struct PendingEpochEvent {
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum PendingEpochEventData {
pub struct PendingEpochEventData {
pub created_at: BlockHeight,
pub kind: PendingEpochEventKind,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum PendingEpochEventKind {
// can't just pass the `Delegation` struct here as it's impossible to determine
// `cumulative_reward_ratio` ahead of time
Delegate {
@@ -36,6 +42,15 @@ pub enum PendingEpochEventData {
},
}
impl PendingEpochEventKind {
pub fn attach_source_height(self, created_at: BlockHeight) -> PendingEpochEventData {
PendingEpochEventData {
created_at,
kind: self,
}
}
}
impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
fn from(data: (EpochEventId, PendingEpochEventData)) -> Self {
PendingEpochEvent {
@@ -52,7 +67,13 @@ pub struct PendingIntervalEvent {
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum PendingIntervalEventData {
pub struct PendingIntervalEventData {
pub created_at: BlockHeight,
pub kind: PendingIntervalEventKind,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum PendingIntervalEventKind {
ChangeMixCostParams {
mix_id: MixId,
new_costs: MixNodeCostParams,
@@ -67,6 +88,15 @@ pub enum PendingIntervalEventData {
},
}
impl PendingIntervalEventKind {
pub fn attach_source_height(self, created_at: BlockHeight) -> PendingIntervalEventData {
PendingIntervalEventData {
created_at,
kind: self,
}
}
}
impl From<(IntervalEventId, PendingIntervalEventData)> for PendingIntervalEvent {
fn from(data: (IntervalEventId, PendingIntervalEventData)) -> Self {
PendingIntervalEvent {
@@ -20,6 +20,7 @@ pub type SphinxKeyRef<'a> = &'a str;
pub type EpochId = u32;
pub type IntervalId = u32;
pub type MixId = u32;
pub type BlockHeight = u64;
pub type EpochEventId = u32;
pub type IntervalEventId = u32;
+21 -14
View File
@@ -5,11 +5,11 @@ use crate::currency::{DecCoin, RegisteredCoins};
use crate::error::TypesError;
use crate::mixnode::MixNodeCostParams;
use mixnet_contract_common::{
EpochEventId, IntervalEventId, IntervalRewardingParamsUpdate, MixId,
BlockHeight, EpochEventId, IntervalEventId, IntervalRewardingParamsUpdate, MixId,
PendingEpochEvent as MixnetContractPendingEpochEvent,
PendingEpochEventData as MixnetContractPendingEpochEventData,
PendingEpochEventKind as MixnetContractPendingEpochEventKind,
PendingIntervalEvent as MixnetContractPendingIntervalEvent,
PendingIntervalEventData as MixnetContractPendingIntervalEventData,
PendingIntervalEventKind as MixnetContractPendingIntervalEventKind,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -22,6 +22,7 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
pub struct PendingEpochEvent {
pub id: EpochEventId,
pub created_at: BlockHeight,
pub event: PendingEpochEventData,
}
@@ -32,7 +33,8 @@ impl PendingEpochEvent {
) -> Result<Self, TypesError> {
Ok(PendingEpochEvent {
id: pending_event.id,
event: PendingEpochEventData::try_from_mixnet_contract(pending_event.event, reg)?,
created_at: pending_event.event.created_at,
event: PendingEpochEventData::try_from_mixnet_contract(pending_event.event.kind, reg)?,
})
}
}
@@ -65,11 +67,11 @@ pub enum PendingEpochEventData {
impl PendingEpochEventData {
pub fn try_from_mixnet_contract(
pending_event: MixnetContractPendingEpochEventData,
pending_event: MixnetContractPendingEpochEventKind,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
match pending_event {
MixnetContractPendingEpochEventData::Delegate {
MixnetContractPendingEpochEventKind::Delegate {
owner,
mix_id,
amount,
@@ -80,7 +82,7 @@ impl PendingEpochEventData {
amount: reg.attempt_convert_to_display_dec_coin(amount.into())?,
proxy: proxy.map(|p| p.into_string()),
}),
MixnetContractPendingEpochEventData::Undelegate {
MixnetContractPendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy,
@@ -89,10 +91,10 @@ impl PendingEpochEventData {
mix_id,
proxy: proxy.map(|p| p.into_string()),
}),
MixnetContractPendingEpochEventData::UnbondMixnode { mix_id } => {
MixnetContractPendingEpochEventKind::UnbondMixnode { mix_id } => {
Ok(PendingEpochEventData::UnbondMixnode { mix_id })
}
MixnetContractPendingEpochEventData::UpdateActiveSetSize { new_size } => {
MixnetContractPendingEpochEventKind::UpdateActiveSetSize { new_size } => {
Ok(PendingEpochEventData::UpdateActiveSetSize { new_size })
}
}
@@ -107,6 +109,7 @@ impl PendingEpochEventData {
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
pub struct PendingIntervalEvent {
pub id: IntervalEventId,
pub created_at: BlockHeight,
pub event: PendingIntervalEventData,
}
@@ -117,7 +120,11 @@ impl PendingIntervalEvent {
) -> Result<Self, TypesError> {
Ok(PendingIntervalEvent {
id: pending_event.id,
event: PendingIntervalEventData::try_from_mixnet_contract(pending_event.event, reg)?,
created_at: pending_event.event.created_at,
event: PendingIntervalEventData::try_from_mixnet_contract(
pending_event.event.kind,
reg,
)?,
})
}
}
@@ -145,11 +152,11 @@ pub enum PendingIntervalEventData {
impl PendingIntervalEventData {
pub fn try_from_mixnet_contract(
pending_event: MixnetContractPendingIntervalEventData,
pending_event: MixnetContractPendingIntervalEventKind,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
match pending_event {
MixnetContractPendingIntervalEventData::ChangeMixCostParams { mix_id, new_costs } => {
MixnetContractPendingIntervalEventKind::ChangeMixCostParams { mix_id, new_costs } => {
Ok(PendingIntervalEventData::ChangeMixCostParams {
mix_id,
new_costs: MixNodeCostParams::from_mixnet_contract_mixnode_cost_params(
@@ -157,10 +164,10 @@ impl PendingIntervalEventData {
)?,
})
}
MixnetContractPendingIntervalEventData::UpdateRewardingParams { update } => {
MixnetContractPendingIntervalEventKind::UpdateRewardingParams { update } => {
Ok(PendingIntervalEventData::UpdateRewardingParams { update })
}
MixnetContractPendingIntervalEventData::UpdateIntervalConfig {
MixnetContractPendingIntervalEventKind::UpdateIntervalConfig {
epochs_in_interval,
epoch_duration_secs,
} => Ok(PendingIntervalEventData::UpdateIntervalConfig {
+12 -8
View File
@@ -169,17 +169,19 @@ pub fn execute(
owner_signature,
),
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, info)
crate::mixnodes::transactions::try_remove_mixnode(deps, env, info)
}
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(deps, info, owner)
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(deps, env, info, owner)
}
ExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params(deps, info, new_costs)
crate::mixnodes::transactions::try_update_mixnode_cost_params(
deps, env, info, new_costs,
)
}
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params_on_behalf(
deps, info, new_costs, owner,
deps, env, info, new_costs, owner,
)
}
ExecuteMsg::UpdateMixnodeConfig { new_config } => {
@@ -223,19 +225,21 @@ pub fn execute(
// delegation-related:
ExecuteMsg::DelegateToMixnode { mix_id } => {
crate::delegations::transactions::try_delegate_to_mixnode(deps, info, mix_id)
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_id)
}
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_delegate_to_mixnode_on_behalf(
deps, info, mix_id, delegate,
deps, env, info, mix_id, delegate,
)
}
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode(deps, info, mix_id)
crate::delegations::transactions::try_remove_delegation_from_mixnode(
deps, env, info, mix_id,
)
}
ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode_on_behalf(
deps, info, mix_id, delegate,
deps, env, info, mix_id, delegate,
)
}
@@ -453,6 +453,7 @@ mod tests {
#[test]
fn all_retrieved_delegations_are_from_the_specified_delegator() {
let mut test = TestSetup::new();
let env = test.env();
// it means we have, for example, delegation from "delegator1" towards mix1, mix2, ...., from "delegator2" towards mix1, mix2, ...., etc
add_dummy_mixes_with_delegations(&mut test, 50, 100);
@@ -462,6 +463,7 @@ mod tests {
for mix_id in 1..=25 {
try_delegate_to_mixnode_on_behalf(
test.deps_mut(),
env.clone(),
mock_info(vesting_contract.as_ref(), &[coin(100_000, TEST_COIN_DENOM)]),
mix_id,
with_proxy.into(),
@@ -6,34 +6,37 @@ use crate::interval::storage as interval_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use crate::support::helpers::validate_delegation_stake;
use cosmwasm_std::{Addr, Coin, DepsMut, MessageInfo, Response};
use cosmwasm_std::{Addr, Coin, DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_pending_delegation_event, new_pending_undelegation_event,
};
use mixnet_contract_common::pending_events::PendingEpochEventData;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
use mixnet_contract_common::{Delegation, MixId};
pub(crate) fn try_delegate_to_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_delegate_to_mixnode(deps, mix_id, info.sender, info.funds, None)
_try_delegate_to_mixnode(deps, env, mix_id, info.sender, info.funds, None)
}
pub(crate) fn try_delegate_to_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
delegate: String,
) -> Result<Response, MixnetContractError> {
let delegate = deps.api.addr_validate(&delegate)?;
_try_delegate_to_mixnode(deps, mix_id, delegate, info.funds, Some(info.sender))
_try_delegate_to_mixnode(deps, env, mix_id, delegate, info.funds, Some(info.sender))
}
pub(crate) fn _try_delegate_to_mixnode(
deps: DepsMut<'_>,
env: Env,
mix_id: MixId,
delegate: Addr,
amount: Vec<Coin>,
@@ -59,37 +62,40 @@ pub(crate) fn _try_delegate_to_mixnode(
// push the event onto the queue and wait for it to be picked up at the end of the epoch
let cosmos_event = new_pending_delegation_event(&delegate, &proxy, &delegation, mix_id);
let epoch_event = PendingEpochEventData::Delegate {
let epoch_event = PendingEpochEventKind::Delegate {
owner: delegate,
mix_id,
amount: delegation,
proxy,
};
interval_storage::push_new_epoch_event(deps.storage, &epoch_event)?;
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(Response::new().add_event(cosmos_event))
}
pub(crate) fn try_remove_delegation_from_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_remove_delegation_from_mixnode(deps, mix_id, info.sender, None)
_try_remove_delegation_from_mixnode(deps, env, mix_id, info.sender, None)
}
pub(crate) fn try_remove_delegation_from_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
delegate: String,
) -> Result<Response, MixnetContractError> {
let delegate = deps.api.addr_validate(&delegate)?;
_try_remove_delegation_from_mixnode(deps, mix_id, delegate, Some(info.sender))
_try_remove_delegation_from_mixnode(deps, env, mix_id, delegate, Some(info.sender))
}
pub(crate) fn _try_remove_delegation_from_mixnode(
deps: DepsMut<'_>,
env: Env,
mix_id: MixId,
delegate: Addr,
proxy: Option<Addr>,
@@ -111,12 +117,12 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
// push the event onto the queue and wait for it to be picked up at the end of the epoch
let cosmos_event = new_pending_undelegation_event(&delegate, &proxy, mix_id);
let epoch_event = PendingEpochEventData::Undelegate {
let epoch_event = PendingEpochEventKind::Undelegate {
owner: delegate,
mix_id,
proxy,
};
interval_storage::push_new_epoch_event(deps.storage, &epoch_event)?;
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(Response::new().add_event(cosmos_event))
}
@@ -138,10 +144,11 @@ mod tests {
#[test]
fn can_only_be_done_towards_an_existing_mixnode() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let sender = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]);
let res = try_delegate_to_mixnode(test.deps_mut(), sender, 42);
let res = try_delegate_to_mixnode(test.deps_mut(), env, sender, 42);
assert_eq!(
res,
Err(MixnetContractError::MixNodeBondNotFound { mix_id: 42 })
@@ -151,17 +158,19 @@ mod tests {
#[test]
fn must_contain_non_zero_amount_of_coins() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let sender1 = mock_info(owner, &[coin(0, TEST_COIN_DENOM)]);
let sender2 = mock_info(owner, &[]);
let sender3 = mock_info(owner, &[coin(1000, "some-weird-coin")]);
let res = try_delegate_to_mixnode(test.deps_mut(), sender1, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id);
assert_eq!(res, Err(MixnetContractError::EmptyDelegation));
let res = try_delegate_to_mixnode(test.deps_mut(), sender2, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender2, mix_id);
assert_eq!(res, Err(MixnetContractError::EmptyDelegation));
let res = try_delegate_to_mixnode(test.deps_mut(), sender3, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env, sender3, mix_id);
assert_eq!(
res,
Err(MixnetContractError::WrongDenom {
@@ -174,6 +183,8 @@ mod tests {
#[test]
fn if_applicable_must_contain_at_least_the_minimum_pledge() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let sender1 = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]);
@@ -188,7 +199,7 @@ mod tests {
.save(test.deps_mut().storage, &contract_state)
.unwrap();
let res = try_delegate_to_mixnode(test.deps_mut(), sender1, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id);
assert_eq!(
res,
Err(MixnetContractError::InsufficientDelegation {
@@ -197,13 +208,14 @@ mod tests {
})
);
let res = try_delegate_to_mixnode(test.deps_mut(), sender2, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env, sender2, mix_id);
assert!(res.is_ok())
}
#[test]
fn can_only_be_done_towards_fully_bonded_mixnode() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let sender = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]);
@@ -226,17 +238,33 @@ mod tests {
)
.unwrap();
try_remove_mixnode(test.deps_mut(), mock_info("mix-owner-unbonded", &[])).unwrap();
try_remove_mixnode(
test.deps_mut(),
env.clone(),
mock_info("mix-owner-unbonded", &[]),
)
.unwrap();
try_remove_mixnode(
test.deps_mut(),
env.clone(),
mock_info("mix-owner-unbonded-leftover", &[]),
)
.unwrap();
test.execute_all_pending_events();
try_remove_mixnode(test.deps_mut(), mock_info("mix-owner-unbonding", &[])).unwrap();
try_remove_mixnode(
test.deps_mut(),
env.clone(),
mock_info("mix-owner-unbonding", &[]),
)
.unwrap();
let res = try_delegate_to_mixnode(test.deps_mut(), sender.clone(), mix_id_unbonding);
let res = try_delegate_to_mixnode(
test.deps_mut(),
env.clone(),
sender.clone(),
mix_id_unbonding,
);
assert_eq!(
res,
Err(MixnetContractError::MixnodeIsUnbonding {
@@ -244,7 +272,12 @@ mod tests {
})
);
let res = try_delegate_to_mixnode(test.deps_mut(), sender.clone(), mix_id_unbonded);
let res = try_delegate_to_mixnode(
test.deps_mut(),
env.clone(),
sender.clone(),
mix_id_unbonded,
);
assert_eq!(
res,
Err(MixnetContractError::MixNodeBondNotFound {
@@ -252,7 +285,8 @@ mod tests {
})
);
let res = try_delegate_to_mixnode(test.deps_mut(), sender, mix_id_unbonded_leftover);
let res =
try_delegate_to_mixnode(test.deps_mut(), env, sender, mix_id_unbonded_leftover);
assert_eq!(
res,
Err(MixnetContractError::MixNodeBondNotFound {
@@ -264,22 +298,26 @@ mod tests {
#[test]
fn can_still_be_done_if_prior_delegation_exists() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let sender1 = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]);
let sender2 = mock_info(owner, &[coin(50_000_000, TEST_COIN_DENOM)]);
let res = try_delegate_to_mixnode(test.deps_mut(), sender1, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id);
assert!(res.is_ok());
// still OK
let res = try_delegate_to_mixnode(test.deps_mut(), sender2, mix_id);
let res = try_delegate_to_mixnode(test.deps_mut(), env, sender2, mix_id);
assert!(res.is_ok())
}
#[test]
fn correctly_pushes_appropriate_epoch_event() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
@@ -289,15 +327,15 @@ mod tests {
let sender1 = mock_info(owner, &[amount1.clone()]);
let sender2 = mock_info(test.vesting_contract().as_str(), &[amount2.clone()]);
try_delegate_to_mixnode(test.deps_mut(), sender1, mix_id).unwrap();
try_delegate_to_mixnode_on_behalf(test.deps_mut(), sender2, mix_id, owner.into())
try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id).unwrap();
try_delegate_to_mixnode_on_behalf(test.deps_mut(), env, sender2, mix_id, owner.into())
.unwrap();
let events = test.pending_epoch_events();
assert_eq!(
events[0],
PendingEpochEventData::Delegate {
events[0].kind,
PendingEpochEventKind::Delegate {
owner: Addr::unchecked(owner),
mix_id,
amount: amount1,
@@ -306,8 +344,8 @@ mod tests {
);
assert_eq!(
events[1],
PendingEpochEventData::Delegate {
events[1].kind,
PendingEpochEventKind::Delegate {
owner: Addr::unchecked(owner),
mix_id,
amount: amount2,
@@ -329,11 +367,12 @@ mod tests {
#[test]
fn cannot_be_performed_if_delegation_never_existed() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let sender = mock_info(owner, &[]);
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let res = try_remove_delegation_from_mixnode(test.deps_mut(), sender, mix_id);
let res = try_remove_delegation_from_mixnode(test.deps_mut(), env, sender, mix_id);
assert_eq!(
res,
Err(MixnetContractError::NoMixnodeDelegationFound {
@@ -347,14 +386,16 @@ mod tests {
#[test]
fn cannot_be_performed_if_the_delegation_is_still_pending() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let sender1 = mock_info(owner, &[coin(100_000_000, TEST_COIN_DENOM)]);
let sender2 = mock_info(owner, &[]);
try_delegate_to_mixnode(test.deps_mut(), sender1, mix_id).unwrap();
try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id).unwrap();
let res = try_remove_delegation_from_mixnode(test.deps_mut(), sender2, mix_id);
let res = try_remove_delegation_from_mixnode(test.deps_mut(), env, sender2, mix_id);
assert_eq!(
res,
Err(MixnetContractError::NoMixnodeDelegationFound {
@@ -368,6 +409,8 @@ mod tests {
#[test]
fn as_long_as_delegation_exists_can_always_be_performed() {
let mut test = TestSetup::new();
let env = test.env();
let owner = "delegator";
let sender = mock_info(owner, &[]);
@@ -382,19 +425,30 @@ mod tests {
try_remove_mixnode(
test.deps_mut(),
env.clone(),
mock_info("mix-owner-unbonded-leftover", &[]),
)
.unwrap();
test.execute_all_pending_events();
try_remove_mixnode(test.deps_mut(), mock_info("mix-owner-unbonding", &[])).unwrap();
try_remove_mixnode(
test.deps_mut(),
env.clone(),
mock_info("mix-owner-unbonding", &[]),
)
.unwrap();
let res =
try_remove_delegation_from_mixnode(test.deps_mut(), sender.clone(), normal_mix_id);
let res = try_remove_delegation_from_mixnode(
test.deps_mut(),
env.clone(),
sender.clone(),
normal_mix_id,
);
assert!(res.is_ok());
let res = try_remove_delegation_from_mixnode(
test.deps_mut(),
env.clone(),
sender.clone(),
mix_id_unbonding,
);
@@ -402,6 +456,7 @@ mod tests {
let res = try_remove_delegation_from_mixnode(
test.deps_mut(),
env,
sender,
mix_id_unbonded_leftover,
);
@@ -274,7 +274,7 @@ pub mod tests {
assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result);
// but after he unbonds it, it's all fine again
pending_events::unbond_mixnode(deps.as_mut(), &env, mix_id).unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &env, 123, mix_id).unwrap();
let result = try_add_gateway(deps.as_mut(), env, info, gateway, sig);
assert!(result.is_ok());
+5 -1
View File
@@ -6,11 +6,12 @@ use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::new_interval_config_update_event;
use mixnet_contract_common::Interval;
use mixnet_contract_common::{BlockHeight, Interval};
use std::time::Duration;
pub(crate) fn change_interval_config(
store: &mut dyn Storage,
request_creation: BlockHeight,
mut current_interval: Interval,
epochs_in_interval: u32,
epoch_duration_secs: u64,
@@ -25,6 +26,7 @@ pub(crate) fn change_interval_config(
storage::save_interval(store, &current_interval)?;
Ok(Response::new().add_event(new_interval_config_update_event(
request_creation,
epochs_in_interval,
epoch_duration_secs,
rewarding_params.interval,
@@ -50,6 +52,7 @@ mod tests {
// if we half the number of epochs, the reward budget should get doubled
change_interval_config(
&mut deps.storage,
123,
initial_interval,
initial_interval.epochs_in_interval() / 2,
initial_interval.epoch_length_secs(),
@@ -72,6 +75,7 @@ mod tests {
// and similarly when we double number of epochs, the reward budget should get halved
change_interval_config(
&mut deps.storage,
123,
initial_interval,
initial_interval.epochs_in_interval() * 2,
initial_interval.epoch_length_secs(),
+84 -39
View File
@@ -17,9 +17,12 @@ use mixnet_contract_common::events::{
new_rewarding_params_update_event, new_undelegation_event,
};
use mixnet_contract_common::mixnode::MixNodeCostParams;
use mixnet_contract_common::pending_events::{PendingEpochEventData, PendingIntervalEventData};
use mixnet_contract_common::pending_events::{
PendingEpochEventData, PendingEpochEventKind, PendingIntervalEventData,
PendingIntervalEventKind,
};
use mixnet_contract_common::reward_params::IntervalRewardingParamsUpdate;
use mixnet_contract_common::{Delegation, MixId};
use mixnet_contract_common::{BlockHeight, Delegation, MixId};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
pub(crate) trait ContractExecutableEvent {
@@ -32,6 +35,7 @@ pub(crate) trait ContractExecutableEvent {
pub(crate) fn delegate(
deps: DepsMut<'_>,
env: &Env,
created_at: BlockHeight,
owner: Addr,
mix_id: MixId,
amount: Coin,
@@ -105,7 +109,14 @@ pub(crate) fn delegate(
// add the amount we're intending to delegate (whether it's fresh or we're adding to the existing one)
mix_rewarding.add_base_delegation(stored_delegation_amount.amount);
let cosmos_event = new_delegation_event(&owner, &proxy, &new_delegation_amount, mix_id);
let cosmos_event = new_delegation_event(
created_at,
&owner,
&proxy,
&new_delegation_amount,
mix_id,
mix_rewarding.total_unit_reward,
);
let delegation = Delegation::new(
owner,
@@ -130,6 +141,7 @@ pub(crate) fn delegate(
pub(crate) fn undelegate(
deps: DepsMut<'_>,
created_at: BlockHeight,
owner: Addr,
mix_id: MixId,
proxy: Option<Addr>,
@@ -153,7 +165,7 @@ pub(crate) fn undelegate(
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![tokens_to_return.clone()]);
let mut response = Response::new()
.add_message(return_tokens)
.add_event(new_undelegation_event(&owner, &proxy, mix_id));
.add_event(new_undelegation_event(created_at, &owner, &proxy, mix_id));
if let Some(proxy) = &proxy {
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
@@ -177,6 +189,7 @@ pub(crate) fn undelegate(
pub(crate) fn unbond_mixnode(
deps: DepsMut<'_>,
env: &Env,
created_at: BlockHeight,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
// if we're here it means user executed `_try_remove_mixnode` and as a result node was set to be
@@ -207,7 +220,7 @@ pub(crate) fn unbond_mixnode(
let mut response = Response::new()
.add_message(return_tokens)
.add_event(new_mixnode_unbonding_event(mix_id));
.add_event(new_mixnode_unbonding_event(created_at, mix_id));
if let Some(proxy) = &proxy {
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
@@ -229,6 +242,7 @@ pub(crate) fn unbond_mixnode(
pub(crate) fn update_active_set_size(
deps: DepsMut<'_>,
created_at: BlockHeight,
active_set_size: u32,
) -> Result<Response, MixnetContractError> {
// We don't have to check for authorization as this event can only be pushed
@@ -241,28 +255,30 @@ pub(crate) fn update_active_set_size(
rewarding_params.try_change_active_set_size(active_set_size)?;
rewards_storage::REWARDING_PARAMS.save(deps.storage, &rewarding_params)?;
Ok(Response::new().add_event(new_active_set_update_event(active_set_size)))
Ok(Response::new().add_event(new_active_set_update_event(created_at, active_set_size)))
}
impl ContractExecutableEvent for PendingEpochEventData {
fn execute(self, deps: DepsMut<'_>, env: &Env) -> Result<Response, MixnetContractError> {
// note that the basic validation on all those events was already performed before
// they were pushed onto the queue
match self {
PendingEpochEventData::Delegate {
match self.kind {
PendingEpochEventKind::Delegate {
owner,
mix_id,
amount,
proxy,
} => delegate(deps, env, owner, mix_id, amount, proxy),
PendingEpochEventData::Undelegate {
} => delegate(deps, env, self.created_at, owner, mix_id, amount, proxy),
PendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy,
} => undelegate(deps, owner, mix_id, proxy),
PendingEpochEventData::UnbondMixnode { mix_id } => unbond_mixnode(deps, env, mix_id),
PendingEpochEventData::UpdateActiveSetSize { new_size } => {
update_active_set_size(deps, new_size)
} => undelegate(deps, self.created_at, owner, mix_id, proxy),
PendingEpochEventKind::UnbondMixnode { mix_id } => {
unbond_mixnode(deps, env, self.created_at, mix_id)
}
PendingEpochEventKind::UpdateActiveSetSize { new_size } => {
update_active_set_size(deps, self.created_at, new_size)
}
}
}
@@ -270,6 +286,7 @@ impl ContractExecutableEvent for PendingEpochEventData {
pub(crate) fn change_mix_cost_params(
deps: DepsMut<'_>,
created_at: BlockHeight,
mix_id: MixId,
new_costs: MixNodeCostParams,
) -> Result<Response, MixnetContractError> {
@@ -285,7 +302,7 @@ pub(crate) fn change_mix_cost_params(
_ => return Ok(Response::default()),
};
let cosmos_event = new_mixnode_cost_params_update_event(mix_id, &new_costs);
let cosmos_event = new_mixnode_cost_params_update_event(created_at, mix_id, &new_costs);
// TODO: can we just change cost_params without breaking rewarding calculation?
// (I'm almost certain we can, but well, it has to be tested)
@@ -297,6 +314,7 @@ pub(crate) fn change_mix_cost_params(
pub(crate) fn update_rewarding_params(
deps: DepsMut<'_>,
created_at: BlockHeight,
updated_params: IntervalRewardingParamsUpdate,
) -> Result<Response, MixnetContractError> {
// We don't have to check for authorization as this event can only be pushed
@@ -311,6 +329,7 @@ pub(crate) fn update_rewarding_params(
rewards_storage::REWARDING_PARAMS.save(deps.storage, &rewarding_params)?;
Ok(Response::new().add_event(new_rewarding_params_update_event(
created_at,
updated_params,
rewarding_params.interval,
)))
@@ -318,6 +337,7 @@ pub(crate) fn update_rewarding_params(
pub(crate) fn update_interval_config(
deps: DepsMut,
created_at: BlockHeight,
epochs_in_interval: u32,
epoch_duration_secs: u64,
) -> Result<Response, MixnetContractError> {
@@ -326,8 +346,10 @@ pub(crate) fn update_interval_config(
// Furthermore, we don't need to check whether the interval is finished as the
// queue is only emptied upon the interval finishing.
let interval = storage::current_interval(deps.storage)?;
change_interval_config(
deps.storage,
created_at,
interval,
epochs_in_interval,
epoch_duration_secs,
@@ -338,18 +360,23 @@ impl ContractExecutableEvent for PendingIntervalEventData {
fn execute(self, deps: DepsMut<'_>, _env: &Env) -> Result<Response, MixnetContractError> {
// note that the basic validation on all those events was already performed before
// they were pushed onto the queue
match self {
PendingIntervalEventData::ChangeMixCostParams {
match self.kind {
PendingIntervalEventKind::ChangeMixCostParams {
mix_id: mix,
new_costs,
} => change_mix_cost_params(deps, mix, new_costs),
PendingIntervalEventData::UpdateRewardingParams { update } => {
update_rewarding_params(deps, update)
} => change_mix_cost_params(deps, self.created_at, mix, new_costs),
PendingIntervalEventKind::UpdateRewardingParams { update } => {
update_rewarding_params(deps, self.created_at, update)
}
PendingIntervalEventData::UpdateIntervalConfig {
PendingIntervalEventKind::UpdateIntervalConfig {
epochs_in_interval,
epoch_duration_secs,
} => update_interval_config(deps, epochs_in_interval, epoch_duration_secs),
} => update_interval_config(
deps,
self.created_at,
epochs_in_interval,
epoch_duration_secs,
),
}
}
}
@@ -390,11 +417,12 @@ mod tests {
test.add_immediate_delegation(owner1, delegation, mix_id);
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, mix_id).unwrap();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let res_increase = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
@@ -420,6 +448,7 @@ mod tests {
let res_fresh = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner2),
mix_id,
delegation_coin.clone(),
@@ -453,11 +482,12 @@ mod tests {
test.add_immediate_delegation(owner1, delegation, mix_id);
let env = test.env();
try_remove_mixnode(test.deps_mut(), mock_info("mix-owner", &[])).unwrap();
try_remove_mixnode(test.deps_mut(), env.clone(), mock_info("mix-owner", &[])).unwrap();
let res_increase = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
@@ -483,6 +513,7 @@ mod tests {
let res_fresh = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner2),
mix_id,
delegation_coin.clone(),
@@ -518,6 +549,7 @@ mod tests {
let res = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin_new,
@@ -580,6 +612,7 @@ mod tests {
let res = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin_new,
@@ -645,6 +678,7 @@ mod tests {
let res = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin.clone(),
@@ -679,7 +713,7 @@ mod tests {
let owner2 = "delegator2";
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, mix_id).unwrap();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let vesting_contract = test.vesting_contract();
let dummy_proxy = Addr::unchecked("not-vesting-contract");
@@ -688,6 +722,7 @@ mod tests {
let res_vesting = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
@@ -734,6 +769,7 @@ mod tests {
let res_other_proxy = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
@@ -778,7 +814,7 @@ mod tests {
let owner = Addr::unchecked("delegator");
let res = undelegate(test.deps_mut(), owner, mix_id, None).unwrap();
let res = undelegate(test.deps_mut(), 123, owner, mix_id, None).unwrap();
assert!(get_bank_send_msg(&res).is_none());
}
@@ -793,7 +829,7 @@ mod tests {
// this should never happen in actual code, but if we manually messed something up,
// lets make sure this throws an error
rewards_storage::MIXNODE_REWARDING.remove(test.deps_mut().storage, mix_id);
let res = undelegate(test.deps_mut(), owner, mix_id, None);
let res = undelegate(test.deps_mut(), 123, owner, mix_id, None);
assert!(matches!(
res,
Err(MixnetContractError::InconsistentState { .. })
@@ -827,7 +863,8 @@ mod tests {
let expected_return = delegation + truncated_reward.u128();
let res = undelegate(test.deps_mut(), Addr::unchecked(owner), mix_id, None).unwrap();
let res =
undelegate(test.deps_mut(), 123, Addr::unchecked(owner), mix_id, None).unwrap();
let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap();
assert_eq!(receiver, owner);
assert_eq!(sent_amount[0].amount.u128(), expected_return);
@@ -875,6 +912,7 @@ mod tests {
// for a fresh delegation, nothing was added to the storage either
let res_vesting = undelegate(
test.deps_mut(),
123,
Addr::unchecked(owner1),
mix_id,
Some(vesting_contract.clone()),
@@ -920,6 +958,7 @@ mod tests {
let res_other_proxy = undelegate(
test.deps_mut(),
123,
Addr::unchecked(owner2),
mix_id,
Some(dummy_proxy.clone()),
@@ -964,7 +1003,7 @@ mod tests {
let mut test = TestSetup::new();
let env = test.env();
let res = unbond_mixnode(test.deps_mut(), &env, 1);
let res = unbond_mixnode(test.deps_mut(), &env, 123, 1);
assert!(matches!(
res,
Err(MixnetContractError::InconsistentState { .. })
@@ -994,7 +1033,7 @@ mod tests {
let expected_return = pledge + truncated_reward;
let env = test.env();
let res = unbond_mixnode(test.deps_mut(), &env, mix_id).unwrap();
let res = unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap();
assert_eq!(receiver, owner);
assert_eq!(sent_amount[0].amount, expected_return);
@@ -1042,7 +1081,7 @@ mod tests {
test.add_dummy_mixnode_with_proxy(owner2, Some(pledge), dummy_proxy.clone());
let env = test.env();
let res_vesting = unbond_mixnode(test.deps_mut(), &env, mix_id_vesting).unwrap();
let res_vesting = unbond_mixnode(test.deps_mut(), &env, 123, mix_id_vesting).unwrap();
assert!(mixnodes_storage::mixnode_bonds()
.may_load(test.deps().storage, mix_id_vesting)
@@ -1077,7 +1116,7 @@ mod tests {
assert!(found_track);
let res_other_proxy =
unbond_mixnode(test.deps_mut(), &env, mix_id_other_proxy).unwrap();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id_other_proxy).unwrap();
assert!(mixnodes_storage::mixnode_bonds()
.may_load(test.deps().storage, mix_id_other_proxy)
.unwrap()
@@ -1103,7 +1142,7 @@ mod tests {
.load(test.deps().storage)
.unwrap();
update_active_set_size(test.deps_mut(), 50).unwrap();
update_active_set_size(test.deps_mut(), 123, 50).unwrap();
let updated = rewards_storage::REWARDING_PARAMS
.load(test.deps().storage)
.unwrap();
@@ -1124,14 +1163,14 @@ mod tests {
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, mix_id).unwrap();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let new_params = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(42).unwrap(),
interval_operating_cost: coin(123_456_789, TEST_COIN_DENOM),
};
let res = change_mix_cost_params(test.deps_mut(), mix_id, new_params);
let res = change_mix_cost_params(test.deps_mut(), 123, mix_id, new_params);
assert_eq!(res, Ok(Response::default()));
}
@@ -1147,11 +1186,16 @@ mod tests {
interval_operating_cost: coin(123_456_789, TEST_COIN_DENOM),
};
let res = change_mix_cost_params(test.deps_mut(), mix_id, new_params.clone());
let res = change_mix_cost_params(test.deps_mut(), 123, mix_id, new_params.clone());
assert_eq!(
res,
Ok(Response::new()
.add_event(new_mixnode_cost_params_update_event(mix_id, &new_params)))
Ok(
Response::new().add_event(new_mixnode_cost_params_update_event(
123,
mix_id,
&new_params
))
)
);
let after = test.mix_rewarding(mix_id).cost_params;
@@ -1182,7 +1226,7 @@ mod tests {
rewarded_set_size: None,
};
let res = update_rewarding_params(test.deps_mut(), update);
let res = update_rewarding_params(test.deps_mut(), 123, update);
assert!(res.is_ok());
let after = rewards_storage::REWARDING_PARAMS
.load(test.deps().storage)
@@ -1230,6 +1274,7 @@ mod tests {
// and change epoch length
update_interval_config(
test.deps_mut(),
123,
interval_before.epochs_in_interval() / 2,
1234,
)
+8 -6
View File
@@ -238,7 +238,7 @@ mod tests {
mod pending_epoch_events {
use super::*;
use cosmwasm_std::Addr;
use mixnet_contract_common::pending_events::PendingEpochEventData;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
use rand_chacha::rand_core::RngCore;
fn push_n_dummy_epoch_actions(test: &mut TestSetup, n: usize) {
@@ -248,12 +248,13 @@ mod tests {
}
fn push_dummy_epoch_action(test: &mut TestSetup) {
let dummy_action = PendingEpochEventData::Undelegate {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
storage::push_new_epoch_event(test.deps_mut().storage, &dummy_action).unwrap();
let env = test.env();
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
#[test]
@@ -379,7 +380,7 @@ mod tests {
mod pending_interval_events {
use super::*;
use crate::support::tests::fixtures;
use mixnet_contract_common::pending_events::PendingIntervalEventData;
use mixnet_contract_common::pending_events::PendingIntervalEventKind;
use rand_chacha::rand_core::RngCore;
fn push_n_dummy_interval_actions(test: &mut TestSetup, n: usize) {
@@ -389,11 +390,12 @@ mod tests {
}
fn push_dummy_interval_action(test: &mut TestSetup) {
let dummy_action = PendingIntervalEventData::ChangeMixCostParams {
let dummy_action = PendingIntervalEventKind::ChangeMixCostParams {
mix_id: test.rng.next_u32(),
new_costs: fixtures::mix_node_cost_params_fixture(),
};
storage::push_new_interval_event(test.deps_mut().storage, &dummy_action).unwrap();
let env = test.env();
storage::push_new_interval_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
#[test]
+13 -7
View File
@@ -6,11 +6,13 @@ use crate::constants::{
LAST_EPOCH_EVENT_ID_KEY, LAST_INTERVAL_EVENT_ID_KEY, PENDING_EPOCH_EVENTS_NAMESPACE,
PENDING_INTERVAL_EVENTS_NAMESPACE, REWARDED_SET_KEY,
};
use cosmwasm_std::{Order, StdResult, Storage};
use cosmwasm_std::{Env, Order, StdResult, Storage};
use cw_storage_plus::{Item, Map};
use mixnet_contract_common::pending_events::{PendingEpochEventData, PendingIntervalEventData};
use mixnet_contract_common::pending_events::{
PendingEpochEventData, PendingEpochEventKind, PendingIntervalEventData,
};
use mixnet_contract_common::{
EpochEventId, Interval, IntervalEventId, MixId, RewardedSetNodeStatus,
EpochEventId, Interval, IntervalEventId, MixId, PendingIntervalEventKind, RewardedSetNodeStatus,
};
use std::collections::HashMap;
@@ -64,18 +66,22 @@ pub(crate) fn next_interval_event_id_counter(
pub(crate) fn push_new_epoch_event(
storage: &mut dyn Storage,
event: &PendingEpochEventData,
env: &Env,
event: PendingEpochEventKind,
) -> StdResult<()> {
let event_id = next_epoch_event_id_counter(storage)?;
PENDING_EPOCH_EVENTS.save(storage, event_id, event)
let event_data = event.attach_source_height(env.block.height);
PENDING_EPOCH_EVENTS.save(storage, event_id, &event_data)
}
pub(crate) fn push_new_interval_event(
storage: &mut dyn Storage,
event: &PendingIntervalEventData,
env: &Env,
event: PendingIntervalEventKind,
) -> StdResult<()> {
let event_id = next_interval_event_id_counter(storage)?;
PENDING_INTERVAL_EVENTS.save(storage, event_id, event)
let event_data = event.attach_source_height(env.block.height);
PENDING_INTERVAL_EVENTS.save(storage, event_id, &event_data)
}
pub(crate) fn update_rewarded_set(
+69 -44
View File
@@ -15,7 +15,7 @@ use mixnet_contract_common::events::{
new_pending_interval_config_update_event, new_pending_interval_events_execution_event,
new_reconcile_pending_events,
};
use mixnet_contract_common::pending_events::PendingIntervalEventData;
use mixnet_contract_common::pending_events::PendingIntervalEventKind;
use mixnet_contract_common::MixId;
use std::collections::BTreeSet;
@@ -270,17 +270,18 @@ pub(crate) fn try_update_interval_config(
if force_immediately || interval.is_current_interval_over(&env) {
change_interval_config(
deps.storage,
env.block.height,
interval,
epochs_in_interval,
epoch_duration_secs,
)
} else {
// push the interval event
let interval_event = PendingIntervalEventData::UpdateIntervalConfig {
let interval_event = PendingIntervalEventKind::UpdateIntervalConfig {
epochs_in_interval,
epoch_duration_secs,
};
push_new_interval_event(deps.storage, &interval_event)?;
push_new_interval_event(deps.storage, &env, interval_event)?;
let time_left = interval.secs_until_current_interval_end(&env);
Ok(
Response::new().add_event(new_pending_interval_config_update_event(
@@ -298,31 +299,33 @@ mod tests {
use crate::support::tests::fixtures;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::Addr;
use mixnet_contract_common::pending_events::PendingEpochEventData;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
fn push_n_dummy_epoch_actions(test: &mut TestSetup, n: usize) {
// if you attempt to undelegate non-existent delegation,
// it will return an empty response, but will not fail
let env = test.env();
for i in 0..n {
let dummy_action = PendingEpochEventData::Undelegate {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: i as MixId,
proxy: None,
};
storage::push_new_epoch_event(test.deps_mut().storage, &dummy_action).unwrap();
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
}
fn push_n_dummy_interval_actions(test: &mut TestSetup, n: usize) {
// if you attempt to update cost parameters of an unbonded mixnode,
// it will return an empty response, but will not fail
let env = test.env();
for i in 0..n {
let dummy_action = PendingIntervalEventData::ChangeMixCostParams {
let dummy_action = PendingIntervalEventKind::ChangeMixCostParams {
mix_id: i as MixId,
new_costs: fixtures::mix_node_cost_params_fixture(),
};
storage::push_new_interval_event(test.deps_mut().storage, &dummy_action).unwrap();
storage::push_new_interval_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
}
@@ -335,7 +338,7 @@ mod tests {
new_active_set_update_event, new_delegation_on_unbonded_node_event,
new_undelegation_event,
};
use mixnet_contract_common::pending_events::PendingEpochEventData;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
#[test]
fn without_limit_executes_all_actions() {
@@ -398,14 +401,15 @@ mod tests {
);
push_n_dummy_epoch_actions(&mut test, 10);
let action_with_event = PendingEpochEventData::UpdateActiveSetSize { new_size: 50 };
storage::push_new_epoch_event(test.deps_mut().storage, &action_with_event).unwrap();
let action_with_event = PendingEpochEventKind::UpdateActiveSetSize { new_size: 50 };
storage::push_new_epoch_event(test.deps_mut().storage, &env, action_with_event)
.unwrap();
push_n_dummy_epoch_actions(&mut test, 10);
let (res, executed) =
perform_pending_epoch_actions(test.deps_mut(), &env, None).unwrap();
assert_eq!(
res,
Response::new().add_event(new_active_set_update_event(50))
Response::new().add_event(new_active_set_update_event(env.block.height, 50))
);
assert_eq!(executed, 21);
assert_eq!(
@@ -433,13 +437,13 @@ mod tests {
// delegate to node that doesn't exist,
// we expect to receive BankMsg with tokens being returned,
// and event regarding delegation
let non_existent_delegation = PendingEpochEventData::Delegate {
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
storage::push_new_epoch_event(test.deps_mut().storage, &non_existent_delegation)
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
@@ -453,13 +457,13 @@ mod tests {
// delegation to node that doesn't exist with vesting contract
// we expect the same as above PLUS TrackUndelegation message
let non_existent_delegation = PendingEpochEventData::Delegate {
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp2"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: Some(vesting_contract.clone()),
};
storage::push_new_epoch_event(test.deps_mut().storage, &non_existent_delegation)
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp2"),
@@ -479,18 +483,24 @@ mod tests {
expected_messages.push(SubMsg::new(track_undelegate_message));
// updating active set should only emit events and no cosmos messages
let action_with_event = PendingEpochEventData::UpdateActiveSetSize { new_size: 50 };
storage::push_new_epoch_event(test.deps_mut().storage, &action_with_event).unwrap();
expected_events.push(new_active_set_update_event(50));
let action_with_event = PendingEpochEventKind::UpdateActiveSetSize { new_size: 50 };
storage::push_new_epoch_event(test.deps_mut().storage, &env, action_with_event)
.unwrap();
expected_events.push(new_active_set_update_event(env.block.height, 50));
// undelegation just returns tokens and emits event
let legit_undelegate = PendingEpochEventData::Undelegate {
let legit_undelegate = PendingEpochEventKind::Undelegate {
owner: delegator.clone(),
mix_id: legit_mix,
proxy: None,
};
storage::push_new_epoch_event(test.deps_mut().storage, &legit_undelegate).unwrap();
expected_events.push(new_undelegation_event(&delegator, &None, legit_mix));
storage::push_new_epoch_event(test.deps_mut().storage, &env, legit_undelegate).unwrap();
expected_events.push(new_undelegation_event(
env.block.height,
&delegator,
&None,
legit_mix,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
to_address: delegator.into_string(),
amount: coins(amount, TEST_COIN_DENOM),
@@ -640,16 +650,20 @@ mod tests {
rewarded_set_size: Some(500),
..Default::default()
};
let action_with_event = PendingIntervalEventData::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &action_with_event).unwrap();
let action_with_event = PendingIntervalEventKind::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &env, action_with_event)
.unwrap();
push_n_dummy_interval_actions(&mut test, 10);
let (res, executed) =
perform_pending_interval_actions(test.deps_mut(), &env, None).unwrap();
let updated_params = test.rewarding_params().interval;
assert_eq!(
res,
Response::new()
.add_event(new_rewarding_params_update_event(update, updated_params))
Response::new().add_event(new_rewarding_params_update_event(
env.block.height,
update,
updated_params
))
);
assert_eq!(executed, 21);
assert_eq!(
@@ -673,38 +687,45 @@ mod tests {
profit_margin_percent: Percent::from_percentage_value(12).unwrap(),
interval_operating_cost: coin(123_000, TEST_COIN_DENOM),
};
let cost_change = PendingIntervalEventData::ChangeMixCostParams {
let cost_change = PendingIntervalEventKind::ChangeMixCostParams {
mix_id: legit_mix,
new_costs: new_costs.clone(),
};
storage::push_new_interval_event(test.deps_mut().storage, &cost_change).unwrap();
expected_events.push(new_mixnode_cost_params_update_event(legit_mix, &new_costs));
storage::push_new_interval_event(test.deps_mut().storage, &env, cost_change).unwrap();
expected_events.push(new_mixnode_cost_params_update_event(
env.block.height,
legit_mix,
&new_costs,
));
let update = IntervalRewardingParamsUpdate {
rewarded_set_size: Some(500),
..Default::default()
};
let change_params = PendingIntervalEventData::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &change_params).unwrap();
let change_params = PendingIntervalEventKind::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &env, change_params).unwrap();
let interval = test.current_interval();
let mut expected_updated = test.rewarding_params();
expected_updated
.try_apply_updates(update, interval.epochs_in_interval())
.unwrap();
expected_events.push(new_rewarding_params_update_event(
env.block.height,
update,
expected_updated.interval,
));
let change_interval = PendingIntervalEventData::UpdateIntervalConfig {
let change_interval = PendingIntervalEventKind::UpdateIntervalConfig {
epochs_in_interval: 123,
epoch_duration_secs: 1000,
};
let mut expected_updated2 = expected_updated;
expected_updated2.apply_epochs_in_interval_change(123);
storage::push_new_interval_event(test.deps_mut().storage, &change_interval).unwrap();
storage::push_new_interval_event(test.deps_mut().storage, &env, change_interval)
.unwrap();
expected_events.push(new_interval_config_update_event(
env.block.height,
123,
1000,
expected_updated2.interval,
@@ -785,7 +806,7 @@ mod tests {
use mixnet_contract_common::events::{
new_delegation_on_unbonded_node_event, new_rewarding_params_update_event,
};
use mixnet_contract_common::pending_events::PendingEpochEventData;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
use mixnet_contract_common::reward_params::IntervalRewardingParamsUpdate;
#[test]
@@ -967,18 +988,19 @@ mod tests {
#[test]
fn catches_all_emitted_cosmos_events_and_messages() {
let mut test = TestSetup::new();
let env = test.env();
let mut expected_events = vec![new_reconcile_pending_events()];
let mut expected_messages: Vec<SubMsg<Empty>> = Vec::new();
// epoch event
let non_existent_delegation = PendingEpochEventData::Delegate {
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
storage::push_new_epoch_event(test.deps_mut().storage, &non_existent_delegation)
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
@@ -996,14 +1018,15 @@ mod tests {
rewarded_set_size: Some(500),
..Default::default()
};
let change_params = PendingIntervalEventData::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &change_params).unwrap();
let change_params = PendingIntervalEventKind::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &env, change_params).unwrap();
let interval = test.current_interval();
let mut expected_updated = test.rewarding_params();
expected_updated
.try_apply_updates(update, interval.epochs_in_interval())
.unwrap();
expected_events.push(new_rewarding_params_update_event(
env.block.height,
update,
expected_updated.interval,
));
@@ -1238,18 +1261,19 @@ mod tests {
#[test]
fn if_executes_any_events_it_propagates_responses() {
let mut test = TestSetup::new();
let env = test.env();
let current_active_set = test.rewarding_params().active_set_size;
let mut expected_events = Vec::new();
let mut expected_messages: Vec<SubMsg<Empty>> = Vec::new();
let non_existent_delegation = PendingEpochEventData::Delegate {
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
storage::push_new_epoch_event(test.deps_mut().storage, &non_existent_delegation)
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
@@ -1267,14 +1291,15 @@ mod tests {
rewarded_set_size: Some(500),
..Default::default()
};
let change_params = PendingIntervalEventData::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &change_params).unwrap();
let change_params = PendingIntervalEventKind::UpdateRewardingParams { update };
storage::push_new_interval_event(test.deps_mut().storage, &env, change_params).unwrap();
let interval = test.current_interval();
let mut expected_updated = test.rewarding_params();
expected_updated
.try_apply_updates(update, interval.epochs_in_interval())
.unwrap();
expected_events.push(new_rewarding_params_update_event(
env.block.height,
update,
expected_updated.interval,
));
@@ -1509,8 +1534,8 @@ mod tests {
// make sure it's actually saved to pending events
let events = test.pending_interval_events();
assert!(matches!(events[0],
PendingIntervalEventData::UpdateIntervalConfig { epochs_in_interval, epoch_duration_secs } if epochs_in_interval == 100 && epoch_duration_secs == 1000
assert!(matches!(events[0].kind,
PendingIntervalEventKind::UpdateIntervalConfig { epochs_in_interval, epoch_duration_secs } if epochs_in_interval == 100 && epoch_duration_secs == 1000
));
test.execute_all_pending_events();
+2 -2
View File
@@ -1174,7 +1174,7 @@ pub(crate) mod tests {
.save(deps.as_mut().storage, id, &rewarding_details)
.unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), id).unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, id).unwrap();
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
assert!(res.mixnode_details.is_none());
assert_eq!(address, res.address);
@@ -1263,7 +1263,7 @@ pub(crate) mod tests {
// add and unbond the mixnode
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, sender, good_mixnode_pledge());
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), mix_id).unwrap();
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, mix_id).unwrap();
let res = query_unbonded_mixnode(deps.as_ref(), mix_id).unwrap();
assert_eq!(res.unbonded_info.unwrap().owner, sender);
+50 -24
View File
@@ -17,7 +17,7 @@ use mixnet_contract_common::events::{
new_mixnode_pending_cost_params_update_event, new_pending_mixnode_unbonding_event,
};
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use mixnet_contract_common::pending_events::{PendingEpochEventData, PendingIntervalEventData};
use mixnet_contract_common::pending_events::{PendingEpochEventKind, PendingIntervalEventKind};
use mixnet_contract_common::MixNode;
pub fn try_add_mixnode(
@@ -119,23 +119,26 @@ fn _try_add_mixnode(
pub fn try_remove_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_remove_mixnode(deps, owner, Some(proxy))
_try_remove_mixnode(deps, env, owner, Some(proxy))
}
pub fn try_remove_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_remove_mixnode(deps, info.sender, None)
_try_remove_mixnode(deps, env, info.sender, None)
}
pub(crate) fn _try_remove_mixnode(
deps: DepsMut<'_>,
env: Env,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
@@ -161,10 +164,10 @@ pub(crate) fn _try_remove_mixnode(
)?;
// push the event to execute it at the end of the epoch
let epoch_event = PendingEpochEventData::UnbondMixnode {
let epoch_event = PendingEpochEventKind::UnbondMixnode {
mix_id: existing_bond.mix_id,
};
interval_storage::push_new_epoch_event(deps.storage, &epoch_event)?;
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(
Response::new().add_event(new_pending_mixnode_unbonding_event(
@@ -229,26 +232,29 @@ pub(crate) fn _try_update_mixnode_config(
pub(crate) fn try_update_mixnode_cost_params(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
new_costs: MixNodeCostParams,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_mixnode_cost_params(deps, new_costs, owner, None)
_try_update_mixnode_cost_params(deps, env, new_costs, owner, None)
}
pub(crate) fn try_update_mixnode_cost_params_on_behalf(
deps: DepsMut,
env: Env,
info: MessageInfo,
new_costs: MixNodeCostParams,
owner: String,
) -> Result<Response, MixnetContractError> {
let owner = deps.api.addr_validate(&owner)?;
let proxy = info.sender;
_try_update_mixnode_cost_params(deps, new_costs, owner, Some(proxy))
_try_update_mixnode_cost_params(deps, env, new_costs, owner, Some(proxy))
}
pub(crate) fn _try_update_mixnode_cost_params(
deps: DepsMut,
env: Env,
new_costs: MixNodeCostParams,
owner: Addr,
proxy: Option<Addr>,
@@ -267,11 +273,11 @@ pub(crate) fn _try_update_mixnode_cost_params(
);
// push the interval event
let interval_event = PendingIntervalEventData::ChangeMixCostParams {
let interval_event = PendingIntervalEventKind::ChangeMixCostParams {
mix_id: existing_bond.mix_id,
new_costs,
};
push_new_interval_event(deps.storage, &interval_event)?;
push_new_interval_event(deps.storage, &env, interval_event)?;
Ok(Response::new().add_event(cosmos_event))
}
@@ -418,7 +424,7 @@ pub mod tests {
let info = mock_info(sender, &[]);
// trying to remove your mixnode fails if you never had one in the first place
let res = try_remove_mixnode(deps.as_mut(), info.clone());
let res = try_remove_mixnode(deps.as_mut(), env.clone(), info.clone());
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedMixNodeBond {
@@ -426,12 +432,18 @@ pub mod tests {
})
);
let mix_id =
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, sender, good_mixnode_pledge());
let mix_id = test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env.clone(),
sender,
good_mixnode_pledge(),
);
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_remove_mixnode_on_behalf(
deps.as_mut(),
env.clone(),
mock_info("proxy", &[]),
sender.to_string(),
);
@@ -444,7 +456,7 @@ pub mod tests {
);
// "normal" unbonding succeeds and unbonding event is pushed to the pending epoch events
let res = try_remove_mixnode(deps.as_mut(), info.clone());
let res = try_remove_mixnode(deps.as_mut(), env.clone(), info.clone());
assert!(res.is_ok());
let mut pending_events = interval_storage::PENDING_EPOCH_EVENTS
.range(deps.as_ref().storage, None, None, Order::Ascending)
@@ -453,10 +465,13 @@ pub mod tests {
assert_eq!(pending_events.len(), 1);
let event = pending_events.pop().unwrap();
assert_eq!(1, event.0);
assert_eq!(PendingEpochEventData::UnbondMixnode { mix_id }, event.1);
assert_eq!(
PendingEpochEventKind::UnbondMixnode { mix_id },
event.1.kind
);
// but fails if repeated (since the node is already in the "unbonding" state)(
let res = try_remove_mixnode(deps.as_mut(), info);
let res = try_remove_mixnode(deps.as_mut(), env, info);
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
@@ -488,7 +503,7 @@ pub mod tests {
let mix_id = test_helpers::add_mixnode(
&mut rng,
deps.as_mut(),
env,
env.clone(),
sender,
tests::fixtures::good_mixnode_pledge(),
);
@@ -521,7 +536,7 @@ pub mod tests {
assert_eq!(mix.mix_node.version, update.version);
// but we cannot perform any updates whilst the mixnode is already unbonding
try_remove_mixnode(deps.as_mut(), info.clone()).unwrap();
try_remove_mixnode(deps.as_mut(), env, info.clone()).unwrap();
let res = try_update_mixnode_config(deps.as_mut(), info, update);
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
@@ -540,7 +555,12 @@ pub mod tests {
};
// try updating a non existing mixnode bond
let res = try_update_mixnode_cost_params(deps.as_mut(), info.clone(), update.clone());
let res = try_update_mixnode_cost_params(
deps.as_mut(),
env.clone(),
info.clone(),
update.clone(),
);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedMixNodeBond {
@@ -559,6 +579,7 @@ pub mod tests {
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_mixnode_cost_params_on_behalf(
deps.as_mut(),
env.clone(),
mock_info("proxy", &[]),
update.clone(),
sender.to_string(),
@@ -571,7 +592,12 @@ pub mod tests {
})
);
// "normal" update succeeds
let res = try_update_mixnode_cost_params(deps.as_mut(), info.clone(), update.clone());
let res = try_update_mixnode_cost_params(
deps.as_mut(),
env.clone(),
info.clone(),
update.clone(),
);
assert!(res.is_ok());
// see if the event has been pushed onto the queue
@@ -583,15 +609,15 @@ pub mod tests {
let event = pending_events.pop().unwrap();
assert_eq!(1, event.0);
assert_eq!(
PendingIntervalEventData::ChangeMixCostParams {
PendingIntervalEventKind::ChangeMixCostParams {
mix_id,
new_costs: update.clone()
},
event.1
event.1.kind
);
// execute the event
test_helpers::execute_all_pending_events(deps.as_mut(), env);
test_helpers::execute_all_pending_events(deps.as_mut(), env.clone());
// and see if the config has actually been updated
let mix = get_mixnode_details_by_id(deps.as_ref().storage, mix_id)
@@ -600,8 +626,8 @@ pub mod tests {
assert_eq!(mix.rewarding_details.cost_params, update);
// but we cannot perform any updates whilst the mixnode is already unbonding
try_remove_mixnode(deps.as_mut(), info.clone()).unwrap();
let res = try_update_mixnode_cost_params(deps.as_mut(), info, update);
try_remove_mixnode(deps.as_mut(), env.clone(), info.clone()).unwrap();
let res = try_update_mixnode_cost_params(deps.as_mut(), env, info, update);
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
+16 -8
View File
@@ -345,7 +345,8 @@ mod tests {
total_earned += dist.operator;
let sender = mock_info(owner, &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap();
let res2 = query_pending_mixnode_operator_reward(test.deps(), mix_id).unwrap();
@@ -371,7 +372,8 @@ mod tests {
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let sender = mock_info(owner, &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
test.execute_all_pending_events();
let res = query_pending_operator_reward(test.deps(), owner.into()).unwrap();
@@ -493,7 +495,8 @@ mod tests {
total_earned += dist.delegates;
let sender = mock_info("mix-owner", &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
let res =
query_pending_delegator_reward(test.deps(), owner.into(), mix_id, None).unwrap();
@@ -522,7 +525,8 @@ mod tests {
total_earned += dist.delegates;
let sender = mock_info("mix-owner", &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
test.execute_all_pending_events();
let res =
@@ -657,7 +661,8 @@ mod tests {
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let sender = mock_info(owner, &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
let res = query_estimated_current_epoch_operator_reward(
test.deps(),
@@ -682,7 +687,8 @@ mod tests {
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let sender = mock_info(owner, &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
test.execute_all_pending_events();
let res = query_estimated_current_epoch_operator_reward(
@@ -833,7 +839,8 @@ mod tests {
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let sender = mock_info("mix-owner", &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
let res = query_estimated_current_epoch_delegator_reward(
test.deps(),
@@ -862,7 +869,8 @@ mod tests {
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let sender = mock_info("mix-owner", &[]);
try_remove_mixnode(test.deps_mut(), sender).unwrap();
let env = test.env();
try_remove_mixnode(test.deps_mut(), env, sender).unwrap();
test.execute_all_pending_events();
let res = query_estimated_current_epoch_delegator_reward(
+22 -17
View File
@@ -22,7 +22,7 @@ use mixnet_contract_common::events::{
new_withdraw_delegator_reward_event, new_withdraw_operator_reward_event,
new_zero_uptime_mix_operator_rewarding_event,
};
use mixnet_contract_common::pending_events::{PendingEpochEventData, PendingIntervalEventData};
use mixnet_contract_common::pending_events::{PendingEpochEventKind, PendingIntervalEventKind};
use mixnet_contract_common::reward_params::{
IntervalRewardingParamsUpdate, NodeRewardParams, Performance,
};
@@ -288,13 +288,16 @@ pub(crate) fn try_update_active_set_size(
if force_immediately || interval.is_current_epoch_over(&env) {
rewarding_params.try_change_active_set_size(active_set_size)?;
storage::REWARDING_PARAMS.save(deps.storage, &rewarding_params)?;
Ok(Response::new().add_event(new_active_set_update_event(active_set_size)))
Ok(Response::new().add_event(new_active_set_update_event(
env.block.height,
active_set_size,
)))
} else {
// push the epoch event
let epoch_event = PendingEpochEventData::UpdateActiveSetSize {
let epoch_event = PendingEpochEventKind::UpdateActiveSetSize {
new_size: active_set_size,
};
push_new_epoch_event(deps.storage, &epoch_event)?;
push_new_epoch_event(deps.storage, &env, epoch_event)?;
let time_left = interval.secs_until_current_interval_end(&env);
Ok(
Response::new().add_event(new_pending_active_set_update_event(
@@ -324,15 +327,16 @@ pub(crate) fn try_update_rewarding_params(
rewarding_params.try_apply_updates(updated_params, interval.epochs_in_interval())?;
storage::REWARDING_PARAMS.save(deps.storage, &rewarding_params)?;
Ok(Response::new().add_event(new_rewarding_params_update_event(
env.block.height,
updated_params,
rewarding_params.interval,
)))
} else {
// push the interval event
let interval_event = PendingIntervalEventData::UpdateRewardingParams {
let interval_event = PendingIntervalEventKind::UpdateRewardingParams {
update: updated_params,
};
push_new_interval_event(deps.storage, &interval_event)?;
push_new_interval_event(deps.storage, &env, interval_event)?;
let time_left = interval.secs_until_current_interval_end(&env);
Ok(
Response::new().add_event(new_pending_rewarding_params_update_event(
@@ -358,7 +362,8 @@ pub mod tests {
use cosmwasm_std::{Decimal, Uint128};
use mixnet_contract_common::events::{
MixnetEventType, BOND_NOT_FOUND_VALUE, DELEGATES_REWARD_KEY, NO_REWARD_REASON_KEY,
OPERATOR_REWARD_KEY, PRIOR_DELEGATES_KEY, PRIOR_UNIT_REWARD, ZERO_PERFORMANCE_VALUE,
OPERATOR_REWARD_KEY, PRIOR_DELEGATES_KEY, PRIOR_UNIT_REWARD_KEY,
ZERO_PERFORMANCE_VALUE,
};
use mixnet_contract_common::helpers::compare_decimals;
use mixnet_contract_common::RewardedSetNodeStatus;
@@ -418,9 +423,9 @@ pub mod tests {
&rewarding_details,
)
.unwrap();
pending_events::unbond_mixnode(test.deps_mut(), &env, mix_id_unbonded).unwrap();
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id_unbonded).unwrap();
pending_events::unbond_mixnode(test.deps_mut(), &env, mix_id_unbonded_leftover)
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id_unbonded_leftover)
.unwrap();
let env = test.env();
@@ -841,7 +846,7 @@ pub mod tests {
let prior_unit_reward: Decimal = find_attribute(
Some(MixnetEventType::MixnodeRewarding),
PRIOR_UNIT_REWARD,
PRIOR_UNIT_REWARD_KEY,
&res1,
)
.parse()
@@ -903,7 +908,7 @@ pub mod tests {
let prior_unit_reward: Decimal = find_attribute(
Some(MixnetEventType::MixnodeRewarding),
PRIOR_UNIT_REWARD,
PRIOR_UNIT_REWARD_KEY,
&res2,
)
.parse()
@@ -981,7 +986,7 @@ pub mod tests {
let prior_unit_reward: Decimal = find_attribute(
Some(MixnetEventType::MixnodeRewarding),
PRIOR_UNIT_REWARD,
PRIOR_UNIT_REWARD_KEY,
&res1,
)
.parse()
@@ -1050,7 +1055,7 @@ pub mod tests {
let prior_unit_reward: Decimal = find_attribute(
Some(MixnetEventType::MixnodeRewarding),
PRIOR_UNIT_REWARD,
PRIOR_UNIT_REWARD_KEY,
&res2,
)
.parse()
@@ -1223,7 +1228,7 @@ pub mod tests {
.unwrap();
let env = test.env();
pending_events::unbond_mixnode(test.deps_mut(), &env, mix_id_unbonded_leftover)
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id_unbonded_leftover)
.unwrap();
let res =
@@ -1505,7 +1510,7 @@ pub mod tests {
.unwrap();
let env = test.env();
pending_events::unbond_mixnode(test.deps_mut(), &env, mix_id_unbonded_leftover)
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id_unbonded_leftover)
.unwrap();
let res = try_withdraw_operator_reward(test.deps_mut(), sender1);
@@ -1675,7 +1680,7 @@ pub mod tests {
// make sure it's actually saved to pending events
let events = test.pending_epoch_events();
assert!(
matches!(events[0], PendingEpochEventData::UpdateActiveSetSize { new_size } if new_size == 42)
matches!(events[0].kind, PendingEpochEventKind::UpdateActiveSetSize { new_size } if new_size == 42)
);
test.execute_all_pending_events();
@@ -1830,7 +1835,7 @@ pub mod tests {
// make sure it's actually saved to pending events
let events = test.pending_interval_events();
assert!(
matches!(events[0],PendingIntervalEventData::UpdateRewardingParams { update } if update.rewarded_set_size == Some(123))
matches!(events[0].kind,PendingIntervalEventKind::UpdateRewardingParams { update } if update.rewarded_set_size == Some(123))
);
test.execute_all_pending_events();
+25 -8
View File
@@ -216,13 +216,19 @@ pub mod test_helpers {
.load(self.deps().storage, mix_id)
.unwrap();
try_remove_mixnode(self.deps_mut(), mock_info(bond_details.owner.as_str(), &[]))
.unwrap();
let env = self.env();
try_remove_mixnode(
self.deps_mut(),
env,
mock_info(bond_details.owner.as_str(), &[]),
)
.unwrap();
}
pub fn immediately_unbond_mixnode(&mut self, mix_id: MixId) {
let env = self.env();
pending_events::unbond_mixnode(self.deps_mut(), &env, mix_id).unwrap();
pending_events::unbond_mixnode(self.deps_mut(), &env, env.block.height, mix_id)
.unwrap();
}
pub fn add_immediate_delegation(
@@ -240,6 +246,7 @@ pub mod test_helpers {
pending_events::delegate(
self.deps_mut(),
&env,
env.block.height,
Addr::unchecked(delegator),
target,
amount,
@@ -264,6 +271,7 @@ pub mod test_helpers {
pending_events::delegate(
self.deps_mut(),
&env,
env.block.height,
Addr::unchecked(delegator),
target,
amount,
@@ -284,12 +292,20 @@ pub mod test_helpers {
denom,
amount: amount.into(),
};
delegate(self.deps_mut(), delegator, vec![amount], target)
let env = self.env();
delegate(self.deps_mut(), env, delegator, vec![amount], target)
}
pub fn remove_immediate_delegation(&mut self, delegator: &str, target: MixId) {
pending_events::undelegate(self.deps_mut(), Addr::unchecked(delegator), target, None)
.unwrap();
let height = self.env.block.height;
pending_events::undelegate(
self.deps_mut(),
height,
Addr::unchecked(delegator),
target,
None,
)
.unwrap();
}
pub fn skip_to_next_epoch_end(&mut self) {
@@ -556,6 +572,7 @@ pub mod test_helpers {
pending_events::delegate(
deps.branch(),
&env,
env.block.height,
Addr::unchecked(&format!("owner{}", i)),
mix_id,
tests::fixtures::good_mixnode_pledge().pop().unwrap(),
@@ -775,9 +792,9 @@ pub mod test_helpers {
deps
}
pub fn delegate(deps: DepsMut<'_>, sender: &str, stake: Vec<Coin>, mix_id: MixId) {
pub fn delegate(deps: DepsMut<'_>, env: Env, sender: &str, stake: Vec<Coin>, mix_id: MixId) {
let info = mock_info(sender, &stake);
try_delegate_to_mixnode(deps, info, mix_id).unwrap();
try_delegate_to_mixnode(deps, env, info, mix_id).unwrap();
}
pub(crate) fn read_delegation(
@@ -1,6 +1,7 @@
import type { PendingEpochEventData } from './PendingEpochEventData';
import type {PendingEpochEventData} from "./PendingEpochEventData";
export interface PendingEpochEvent {
id: number;
event: PendingEpochEventData;
id: number,
created_at: bigint,
event: PendingEpochEventData,
}
@@ -1,6 +1,7 @@
import type { PendingIntervalEventData } from './PendingIntervalEventData';
import type {PendingIntervalEventData} from "./PendingIntervalEventData";
export interface PendingIntervalEvent {
id: number;
event: PendingIntervalEventData;
id: number,
created_at: bigint,
event: PendingIntervalEventData,
}