additional rewarding-related unit tests

This commit is contained in:
Jędrzej Stuczyński
2024-10-10 13:24:11 +01:00
parent 050a58affa
commit f76d677f64
15 changed files with 565 additions and 207 deletions
@@ -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()
}
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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)
+16 -15
View File
@@ -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();
+2 -2
View File
@@ -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();
+21 -21
View File
@@ -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);
+364 -69
View File
@@ -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 {
+1 -34
View File
@@ -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,
+2 -2
View File
@@ -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))
}
+114 -56
View File
@@ -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),
&current,
)
.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>>,