additional unit tests for compatibility-based txs + bug fixes

This commit is contained in:
Jędrzej Stuczyński
2024-09-23 11:53:48 +01:00
parent 5c08a89be3
commit 9a9d91cb4f
11 changed files with 1255 additions and 242 deletions
@@ -886,50 +886,54 @@ mod tests {
MixnetQueryMsg::GetSigningNonce { address } => {
client.get_signing_nonce(&address.parse().unwrap()).ignore()
}
QueryMsg::GetPreassignedGatewayIds { start_after, limit } => client
MixnetQueryMsg::GetPreassignedGatewayIds { start_after, limit } => client
.get_preassigned_gateway_ids_paged(start_after, limit)
.ignore(),
QueryMsg::GetNymNodeBondsPaged { limit, start_after } => {
MixnetQueryMsg::GetNymNodeBondsPaged { limit, start_after } => {
client.get_nymnode_bonds_paged(limit, start_after).ignore()
}
QueryMsg::GetNymNodesDetailedPaged { limit, start_after } => client
MixnetQueryMsg::GetNymNodesDetailedPaged { limit, start_after } => client
.get_nymnodes_detailed_paged(limit, start_after)
.ignore(),
QueryMsg::GetUnbondedNymNode { node_id } => {
MixnetQueryMsg::GetUnbondedNymNode { node_id } => {
client.get_unbonded_nymnode_information(node_id).ignore()
}
QueryMsg::GetUnbondedNymNodesPaged { limit, start_after } => client
MixnetQueryMsg::GetUnbondedNymNodesPaged { limit, start_after } => client
.get_unbonded_nymnodes_paged(limit, start_after)
.ignore(),
QueryMsg::GetUnbondedNymNodesByOwnerPaged {
MixnetQueryMsg::GetUnbondedNymNodesByOwnerPaged {
owner,
limit,
start_after,
} => client
.get_unbonded_nymnodes_by_owner_paged(&owner.parse().unwrap(), limit, start_after)
.ignore(),
QueryMsg::GetUnbondedNymNodesByIdentityKeyPaged {
MixnetQueryMsg::GetUnbondedNymNodesByIdentityKeyPaged {
identity_key,
limit,
start_after,
} => client
.get_unbonded_nymnodes_by_identity_paged(&identity_key, limit, start_after)
.ignore(),
QueryMsg::GetOwnedNymNode { address } => {
MixnetQueryMsg::GetOwnedNymNode { address } => {
client.get_owned_nymnode(&address.parse().unwrap()).ignore()
}
QueryMsg::GetNymNodeDetails { node_id } => client.get_nymnode_details(node_id).ignore(),
QueryMsg::GetNymNodeDetailsByIdentityKey { node_identity } => client
MixnetQueryMsg::GetNymNodeDetails { node_id } => {
client.get_nymnode_details(node_id).ignore()
}
MixnetQueryMsg::GetNymNodeDetailsByIdentityKey { node_identity } => client
.get_nymnode_details_by_identity(node_identity)
.ignore(),
QueryMsg::GetNodeRewardingDetails { node_id } => {
MixnetQueryMsg::GetNodeRewardingDetails { node_id } => {
client.get_nymnode_rewarding_details(node_id).ignore()
}
QueryMsg::GetNodeStakeSaturation { node_id } => {
MixnetQueryMsg::GetNodeStakeSaturation { node_id } => {
client.get_node_stake_saturation(node_id).ignore()
}
QueryMsg::GetRoleAssignment { role } => client.get_role_assignment(role).ignore(),
QueryMsg::GetRewardedSetMetadata {} => client.get_rewarded_set_metadata().ignore(),
MixnetQueryMsg::GetRoleAssignment { role } => client.get_role_assignment(role).ignore(),
MixnetQueryMsg::GetRewardedSetMetadata {} => {
client.get_rewarded_set_metadata().ignore()
}
}
}
}
@@ -672,6 +672,7 @@ pub enum QueryMsg {
// delegation-related:
/// Gets all delegations associated with particular node
#[cfg_attr(feature = "schema", returns(PagedNodeDelegationsResponse))]
#[serde(alias = "GetMixnodeDelegations", alias = "get_mixnode_delegations")]
GetNodeDelegations {
/// Id of the node to query.
#[serde(alias = "mix_id")]
@@ -166,6 +166,10 @@ impl<T> RangedValue<T>
where
T: Copy + PartialOrd + PartialEq,
{
pub fn new(minimum: T, maximum: T) -> Self {
RangedValue { minimum, maximum }
}
pub fn normalise(&self, value: T) -> T {
if value < self.minimum {
self.minimum
+433
View File
@@ -142,3 +142,436 @@ pub fn may_get_bond(
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::bail;
#[cfg(test)]
mod reward_withdrawing_permission {
use super::*;
use crate::support::tests::test_helpers::TestSetup;
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", None);
let details = test.mixnode_by_id(node_id).unwrap();
// node must not be in the process of unbonding
assert!(ensure_can_withdraw_rewards(&details).is_ok());
test.start_unbonding_mixnode(node_id);
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_withdraw_rewards(&details).unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", None);
let details = test.nymnode_by_id(node_id).unwrap();
// node must not be in the process of unbonding
assert!(ensure_can_withdraw_rewards(&details).is_ok());
test.start_unbonding_nymnode(node_id);
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_withdraw_rewards(&details).unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
Ok(())
}
}
#[cfg(test)]
mod modifying_cost_params_permission {
use super::*;
use crate::support::tests::test_helpers::TestSetup;
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", None);
let details = test.mixnode_by_id(node_id).unwrap();
assert!(ensure_can_modify_cost_params(test.deps().storage, &details).is_ok());
// epoch must not be mid-transition
test.skip_to_current_epoch_end();
test.start_epoch_transition();
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_modify_cost_params(test.deps().storage, &details).unwrap_err();
assert!(matches!(
res,
MixnetContractError::EpochAdvancementInProgress { .. }
));
test.set_epoch_in_progress_state();
// node must not be in the process of unbonding
test.start_unbonding_mixnode(node_id);
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_modify_cost_params(test.deps().storage, &details).unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", None);
let details = test.nymnode_by_id(node_id).unwrap();
assert!(ensure_can_modify_cost_params(test.deps().storage, &details).is_ok());
// epoch must not be mid-transition
test.skip_to_current_epoch_end();
test.start_epoch_transition();
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_modify_cost_params(test.deps().storage, &details).unwrap_err();
assert!(matches!(
res,
MixnetContractError::EpochAdvancementInProgress { .. }
));
test.set_epoch_in_progress_state();
// node must not be in the process of unbonding
test.start_unbonding_nymnode(node_id);
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_modify_cost_params(test.deps().storage, &details).unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
Ok(())
}
}
#[cfg(test)]
mod increasing_pledge_permission {
use super::*;
use crate::compat::transactions::{try_decrease_pledge, try_increase_pledge};
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", None);
let details = test.mixnode_by_id(node_id).unwrap();
assert!(ensure_can_increase_pledge(test.deps().storage, &details).is_ok());
// epoch must not be mid-transition
test.skip_to_current_epoch_end();
test.start_epoch_transition();
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert!(matches!(
res,
MixnetContractError::EpochAdvancementInProgress { .. }
));
test.set_epoch_in_progress_state();
// node must not be in the process of unbonding
test.start_unbonding_mixnode(node_id);
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
// node can't have any pending pledge changes:
// - increase
let node_id = test.add_legacy_mixnode("owner2", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?;
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
// - decrease
let node_id = test.add_legacy_mixnode("owner3", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
let env = test.env();
try_decrease_pledge(
test.deps_mut(),
env,
mock_info("owner3", &[]),
pledge_change,
)?;
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", None);
let details = test.nymnode_by_id(node_id).unwrap();
assert!(ensure_can_increase_pledge(test.deps().storage, &details).is_ok());
// epoch must not be mid-transition
test.skip_to_current_epoch_end();
test.start_epoch_transition();
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert!(matches!(
res,
MixnetContractError::EpochAdvancementInProgress { .. }
));
test.set_epoch_in_progress_state();
// node must not be in the process of unbonding
test.start_unbonding_nymnode(node_id);
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
// node can't have any pending pledge changes:
// - increase
let node_id = test.add_dummy_nymnode("owner2", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?;
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
// - decrease
let node_id = test.add_dummy_nymnode("owner3", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
let env = test.env();
try_decrease_pledge(
test.deps_mut(),
env,
mock_info("owner3", &[]),
pledge_change,
)?;
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_increase_pledge(test.deps().storage, &details).unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
Ok(())
}
}
#[cfg(test)]
mod decreasing_pledge_permission {
use super::*;
use crate::compat::transactions::{try_decrease_pledge, try_increase_pledge};
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_info;
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", Some(100_000_000_000u128.into()));
let valid_decrease = test.coin(100);
let details = test.mixnode_by_id(node_id).unwrap();
assert!(
ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease).is_ok()
);
// epoch must not be mid-transition
test.skip_to_current_epoch_end();
test.start_epoch_transition();
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert!(matches!(
res,
MixnetContractError::EpochAdvancementInProgress { .. }
));
test.set_epoch_in_progress_state();
// node must not be in the process of unbonding
test.start_unbonding_mixnode(node_id);
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
// node can't have any pending pledge changes:
// - increase
let node_id = test.add_legacy_mixnode("owner2", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?;
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
// - decrease
let node_id = test.add_legacy_mixnode("owner3", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
let env = test.env();
try_decrease_pledge(
test.deps_mut(),
env,
mock_info("owner3", &[]),
pledge_change,
)?;
let details = test.mixnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
// denom must match
let node_id = test.add_legacy_mixnode("owner4", Some(100_000_000_000u128.into()));
let details = test.mixnode_by_id(node_id).unwrap();
let bad_decrease = coin(123, "weird-denom");
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease)
.unwrap_err();
assert!(matches!(res, MixnetContractError::WrongDenom { .. }));
// value must be non-zero
let node_id = test.add_legacy_mixnode("owner5", Some(100_000_000_000u128.into()));
let details = test.mixnode_by_id(node_id).unwrap();
let bad_decrease = test.coin(0);
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease)
.unwrap_err();
assert_eq!(res, MixnetContractError::ZeroCoinAmount);
// new pledge must be bigger than minimum
let node_id = test.add_legacy_mixnode("owner6", Some(100_000_100u128.into()));
let details = test.mixnode_by_id(node_id).unwrap();
let bad_decrease = test.coin(101);
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease)
.unwrap_err();
assert!(matches!(
res,
MixnetContractError::InvalidPledgeReduction { .. }
));
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", Some(100_000_000_000u128.into()));
let valid_decrease = test.coin(100);
let details = test.nymnode_by_id(node_id).unwrap();
assert!(
ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease).is_ok()
);
// epoch must not be mid-transition
test.skip_to_current_epoch_end();
test.start_epoch_transition();
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert!(matches!(
res,
MixnetContractError::EpochAdvancementInProgress { .. }
));
test.set_epoch_in_progress_state();
// node must not be in the process of unbonding
test.start_unbonding_nymnode(node_id);
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert_eq!(res, MixnetContractError::NodeIsUnbonding { node_id });
// node can't have any pending pledge changes:
// - increase
let node_id = test.add_dummy_nymnode("owner2", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
test.execute_fn(try_increase_pledge, mock_info("owner2", &[pledge_change]))?;
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
// - decrease
let node_id = test.add_dummy_nymnode("owner3", Some(100_000_000_000u128.into()));
let pledge_change = test.coin(100000);
let env = test.env();
try_decrease_pledge(
test.deps_mut(),
env,
mock_info("owner3", &[]),
pledge_change,
)?;
let details = test.nymnode_by_id(node_id).unwrap();
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &valid_decrease)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::PendingPledgeChange {
pending_event_id: details.pending_changes.pledge_change.unwrap()
}
);
// denom must match
let node_id = test.add_dummy_nymnode("owner4", Some(100_000_000_000u128.into()));
let details = test.nymnode_by_id(node_id).unwrap();
let bad_decrease = coin(123, "weird-denom");
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease)
.unwrap_err();
assert!(matches!(res, MixnetContractError::WrongDenom { .. }));
// value must be non-zero
let node_id = test.add_dummy_nymnode("owner5", Some(100_000_000_000u128.into()));
let details = test.nymnode_by_id(node_id).unwrap();
let bad_decrease = test.coin(0);
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease)
.unwrap_err();
assert_eq!(res, MixnetContractError::ZeroCoinAmount);
// new pledge must be bigger than minimum
let node_id = test.add_dummy_nymnode("owner6", Some(100_000_100u128.into()));
let details = test.nymnode_by_id(node_id).unwrap();
let bad_decrease = test.coin(101);
let res = ensure_can_decrease_pledge(test.deps().storage, &details, &bad_decrease)
.unwrap_err();
assert!(matches!(
res,
MixnetContractError::InvalidPledgeReduction { .. }
));
Ok(())
}
}
}
+568
View File
@@ -93,3 +93,571 @@ pub(crate) fn try_withdraw_operator_reward(
Err(MixnetContractError::NoAssociatedNodeBond { owner: info.sender })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(test)]
mod increasing_pledge {
use super::*;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Addr;
#[test]
fn when_there_are_no_nodes() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let sender = mock_info("owner", &[test.coin(100000)]);
let err = test.execute_fn(try_increase_pledge, sender).unwrap_err();
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", Some(100_000_000u128.into()));
let sender = mock_info("owner", &[test.coin(100_000)]);
test.assert_simple_execution(try_increase_pledge, sender);
let after = test.mixnode_by_id(node_id).unwrap();
let event_id = after.pending_changes.pledge_change.unwrap();
assert_eq!(event_id, 1);
test.execute_all_pending_events();
let after = test.mixnode_by_id(node_id).unwrap();
assert_eq!(
after.bond_information.original_pledge.amount.u128(),
100_100_000u128
);
Ok(())
}
#[test]
fn for_legacy_gateway() -> anyhow::Result<()> {
let mut test = TestSetup::new();
test.add_legacy_gateway("owner", None);
let sender = mock_info("owner", &[test.coin(100000)]);
let err = test.execute_fn(try_increase_pledge, sender).unwrap_err();
// it's illegal to increase pledge for legacy gateways
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", Some(100_000_000u128.into()));
let sender = mock_info("owner", &[test.coin(100_000)]);
test.assert_simple_execution(try_increase_pledge, sender);
let after = test.nymnode_by_id(node_id).unwrap();
let event_id = after.pending_changes.pledge_change.unwrap();
assert_eq!(event_id, 1);
test.execute_all_pending_events();
let after = test.nymnode_by_id(node_id).unwrap();
assert_eq!(
after.bond_information.original_pledge.amount.u128(),
100_100_000u128
);
Ok(())
}
}
#[cfg(test)]
mod decreasing_pledge {
use super::*;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Addr;
#[test]
fn when_there_are_no_nodes() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let sender = mock_info("owner", &[]);
let env = test.env();
let decrease_by = test.coin(1000);
let err = try_decrease_pledge(test.deps_mut(), env, sender, decrease_by).unwrap_err();
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", Some(120_000_000u128.into()));
let sender = mock_info("owner", &[]);
let env = test.env();
let decrease_by = test.coin(1000);
try_decrease_pledge(test.deps_mut(), env, sender, decrease_by)?;
let after = test.mixnode_by_id(node_id).unwrap();
let event_id = after.pending_changes.pledge_change.unwrap();
assert_eq!(event_id, 1);
test.execute_all_pending_events();
let after = test.mixnode_by_id(node_id).unwrap();
assert_eq!(
after.bond_information.original_pledge.amount.u128(),
119_999_000u128
);
Ok(())
}
#[test]
fn for_legacy_gateway() -> anyhow::Result<()> {
let mut test = TestSetup::new();
test.add_legacy_gateway("owner", None);
let sender = mock_info("owner", &[]);
let env = test.env();
let decrease_by = test.coin(1000);
let err = try_decrease_pledge(test.deps_mut(), env, sender, decrease_by).unwrap_err();
// it's illegal to decrease pledge for legacy gateways
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", Some(120_000_000u128.into()));
let sender = mock_info("owner", &[]);
let env = test.env();
let decrease_by = test.coin(1000);
try_decrease_pledge(test.deps_mut(), env, sender, decrease_by)?;
let after = test.nymnode_by_id(node_id).unwrap();
let event_id = after.pending_changes.pledge_change.unwrap();
assert_eq!(event_id, 1);
test.execute_all_pending_events();
let after = test.nymnode_by_id(node_id).unwrap();
assert_eq!(
after.bond_information.original_pledge.amount.u128(),
119_999_000u128
);
Ok(())
}
}
#[cfg(test)]
mod updating_cost_params {
use super::*;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Addr, Uint128};
use mixnet_contract_common::{OperatingCostRange, ProfitMarginRange};
use nym_contracts_common::Percent;
fn new_dummy_params() -> NodeCostParams {
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(69).unwrap(),
interval_operating_cost: Coin {
denom: TEST_COIN_DENOM.to_string(),
amount: 123456789u128.into(),
},
}
}
#[test]
fn profit_margin_must_be_within_range() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let minimum = Percent::from_percentage_value(10)?;
let maximum = Percent::from_percentage_value(80)?;
let range = ProfitMarginRange::new(minimum, maximum);
test.update_profit_margin_range(range);
// below lower
test.add_dummy_nymnode("owner1", None);
let sender = mock_info("owner1", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.profit_margin_percent = Percent::from_percentage_value(9)?;
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::ProfitMarginOutsideRange { .. }
));
// zero
test.add_dummy_nymnode("owner2", None);
let sender = mock_info("owner2", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.profit_margin_percent = Percent::zero();
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::ProfitMarginOutsideRange { .. }
));
// exactly at lower
test.add_dummy_nymnode("owner3", None);
let sender = mock_info("owner3", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.profit_margin_percent = minimum;
let res = try_update_cost_params(test.deps_mut(), env, sender, update.clone());
assert!(res.is_ok());
// above upper
test.add_dummy_nymnode("owner4", None);
let sender = mock_info("owner4", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.profit_margin_percent = Percent::from_percentage_value(81)?;
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::ProfitMarginOutsideRange { .. }
));
// hundred
test.add_dummy_nymnode("owner5", None);
let sender = mock_info("owner5", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.profit_margin_percent = Percent::hundred();
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::ProfitMarginOutsideRange { .. }
));
// exactly at upper
test.add_dummy_nymnode("owner6", None);
let sender = mock_info("owner6", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.profit_margin_percent = maximum;
let res = try_update_cost_params(test.deps_mut(), env, sender, update.clone());
assert!(res.is_ok());
Ok(())
}
#[test]
fn operating_cost_must_be_within_range() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let minimum = Uint128::new(1000);
let maximum = Uint128::new(100_000_000);
let range = OperatingCostRange::new(minimum, maximum);
test.update_operating_cost_range(range);
// below lower
test.add_dummy_nymnode("owner1", None);
let sender = mock_info("owner1", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.interval_operating_cost = test.coin(999);
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::OperatingCostOutsideRange { .. }
));
// zero
test.add_dummy_nymnode("owner2", None);
let sender = mock_info("owner2", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.interval_operating_cost = test.coin(0);
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::OperatingCostOutsideRange { .. }
));
// exactly at lower
test.add_dummy_nymnode("owner3", None);
let sender = mock_info("owner3", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.interval_operating_cost = test.coin(minimum.u128());
let res = try_update_cost_params(test.deps_mut(), env, sender, update.clone());
assert!(res.is_ok());
// above upper
test.add_dummy_nymnode("owner4", None);
let sender = mock_info("owner4", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.interval_operating_cost = test.coin(100_000_001);
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::OperatingCostOutsideRange { .. }
));
// max
test.add_dummy_nymnode("owner5", None);
let sender = mock_info("owner5", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.interval_operating_cost = test.coin(u128::MAX);
let err =
try_update_cost_params(test.deps_mut(), env, sender, update.clone()).unwrap_err();
assert!(matches!(
err,
MixnetContractError::OperatingCostOutsideRange { .. }
));
// exactly at upper
test.add_dummy_nymnode("owner6", None);
let sender = mock_info("owner6", &[]);
let env = test.env();
let mut update = new_dummy_params();
update.interval_operating_cost = test.coin(100_000_000);
let res = try_update_cost_params(test.deps_mut(), env, sender, update.clone());
assert!(res.is_ok());
Ok(())
}
#[test]
fn when_there_are_no_nodes() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let sender = mock_info("owner", &[]);
let env = test.env();
let err = try_update_cost_params(test.deps_mut(), env, sender, new_dummy_params())
.unwrap_err();
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_legacy_mixnode("owner", None);
let sender = mock_info("owner", &[]);
let env = test.env();
let update = new_dummy_params();
try_update_cost_params(test.deps_mut(), env, sender, update.clone())?;
let after = test.mixnode_by_id(node_id).unwrap();
let event_id = after.pending_changes.cost_params_change.unwrap();
assert_eq!(event_id, 1);
test.execute_all_pending_events();
let after = test.mixnode_by_id(node_id).unwrap();
assert_eq!(update, after.rewarding_details.cost_params);
Ok(())
}
#[test]
fn for_legacy_gateway() -> anyhow::Result<()> {
let mut test = TestSetup::new();
test.add_legacy_gateway("owner", None);
let sender = mock_info("owner", &[]);
let env = test.env();
let err = try_update_cost_params(test.deps_mut(), env, sender, new_dummy_params())
.unwrap_err();
// it's illegal to update cost parameters for legacy gateways
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let node_id = test.add_dummy_nymnode("owner", None);
let sender = mock_info("owner", &[]);
let env = test.env();
let update = new_dummy_params();
try_update_cost_params(test.deps_mut(), env, sender, update.clone())?;
let after = test.nymnode_by_id(node_id).unwrap();
let event_id = after.pending_changes.cost_params_change.unwrap();
assert_eq!(event_id, 1);
test.execute_all_pending_events();
let after = test.nymnode_by_id(node_id).unwrap();
assert_eq!(update, after.rewarding_details.cost_params);
Ok(())
}
}
#[cfg(test)]
mod withdrawing_operator_reward {
use super::*;
use crate::support::tests::test_helpers;
use crate::support::tests::test_helpers::{ExtractBankMsg, TestSetup};
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Addr;
#[test]
fn when_there_are_no_nodes() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let sender = mock_info("owner", &[]);
let err = try_withdraw_operator_reward(test.deps_mut(), sender).unwrap_err();
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_legacy_mixnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
// no rewards
test.add_legacy_mixnode("owner1", None);
let sender = mock_info("owner1", &[]);
let res = try_withdraw_operator_reward(test.deps_mut(), sender)?;
let maybe_bank = res.unwrap_bank_msg();
assert!(maybe_bank.is_none());
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.start_epoch_transition();
test.legacy_reward_with_distribution_and_legacy_work_factor(
node_id,
test_helpers::performance(100.),
);
let res = try_withdraw_operator_reward(test.deps_mut(), sender)?;
let maybe_bank = res.unwrap_bank_msg();
assert!(maybe_bank.is_some());
Ok(())
}
#[test]
fn for_legacy_gateway() -> anyhow::Result<()> {
let mut test = TestSetup::new();
test.add_legacy_gateway("owner", None);
let sender = mock_info("owner", &[]);
let err = try_withdraw_operator_reward(test.deps_mut(), sender).unwrap_err();
// no rewards for legacy gateways...
assert_eq!(
MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("owner"),
},
err
);
Ok(())
}
#[test]
fn for_nymnode() -> anyhow::Result<()> {
let mut test = TestSetup::new();
// no rewards
test.add_dummy_nymnode("owner1", None);
let sender = mock_info("owner1", &[]);
let res = try_withdraw_operator_reward(test.deps_mut(), sender)?;
let maybe_bank = res.unwrap_bank_msg();
assert!(maybe_bank.is_none());
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.start_epoch_transition();
test.legacy_reward_with_distribution_and_legacy_work_factor(
node_id,
test_helpers::performance(100.),
);
let res = try_withdraw_operator_reward(test.deps_mut(), sender)?;
let maybe_bank = res.unwrap_bank_msg();
assert!(maybe_bank.is_some());
Ok(())
}
}
}
+18 -6
View File
@@ -143,7 +143,7 @@ pub mod tests {
use crate::gateways::queries;
use crate::interval::pending_events;
use crate::mixnet_contract_settings::storage::minimum_node_pledge;
use crate::nodes::helpers::get_node_details_by_identity;
use crate::nodes::helpers::{get_node_details_by_identity, must_get_node_bond_by_owner};
use crate::signing::storage as signing_storage;
use crate::support::tests;
use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge};
@@ -153,7 +153,7 @@ pub mod tests {
use mixnet_contract_common::ExecuteMsg;
#[test]
fn gateway_add() {
fn gateway_add() -> anyhow::Result<()> {
let mut test = TestSetup::new();
// if we fail validation (by say not sending enough funds
@@ -218,10 +218,22 @@ pub mod tests {
assert!(result.is_ok());
// and the node has been added as a nym-node
let nym_node = get_node_details_by_identity(test.deps().storage, gateway.identity_key)
.unwrap()
.unwrap();
assert_eq!(nym_node.bond_information.owner, info.sender)
let nym_node =
get_node_details_by_identity(test.deps().storage, gateway.identity_key.clone())
.unwrap()
.unwrap();
assert_eq!(nym_node.bond_information.owner, info.sender);
let maybe_legacy =
storage::gateways().may_load(test.deps().storage, &gateway.identity_key)?;
assert!(maybe_legacy.is_none());
// make sure we got assigned the next id (note: we have already bonded a mixnode and a gateway before in this test)
let bond =
must_get_node_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap();
assert_eq!(3, bond.node_id);
Ok(())
}
#[test]
@@ -34,7 +34,7 @@ pub(crate) fn profit_margin_range(
.map(|state| state.params.profit_margin)?)
}
pub(crate) fn interval_oprating_cost_range(
pub(crate) fn interval_operating_cost_range(
storage: &dyn Storage,
) -> Result<OperatingCostRange, MixnetContractError> {
Ok(CONTRACT_STATE
+132 -207
View File
@@ -257,12 +257,10 @@ pub fn try_migrate_to_nymnode(
#[cfg(test)]
pub mod tests {
use super::*;
use crate::compat::transactions::{
try_decrease_pledge, try_increase_pledge, try_update_cost_params,
};
use crate::compat::transactions::try_increase_pledge;
use crate::contract::execute;
use crate::mixnet_contract_settings::storage::minimum_node_pledge;
use crate::mixnodes::helpers::get_mixnode_details_by_id;
use crate::mixnodes::helpers::{get_mixnode_details_by_id, get_mixnode_details_by_identity};
use crate::nodes::helpers::{get_node_details_by_identity, must_get_node_bond_by_owner};
use crate::signing::storage as signing_storage;
use crate::support::tests::fixtures::{good_mixnode_pledge, TEST_COIN_DENOM};
@@ -275,7 +273,7 @@ pub mod tests {
use mixnet_contract_common::{EpochState, EpochStatus, ExecuteMsg, Percent};
#[test]
fn mixnode_add() {
fn mixnode_add() -> anyhow::Result<()> {
let mut test = TestSetup::new();
let env = test.env();
@@ -357,15 +355,22 @@ pub mod tests {
assert!(result.is_ok());
// and the node has been added as a nym-node
let nym_node = get_node_details_by_identity(test.deps().storage, mixnode.identity_key)
.unwrap()
.unwrap();
let nym_node =
get_node_details_by_identity(test.deps().storage, mixnode.identity_key.clone())
.unwrap()
.unwrap();
assert_eq!(nym_node.bond_information.owner, info.sender);
let maybe_legacy =
get_mixnode_details_by_identity(test.deps().storage, mixnode.identity_key)?;
assert!(maybe_legacy.is_none());
// make sure we got assigned the next id (note: we have already bonded a mixnode and a gateway before in this test)
let bond =
must_get_node_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap();
assert_eq!(3, bond.node_id);
Ok(())
}
#[test]
@@ -524,7 +529,9 @@ pub mod tests {
// prior increase
let owner = "mix-owner1";
test.add_legacy_mixnode(owner, None);
let node_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(node_id).unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
try_increase_pledge(test.deps_mut(), env.clone(), sender.clone()).unwrap();
@@ -538,10 +545,10 @@ pub mod tests {
// prior decrease
let owner = "mix-owner2";
test.add_legacy_mixnode(owner, Some(Uint128::new(10000000000)));
let sender = mock_info(owner, &[]);
let node_id = test.add_legacy_mixnode(owner, Some(Uint128::new(10000000000)));
let details = test.mixnode_by_id(node_id).unwrap();
let amount = test.coin(1000);
try_decrease_pledge(test.deps_mut(), env.clone(), sender, amount).unwrap();
try_decrease_mixnode_pledge(test.deps_mut(), env.clone(), amount, details).unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
let res = try_remove_mixnode(test.deps_mut(), env.clone(), sender);
@@ -640,15 +647,20 @@ pub mod tests {
let mut test = TestSetup::new();
let env = test.env();
let owner = "alice";
let info = mock_info(owner, &[]);
test.add_legacy_mixnode(owner, None);
let node_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(node_id).unwrap();
let mut status = EpochStatus::new(test.rewarding_validator().sender);
status.state = bad_state;
interval_storage::save_current_epoch_status(test.deps_mut().storage, &status).unwrap();
let res = try_update_cost_params(test.deps_mut(), env.clone(), info, update.clone());
let res = try_update_mixnode_cost_params(
test.deps_mut(),
env.clone(),
update.clone(),
details,
);
assert!(matches!(
res,
Err(MixnetContractError::EpochAdvancementInProgress { .. })
@@ -668,21 +680,16 @@ pub mod tests {
interval_operating_cost: Coin::new(12345678, TEST_COIN_DENOM),
};
// try updating a non-existing mixnode bond
let res =
try_update_cost_params(test.deps_mut(), env.clone(), info.clone(), update.clone());
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked(owner)
})
);
let mix_id = test.add_legacy_mixnode(owner, None);
let node_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(node_id).unwrap();
// "normal" update succeeds
let res =
try_update_cost_params(test.deps_mut(), env.clone(), info.clone(), update.clone());
let res = try_update_mixnode_cost_params(
test.deps_mut(),
env.clone(),
update.clone(),
details.clone(),
);
assert!(res.is_ok());
// see if the event has been pushed onto the queue
@@ -695,7 +702,7 @@ pub mod tests {
assert_eq!(1, event.0);
assert_eq!(
PendingIntervalEventKind::ChangeMixCostParams {
mix_id,
mix_id: node_id,
new_costs: update.clone(),
},
event.1.kind
@@ -705,18 +712,16 @@ pub mod tests {
test_helpers::execute_all_pending_events(test.deps_mut(), env.clone());
// and see if the config has actually been updated
let mix = get_mixnode_details_by_id(test.deps().storage, mix_id)
let mix = get_mixnode_details_by_id(test.deps().storage, node_id)
.unwrap()
.unwrap();
assert_eq!(mix.rewarding_details.cost_params, update);
// but we cannot perform any updates whilst the mixnode is already unbonding
try_remove_mixnode(test.deps_mut(), env.clone(), info.clone()).unwrap();
let res = try_update_cost_params(test.deps_mut(), env, info, update);
assert_eq!(
res,
Err(MixnetContractError::NodeIsUnbonding { node_id: mix_id })
)
let details = test.mixnode_by_id(node_id).unwrap();
let res = try_update_mixnode_cost_params(test.deps_mut(), env, update, details);
assert_eq!(res, Err(MixnetContractError::NodeIsUnbonding { node_id }))
}
#[test]
@@ -796,15 +801,15 @@ pub mod tests {
let env = test.env();
let owner = "mix-owner";
test.add_legacy_mixnode(owner, None);
let mut status = EpochStatus::new(test.rewarding_validator().sender);
status.state = bad_state;
interval_storage::save_current_epoch_status(test.deps_mut().storage, &status)
.unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
let res = try_increase_pledge(test.deps_mut(), env, sender);
let node_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(node_id).unwrap();
let increase = test.coins(1000);
let res = try_increase_mixnode_pledge(test.deps_mut(), env, increase, details);
assert!(matches!(
res,
@@ -814,38 +819,21 @@ pub mod tests {
}
#[test]
fn is_not_allowed_if_account_doesnt_own_mixnode() {
fn is_not_allowed_if_mixnode_has_unbonded() {
let mut test = TestSetup::new();
let env = test.env();
let sender = mock_info("not-mix-owner", &[]);
let res = try_increase_pledge(test.deps_mut(), env, sender);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("not-mix-owner")
})
)
}
#[test]
fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() {
let mut test = TestSetup::new();
let env = test.env();
// TODO: I dislike this cross-test access, but it provides us with exactly what we need
// perhaps it should be refactored a bit?
let owner_unbonding = Addr::unchecked(OWNER_UNBONDING);
let owner_unbonded = Addr::unchecked(OWNER_UNBONDED);
let owner_unbonded_leftover = Addr::unchecked(OWNER_UNBONDED_LEFTOVER);
let ids = setup_mix_combinations(&mut test, None);
let mix_id_unbonding = ids[1].mix_id;
let res = try_increase_pledge(
let increase = test.coins(1000);
let details = test.mixnode_by_id(mix_id_unbonding).unwrap();
let res = try_increase_mixnode_pledge(
test.deps_mut(),
env.clone(),
mock_info(owner_unbonding.as_str(), &[]),
increase.clone(),
details,
);
assert_eq!(
res,
@@ -853,32 +841,6 @@ pub mod tests {
node_id: mix_id_unbonding
})
);
// if the nodes are gone we treat them as tey never existed in the first place
// (regardless of if there's some leftover data)
let res = try_increase_pledge(
test.deps_mut(),
env.clone(),
mock_info(owner_unbonded_leftover.as_str(), &[]),
);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: owner_unbonded_leftover
})
);
let res = try_increase_pledge(
test.deps_mut(),
env,
mock_info(owner_unbonded.as_str(), &[]),
);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: owner_unbonded
})
)
}
#[test]
@@ -887,14 +849,21 @@ pub mod tests {
let env = test.env();
let owner = "mix-owner";
test.add_legacy_mixnode(owner, None);
let node_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(node_id).unwrap();
let sender_empty = mock_info(owner, &[]);
let res = try_increase_pledge(test.deps_mut(), env.clone(), sender_empty);
let sender_empty = Vec::new();
let res = try_increase_mixnode_pledge(
test.deps_mut(),
env.clone(),
sender_empty,
details.clone(),
);
assert_eq!(res, Err(MixnetContractError::NoBondFound));
let sender_zero = mock_info(owner, &[test.coin(0)]);
let res = try_increase_pledge(test.deps_mut(), env, sender_zero);
let sender_zero = test.coins(0);
let res =
try_increase_mixnode_pledge(test.deps_mut(), env, sender_zero, details.clone());
assert_eq!(
res,
Err(MixnetContractError::InsufficientPledge {
@@ -911,11 +880,19 @@ pub mod tests {
// prior increase
let owner = "mix-owner1";
test.add_legacy_mixnode(owner, None);
let sender = mock_info(owner, &[test.coin(1000)]);
try_increase_pledge(test.deps_mut(), env.clone(), sender.clone()).unwrap();
let node_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(node_id).unwrap();
let sender = test.coins(1000);
try_increase_mixnode_pledge(
test.deps_mut(),
env.clone(),
sender.clone(),
details.clone(),
)
.unwrap();
let res = try_increase_pledge(test.deps_mut(), env.clone(), sender);
let details = test.mixnode_by_id(node_id).unwrap();
let res = try_increase_mixnode_pledge(test.deps_mut(), env.clone(), sender, details);
assert_eq!(
res,
Err(MixnetContractError::PendingPledgeChange {
@@ -925,39 +902,22 @@ pub mod tests {
// prior decrease
let owner = "mix-owner2";
test.add_legacy_mixnode(owner, Some(Uint128::new(10000000000)));
let sender = mock_info(owner, &[]);
let amount = test.coin(1000);
try_decrease_pledge(test.deps_mut(), env.clone(), sender, amount).unwrap();
let node_id = test.add_legacy_mixnode(owner, Some(Uint128::new(10000000000)));
let details = test.mixnode_by_id(node_id).unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
let res = try_increase_pledge(test.deps_mut(), env.clone(), sender);
let amount = test.coin(1000);
try_decrease_mixnode_pledge(test.deps_mut(), env.clone(), amount, details.clone())
.unwrap();
let sender = test.coins(10000);
let details = test.mixnode_by_id(node_id).unwrap();
let res = try_increase_mixnode_pledge(test.deps_mut(), env.clone(), sender, details);
assert_eq!(
res,
Err(MixnetContractError::PendingPledgeChange {
pending_event_id: 2
})
);
// artificial event
let owner = "mix-owner3";
let mix_id = test.add_legacy_mixnode(owner, None);
let pending_change = PendingMixNodeChanges {
pledge_change: Some(1234),
cost_params_change: None,
};
storage::PENDING_MIXNODE_CHANGES
.save(test.deps_mut().storage, mix_id, &pending_change)
.unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
let res = try_increase_pledge(test.deps_mut(), env, sender);
assert_eq!(
res,
Err(MixnetContractError::PendingPledgeChange {
pending_event_id: 1234
})
);
}
#[test]
@@ -966,12 +926,13 @@ pub mod tests {
let env = test.env();
let owner = "mix-owner";
let mix_id = test.add_legacy_mixnode(owner, None);
let details = test.mixnode_by_id(mix_id).unwrap();
let events = test.pending_epoch_events();
assert!(events.is_empty());
let sender = mock_info(owner, &[test.coin(1000)]);
try_increase_pledge(test.deps_mut(), env, sender).unwrap();
let sender = test.coins(1000);
try_increase_mixnode_pledge(test.deps_mut(), env, sender, details).unwrap();
let events = test.pending_epoch_events();
@@ -1012,15 +973,15 @@ pub mod tests {
let owner = "mix-owner";
let decrease = test.coin(1000);
test.add_legacy_mixnode(owner, Some(Uint128::new(100_000_000_000)));
let node_id = test.add_legacy_mixnode(owner, Some(Uint128::new(100_000_000_000)));
let details = test.mixnode_by_id(node_id).unwrap();
let mut status = EpochStatus::new(test.rewarding_validator().sender);
status.state = bad_state;
interval_storage::save_current_epoch_status(test.deps_mut().storage, &status)
.unwrap();
let sender = mock_info(owner, &[]);
let res = try_decrease_pledge(test.deps_mut(), env, sender, decrease);
let res = try_decrease_mixnode_pledge(test.deps_mut(), env, decrease, details);
assert!(matches!(
res,
@@ -1030,44 +991,21 @@ pub mod tests {
}
#[test]
fn is_not_allowed_if_account_doesnt_own_mixnode() {
let mut test = TestSetup::new();
let env = test.env();
let sender = mock_info("not-mix-owner", &[]);
let decrease = test.coin(1000);
let res = try_decrease_pledge(test.deps_mut(), env, sender, decrease);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: Addr::unchecked("not-mix-owner")
})
)
}
#[test]
fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() {
fn is_not_allowed_if_mixnode_is_unbonding() {
let mut test = TestSetup::new();
let env = test.env();
// just to make sure that after decrease the value would still be above the minimum
let stake = Uint128::new(100_000_000_000);
let decrease = test.coin(1000);
// TODO: I dislike this cross-test access, but it provides us with exactly what we need
// perhaps it should be refactored a bit?
let owner_unbonding = Addr::unchecked(OWNER_UNBONDING);
let owner_unbonded = Addr::unchecked(OWNER_UNBONDED);
let owner_unbonded_leftover = Addr::unchecked(OWNER_UNBONDED_LEFTOVER);
let ids = setup_mix_combinations(&mut test, Some(stake));
let ids = setup_mix_combinations(&mut test, None);
let mix_id_unbonding = ids[1].mix_id;
let res = try_decrease_pledge(
let decrease = test.coin(1000);
let details = test.mixnode_by_id(mix_id_unbonding).unwrap();
let res = try_decrease_mixnode_pledge(
test.deps_mut(),
env.clone(),
mock_info(owner_unbonding.as_str(), &[]),
decrease.clone(),
details,
);
assert_eq!(
res,
@@ -1075,34 +1013,6 @@ pub mod tests {
node_id: mix_id_unbonding
})
);
// if the nodes are gone we treat them as tey never existed in the first place
// (regardless of if there's some leftover data)
let res = try_decrease_pledge(
test.deps_mut(),
env.clone(),
mock_info(owner_unbonded_leftover.as_str(), &[]),
decrease.clone(),
);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: owner_unbonded_leftover
})
);
let res = try_decrease_pledge(
test.deps_mut(),
env,
mock_info(owner_unbonded.as_str(), &[]),
decrease,
);
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedNodeBond {
owner: owner_unbonded
})
)
}
#[test]
@@ -1114,17 +1024,17 @@ pub mod tests {
let minimum_pledge = minimum_node_pledge(test.deps().storage).unwrap();
let pledge_amount = minimum_pledge.amount + Uint128::new(100);
let pledged = test.coin(pledge_amount.u128());
test.add_legacy_mixnode(owner, Some(pledge_amount));
let node_id = test.add_legacy_mixnode(owner, Some(pledge_amount));
let details = test.mixnode_by_id(node_id).unwrap();
let invalid_decrease = test.coin(150);
let valid_decrease = test.coin(50);
let sender = mock_info(owner, &[]);
let res = try_decrease_pledge(
let res = try_decrease_mixnode_pledge(
test.deps_mut(),
env.clone(),
sender.clone(),
invalid_decrease.clone(),
details.clone(),
);
assert_eq!(
res,
@@ -1136,7 +1046,7 @@ pub mod tests {
})
);
let res = try_decrease_pledge(test.deps_mut(), env, sender, valid_decrease);
let res = try_decrease_mixnode_pledge(test.deps_mut(), env, valid_decrease, details);
assert!(res.is_ok())
}
@@ -1149,10 +1059,10 @@ pub mod tests {
let decrease = test.coin(0);
let owner = "mix-owner";
test.add_legacy_mixnode(owner, Some(stake));
let node_id = test.add_legacy_mixnode(owner, Some(stake));
let details = test.mixnode_by_id(node_id).unwrap();
let sender = mock_info(owner, &[]);
let res = try_decrease_pledge(test.deps_mut(), env, sender, decrease);
let res = try_decrease_mixnode_pledge(test.deps_mut(), env, decrease, details);
assert_eq!(res, Err(MixnetContractError::ZeroCoinAmount))
}
@@ -1165,11 +1075,20 @@ pub mod tests {
// prior increase
let owner = "mix-owner1";
test.add_legacy_mixnode(owner, Some(stake));
let sender = mock_info(owner, &[test.coin(1000)]);
try_increase_pledge(test.deps_mut(), env.clone(), sender.clone()).unwrap();
let node_id = test.add_legacy_mixnode(owner, Some(stake));
let details = test.mixnode_by_id(node_id).unwrap();
let res = try_decrease_pledge(test.deps_mut(), env.clone(), sender, decrease.clone());
let sender = test.coins(1000);
try_increase_mixnode_pledge(test.deps_mut(), env.clone(), sender.clone(), details)
.unwrap();
let details = test.mixnode_by_id(node_id).unwrap();
let res = try_decrease_mixnode_pledge(
test.deps_mut(),
env.clone(),
decrease.clone(),
details,
);
assert_eq!(
res,
Err(MixnetContractError::PendingPledgeChange {
@@ -1179,13 +1098,18 @@ pub mod tests {
// prior decrease
let owner = "mix-owner2";
test.add_legacy_mixnode(owner, Some(stake));
let sender = mock_info(owner, &[]);
let node_id = test.add_legacy_mixnode(owner, Some(stake));
let details = test.mixnode_by_id(node_id).unwrap();
let amount = test.coin(1000);
try_decrease_pledge(test.deps_mut(), env.clone(), sender, amount).unwrap();
try_decrease_mixnode_pledge(test.deps_mut(), env.clone(), amount, details).unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
let res = try_decrease_pledge(test.deps_mut(), env.clone(), sender, decrease.clone());
let details = test.mixnode_by_id(node_id).unwrap();
let res = try_decrease_mixnode_pledge(
test.deps_mut(),
env.clone(),
decrease.clone(),
details,
);
assert_eq!(
res,
Err(MixnetContractError::PendingPledgeChange {
@@ -1204,8 +1128,8 @@ pub mod tests {
.save(test.deps_mut().storage, mix_id, &pending_change)
.unwrap();
let sender = mock_info(owner, &[test.coin(1000)]);
let res = try_decrease_pledge(test.deps_mut(), env, sender, decrease);
let details = test.mixnode_by_id(mix_id).unwrap();
let res = try_decrease_mixnode_pledge(test.deps_mut(), env, decrease, details);
assert_eq!(
res,
Err(MixnetContractError::PendingPledgeChange {
@@ -1225,12 +1149,13 @@ pub mod tests {
let owner = "mix-owner";
let mix_id = test.add_legacy_mixnode(owner, Some(stake));
let details = test.mixnode_by_id(mix_id).unwrap();
let events = test.pending_epoch_events();
assert!(events.is_empty());
let sender = mock_info(owner, &[]);
try_decrease_pledge(test.deps_mut(), env, sender, decrease.clone()).unwrap();
try_decrease_mixnode_pledge(test.deps_mut(), env, decrease.clone(), details).unwrap();
let events = test.pending_epoch_events();
+11 -5
View File
@@ -13,7 +13,7 @@ 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,
AttachSendTokens,
};
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
@@ -149,14 +149,20 @@ pub(crate) fn try_withdraw_nym_node_operator_reward(
deps: DepsMut<'_>,
node_details: NymNodeDetails,
) -> Result<Response, MixnetContractError> {
ensure_can_withdraw_rewards(&node_details)?;
let node_id = node_details.node_id();
let owner = node_details.bond_information.owner.clone();
let reward = helpers::withdraw_operator_reward(deps.storage, node_details)?;
ensure_can_withdraw_rewards(&node_details)?;
Ok(Response::new().add_event(new_withdraw_operator_reward_event(&owner, reward, node_id)))
let reward = helpers::withdraw_operator_reward(deps.storage, node_details)?;
let mut response = Response::new();
// if the reward is zero, don't track or send anything - there's no point
if !reward.amount.is_zero() {
response = response.send_tokens(&owner, reward.clone())
}
Ok(response.add_event(new_withdraw_operator_reward_event(&owner, reward, node_id)))
}
pub(crate) fn try_withdraw_mixnode_operator_reward(
+1 -1
View File
@@ -301,7 +301,7 @@ pub fn ensure_operating_cost_within_range(
storage: &dyn Storage,
operating_cost: &Coin,
) -> Result<(), MixnetContractError> {
let range = mixnet_params_storage::interval_oprating_cost_range(storage)?;
let range = mixnet_params_storage::interval_operating_cost_range(storage)?;
if !range.within_range(operating_cost.amount) {
return Err(MixnetContractError::OperatingCostOutsideRange {
denom: operating_cost.denom.clone(),
+68 -8
View File
@@ -16,14 +16,18 @@ pub mod test_helpers {
perform_pending_epoch_actions, perform_pending_interval_actions, try_begin_epoch_transition,
};
use crate::interval::{pending_events, storage as interval_storage};
use crate::mixnet_contract_settings::queries::query_contract_settings_params;
use crate::mixnet_contract_settings::storage::{
self as mixnet_params_storage, minimum_node_pledge,
};
use crate::mixnet_contract_settings::storage::{rewarding_denom, rewarding_validator_address};
use crate::mixnodes::helpers::get_mixnode_details_by_id;
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::storage::mixnode_bonds;
use crate::mixnodes::transactions::try_remove_mixnode;
use crate::nodes::helpers::{get_node_details_by_identity, must_get_node_bond_by_owner};
use crate::nodes::helpers::{
get_node_details_by_id, get_node_details_by_identity, must_get_node_bond_by_owner,
};
use crate::nodes::storage as nymnodes_storage;
use crate::nodes::storage::helpers::RoleStorageBucket;
use crate::nodes::storage::rewarded_set::{ACTIVE_ROLES_BUCKET, ROLES, ROLES_METADATA};
@@ -68,10 +72,11 @@ pub mod test_helpers {
use mixnet_contract_common::rewarding::simulator::Simulator;
use mixnet_contract_common::rewarding::RewardDistribution;
use mixnet_contract_common::{
Delegation, EpochEventId, EpochState, EpochStatus, ExecuteMsg, Gateway,
GatewayBondingPayload, IdentityKey, IdentityKeyRef, InitialRewardingParams, InstantiateMsg,
Interval, MixNode, MixNodeBond, MixnodeBondingPayload, NodeId, NymNode, NymNodeBond,
NymNodeBondingPayload, Percent, RoleAssignment, SignableGatewayBondingMsg,
ContractStateParams, Delegation, EpochEventId, EpochState, EpochStatus, ExecuteMsg,
Gateway, GatewayBondingPayload, IdentityKey, IdentityKeyRef, InitialRewardingParams,
InstantiateMsg, Interval, MixNode, MixNodeBond, MixNodeDetails, MixnodeBondingPayload,
NodeId, NymNode, NymNodeBond, NymNodeBondingPayload, NymNodeDetails, OperatingCostRange,
Percent, ProfitMarginRange, RoleAssignment, SignableGatewayBondingMsg,
SignableMixNodeBondingMsg, SignableNymNodeBondingMsg,
};
use nym_contracts_common::signing::{
@@ -87,6 +92,23 @@ pub mod test_helpers {
use std::str::FromStr;
use std::time::Duration;
pub(crate) trait ExtractBankMsg {
fn unwrap_bank_msg(self) -> Option<BankMsg>;
}
impl ExtractBankMsg for Response {
fn unwrap_bank_msg(self) -> Option<BankMsg> {
for msg in self.messages {
match msg.msg {
CosmosMsg::Bank(bank_msg) => return Some(bank_msg),
_ => continue,
}
}
None
}
}
pub enum NodeQueryType {
ById(NodeId),
ByIdentity(IdentityKey),
@@ -168,7 +190,6 @@ pub mod test_helpers {
self.env.clone()
}
#[allow(unused)]
pub fn execute(
&mut self,
info: MessageInfo,
@@ -187,7 +208,6 @@ pub mod test_helpers {
self.execute(self.mock_info(sender), msg)
}
#[allow(unused)]
pub fn execute_fn<F>(
&mut self,
exec_fn: F,
@@ -213,7 +233,6 @@ pub mod test_helpers {
self.execute_fn(exec_fn, info)
}
#[allow(unused)]
#[track_caller]
pub fn assert_simple_execution<F>(&mut self, exec_fn: F, info: MessageInfo) -> Response
where
@@ -239,6 +258,36 @@ pub mod test_helpers {
.unwrap_or_else(|err| panic!("{caller} failed with: '{err}' ({err:?})"))
}
pub fn update_profit_margin_range(&mut self, range: ProfitMarginRange) {
let current = query_contract_settings_params(self.deps()).unwrap();
self.execute(
self.owner(),
ExecuteMsg::UpdateContractStateParams {
updated_parameters: ContractStateParams {
profit_margin: range,
..current
},
},
)
.unwrap();
}
pub fn update_operating_cost_range(&mut self, range: OperatingCostRange) {
let current = query_contract_settings_params(self.deps()).unwrap();
self.execute(
self.owner(),
ExecuteMsg::UpdateContractStateParams {
updated_parameters: ContractStateParams {
interval_operating_cost: range,
..current
},
},
)
.unwrap();
}
pub fn get_node_id(&self, query_type: impl Into<NodeQueryType>) -> NodeId {
match query_type.into() {
NodeQueryType::ById(id) => id,
@@ -452,6 +501,7 @@ pub mod test_helpers {
(bond.node_id, keypair)
}
#[track_caller]
pub fn add_legacy_mixnode(&mut self, owner: &str, stake: Option<Uint128>) -> NodeId {
let stake = self.make_mix_pledge(stake);
let (mixnode, _, _) = self.mixnode_with_signature(owner, Some(stake.clone()));
@@ -547,6 +597,14 @@ pub mod test_helpers {
self.make_node_pledge(stake)
}
pub fn mixnode_by_id(&self, node_id: NodeId) -> Option<MixNodeDetails> {
get_mixnode_details_by_id(self.deps().storage, node_id).unwrap()
}
pub fn nymnode_by_id(&self, node_id: NodeId) -> Option<NymNodeDetails> {
get_node_details_by_id(self.deps().storage, node_id).unwrap()
}
pub fn mixnode_bonding_signature(
&mut self,
key: &identity::PrivateKey,
@@ -657,6 +715,7 @@ pub mod test_helpers {
(gateway, owner_signature)
}
#[track_caller]
pub fn start_unbonding_mixnode(&mut self, mix_id: NodeId) {
let bond_details = mixnodes_storage::mixnode_bonds()
.load(self.deps().storage, mix_id)
@@ -671,6 +730,7 @@ pub mod test_helpers {
.unwrap();
}
#[track_caller]
pub fn start_unbonding_nymnode(&mut self, node_id: NodeId) {
let bond_details = nymnodes_storage::nym_nodes()
.load(self.deps().storage, node_id)