additional rewarding-related unit tests
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{BankMsg, Coin, CosmosMsg, Response};
|
||||
|
||||
pub trait ResponseExt<T> {
|
||||
fn add_optional_message(self, msg: Option<impl Into<CosmosMsg<T>>>) -> Self;
|
||||
|
||||
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self;
|
||||
}
|
||||
|
||||
impl<T> ResponseExt<T> for Response<T> {
|
||||
fn add_optional_message(self, msg: Option<impl Into<CosmosMsg<T>>>) -> Self {
|
||||
if let Some(msg) = msg {
|
||||
self.add_message(msg)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self {
|
||||
self.add_message(BankMsg::Send {
|
||||
to_address: to.as_ref().to_string(),
|
||||
amount: vec![amount],
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,6 @@ pub mod events;
|
||||
pub mod signing;
|
||||
pub mod types;
|
||||
|
||||
pub mod helpers;
|
||||
|
||||
pub use types::*;
|
||||
|
||||
@@ -193,8 +193,6 @@ impl RewardedSetMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
// important note: this currently does **NOT** include gateway role as they're not being rewarded
|
||||
// and the metadata is primarily used for data lookup during epoch transition
|
||||
pub fn highest_rewarded_id(&self) -> NodeId {
|
||||
let mut highest = 0;
|
||||
if self.layer1_metadata.highest_id > highest {
|
||||
@@ -206,6 +204,12 @@ impl RewardedSetMetadata {
|
||||
if self.layer3_metadata.highest_id > highest {
|
||||
highest = self.layer3_metadata.highest_id;
|
||||
}
|
||||
if self.entry_gateway_metadata.highest_id > highest {
|
||||
highest = self.entry_gateway_metadata.highest_id;
|
||||
}
|
||||
if self.exit_gateway_metadata.highest_id > highest {
|
||||
highest = self.exit_gateway_metadata.highest_id;
|
||||
}
|
||||
if self.standby_metadata.highest_id > highest {
|
||||
highest = self.standby_metadata.highest_id;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ pub struct RoleAssignment {
|
||||
}
|
||||
|
||||
impl RoleAssignment {
|
||||
pub fn new(role: Role, nodes: Vec<NodeId>) -> RoleAssignment {
|
||||
RoleAssignment { role, nodes }
|
||||
}
|
||||
|
||||
pub fn is_final_assignment(&self) -> bool {
|
||||
self.role.is_standby()
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ mod tests {
|
||||
let node_id = test.add_legacy_mixnode("owner2", None);
|
||||
let sender = mock_info("owner2", &[]);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
test.reward_with_distribution(node_id, active_params);
|
||||
|
||||
@@ -644,7 +644,7 @@ mod tests {
|
||||
let node_id = test.add_dummy_nymnode("owner2", None);
|
||||
let sender = mock_info("owner2", &[]);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
test.reward_with_distribution(node_id, active_params);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ mod tests {
|
||||
test.add_immediate_delegation(delegator, og_amount, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
let dist1 = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.skip_to_next_epoch_end();
|
||||
let dist2 = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use crate::nodes::helpers::save_new_nymnode_with_id;
|
||||
use crate::nodes::transactions::add_nym_node_inner;
|
||||
use crate::support::helpers::ensure_epoch_in_progress_state;
|
||||
use crate::support::helpers::AttachSendTokens;
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::events::{
|
||||
@@ -16,6 +15,7 @@ use mixnet_contract_common::events::{
|
||||
};
|
||||
use mixnet_contract_common::gateway::GatewayConfigUpdate;
|
||||
use mixnet_contract_common::{Gateway, GatewayBondingPayload, NodeCostParams};
|
||||
use nym_contracts_common::helpers::ResponseExt;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
|
||||
pub(crate) fn try_add_gateway(
|
||||
@@ -351,7 +351,7 @@ pub mod tests {
|
||||
assert_eq!(&Addr::unchecked("bob"), first_node.owner());
|
||||
|
||||
// add a node owned by fred
|
||||
let fred_identity = test.add_legacy_gateway("fred", None);
|
||||
let (fred_identity, _) = test.add_legacy_gateway("fred", None);
|
||||
|
||||
// let's make sure we now have 2 nodes:
|
||||
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||
|
||||
@@ -17,6 +17,7 @@ use mixnet_contract_common::pending_events::{
|
||||
};
|
||||
use mixnet_contract_common::reward_params::{ActiveSetUpdate, IntervalRewardingParamsUpdate};
|
||||
use mixnet_contract_common::{BlockHeight, Delegation, NodeId};
|
||||
use nym_contracts_common::helpers::ResponseExt;
|
||||
|
||||
use crate::delegations;
|
||||
use crate::delegations::storage as delegations_storage;
|
||||
@@ -28,7 +29,7 @@ use crate::nodes::helpers::{cleanup_post_unbond_nym_node_storage, get_node_detai
|
||||
use crate::nodes::storage as nymnodes_storage;
|
||||
use crate::rewards::storage as rewards_storage;
|
||||
use crate::rewards::storage::RewardingStorage;
|
||||
use crate::support::helpers::{ensure_any_node_bonded, AttachSendTokens};
|
||||
use crate::support::helpers::ensure_any_node_bonded;
|
||||
|
||||
pub(crate) trait ContractExecutableEvent {
|
||||
// note: the error only means a HARD error like we failed to read from storage.
|
||||
@@ -837,7 +838,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.0);
|
||||
|
||||
// perform some rewarding here to advance the unit delegation beyond the initial value
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.skip_to_next_epoch_end();
|
||||
@@ -911,7 +912,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.0);
|
||||
|
||||
// perform some rewarding here to advance the unit delegation beyond the initial value
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.skip_to_next_epoch_end();
|
||||
@@ -1001,7 +1002,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.0);
|
||||
|
||||
// perform some rewarding here to advance the unit delegation beyond the initial value
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.skip_to_next_epoch_end();
|
||||
@@ -1135,7 +1136,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let active_params = test.active_node_params(100.0);
|
||||
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.skip_to_next_epoch_end();
|
||||
let dist1 = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.skip_to_next_epoch_end();
|
||||
@@ -1265,7 +1266,7 @@ mod tests {
|
||||
test.add_immediate_delegation("carol", 111_111_111u128, mix_id_full_pledge);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
|
||||
let dist1 = test.reward_with_distribution_ignore_state(mix_id_repledge, active_params);
|
||||
let dist2 =
|
||||
@@ -1289,7 +1290,7 @@ mod tests {
|
||||
test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge]);
|
||||
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id_repledge, active_params);
|
||||
|
||||
@@ -1327,7 +1328,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
|
||||
// go through few epochs of rewarding
|
||||
for _ in 0..500 {
|
||||
@@ -1356,7 +1357,7 @@ mod tests {
|
||||
test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge]);
|
||||
|
||||
let mut cumulative_op_reward = Decimal::zero();
|
||||
let mut cumulative_del_reward = Decimal::zero();
|
||||
@@ -1405,7 +1406,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
|
||||
// go through few more epochs of rewarding
|
||||
for _ in 0..500 {
|
||||
@@ -1544,7 +1545,7 @@ mod tests {
|
||||
test.add_immediate_delegation("carol", 111_111_111u128, mix_id_full_pledge);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
|
||||
let dist1 = test.reward_with_distribution_ignore_state(mix_id_repledge, active_params);
|
||||
let dist2 =
|
||||
@@ -1568,7 +1569,7 @@ mod tests {
|
||||
test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge]);
|
||||
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id_repledge, active_params);
|
||||
|
||||
@@ -1606,7 +1607,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
|
||||
// go through few epochs of rewarding
|
||||
for _ in 0..500 {
|
||||
@@ -1635,7 +1636,7 @@ mod tests {
|
||||
test.add_immediate_delegation("carol", 111_111_111_000u128, mix_id_repledge);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge]);
|
||||
|
||||
let mut cumulative_op_reward = Decimal::zero();
|
||||
let mut cumulative_del_reward = Decimal::zero();
|
||||
@@ -1684,7 +1685,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id_repledge, mix_id_full_pledge]);
|
||||
|
||||
// go through few more epochs of rewarding
|
||||
for _ in 0..500 {
|
||||
|
||||
@@ -924,7 +924,7 @@ mod tests {
|
||||
let mut test = TestSetup::new();
|
||||
let rewarding_validator = test.rewarding_validator();
|
||||
|
||||
test.force_change_rewarded_set(vec![1, 2, 3, 4, 5]);
|
||||
test.force_change_mix_rewarded_set(vec![1, 2, 3, 4, 5]);
|
||||
test.skip_to_current_epoch_end();
|
||||
let env = test.env();
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ mod tests {
|
||||
assert_eq!(res.amount, Uint128::zero());
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
let dist1 = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
@@ -309,7 +309,7 @@ mod tests {
|
||||
assert_eq!(res.amount, Uint128::zero());
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
let dist1 = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
|
||||
@@ -316,7 +316,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
let mut total_earned = Decimal::zero();
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
@@ -361,7 +361,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
let mut total_earned = Decimal::zero();
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
@@ -391,7 +391,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
@@ -469,7 +469,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
let mut total_earned = Decimal::zero();
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
@@ -515,7 +515,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
let mut total_earned = Decimal::zero();
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
@@ -547,7 +547,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
let mut total_earned = Decimal::zero();
|
||||
let dist = test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
@@ -585,7 +585,7 @@ mod tests {
|
||||
test.add_immediate_delegation(del2, 150_000_000u32, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
@@ -694,7 +694,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let sender = mock_info(owner, &[]);
|
||||
@@ -722,7 +722,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let sender = mock_info(owner, &[]);
|
||||
@@ -748,9 +748,9 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.force_change_rewarded_set(vec![]);
|
||||
test.force_change_mix_rewarded_set(vec![]);
|
||||
|
||||
let res = query_estimated_current_epoch_operator_reward(
|
||||
test.deps(),
|
||||
@@ -772,7 +772,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let res = query_estimated_current_epoch_operator_reward(
|
||||
@@ -795,7 +795,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let mix_rewarding = test.mix_rewarding(mix_id);
|
||||
@@ -861,7 +861,7 @@ mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let res = query_estimated_current_epoch_delegator_reward(
|
||||
@@ -887,7 +887,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let sender = mock_info("mix-owner", &[]);
|
||||
@@ -918,7 +918,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let sender = mock_info("mix-owner", &[]);
|
||||
@@ -950,9 +950,9 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
test.force_change_rewarded_set(vec![]);
|
||||
test.force_change_mix_rewarded_set(vec![]);
|
||||
|
||||
let res = query_estimated_current_epoch_delegator_reward(
|
||||
test.deps(),
|
||||
@@ -978,7 +978,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
test.reward_with_distribution_ignore_state(mix_id, active_params);
|
||||
|
||||
let res = query_estimated_current_epoch_delegator_reward(
|
||||
@@ -1004,7 +1004,7 @@ mod tests {
|
||||
test.add_immediate_delegation(owner, initial_stake, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
|
||||
let mix_rewarding = test.mix_rewarding(mix_id);
|
||||
let res = query_estimated_current_epoch_delegator_reward(
|
||||
@@ -1053,7 +1053,7 @@ mod tests {
|
||||
test.add_immediate_delegation(del2, initial_stake2, mix_id);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![mix_id]);
|
||||
test.force_change_mix_rewarded_set(vec![mix_id]);
|
||||
let params = test.active_node_params(95.0);
|
||||
test.reward_with_distribution_ignore_state(mix_id, params);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::rewards::helpers::update_and_save_last_rewarded;
|
||||
use crate::rewards::storage::RewardingStorage;
|
||||
use crate::support::helpers::{
|
||||
ensure_any_node_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state,
|
||||
AttachSendTokens,
|
||||
};
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
@@ -29,6 +28,7 @@ use mixnet_contract_common::reward_params::{
|
||||
ActiveSetUpdate, IntervalRewardingParamsUpdate, NodeRewardingParameters,
|
||||
};
|
||||
use mixnet_contract_common::{Delegation, EpochState, MixNodeDetails, NodeId, NymNodeDetails};
|
||||
use nym_contracts_common::helpers::ResponseExt;
|
||||
|
||||
pub(crate) fn try_reward_node(
|
||||
deps: DepsMut<'_>,
|
||||
@@ -315,10 +315,51 @@ pub mod tests {
|
||||
use crate::mixnodes::storage as mixnodes_storage;
|
||||
use crate::support::tests::fixtures::active_set_update_fixture;
|
||||
use crate::support::tests::test_helpers;
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
|
||||
// a simple wrapper to streamline checking for rewarding results
|
||||
trait TestRewarding {
|
||||
fn execute_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Result<Response, MixnetContractError>;
|
||||
|
||||
fn assert_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Response;
|
||||
}
|
||||
|
||||
impl TestRewarding for TestSetup {
|
||||
fn execute_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let sender = self.rewarding_validator();
|
||||
self.execute_fn(
|
||||
|deps, env, info| try_reward_node(deps, env, info, node_id, rewarding_params),
|
||||
sender,
|
||||
)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Response {
|
||||
let caller = std::panic::Location::caller();
|
||||
self.execute_rewarding(node_id, rewarding_params)
|
||||
.unwrap_or_else(|err| panic!("{caller} failed with: '{err}' ({err:?})"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mixnode_rewarding {
|
||||
mod legacy_mixnode_rewarding {
|
||||
use super::*;
|
||||
use crate::interval::pending_events;
|
||||
use crate::support::tests::test_helpers::{find_attribute, FindAttribute, TestSetup};
|
||||
@@ -333,46 +374,6 @@ pub mod tests {
|
||||
use mixnet_contract_common::reward_params::WorkFactor;
|
||||
use mixnet_contract_common::EpochStatus;
|
||||
|
||||
// a simple wrapper to streamline checking for rewarding results
|
||||
trait TestRewarding {
|
||||
fn execute_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Result<Response, MixnetContractError>;
|
||||
|
||||
fn assert_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Response;
|
||||
}
|
||||
|
||||
impl TestRewarding for TestSetup {
|
||||
fn execute_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
let sender = self.rewarding_validator();
|
||||
self.execute_fn(
|
||||
|deps, env, info| try_reward_node(deps, env, info, node_id, rewarding_params),
|
||||
sender,
|
||||
)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_rewarding(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
rewarding_params: NodeRewardingParameters,
|
||||
) -> Response {
|
||||
let caller = std::panic::Location::caller();
|
||||
self.execute_rewarding(node_id, rewarding_params)
|
||||
.unwrap_or_else(|err| panic!("{caller} failed with: '{err}' ({err:?})"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod epoch_state_is_correctly_updated {
|
||||
use super::*;
|
||||
@@ -386,7 +387,7 @@ pub mod tests {
|
||||
test.add_rewarded_legacy_mixnode("mix-owner-unbonded-leftover", None);
|
||||
let node_id_never_existed = 42;
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![
|
||||
test.force_change_mix_rewarded_set(vec![
|
||||
node_id_unbonded,
|
||||
node_id_unbonded_leftover,
|
||||
node_id_never_existed,
|
||||
@@ -460,7 +461,7 @@ pub mod tests {
|
||||
let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
|
||||
let zero_performance = test.active_node_params(0.);
|
||||
@@ -479,7 +480,7 @@ pub mod tests {
|
||||
let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
|
||||
let params = NodeRewardingParameters::new(
|
||||
@@ -501,7 +502,7 @@ pub mod tests {
|
||||
let node_id = test.add_dummy_nymnode("node-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
|
||||
let zero_performance = test.active_node_params(0.);
|
||||
@@ -520,7 +521,7 @@ pub mod tests {
|
||||
let node_id = test.add_dummy_nymnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
|
||||
let params = NodeRewardingParameters::new(
|
||||
@@ -542,7 +543,7 @@ pub mod tests {
|
||||
let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
@@ -566,7 +567,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(ids.clone());
|
||||
test.force_change_mix_rewarded_set(ids.clone());
|
||||
test.start_epoch_transition();
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
@@ -611,7 +612,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
|
||||
test.skip_to_current_epoch_end();
|
||||
test.force_change_rewarded_set(vec![1, 2, 3]);
|
||||
test.force_change_mix_rewarded_set(vec![1, 2, 3]);
|
||||
|
||||
let res = test.execute_rewarding(1, active_params);
|
||||
|
||||
@@ -633,7 +634,7 @@ pub mod tests {
|
||||
// skip time to when the following epoch is over (since mixnodes are not eligible for rewarding
|
||||
// in the same epoch they're bonded and we need the rewarding epoch to be over)
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
let params = test.legacy_rewarding_params(node_id, 100.);
|
||||
|
||||
@@ -656,7 +657,7 @@ pub mod tests {
|
||||
let node_id_unbonded_leftover =
|
||||
test.add_rewarded_legacy_mixnode("mix-owner-unbonded-leftover", None);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![
|
||||
test.force_change_mix_rewarded_set(vec![
|
||||
node_id_unbonded,
|
||||
node_id_unbonded_leftover,
|
||||
node_id_never_existed,
|
||||
@@ -712,7 +713,7 @@ pub mod tests {
|
||||
|
||||
// node is in the active set BUT the current epoch has just begun
|
||||
test.skip_to_next_epoch();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
|
||||
let active_params = test.active_node_params(100.);
|
||||
let res = test.execute_rewarding(node_id, active_params);
|
||||
@@ -734,7 +735,7 @@ pub mod tests {
|
||||
let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id, 42]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id, 42]);
|
||||
test.start_epoch_transition();
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
@@ -762,7 +763,7 @@ pub mod tests {
|
||||
let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id, 42]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id, 42]);
|
||||
test.start_epoch_transition();
|
||||
let zero_perf_params = test.active_node_params(0.);
|
||||
let active_params = test.active_node_params(100.);
|
||||
@@ -800,7 +801,7 @@ pub mod tests {
|
||||
let node_id = test.add_rewarded_legacy_mixnode("mix-owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id, 42]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id, 42]);
|
||||
test.start_epoch_transition();
|
||||
|
||||
let zero_work_params =
|
||||
@@ -842,7 +843,7 @@ pub mod tests {
|
||||
let node_id3 = test.add_rewarded_legacy_mixnode("mix-owner3", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id1, node_id2, node_id3]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id1, node_id2, node_id3]);
|
||||
test.start_epoch_transition();
|
||||
let params = test.active_node_params(98.0);
|
||||
|
||||
@@ -908,7 +909,7 @@ pub mod tests {
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.start_epoch_transition();
|
||||
test.force_change_rewarded_set(vec![node_id1, node_id2, node_id3]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id1, node_id2, node_id3]);
|
||||
let performance = test_helpers::performance(98.0);
|
||||
|
||||
test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id2);
|
||||
@@ -981,7 +982,7 @@ pub mod tests {
|
||||
let node_id2 = test.add_rewarded_legacy_mixnode("mix-owner2", Some(operator2));
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id1, node_id2]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id1, node_id2]);
|
||||
let performance = test_helpers::performance(98.0);
|
||||
|
||||
test.add_immediate_delegation("delegator1", Uint128::new(100_000_000), node_id1);
|
||||
@@ -1207,12 +1208,305 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod gateway_role_rewarding {
|
||||
mod legacy_gateway_rewarding {
|
||||
use super::*;
|
||||
use crate::support::tests::test_helpers::FindAttribute;
|
||||
use mixnet_contract_common::events::{BOND_NOT_FOUND_VALUE, NO_REWARD_REASON_KEY};
|
||||
use mixnet_contract_common::nym_node::Role;
|
||||
use mixnet_contract_common::RoleAssignment;
|
||||
|
||||
#[test]
|
||||
fn unimplemented() {
|
||||
todo!()
|
||||
fn regardless_of_performance_or_work_they_get_nothing() {
|
||||
let mut test = TestSetup::new();
|
||||
let (_, node_id) = test.add_legacy_gateway("owner", None);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_assign_rewarded_set(vec![RoleAssignment::new(
|
||||
Role::EntryGateway,
|
||||
vec![node_id],
|
||||
)]);
|
||||
test.start_epoch_transition();
|
||||
|
||||
let rewarding_params = test.active_node_params(100.);
|
||||
let res = test.assert_rewarding(node_id, rewarding_params);
|
||||
|
||||
let reward_attr = res.any_attribute(NO_REWARD_REASON_KEY);
|
||||
assert_eq!(reward_attr, BOND_NOT_FOUND_VALUE);
|
||||
|
||||
// make sure the epoch actually progressed (i.e. unrewarded gateway hasn't stalled it)
|
||||
let current = test.current_epoch_state();
|
||||
assert_eq!(current, EpochState::ReconcilingEvents)
|
||||
}
|
||||
}
|
||||
|
||||
// rewarding for entry gateway, exit gateway and standby nym-nodes
|
||||
#[cfg(test)]
|
||||
mod non_legacy_rewarding {
|
||||
use super::*;
|
||||
use crate::interval::pending_events;
|
||||
use crate::support::tests::test_helpers::FindAttribute;
|
||||
use cosmwasm_std::{Decimal, Uint128};
|
||||
use mixnet_contract_common::events::{
|
||||
BOND_NOT_FOUND_VALUE, NO_REWARD_REASON_KEY, OPERATOR_REWARD_KEY,
|
||||
ZERO_PERFORMANCE_OR_WORK_VALUE,
|
||||
};
|
||||
use mixnet_contract_common::nym_node::Role;
|
||||
use mixnet_contract_common::reward_params::WorkFactor;
|
||||
use mixnet_contract_common::RoleAssignment;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
struct RewardingSetup {
|
||||
standby_node: NodeId,
|
||||
entry_node: NodeId,
|
||||
exit_node: NodeId,
|
||||
mixing_node: NodeId,
|
||||
|
||||
inner: TestSetup,
|
||||
}
|
||||
|
||||
impl RewardingSetup {
|
||||
pub fn new_rewarding_setup() -> Self {
|
||||
let mut inner = TestSetup::new();
|
||||
let mixing_node = inner.add_dummy_nymnode("mixing-owner", None);
|
||||
let entry_node = inner.add_dummy_nymnode("entry-owner", None);
|
||||
let exit_node = inner.add_dummy_nymnode("exit-owner", None);
|
||||
let standby_node = inner.add_dummy_nymnode("standby-owner", None);
|
||||
|
||||
RewardingSetup {
|
||||
standby_node,
|
||||
entry_node,
|
||||
exit_node,
|
||||
mixing_node,
|
||||
inner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> Vec<NodeId> {
|
||||
vec![
|
||||
self.mixing_node,
|
||||
self.entry_node,
|
||||
self.exit_node,
|
||||
self.standby_node,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn reset_rewarded_set(&mut self) {
|
||||
self.inner.force_assign_rewarded_set(vec![
|
||||
RoleAssignment {
|
||||
role: Role::Layer1,
|
||||
nodes: vec![self.mixing_node],
|
||||
},
|
||||
RoleAssignment {
|
||||
role: Role::EntryGateway,
|
||||
nodes: vec![self.entry_node],
|
||||
},
|
||||
RoleAssignment {
|
||||
role: Role::ExitGateway,
|
||||
nodes: vec![self.exit_node],
|
||||
},
|
||||
RoleAssignment {
|
||||
role: Role::Standby,
|
||||
nodes: vec![self.standby_node],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
pub fn local_node_role(&self, node_id: NodeId) -> Role {
|
||||
match node_id {
|
||||
n if n == self.mixing_node => Role::Layer1,
|
||||
n if n == self.entry_node => Role::EntryGateway,
|
||||
n if n == self.exit_node => Role::ExitGateway,
|
||||
n if n == self.standby_node => Role::Standby,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_rewarded_set(&mut self, node_id: NodeId) {
|
||||
let role = self.local_node_role(node_id);
|
||||
self.inner.force_assign_rewarded_set(vec![RoleAssignment {
|
||||
role,
|
||||
nodes: vec![node_id],
|
||||
}])
|
||||
}
|
||||
|
||||
pub fn reward_all(
|
||||
&mut self,
|
||||
performance: f32,
|
||||
) -> HashMap<NodeId, Result<Response, MixnetContractError>> {
|
||||
let mut results = HashMap::new();
|
||||
|
||||
self.skip_to_next_epoch_end();
|
||||
self.reset_rewarded_set();
|
||||
self.start_epoch_transition();
|
||||
|
||||
let active_params = self.active_node_params(performance);
|
||||
let standby_params = self.standby_node_params(performance);
|
||||
|
||||
let mixing_node = self.mixing_node;
|
||||
let entry_node = self.entry_node;
|
||||
let exit_node = self.exit_node;
|
||||
let standby_node = self.standby_node;
|
||||
|
||||
results.insert(
|
||||
mixing_node,
|
||||
self.execute_rewarding(mixing_node, active_params),
|
||||
);
|
||||
results.insert(
|
||||
entry_node,
|
||||
self.execute_rewarding(entry_node, active_params),
|
||||
);
|
||||
results.insert(exit_node, self.execute_rewarding(exit_node, active_params));
|
||||
results.insert(
|
||||
standby_node,
|
||||
self.execute_rewarding(standby_node, standby_params),
|
||||
);
|
||||
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for RewardingSetup {
|
||||
type Target = TestSetup;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for RewardingSetup {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_target_node_has_zero_performance() {
|
||||
let mut test = RewardingSetup::new_rewarding_setup();
|
||||
let results = test.reward_all(0.);
|
||||
for res in results.into_values() {
|
||||
let reward_attr = res.unwrap().any_attribute(NO_REWARD_REASON_KEY);
|
||||
assert_eq!(reward_attr, ZERO_PERFORMANCE_OR_WORK_VALUE);
|
||||
}
|
||||
|
||||
let current = test.current_epoch_state();
|
||||
assert_eq!(current, EpochState::ReconcilingEvents)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_target_node_has_zero_work_factor() {
|
||||
let mut test = RewardingSetup::new_rewarding_setup();
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.reset_rewarded_set();
|
||||
test.start_epoch_transition();
|
||||
|
||||
let params =
|
||||
NodeRewardingParameters::new(test_helpers::performance(100.), WorkFactor::zero());
|
||||
|
||||
for node in test.nodes() {
|
||||
let res = test.assert_rewarding(node, params);
|
||||
let reward_attr = res.any_attribute(NO_REWARD_REASON_KEY);
|
||||
assert_eq!(reward_attr, ZERO_PERFORMANCE_OR_WORK_VALUE);
|
||||
}
|
||||
|
||||
let current = test.current_epoch_state();
|
||||
assert_eq!(current, EpochState::ReconcilingEvents)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_theres_only_one_node_to_reward() {
|
||||
let test_lookup = RewardingSetup::new_rewarding_setup();
|
||||
|
||||
for node in test_lookup.nodes() {
|
||||
let mut actual_setup = RewardingSetup::new_rewarding_setup();
|
||||
actual_setup.add_to_rewarded_set(node);
|
||||
let mut res = actual_setup.reward_all(100.);
|
||||
|
||||
// get the response for this particular node
|
||||
let res = res.remove(&node).unwrap().unwrap();
|
||||
let reward: Decimal = res.any_parsed_attribute(OPERATOR_REWARD_KEY);
|
||||
assert!(!reward.is_zero());
|
||||
|
||||
let current = actual_setup.current_epoch_state();
|
||||
assert_eq!(current, EpochState::ReconcilingEvents)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_theres_multiple_nodes_to_reward() {
|
||||
let mut test = RewardingSetup::new_rewarding_setup();
|
||||
let results = test.reward_all(100.);
|
||||
for res in results.into_values() {
|
||||
let reward: Decimal = res.unwrap().any_parsed_attribute(OPERATOR_REWARD_KEY);
|
||||
assert!(!reward.is_zero());
|
||||
}
|
||||
|
||||
let current = test.current_epoch_state();
|
||||
assert_eq!(current, EpochState::ReconcilingEvents)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_be_performed_for_unbonded_nodes() {
|
||||
let test_lookup = RewardingSetup::new_rewarding_setup();
|
||||
|
||||
for node in test_lookup.nodes() {
|
||||
let mut actual_setup = RewardingSetup::new_rewarding_setup();
|
||||
actual_setup.add_to_rewarded_set(node);
|
||||
|
||||
let env = actual_setup.env();
|
||||
|
||||
// add some delegations to indicate the rewarding information shouldn't get removed
|
||||
actual_setup.add_immediate_delegation("delegator", Uint128::new(12345678), node);
|
||||
pending_events::unbond_nym_node(actual_setup.deps_mut(), &env, 123, node).unwrap();
|
||||
|
||||
let mut res = actual_setup.reward_all(100.);
|
||||
|
||||
// get the response for this particular node
|
||||
let res = res.remove(&node).unwrap().unwrap();
|
||||
let reward_attr = res.any_attribute(NO_REWARD_REASON_KEY);
|
||||
assert_eq!(reward_attr, BOND_NOT_FOUND_VALUE);
|
||||
|
||||
let current = actual_setup.current_epoch_state();
|
||||
assert_eq!(current, EpochState::ReconcilingEvents)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_only_be_performed_once_per_node_per_epoch() {
|
||||
let test_lookup = RewardingSetup::new_rewarding_setup();
|
||||
|
||||
let params = test_lookup.active_node_params(100.0);
|
||||
for node in test_lookup.nodes() {
|
||||
let mut actual_setup = RewardingSetup::new_rewarding_setup();
|
||||
|
||||
actual_setup.skip_to_next_epoch_end();
|
||||
|
||||
let extra = actual_setup.add_dummy_nymnode("foomp", None);
|
||||
|
||||
// add extra node to the rewarded set so rewarding wouldn't immediately go into event reconciliation
|
||||
let role = actual_setup.local_node_role(node);
|
||||
actual_setup
|
||||
.inner
|
||||
.force_assign_rewarded_set(vec![RoleAssignment {
|
||||
role,
|
||||
nodes: vec![node, extra],
|
||||
}]);
|
||||
|
||||
actual_setup.start_epoch_transition();
|
||||
|
||||
// first rewarding
|
||||
actual_setup.assert_rewarding(node, params);
|
||||
|
||||
// second rewarding
|
||||
let res = actual_setup.execute_rewarding(node, params).unwrap_err();
|
||||
assert_eq!(
|
||||
res,
|
||||
MixnetContractError::NodeAlreadyRewarded {
|
||||
node_id: node,
|
||||
absolute_epoch_id: 1,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1220,6 +1514,7 @@ pub mod tests {
|
||||
mod withdrawing_delegator_reward {
|
||||
use crate::interval::pending_events;
|
||||
use crate::support::tests::test_helpers::{assert_eq_with_leeway, TestSetup};
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::{BankMsg, CosmosMsg, Decimal, Uint128};
|
||||
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
|
||||
|
||||
@@ -1249,7 +1544,7 @@ pub mod tests {
|
||||
|
||||
// perform some rewarding so that we'd have non-zero rewards
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id1, node_id2]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id1, node_id2]);
|
||||
test.start_epoch_transition();
|
||||
test.reward_with_distribution(node_id1, active_params);
|
||||
test.reward_with_distribution(node_id2, active_params);
|
||||
@@ -1298,7 +1593,7 @@ pub mod tests {
|
||||
|
||||
// reward mix1, but don't reward mix2
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id1, low_stake_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id1, low_stake_id]);
|
||||
test.start_epoch_transition();
|
||||
test.reward_with_distribution(node_id1, active_params);
|
||||
test.reward_with_distribution(low_stake_id, active_params);
|
||||
@@ -1337,7 +1632,7 @@ pub mod tests {
|
||||
|
||||
let active_params = test.active_node_params(100.);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id_unbonding, node_id_unbonded_leftover]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id_unbonding, node_id_unbonded_leftover]);
|
||||
|
||||
// go through few rewarding cycles before unbonding nodes (partially or fully)
|
||||
for _ in 0..10 {
|
||||
@@ -1416,7 +1711,7 @@ pub mod tests {
|
||||
|
||||
let active_params = test.active_node_params(100.);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id_single, node_id_quad]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id_single, node_id_quad]);
|
||||
|
||||
// accumulate some rewards
|
||||
let mut accumulated_single = Decimal::zero();
|
||||
@@ -1577,7 +1872,7 @@ pub mod tests {
|
||||
let active_params = test.active_node_params(100.);
|
||||
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id]);
|
||||
test.start_epoch_transition();
|
||||
test.reward_with_distribution(node_id, active_params);
|
||||
|
||||
@@ -1606,7 +1901,7 @@ pub mod tests {
|
||||
|
||||
// reward mix1, but don't reward mix2
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id1]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id1]);
|
||||
test.start_epoch_transition();
|
||||
test.reward_with_distribution(node_id1, active_params);
|
||||
|
||||
@@ -1639,7 +1934,7 @@ pub mod tests {
|
||||
|
||||
let active_params = test.active_node_params(100.);
|
||||
test.skip_to_next_epoch_end();
|
||||
test.force_change_rewarded_set(vec![node_id_unbonding, node_id_unbonded_leftover]);
|
||||
test.force_change_mix_rewarded_set(vec![node_id_unbonding, node_id_unbonded_leftover]);
|
||||
|
||||
// go through few rewarding cycles before unbonding nodes (partially or fully)
|
||||
for _ in 0..10 {
|
||||
|
||||
@@ -7,46 +7,13 @@ use crate::mixnodes::helpers::must_get_mixnode_bond_by_owner;
|
||||
use crate::mixnodes::storage as mixnodes_storage;
|
||||
use crate::nodes::helpers::must_get_node_bond_by_owner;
|
||||
use crate::nodes::storage as nymnodes_storage;
|
||||
use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Response, Storage};
|
||||
use cosmwasm_std::{Addr, Coin, Storage};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::mixnode::PendingMixNodeChanges;
|
||||
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixNodeBond, NodeId};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_contracts_common::Percent;
|
||||
|
||||
// helper trait to attach `Msg` to a response if it's provided
|
||||
#[allow(dead_code)]
|
||||
pub(crate) trait AttachOptionalMessage<T> {
|
||||
fn add_optional_message(self, msg: Option<impl Into<CosmosMsg<T>>>) -> Self;
|
||||
}
|
||||
|
||||
impl<T> AttachOptionalMessage<T> for Response<T> {
|
||||
fn add_optional_message(self, msg: Option<impl Into<CosmosMsg<T>>>) -> Self {
|
||||
if let Some(msg) = msg {
|
||||
self.add_message(msg)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait AttachSendTokens {
|
||||
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self;
|
||||
}
|
||||
|
||||
impl<T> AttachSendTokens for Response<T> {
|
||||
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self {
|
||||
self.add_message(BankMsg::Send {
|
||||
to_address: to.as_ref().to_string(),
|
||||
amount: vec![amount],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn debug_with_visibility<S: Into<String>>(api: &dyn Api, msg: S) {
|
||||
// api.debug(&*format!("\n\n\n=========================================\n{}\n=========================================\n\n\n", msg.into()));
|
||||
// }
|
||||
|
||||
pub(crate) fn validate_pledge(
|
||||
mut pledge: Vec<Coin>,
|
||||
minimum_pledge: Coin,
|
||||
|
||||
@@ -52,7 +52,7 @@ pub(crate) fn save_new_gateway(
|
||||
gateway: Gateway,
|
||||
owner: Addr,
|
||||
pledge: Coin,
|
||||
) -> Result<IdentityKey, MixnetContractError> {
|
||||
) -> Result<(IdentityKey, NodeId), MixnetContractError> {
|
||||
let gateway_identity = gateway.identity_key.clone();
|
||||
let bond = GatewayBond::new(pledge.clone(), owner.clone(), env.block.height, gateway);
|
||||
|
||||
@@ -61,5 +61,5 @@ pub(crate) fn save_new_gateway(
|
||||
let id = next_nymnode_id_counter(storage)?;
|
||||
PREASSIGNED_LEGACY_IDS.save(storage, gateway_identity.clone(), &id)?;
|
||||
|
||||
Ok(gateway_identity)
|
||||
Ok((gateway_identity, id))
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ pub mod test_helpers {
|
||||
pub owner: MessageInfo,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl TestSetup {
|
||||
pub fn new() -> Self {
|
||||
let deps = init_contract();
|
||||
@@ -337,6 +338,12 @@ pub mod test_helpers {
|
||||
interval_storage::current_interval(self.deps().storage).unwrap()
|
||||
}
|
||||
|
||||
pub fn current_epoch_state(&self) -> EpochState {
|
||||
interval_storage::current_epoch_status(self.deps().storage)
|
||||
.unwrap()
|
||||
.state
|
||||
}
|
||||
|
||||
pub fn active_roles_bucket(&self) -> RoleStorageBucket {
|
||||
ACTIVE_ROLES_BUCKET.load(self.deps().storage).unwrap()
|
||||
}
|
||||
@@ -395,52 +402,54 @@ pub mod test_helpers {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn immediately_assign_lowest_mix_layer(&mut self, node_id: NodeId) -> Role {
|
||||
let active_bucket = ACTIVE_ROLES_BUCKET.load(&self.deps.storage).unwrap();
|
||||
|
||||
let mut layer1 = read_assigned_roles(&self.deps.storage, Role::Layer1).unwrap();
|
||||
let mut layer2 = read_assigned_roles(&self.deps.storage, Role::Layer2).unwrap();
|
||||
let mut layer3 = read_assigned_roles(&self.deps.storage, Role::Layer3).unwrap();
|
||||
pub fn lowest_mix_layer(&mut self) -> Role {
|
||||
let layer1 = read_assigned_roles(&self.deps.storage, Role::Layer1).unwrap();
|
||||
let layer2 = read_assigned_roles(&self.deps.storage, Role::Layer2).unwrap();
|
||||
let layer3 = read_assigned_roles(&self.deps.storage, Role::Layer3).unwrap();
|
||||
let l1 = layer1.len();
|
||||
let l2 = layer2.len();
|
||||
let l3 = layer3.len();
|
||||
|
||||
if l1 <= l2 && l1 <= l3 {
|
||||
layer1.push(node_id);
|
||||
ROLES
|
||||
.save(
|
||||
&mut self.deps.storage,
|
||||
(active_bucket as u8, Role::Layer1),
|
||||
&layer1,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Role::Layer1
|
||||
} else if l2 <= l3 && l2 <= l1 {
|
||||
layer2.push(node_id);
|
||||
ROLES
|
||||
.save(
|
||||
&mut self.deps.storage,
|
||||
(active_bucket as u8, Role::Layer2),
|
||||
&layer2,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Role::Layer2
|
||||
} else {
|
||||
layer3.push(node_id);
|
||||
ROLES
|
||||
.save(
|
||||
&mut self.deps.storage,
|
||||
(active_bucket as u8, Role::Layer3),
|
||||
&layer3,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Role::Layer3
|
||||
}
|
||||
}
|
||||
|
||||
pub fn immediately_assign_lowest_mix_layer(&mut self, node_id: NodeId) -> Role {
|
||||
let layer = self.lowest_mix_layer();
|
||||
self.immediately_add_to_role(node_id, layer);
|
||||
layer
|
||||
}
|
||||
|
||||
pub fn immediately_add_to_role(&mut self, node_id: NodeId, role: Role) {
|
||||
let active_bucket = ACTIVE_ROLES_BUCKET.load(&self.deps.storage).unwrap();
|
||||
let mut current = read_assigned_roles(self.deps().storage, role).unwrap();
|
||||
current.push(node_id);
|
||||
ROLES
|
||||
.save(
|
||||
&mut self.deps.storage,
|
||||
(active_bucket as u8, role),
|
||||
¤t,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn immediately_assign_standby_role(&mut self, node_id: NodeId) {
|
||||
self.immediately_add_to_role(node_id, Role::Standby)
|
||||
}
|
||||
|
||||
pub fn immediately_assign_exit_gateway_role(&mut self, node_id: NodeId) {
|
||||
self.immediately_add_to_role(node_id, Role::ExitGateway)
|
||||
}
|
||||
|
||||
pub fn immediately_assign_entry_gateway_role(&mut self, node_id: NodeId) {
|
||||
self.immediately_add_to_role(node_id, Role::EntryGateway)
|
||||
}
|
||||
|
||||
pub fn add_rewarded_set_nymnode(
|
||||
&mut self,
|
||||
owner: &str,
|
||||
@@ -513,7 +522,7 @@ pub mod test_helpers {
|
||||
ensure_no_existing_bond(&info.sender, &self.deps.storage).unwrap();
|
||||
signing_storage::increment_signing_nonce(&mut self.deps.storage, info.sender.clone())
|
||||
.unwrap();
|
||||
let node_id = legacy::save_new_mixnode(
|
||||
legacy::save_new_mixnode(
|
||||
&mut self.deps.storage,
|
||||
env,
|
||||
mixnode,
|
||||
@@ -521,8 +530,38 @@ pub mod test_helpers {
|
||||
info.sender,
|
||||
info.funds[0].clone(),
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn add_rewarded_mixing_node(&mut self, owner: &str, stake: Option<Uint128>) -> NodeId {
|
||||
let node_id = self.add_dummy_nymnode(owner, stake);
|
||||
self.immediately_assign_lowest_mix_layer(node_id);
|
||||
node_id
|
||||
}
|
||||
|
||||
pub fn add_rewarded_entry_gateway_node(
|
||||
&mut self,
|
||||
owner: &str,
|
||||
stake: Option<Uint128>,
|
||||
) -> NodeId {
|
||||
let node_id = self.add_dummy_nymnode(owner, stake);
|
||||
self.immediately_assign_entry_gateway_role(node_id);
|
||||
node_id
|
||||
}
|
||||
|
||||
pub fn add_rewarded_exit_gateway_node(
|
||||
&mut self,
|
||||
owner: &str,
|
||||
stake: Option<Uint128>,
|
||||
) -> NodeId {
|
||||
let node_id = self.add_dummy_nymnode(owner, stake);
|
||||
self.immediately_assign_exit_gateway_role(node_id);
|
||||
node_id
|
||||
}
|
||||
|
||||
pub fn add_standby_node(&mut self, owner: &str, stake: Option<Uint128>) -> NodeId {
|
||||
let node_id = self.add_dummy_nymnode(owner, stake);
|
||||
self.immediately_assign_standby_role(node_id);
|
||||
node_id
|
||||
}
|
||||
|
||||
@@ -537,7 +576,11 @@ pub mod test_helpers {
|
||||
node_id
|
||||
}
|
||||
|
||||
pub fn add_legacy_gateway(&mut self, sender: &str, stake: Option<Uint128>) -> IdentityKey {
|
||||
pub fn add_legacy_gateway(
|
||||
&mut self,
|
||||
sender: &str,
|
||||
stake: Option<Uint128>,
|
||||
) -> (IdentityKey, NodeId) {
|
||||
let stake = self.make_gateway_pledge(stake);
|
||||
let (gateway, _) = self.gateway_with_signature(sender, Some(stake.clone()));
|
||||
|
||||
@@ -929,7 +972,7 @@ pub mod test_helpers {
|
||||
pub fn reset_role_assignment(&mut self) {
|
||||
let active_bucket = ACTIVE_ROLES_BUCKET.load(&self.deps.storage).unwrap();
|
||||
|
||||
for role in vec![
|
||||
for role in [
|
||||
Role::EntryGateway,
|
||||
Role::ExitGateway,
|
||||
Role::Layer1,
|
||||
@@ -943,32 +986,35 @@ pub mod test_helpers {
|
||||
}
|
||||
}
|
||||
|
||||
// note: this does NOT assign gateway role
|
||||
pub fn force_change_rewarded_set(&mut self, nodes: Vec<NodeId>) {
|
||||
// reset current assignment
|
||||
pub fn force_assign_rewarded_set(&mut self, assignment: Vec<RoleAssignment>) {
|
||||
self.reset_role_assignment();
|
||||
let mut roles = HashMap::new();
|
||||
for node in nodes {
|
||||
let role = self.immediately_assign_lowest_mix_layer(node);
|
||||
let assigned = roles.entry(role).or_insert(Vec::new());
|
||||
assigned.push(node)
|
||||
}
|
||||
|
||||
// we cheat a bit to write to the 'active' bucket instead
|
||||
swap_active_role_bucket(self.deps_mut().storage).unwrap();
|
||||
for role_assignment in assignment {
|
||||
let mut sorted_assignment = role_assignment.clone();
|
||||
sorted_assignment.nodes.sort();
|
||||
|
||||
for (role, assignment) in roles {
|
||||
save_assignment(
|
||||
self.deps_mut().storage,
|
||||
RoleAssignment {
|
||||
role,
|
||||
nodes: assignment,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
save_assignment(self.deps_mut().storage, sorted_assignment).unwrap();
|
||||
}
|
||||
swap_active_role_bucket(self.deps_mut().storage).unwrap();
|
||||
}
|
||||
|
||||
// note: this does NOT assign gateway role
|
||||
pub fn force_change_mix_rewarded_set(&mut self, nodes: Vec<NodeId>) {
|
||||
let mut roles = HashMap::new();
|
||||
for node in nodes {
|
||||
let layer = self.lowest_mix_layer();
|
||||
let assigned = roles.entry(layer).or_insert(Vec::new());
|
||||
assigned.push(node)
|
||||
}
|
||||
|
||||
swap_active_role_bucket(self.deps_mut().storage).unwrap();
|
||||
let roles = roles
|
||||
.into_iter()
|
||||
.map(|(role, nodes)| RoleAssignment { role, nodes })
|
||||
.collect();
|
||||
|
||||
self.force_assign_rewarded_set(roles)
|
||||
}
|
||||
|
||||
pub fn instantiate_simulator(&self, node: NodeId) -> Simulator {
|
||||
@@ -1266,6 +1312,18 @@ pub mod test_helpers {
|
||||
E: Into<Option<S>>,
|
||||
S: Into<String>;
|
||||
|
||||
fn any_attribute(&self, attribute: &str) -> String {
|
||||
self.attribute::<_, String>(None, attribute)
|
||||
}
|
||||
|
||||
fn any_parsed_attribute<T>(&self, attribute: &str) -> T
|
||||
where
|
||||
T: FromStr,
|
||||
<T as FromStr>::Err: Debug,
|
||||
{
|
||||
self.parsed_attribute::<_, String, T>(None, attribute)
|
||||
}
|
||||
|
||||
fn parsed_attribute<E, S, T>(&self, event_type: E, attribute: &str) -> T
|
||||
where
|
||||
E: Into<Option<S>>,
|
||||
|
||||
Reference in New Issue
Block a user