removed all on_behalf mixnet contract methods

This commit is contained in:
Jędrzej Stuczyński
2024-05-24 16:38:02 +01:00
parent 65a1d6d91e
commit 70c68796d3
31 changed files with 287 additions and 2771 deletions
@@ -218,7 +218,6 @@ where
#[derive(Serialize)]
pub struct ContractMessageContent<T> {
pub sender: Addr,
pub proxy: Option<Addr>,
pub funds: Vec<Coin>,
pub data: T,
}
@@ -233,25 +232,17 @@ where
}
impl<T> ContractMessageContent<T> {
pub fn new(sender: Addr, proxy: Option<Addr>, funds: Vec<Coin>, data: T) -> Self {
pub fn new(sender: Addr, funds: Vec<Coin>, data: T) -> Self {
ContractMessageContent {
sender,
proxy,
funds,
data,
}
}
pub fn new_with_info(info: MessageInfo, signer: Addr, data: T) -> Self {
let proxy = if info.sender == signer {
None
} else {
Some(info.sender)
};
ContractMessageContent {
sender: signer,
proxy,
funds: info.funds,
data,
}
@@ -65,7 +65,6 @@ impl Delegation {
cumulative_reward_ratio: Decimal,
amount: Coin,
height: u64,
proxy: Option<Addr>,
) -> Self {
assert!(
amount.amount <= TOKEN_SUPPLY,
@@ -78,7 +77,7 @@ impl Delegation {
cumulative_reward_ratio,
amount,
height,
proxy,
proxy: None,
}
}
@@ -76,22 +76,6 @@ pub enum MixnetContractError {
#[error("Received multiple coin types during staking")]
MultipleDenoms,
#[error("Proxy address mismatch, expected {existing}, got {incoming}")]
ProxyMismatch { existing: String, incoming: String },
#[error("Proxy address ({received}) is not set to the vesting contract ({vesting_contract})")]
ProxyIsNotVestingContract {
received: Addr,
vesting_contract: Addr,
},
#[error(
"Sender of this message ({received}) is not the vesting contract ({vesting_contract})"
)]
SenderIsNotVestingContract {
received: Addr,
vesting_contract: Addr,
},
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
MalformedEd25519IdentityKey(String),
@@ -239,6 +223,9 @@ pub enum MixnetContractError {
#[from]
source: ApiVerifierError,
},
#[error("this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again")]
DisabledVestingOperation,
}
impl MixnetContractError {
@@ -103,7 +103,6 @@ impl Display for MixnetEventType {
// attributes that are used in multiple places
pub const OWNER_KEY: &str = "owner";
pub const AMOUNT_KEY: &str = "amount";
pub const PROXY_KEY: &str = "proxy";
// event-specific attributes
@@ -163,7 +162,6 @@ pub const NEW_EPOCHS_IN_INTERVAL: &str = "new_epochs_in_interval";
pub fn new_delegation_event(
created_at: BlockHeight,
delegator: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
mix_id: MixId,
unit_reward: Decimal,
@@ -171,58 +169,34 @@ pub fn new_delegation_event(
Event::new(MixnetEventType::Delegation)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
.add_attribute(UNIT_REWARD_KEY, unit_reward.to_string())
}
pub fn new_delegation_on_unbonded_node_event(
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
pub fn new_delegation_on_unbonded_node_event(delegator: &Addr, mix_id: MixId) -> Event {
Event::new(MixnetEventType::Delegation)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
pub fn new_pending_delegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
mix_id: MixId,
) -> Event {
pub fn new_pending_delegation_event(delegator: &Addr, amount: &Coin, mix_id: MixId) -> Event {
Event::new(MixnetEventType::PendingDelegation)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
pub fn new_withdraw_operator_reward_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: Coin,
mix_id: MixId,
) -> Event {
pub fn new_withdraw_operator_reward_event(owner: &Addr, amount: Coin, mix_id: MixId) -> Event {
Event::new(MixnetEventType::WithdrawOperatorReward)
.add_attribute(OWNER_KEY, owner.as_str())
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_withdraw_delegator_reward_event(
delegator: &Addr,
proxy: &Option<Addr>,
amount: Coin,
mix_id: MixId,
) -> Event {
pub fn new_withdraw_delegator_reward_event(delegator: &Addr, amount: Coin, mix_id: MixId) -> Event {
Event::new(MixnetEventType::WithdrawDelegatorReward)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
@@ -278,59 +252,43 @@ pub fn new_pending_rewarding_params_update_event(
)
}
pub fn new_undelegation_event(
created_at: BlockHeight,
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
pub fn new_undelegation_event(created_at: BlockHeight, delegator: &Addr, mix_id: MixId) -> Event {
Event::new(MixnetEventType::Undelegation)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_pending_undelegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
pub fn new_pending_undelegation_event(delegator: &Addr, mix_id: MixId) -> Event {
Event::new(MixnetEventType::PendingUndelegation)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_gateway_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef<'_>,
) -> Event {
Event::new(MixnetEventType::GatewayBonding)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_gateway_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef<'_>,
) -> Event {
Event::new(MixnetEventType::GatewayUnbonding)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef<'_>,
mix_id: MixId,
@@ -341,7 +299,6 @@ pub fn new_mixnode_bonding_event(
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
.add_attribute(AMOUNT_KEY, amount.to_string())
}
@@ -380,7 +337,6 @@ pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Ev
pub fn new_pending_mixnode_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
identity: IdentityKeyRef<'_>,
mix_id: MixId,
) -> Event {
@@ -388,43 +344,33 @@ pub fn new_pending_mixnode_unbonding_event(
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
}
pub fn new_mixnode_config_update_event(
mix_id: MixId,
owner: &Addr,
proxy: &Option<Addr>,
update: &MixNodeConfigUpdate,
) -> Event {
Event::new(MixnetEventType::MixnodeConfigUpdate)
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(UPDATED_MIXNODE_CONFIG_KEY, update.to_inline_json())
}
pub fn new_gateway_config_update_event(
owner: &Addr,
proxy: &Option<Addr>,
update: &GatewayConfigUpdate,
) -> Event {
pub fn new_gateway_config_update_event(owner: &Addr, update: &GatewayConfigUpdate) -> Event {
Event::new(MixnetEventType::GatewayConfigUpdate)
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(UPDATED_GATEWAY_CONFIG_KEY, update.to_inline_json())
}
pub fn new_mixnode_pending_cost_params_update_event(
mix_id: MixId,
owner: &Addr,
proxy: &Option<Addr>,
new_costs: &MixNodeCostParams,
) -> Event {
Event::new(MixnetEventType::PendingMixnodeCostParamsUpdate)
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
}
@@ -3,7 +3,6 @@
use crate::{IdentityKey, IdentityKeyRef};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};
@@ -84,10 +83,10 @@ impl FamilyHead {
}
impl Family {
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: String) -> Self {
pub fn new(head: FamilyHead, label: String) -> Self {
Family {
head,
proxy: proxy.map(|p| p.to_string()),
proxy: None,
label,
}
}
@@ -55,19 +55,13 @@ pub struct GatewayBond {
}
impl GatewayBond {
pub fn new(
pledge_amount: Coin,
owner: Addr,
block_height: u64,
gateway: Gateway,
proxy: Option<Addr>,
) -> Self {
pub fn new(pledge_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
GatewayBond {
pledge_amount,
owner,
block_height,
gateway,
proxy,
proxy: None,
}
}
@@ -518,7 +518,6 @@ impl MixNodeBond {
original_pledge: Coin,
layer: Layer,
mix_node: MixNode,
proxy: Option<Addr>,
bonding_height: u64,
) -> Self {
MixNodeBond {
@@ -527,7 +526,7 @@ impl MixNodeBond {
original_pledge,
layer,
mix_node,
proxy,
proxy: None,
bonding_height,
is_unbonding: false,
}
@@ -38,6 +38,7 @@ pub enum PendingEpochEventKind {
/// Request to create a delegation towards particular mixnode.
/// Note that if such delegation already exists, it will get updated with the provided token amount.
#[serde(alias = "Delegate")]
#[non_exhaustive]
Delegate {
/// The address of the owner of the delegation.
owner: Addr,
@@ -55,6 +56,7 @@ pub enum PendingEpochEventKind {
/// Request to remove delegation from particular mixnode.
#[serde(alias = "Undelegate")]
#[non_exhaustive]
Undelegate {
/// The address of the owner of the delegation.
owner: Addr,
@@ -109,6 +111,23 @@ impl PendingEpochEventKind {
kind: self,
}
}
pub fn new_delegate(owner: Addr, mix_id: MixId, amount: Coin) -> Self {
PendingEpochEventKind::Delegate {
owner,
mix_id,
amount,
proxy: None,
}
}
pub fn new_undelegate(owner: Addr, mix_id: MixId) -> Self {
PendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy: None,
}
}
}
impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
@@ -47,7 +47,6 @@ impl SimulatedNode {
self.rewarding_details.total_unit_reward,
delegation,
42,
None,
);
self.delegations.insert(delegator, delegation);
@@ -37,13 +37,12 @@ impl SigningPurpose for MixnodeBondingPayload {
pub fn construct_mixnode_bonding_sign_payload(
nonce: Nonce,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
mix_node: MixNode,
cost_params: MixNodeCostParams,
) -> SignableMixNodeBondingMsg {
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
let content = ContractMessageContent::new(sender, vec![pledge], payload);
SignableMessage::new(nonce, content)
}
@@ -68,12 +67,11 @@ impl SigningPurpose for GatewayBondingPayload {
pub fn construct_gateway_bonding_sign_payload(
nonce: Nonce,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
gateway: Gateway,
) -> SignableGatewayBondingMsg {
let payload = GatewayBondingPayload::new(gateway);
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
let content = ContractMessageContent::new(sender, vec![pledge], payload);
SignableMessage::new(nonce, content)
}
@@ -82,17 +80,14 @@ pub fn construct_gateway_bonding_sign_payload(
pub struct FamilyJoinPermit {
// the granter of this permit
family_head: FamilyHead,
// whether the **member** will want to join via the proxy (i.e. vesting contract)
proxy: Option<Addr>,
// the actual member we want to permit to join
member_node: IdentityKey,
}
impl FamilyJoinPermit {
pub fn new(family_head: FamilyHead, proxy: Option<Addr>, member_node: IdentityKey) -> Self {
pub fn new(family_head: FamilyHead, member_node: IdentityKey) -> Self {
Self {
family_head,
proxy,
member_node,
}
}
@@ -107,10 +102,9 @@ impl SigningPurpose for FamilyJoinPermit {
pub fn construct_family_join_permit(
nonce: Nonce,
family_head: FamilyHead,
proxy: Option<Addr>,
member_node: IdentityKey,
) -> SignableFamilyJoinPermitMsg {
let payload = FamilyJoinPermit::new(family_head, proxy, member_node);
let payload = FamilyJoinPermit::new(family_head, member_node);
// note: we're NOT wrapping it in `ContractMessageContent` because the family head is not going to be the one
// sending the message to the contract
@@ -74,7 +74,6 @@ pub struct TestSetup {
pub rng: ChaCha20Rng,
pub mixnet_contract: Addr,
pub vesting_contract: Addr,
}
impl TestSetup {
@@ -91,7 +90,6 @@ impl TestSetup {
app,
rng: test_rng(),
mixnet_contract: contracts.mixnet_contract_address,
vesting_contract: contracts.vesting_contract_address,
}
}
@@ -99,10 +97,6 @@ impl TestSetup {
self.mixnet_contract.clone()
}
pub fn vesting_contract(&self) -> Addr {
self.vesting_contract.clone()
}
pub fn skip_to_current_epoch_end(&mut self) {
let current_interval: CurrentIntervalResponse = self
.app
@@ -209,7 +203,6 @@ impl TestSetup {
pub fn valid_mixnode_with_sig(
&mut self,
owner: &str,
proxy: Option<Addr>,
cost_params: MixNodeCostParams,
stake: Coin,
) -> (MixNode, MessageSignature) {
@@ -239,8 +232,7 @@ impl TestSetup {
};
let payload = MixnodeBondingPayload::new(mixnode.clone(), cost_params);
let content =
ContractMessageContent::new(Addr::unchecked(owner), proxy, vec![stake], payload);
let content = ContractMessageContent::new(Addr::unchecked(owner), vec![stake], payload);
let sign_payload = SignableMixNodeBondingMsg::new(signing_nonce, content);
let plaintext = sign_payload.to_plaintext().unwrap();
let signature = keypair.private_key().sign(plaintext);
+23 -115
View File
@@ -118,44 +118,6 @@ pub fn execute(
ExecuteMsg::KickFamilyMember { member } => {
crate::families::transactions::try_head_kick_member(deps, info, member)
}
ExecuteMsg::CreateFamilyOnBehalf {
owner_address,
label,
} => crate::families::transactions::try_create_family_on_behalf(
deps,
info,
owner_address,
label,
),
ExecuteMsg::JoinFamilyOnBehalf {
member_address,
join_permit,
family_head,
} => crate::families::transactions::try_join_family_on_behalf(
deps,
info,
member_address,
join_permit,
family_head,
),
ExecuteMsg::LeaveFamilyOnBehalf {
member_address,
family_head,
} => crate::families::transactions::try_leave_family_on_behalf(
deps,
info,
member_address,
family_head,
),
ExecuteMsg::KickFamilyMemberOnBehalf {
head_address,
member,
} => crate::families::transactions::try_head_kick_member_on_behalf(
deps,
info,
head_address,
member,
),
// state/sys-params-related
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address(
@@ -232,62 +194,23 @@ pub fn execute(
cost_params,
owner_signature,
),
ExecuteMsg::BondMixnodeOnBehalf {
mix_node,
cost_params,
owner,
owner_signature,
} => crate::mixnodes::transactions::try_add_mixnode_on_behalf(
deps,
env,
info,
mix_node,
cost_params,
owner,
owner_signature,
),
ExecuteMsg::PledgeMore {} => {
crate::mixnodes::transactions::try_increase_pledge(deps, env, info)
}
ExecuteMsg::PledgeMoreOnBehalf { owner } => {
crate::mixnodes::transactions::try_increase_pledge_on_behalf(deps, env, info, owner)
}
ExecuteMsg::DecreasePledge { decrease_by } => {
crate::mixnodes::transactions::try_decrease_pledge(deps, env, info, decrease_by)
}
ExecuteMsg::DecreasePledgeOnBehalf { owner, decrease_by } => {
crate::mixnodes::transactions::try_decrease_pledge_on_behalf(
deps,
env,
info,
decrease_by,
owner,
)
}
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, env, info)
}
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(deps, env, info, owner)
}
ExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params(
deps, env, info, new_costs,
)
}
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params_on_behalf(
deps, env, info, new_costs, owner,
)
}
ExecuteMsg::UpdateMixnodeConfig { new_config } => {
crate::mixnodes::transactions::try_update_mixnode_config(deps, info, new_config)
}
ExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner } => {
crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
deps, info, new_config, owner,
)
}
// gateway-related:
ExecuteMsg::BondGateway {
@@ -300,52 +223,22 @@ pub fn execute(
gateway,
owner_signature,
),
ExecuteMsg::BondGatewayOnBehalf {
gateway,
owner,
owner_signature,
} => crate::gateways::transactions::try_add_gateway_on_behalf(
deps,
env,
info,
gateway,
owner,
owner_signature,
),
ExecuteMsg::UnbondGateway {} => {
crate::gateways::transactions::try_remove_gateway(deps, info)
}
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
}
ExecuteMsg::UpdateGatewayConfig { new_config } => {
crate::gateways::transactions::try_update_gateway_config(deps, info, new_config)
}
ExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => {
crate::gateways::transactions::try_update_gateway_config_on_behalf(
deps, info, new_config, owner,
)
}
// delegation-related:
ExecuteMsg::DelegateToMixnode { mix_id } => {
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_id)
}
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_delegate_to_mixnode_on_behalf(
deps, env, info, mix_id, delegate,
)
}
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode(
deps, env, info, mix_id,
)
}
ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode_on_behalf(
deps, env, info, mix_id, delegate,
)
}
// reward-related
ExecuteMsg::RewardMixnode {
@@ -356,16 +249,29 @@ pub fn execute(
ExecuteMsg::WithdrawOperatorReward {} => {
crate::rewards::transactions::try_withdraw_operator_reward(deps, info)
}
ExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => {
crate::rewards::transactions::try_withdraw_operator_reward_on_behalf(deps, info, owner)
}
ExecuteMsg::WithdrawDelegatorReward { mix_id } => {
crate::rewards::transactions::try_withdraw_delegator_reward(deps, info, mix_id)
}
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => {
crate::rewards::transactions::try_withdraw_delegator_reward_on_behalf(
deps, info, mix_id, owner,
)
// legacy vesting
ExecuteMsg::CreateFamilyOnBehalf { .. }
| ExecuteMsg::JoinFamilyOnBehalf { .. }
| ExecuteMsg::LeaveFamilyOnBehalf { .. }
| ExecuteMsg::KickFamilyMemberOnBehalf { .. }
| ExecuteMsg::BondMixnodeOnBehalf { .. }
| ExecuteMsg::PledgeMoreOnBehalf { .. }
| ExecuteMsg::DecreasePledgeOnBehalf { .. }
| ExecuteMsg::UnbondMixnodeOnBehalf { .. }
| ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { .. }
| ExecuteMsg::UpdateMixnodeConfigOnBehalf { .. }
| ExecuteMsg::BondGatewayOnBehalf { .. }
| ExecuteMsg::UnbondGatewayOnBehalf { .. }
| ExecuteMsg::UpdateGatewayConfigOnBehalf { .. }
| ExecuteMsg::DelegateToMixnodeOnBehalf { .. }
| ExecuteMsg::UndelegateFromMixnodeOnBehalf { .. }
| ExecuteMsg::WithdrawOperatorRewardOnBehalf { .. }
| ExecuteMsg::WithdrawDelegatorRewardOnBehalf { .. } => {
Err(MixnetContractError::DisabledVestingOperation)
}
// testing-only
@@ -607,13 +513,15 @@ pub fn query(
#[entry_point]
pub fn migrate(
deps: DepsMut<'_>,
mut deps: DepsMut<'_>,
_env: Env,
msg: MigrateMsg,
) -> Result<Response, MixnetContractError> {
set_build_information!(deps.storage)?;
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
crate::queued_migrations::vesting_purge(deps.branch())?;
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
// and vesting contract requiring the mixnet contract address), if we ever want to deploy any new fresh
// environment, one of the contracts will HAVE TO go through a migration
+2 -73
View File
@@ -302,10 +302,7 @@ mod tests {
mod delegator_delegations {
use super::*;
use crate::delegations::transactions::try_delegate_to_mixnode_on_behalf;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{coin, Addr};
use cosmwasm_std::Addr;
#[test]
fn obeys_limits() {
@@ -453,25 +450,10 @@ mod tests {
#[test]
fn all_retrieved_delegations_are_from_the_specified_delegator() {
let mut test = TestSetup::new();
let env = test.env();
// it means we have, for example, delegation from "delegator1" towards mix1, mix2, ...., from "delegator2" towards mix1, mix2, ...., etc
add_dummy_mixes_with_delegations(&mut test, 50, 100);
// add some proxies while we're at it to make sure they're queried for separately
let with_proxy = "delegator42";
let vesting_contract = test.vesting_contract();
for mix_id in 1..=25 {
try_delegate_to_mixnode_on_behalf(
test.deps_mut(),
env.clone(),
mock_info(vesting_contract.as_ref(), &[coin(100_000, TEST_COIN_DENOM)]),
mix_id,
with_proxy.into(),
)
.unwrap();
}
test.execute_all_pending_events();
// make few queries
let res1 =
query_delegator_delegations_paged(test.deps(), "delegator2".into(), None, None)
@@ -490,59 +472,6 @@ mod tests {
.delegations
.into_iter()
.all(|d| d.owner == Addr::unchecked("delegator35")));
let with_proxy_full =
query_delegator_delegations_paged(test.deps(), with_proxy.into(), None, None)
.unwrap();
assert_eq!(with_proxy_full.delegations.len(), 125);
// all delegations have correct owner
assert!(with_proxy_full
.delegations
.iter()
.all(|d| d.owner == Addr::unchecked(with_proxy)));
// and we have 100 delegations without proxy and 25 with
let no_proxy = with_proxy_full
.delegations
.iter()
.filter(|d| d.proxy.is_none())
.count();
assert_eq!(no_proxy, 100);
let proxy = with_proxy_full
.delegations
.iter()
.filter(|d| d.proxy.is_some())
.count();
assert_eq!(proxy, 25);
assert!(with_proxy_full
.delegations
.iter()
.filter(|d| d.proxy.is_some())
.all(|d| d.proxy.as_ref().unwrap() == vesting_contract));
// now make sure that if we do it in paged manner, we'll get exactly the same result
let per_page = Some(15);
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = query_delegator_delegations_paged(
test.deps(),
with_proxy.into(),
start_after,
per_page,
)
.unwrap();
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
assert_eq!(with_proxy_full.delegations, delegations)
}
}
+13 -154
View File
@@ -1,14 +1,12 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use crate::interval::storage as interval_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use crate::support::helpers::{
ensure_epoch_in_progress_state, ensure_sent_by_vesting_contract, validate_delegation_stake,
};
use cosmwasm_std::{Addr, Coin, DepsMut, Env, MessageInfo, Response};
use crate::support::helpers::{ensure_epoch_in_progress_state, validate_delegation_stake};
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_pending_delegation_event, new_pending_undelegation_event,
@@ -21,30 +19,6 @@ pub(crate) fn try_delegate_to_mixnode(
env: Env,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_delegate_to_mixnode(deps, env, mix_id, info.sender, info.funds, None)
}
pub(crate) fn try_delegate_to_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
delegate: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let delegate = deps.api.addr_validate(&delegate)?;
_try_delegate_to_mixnode(deps, env, mix_id, delegate, info.funds, Some(info.sender))
}
pub(crate) fn _try_delegate_to_mixnode(
deps: DepsMut<'_>,
env: Env,
mix_id: MixId,
delegate: Addr,
amount: Vec<Coin>,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// delegation is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
@@ -52,7 +26,7 @@ pub(crate) fn _try_delegate_to_mixnode(
// check if the delegation contains any funds of the appropriate denomination
let contract_state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?;
let delegation = validate_delegation_stake(
amount,
info.funds,
contract_state.params.minimum_mixnode_delegation,
contract_state.rewarding_denom,
)?;
@@ -67,14 +41,9 @@ pub(crate) fn _try_delegate_to_mixnode(
}
// push the event onto the queue and wait for it to be picked up at the end of the epoch
let cosmos_event = new_pending_delegation_event(&delegate, &proxy, &delegation, mix_id);
let cosmos_event = new_pending_delegation_event(&info.sender, &delegation, mix_id);
let epoch_event = PendingEpochEventKind::Delegate {
owner: delegate,
mix_id,
amount: delegation,
proxy,
};
let epoch_event = PendingEpochEventKind::new_delegate(info.sender, mix_id, delegation);
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(Response::new().add_event(cosmos_event))
@@ -85,35 +54,12 @@ pub(crate) fn try_remove_delegation_from_mixnode(
env: Env,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_remove_delegation_from_mixnode(deps, env, mix_id, info.sender, None)
}
pub(crate) fn try_remove_delegation_from_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
delegate: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let delegate = deps.api.addr_validate(&delegate)?;
_try_remove_delegation_from_mixnode(deps, env, mix_id, delegate, Some(info.sender))
}
pub(crate) fn _try_remove_delegation_from_mixnode(
deps: DepsMut<'_>,
env: Env,
mix_id: MixId,
delegate: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// undelegation is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
// see if the delegation even exists
let storage_key = Delegation::generate_storage_key(mix_id, &delegate, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &info.sender, None);
if storage::delegations()
.may_load(deps.storage, storage_key)?
@@ -121,19 +67,15 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
{
return Err(MixnetContractError::NoMixnodeDelegationFound {
mix_id,
address: delegate.into_string(),
proxy: proxy.map(Addr::into_string),
address: info.sender.into_string(),
proxy: None,
});
}
// push the event onto the queue and wait for it to be picked up at the end of the epoch
let cosmos_event = new_pending_undelegation_event(&delegate, &proxy, mix_id);
let cosmos_event = new_pending_undelegation_event(&info.sender, mix_id);
let epoch_event = PendingEpochEventKind::Undelegate {
owner: delegate,
mix_id,
proxy,
};
let epoch_event = PendingEpochEventKind::new_undelegate(info.sender, mix_id);
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(Response::new().add_event(cosmos_event))
@@ -151,7 +93,7 @@ mod tests {
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{coin, Decimal};
use cosmwasm_std::{coin, Addr, Decimal};
use mixnet_contract_common::{EpochState, EpochStatus};
#[test]
@@ -368,65 +310,17 @@ mod tests {
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let amount1 = coin(100_000_000, TEST_COIN_DENOM);
let amount2 = coin(50_000_000, TEST_COIN_DENOM);
let sender1 = mock_info(owner, &[amount1.clone()]);
let sender2 = mock_info(test.vesting_contract().as_str(), &[amount2.clone()]);
try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id).unwrap();
try_delegate_to_mixnode_on_behalf(test.deps_mut(), env, sender2, mix_id, owner.into())
.unwrap();
let events = test.pending_epoch_events();
assert_eq!(
events[0].kind,
PendingEpochEventKind::Delegate {
owner: Addr::unchecked(owner),
mix_id,
amount: amount1,
proxy: None
}
PendingEpochEventKind::new_delegate(Addr::unchecked(owner), mix_id, amount1,)
);
assert_eq!(
events[1].kind,
PendingEpochEventKind::Delegate {
owner: Addr::unchecked(owner),
mix_id,
amount: amount2,
proxy: Some(test.vesting_contract())
}
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let res = try_delegate_to_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
mix_id,
owner.into(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
@@ -573,40 +467,5 @@ mod tests {
);
assert!(res.is_ok());
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
test.add_immediate_delegation_with_illegal_proxy(
owner,
10000u32,
mix_id,
illegal_proxy.clone(),
);
let res = try_remove_delegation_from_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
mix_id,
owner.into(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
}
@@ -4,7 +4,7 @@
use crate::mixnodes::storage as mixnodes_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Deps};
use cosmwasm_std::Deps;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{construct_family_join_permit, IdentityKeyRef};
@@ -13,7 +13,6 @@ use nym_contracts_common::signing::{MessageSignature, Verifier};
pub(crate) fn verify_family_join_permit(
deps: Deps<'_>,
granter: FamilyHead,
proxy: Option<Addr>,
member: IdentityKeyRef,
signature: MessageSignature,
) -> Result<(), MixnetContractError> {
@@ -32,7 +31,7 @@ pub(crate) fn verify_family_join_permit(
});
};
let nonce = signing_storage::get_signing_nonce(deps.storage, head_mixnode.owner)?;
let msg = construct_family_join_permit(nonce, granter, proxy, member.to_owned());
let msg = construct_family_join_permit(nonce, granter, member.to_owned());
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
+15 -285
View File
@@ -1,4 +1,4 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage::{
@@ -7,41 +7,20 @@ use super::storage::{
};
use crate::families::queries::get_family_by_label;
use crate::families::signature_helpers::verify_family_join_permit;
use crate::support::helpers::{ensure_bonded, ensure_sent_by_vesting_contract};
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
use crate::support::helpers::ensure_bonded;
use cosmwasm_std::{DepsMut, MessageInfo, Response};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey};
use nym_contracts_common::signing::MessageSignature;
/// Creates a new MixNode family with senders node as head
pub fn try_create_family(
pub(crate) fn try_create_family(
deps: DepsMut,
info: MessageInfo,
label: String,
) -> Result<Response, MixnetContractError> {
_try_create_family(deps, &info.sender, label, None)
}
pub fn try_create_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
owner_address: String,
label: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner_address = deps.api.addr_validate(&owner_address)?;
_try_create_family(deps, &owner_address, label, Some(info.sender))
}
fn _try_create_family(
deps: DepsMut,
owner: &Addr,
label: String,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
@@ -60,43 +39,19 @@ fn _try_create_family(
return Err(MixnetContractError::FamilyWithLabelExists(label));
}
let family = Family::new(family_head, proxy, label);
let family = Family::new(family_head, label);
save_family(&family, deps.storage)?;
Ok(Response::default())
}
pub fn try_join_family(
pub(crate) fn try_join_family(
deps: DepsMut,
info: MessageInfo,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
_try_join_family(deps, &info.sender, join_permit, family_head, None)
}
pub fn try_join_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let member_address = deps.api.addr_validate(&member_address)?;
let proxy = Some(info.sender);
_try_join_family(deps, &member_address, join_permit, family_head, proxy)
}
fn _try_join_family(
deps: DepsMut,
owner: &Addr,
join_permit: MessageSignature,
family_head: FamilyHead,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
@@ -116,7 +71,6 @@ fn _try_join_family(
verify_family_join_permit(
deps.as_ref(),
family_head.clone(),
proxy,
existing_bond.identity(),
join_permit,
)?;
@@ -128,33 +82,13 @@ fn _try_join_family(
Ok(Response::default())
}
pub fn try_leave_family(
pub(crate) fn try_leave_family(
deps: DepsMut,
info: MessageInfo,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
_try_leave_family(deps, &info.sender, family_head)
}
pub fn try_leave_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let member_address = deps.api.addr_validate(&member_address)?;
_try_leave_family(deps, &member_address, family_head)
}
fn _try_leave_family(
deps: DepsMut,
owner: &Addr,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
@@ -178,32 +112,13 @@ fn _try_leave_family(
Ok(Response::default())
}
pub fn try_head_kick_member(
pub(crate) fn try_head_kick_member(
deps: DepsMut,
info: MessageInfo,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
_try_head_kick_member(deps, &info.sender, member)
}
pub fn try_head_kick_member_on_behalf(
deps: DepsMut,
info: MessageInfo,
head_address: String,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let head_address = deps.api.addr_validate(&head_address)?;
_try_head_kick_member(deps, &head_address, member)
}
fn _try_head_kick_member(
deps: DepsMut,
owner: &Addr,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
let head_bond = crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
let head_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
// make sure we're still in the mixnet
ensure_bonded(&head_bond)?;
@@ -321,7 +236,7 @@ mod test {
assert_eq!(family.head_identity(), family_head.identity());
let join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key);
try_join_family(
test.deps_mut(),
@@ -345,7 +260,7 @@ mod test {
);
let new_join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key);
try_join_family(
test.deps_mut(),
@@ -373,189 +288,4 @@ mod test {
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
}
#[cfg(test)]
mod creating_family {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
test.add_dummy_mixnode(head, None);
let res = try_create_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
"label".to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
#[cfg(test)]
mod joining_family {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
let label = "family";
let new_member = "vin-diesel";
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
false,
);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(head_identity);
let res = try_join_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
join_permit,
family_head,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
#[cfg(test)]
mod leaving_family {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
let label = "family";
let new_member = "vin-diesel";
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(head_identity);
try_join_family_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
join_permit,
family_head.clone(),
)
.unwrap();
let res = try_leave_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
family_head,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
#[cfg(test)]
mod kicking_family_member {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
let label = "family";
let new_member = "vin-diesel";
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(head_identity);
try_join_family_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
join_permit,
family_head,
)
.unwrap();
let res = try_head_kick_member_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
member_keys.public_key().to_base58_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
}
@@ -12,7 +12,6 @@ use nym_contracts_common::signing::Verifier;
pub(crate) fn verify_gateway_bonding_signature(
deps: Deps<'_>,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
gateway: Gateway,
signature: MessageSignature,
@@ -22,7 +21,7 @@ pub(crate) fn verify_gateway_bonding_signature(
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
let msg = construct_gateway_bonding_sign_payload(nonce, sender, proxy, pledge, gateway);
let msg = construct_gateway_bonding_sign_payload(nonce, sender, pledge, gateway);
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
+28 -253
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::helpers::must_get_gateway_bond_by_owner;
@@ -6,10 +6,8 @@ use super::storage;
use crate::gateways::signature_helpers::verify_gateway_bonding_signature;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_no_existing_bond, ensure_proxy_match, ensure_sent_by_vesting_contract, validate_pledge,
};
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use crate::support::helpers::{ensure_no_existing_bond, validate_pledge};
use cosmwasm_std::{BankMsg, DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_gateway_bonding_event, new_gateway_config_update_event, new_gateway_unbonding_event,
@@ -17,72 +15,28 @@ use mixnet_contract_common::events::{
use mixnet_contract_common::gateway::GatewayConfigUpdate;
use mixnet_contract_common::{Gateway, GatewayBond};
use nym_contracts_common::signing::MessageSignature;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
pub fn try_add_gateway(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
gateway: Gateway,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
_try_add_gateway(
deps,
env,
gateway,
info.funds,
info.sender,
owner_signature,
None,
)
}
pub fn try_add_gateway_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
gateway: Gateway,
owner: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_add_gateway(
deps,
env,
gateway,
info.funds,
owner,
owner_signature,
Some(proxy),
)
}
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
// so that we could return a better error message if it doesn't match?
pub(crate) fn _try_add_gateway(
pub(crate) fn try_add_gateway(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
gateway: Gateway,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the pledge contains any funds of the appropriate denomination
let minimum_pledge = mixnet_params_storage::minimum_gateway_pledge(deps.storage)?;
let pledge = validate_pledge(pledge, minimum_pledge)?;
let pledge = validate_pledge(info.funds, minimum_pledge)?;
// if the client has an active bonded mixnode or gateway, don't allow bonding
ensure_no_existing_bond(&owner, deps.storage)?;
ensure_no_existing_bond(&info.sender, deps.storage)?;
// check if somebody else has already bonded a gateway with this identity
if let Some(existing_bond) =
storage::gateways().may_load(deps.storage, &gateway.identity_key)?
{
if existing_bond.owner != owner {
if existing_bond.owner != info.sender {
return Err(MixnetContractError::DuplicateGateway {
owner: existing_bond.owner,
});
@@ -92,105 +46,62 @@ pub(crate) fn _try_add_gateway(
// check if this sender actually owns the gateway by checking the signature
verify_gateway_bonding_signature(
deps.as_ref(),
owner.clone(),
proxy.clone(),
info.sender.clone(),
pledge.clone(),
gateway.clone(),
owner_signature,
)?;
// update the signing nonce associated with this sender so that the future signature would be made on the new value
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
signing_storage::increment_signing_nonce(deps.storage, info.sender.clone())?;
let gateway_identity = gateway.identity_key.clone();
let bond = GatewayBond::new(
pledge.clone(),
owner.clone(),
info.sender.clone(),
env.block.height,
gateway,
proxy.clone(),
);
storage::gateways().save(deps.storage, bond.identity(), &bond)?;
Ok(Response::new().add_event(new_gateway_bonding_event(
&owner,
&proxy,
&info.sender,
&pledge,
&gateway_identity,
)))
}
pub fn try_remove_gateway_on_behalf(
pub(crate) fn try_remove_gateway(
deps: DepsMut<'_>,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_remove_gateway(deps, owner, Some(proxy))
}
pub fn try_remove_gateway(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_remove_gateway(deps, info.sender, None)
}
pub(crate) fn _try_remove_gateway(
deps: DepsMut<'_>,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// try to find the node of the sender
let gateway_bond = match storage::gateways()
.idx
.owner
.item(deps.storage, owner.clone())?
.item(deps.storage, info.sender.clone())?
{
Some(record) => record.1,
None => return Err(MixnetContractError::NoAssociatedGatewayBond { owner }),
None => return Err(MixnetContractError::NoAssociatedGatewayBond { owner: info.sender }),
};
if proxy != gateway_bond.proxy {
return Err(MixnetContractError::ProxyMismatch {
existing: gateway_bond
.proxy
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
// send bonded funds back to the bond owner
let return_tokens = BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
to_address: info.sender.to_string(),
amount: vec![gateway_bond.pledge_amount()],
};
// remove the bond
storage::gateways().remove(deps.storage, gateway_bond.identity())?;
let mut response = Response::new().add_message(return_tokens);
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondGateway {
owner: owner.as_str().to_string(),
amount: gateway_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, vec![])?;
response = response.add_message(track_unbond_message);
}
Ok(response.add_event(new_gateway_unbonding_event(
&owner,
&proxy,
&gateway_bond.pledge_amount,
gateway_bond.identity(),
)))
Ok(Response::new()
.add_message(return_tokens)
.add_event(new_gateway_unbonding_event(
&info.sender,
&gateway_bond.pledge_amount,
gateway_bond.identity(),
)))
}
pub(crate) fn try_update_gateway_config(
@@ -198,36 +109,9 @@ pub(crate) fn try_update_gateway_config(
info: MessageInfo,
new_config: GatewayConfigUpdate,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_gateway_config(deps, new_config, owner, None)
}
let existing_bond = must_get_gateway_bond_by_owner(deps.storage, &info.sender)?;
let cfg_update_event = new_gateway_config_update_event(&info.sender, &new_config);
pub(crate) fn try_update_gateway_config_on_behalf(
deps: DepsMut,
info: MessageInfo,
new_config: GatewayConfigUpdate,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner = deps.api.addr_validate(&owner)?;
let proxy = info.sender;
_try_update_gateway_config(deps, new_config, owner, Some(proxy))
}
pub(crate) fn _try_update_gateway_config(
deps: DepsMut,
new_config: GatewayConfigUpdate,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_gateway_bond_by_owner(deps.storage, &owner)?;
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
let cfg_update_event = new_gateway_config_update_event(&owner, &proxy, &new_config);
// clippy beta 1.70.0-beta.1 false positive
#[allow(clippy::redundant_clone)]
let mut updated_bond = existing_bond.clone();
updated_bond.gateway.host = new_config.host;
updated_bond.gateway.mix_port = new_config.mix_port;
@@ -254,10 +138,10 @@ pub mod tests {
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
use crate::support::tests;
use crate::support::tests::fixtures;
use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge};
use crate::support::tests::fixtures::good_mixnode_pledge;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Uint128;
use cosmwasm_std::{Addr, Uint128};
use mixnet_contract_common::ExecuteMsg;
#[test]
@@ -392,42 +276,12 @@ pub mod tests {
.unwrap();
assert_eq!(1, updated_nonce);
_try_remove_gateway(test.deps_mut(), Addr::unchecked(sender), None).unwrap();
try_remove_gateway(test.deps_mut(), info.clone()).unwrap();
let res = try_add_gateway(test.deps_mut(), env, info, gateway, signature);
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
}
#[test]
fn gateway_add_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
let (gateway, sig) = test.gateway_with_signature(owner, None);
let res = try_add_gateway_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &good_gateway_pledge()),
gateway,
owner.to_string(),
sig,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
#[test]
fn gateway_remove() {
let mut test = TestSetup::new();
@@ -495,7 +349,6 @@ pub mod tests {
.add_message(expected_message)
.add_event(new_gateway_unbonding_event(
&Addr::unchecked("fred"),
&None,
&tests::fixtures::good_gateway_pledge()[0],
&fred_identity,
));
@@ -510,33 +363,6 @@ pub mod tests {
assert_eq!(&Addr::unchecked("bob"), nodes[0].owner());
}
#[test]
fn gateway_remove_with_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_gateway_with_illegal_proxy(owner, None, illegal_proxy.clone());
let res = try_remove_gateway_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &good_gateway_pledge()),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
#[test]
fn update_gateway_config() {
let mut test = TestSetup::new();
@@ -561,22 +387,6 @@ pub mod tests {
);
test.add_dummy_gateway(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_gateway_config_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
update.clone(),
owner.to_string(),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: vesting_contract.into_string()
})
);
// "normal" update succeeds
let res = try_update_gateway_config(test.deps_mut(), info, update.clone());
@@ -591,39 +401,4 @@ pub mod tests {
assert_eq!(bond.gateway.location, update.location);
assert_eq!(bond.gateway.version, update.version);
}
#[test]
fn updating_gateway_config_with_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_gateway_with_illegal_proxy(owner, None, illegal_proxy.clone());
let update = GatewayConfigUpdate {
host: "1.1.1.1:1234".to_string(),
mix_port: 1234,
clients_port: 1235,
location: "at home".to_string(),
version: "v1.2.3".to_string(),
};
let res = try_update_gateway_config_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
update,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
+41 -441
View File
@@ -24,7 +24,7 @@ use crate::interval::storage;
use crate::mixnodes::helpers::{cleanup_post_unbond_mixnode_storage, get_mixnode_details_by_id};
use crate::mixnodes::storage as mixnodes_storage;
use crate::rewards::storage as rewards_storage;
use crate::support::helpers::{send_to_proxy_or_owner, VestingTracking};
use crate::support::helpers::AttachSendTokens;
pub(crate) trait ContractExecutableEvent {
// note: the error only means a HARD error like we failed to read from storage.
@@ -40,7 +40,6 @@ pub(crate) fn delegate(
owner: Addr,
mix_id: MixId,
amount: Coin,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the target node still exists (it might have unbonded between this event getting created
// and being executed). Do note that it's absolutely possible for a mixnode to get immediately
@@ -56,20 +55,9 @@ pub(crate) fn delegate(
_ => {
// if mixnode is no longer bonded or in the process of unbonding, return the tokens back to the
// delegator;
// (read the notes regarding possible epoch progressiong halting behaviour in `maybe_add_track_undelegation_message`)
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![amount.clone()]);
let response = Response::new()
.add_message(return_tokens)
.add_event(new_delegation_on_unbonded_node_event(
&owner, &proxy, mix_id,
))
.maybe_add_track_vesting_undelegation_message(
deps.storage,
proxy,
owner.to_string(),
mix_id,
amount,
)?;
.send_tokens(&owner, amount.clone())
.add_event(new_delegation_on_unbonded_node_event(&owner, mix_id));
return Ok(response);
}
@@ -84,7 +72,7 @@ pub(crate) fn delegate(
// if there's an existing delegation, then withdraw the full reward and create a new delegation
// with the sum of both
let storage_key = Delegation::generate_storage_key(mix_id, &owner, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &owner, None);
let old_delegation = if let Some(existing_delegation) =
delegations_storage::delegations().may_load(deps.storage, storage_key.clone())?
{
@@ -106,7 +94,6 @@ pub(crate) fn delegate(
let cosmos_event = new_delegation_event(
created_at,
&owner,
&proxy,
&new_delegation_amount,
mix_id,
mix_rewarding.total_unit_reward,
@@ -118,7 +105,6 @@ pub(crate) fn delegate(
mix_rewarding.total_unit_reward,
stored_delegation_amount,
env.block.height,
proxy,
);
// save on reading since `.save()` would have attempted to read old data that we already have on hand
@@ -138,11 +124,10 @@ pub(crate) fn undelegate(
created_at: BlockHeight,
owner: Addr,
mix_id: MixId,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// see if the delegation still exists (in case of impatient user who decided to send multiple
// undelegation requests in an epoch)
let storage_key = Delegation::generate_storage_key(mix_id, &owner, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &owner, None);
let delegation = match delegations_storage::delegations().may_load(deps.storage, storage_key)? {
None => return Ok(Response::default()),
Some(delegation) => delegation,
@@ -155,18 +140,9 @@ pub(crate) fn undelegate(
let tokens_to_return =
delegations::helpers::undelegate(deps.storage, delegation, mix_rewarding)?;
// (read the notes regarding possible epoch progressiong halting behaviour in `maybe_add_track_undelegation_message`)
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![tokens_to_return.clone()]);
let response = Response::new()
.add_message(return_tokens)
.add_event(new_undelegation_event(created_at, &owner, &proxy, mix_id))
.maybe_add_track_vesting_undelegation_message(
deps.storage,
proxy,
owner.to_string(),
mix_id,
tokens_to_return,
)?;
.send_tokens(&owner, tokens_to_return.clone())
.add_event(new_undelegation_event(created_at, &owner, mix_id));
Ok(response)
}
@@ -197,25 +173,15 @@ pub(crate) fn unbond_mixnode(
.rewarding_details
.operator_pledge_with_reward(rewarding_denom);
let proxy = &node_details.bond_information.proxy;
let owner = &node_details.bond_information.owner;
// send bonded funds (alongside all earned rewards) to the bond owner
let return_tokens = send_to_proxy_or_owner(proxy, owner, vec![tokens.clone()]);
// remove the bond and if there are no delegations left, also the rewarding information
// decrement the associated layer count
cleanup_post_unbond_mixnode_storage(deps.storage, env, &node_details)?;
let response = Response::new()
.add_message(return_tokens)
.add_event(new_mixnode_unbonding_event(created_at, mix_id))
.maybe_add_track_vesting_unbond_mixnode_message(
deps.storage,
proxy.clone(),
owner.clone().into_string(),
tokens,
)?;
.send_tokens(&owner, tokens.clone())
.add_event(new_mixnode_unbonding_event(created_at, mix_id));
Ok(response)
}
@@ -311,12 +277,8 @@ pub(crate) fn decrease_pledge(
updated_bond.original_pledge.amount -= decrease_by.amount;
updated_rewarding.decrease_operator_uint128(decrease_by.amount)?;
let proxy = &mix_details.bond_information.proxy;
let owner = &mix_details.bond_information.owner;
// send the removed tokens back to the operator
let return_tokens = send_to_proxy_or_owner(proxy, owner, vec![decrease_by.clone()]);
// update all: bond information, rewarding details and pending pledge changes
mixnodes_storage::mixnode_bonds().replace(
deps.storage,
@@ -328,14 +290,8 @@ pub(crate) fn decrease_pledge(
mixnodes_storage::PENDING_MIXNODE_CHANGES.save(deps.storage, mix_id, &pending_changes)?;
let response = Response::new()
.add_message(return_tokens)
.add_event(new_pledge_decrease_event(created_at, mix_id, &decrease_by))
.maybe_add_track_vesting_decrease_mixnode_pledge(
deps.storage,
proxy.clone(),
owner.clone().to_string(),
decrease_by,
)?;
.send_tokens(owner, decrease_by.clone())
.add_event(new_pledge_decrease_event(created_at, mix_id, &decrease_by));
Ok(response)
}
@@ -349,13 +305,11 @@ impl ContractExecutableEvent for PendingEpochEventData {
owner,
mix_id,
amount,
proxy,
} => delegate(deps, env, self.created_at, owner, mix_id, amount, proxy),
PendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy,
} => undelegate(deps, self.created_at, owner, mix_id, proxy),
..
} => delegate(deps, env, self.created_at, owner, mix_id, amount),
PendingEpochEventKind::Undelegate { owner, mix_id, .. } => {
undelegate(deps, self.created_at, owner, mix_id)
}
PendingEpochEventKind::PledgeMore { mix_id, amount } => {
increase_pledge(deps, self.created_at, mix_id, amount)
}
@@ -472,33 +426,25 @@ impl ContractExecutableEvent for PendingIntervalEventData {
#[cfg(test)]
mod tests {
use std::time::Duration;
use cosmwasm_std::Decimal;
use mixnet_contract_common::Percent;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use super::*;
use crate::support::tests::test_helpers;
use crate::support::tests::test_helpers::{assert_decimals, TestSetup};
use super::*;
use cosmwasm_std::Decimal;
use mixnet_contract_common::Percent;
use std::time::Duration;
// note that authorization and basic validation has already been performed for all of those
// before being pushed onto the event queues
#[cfg(test)]
mod delegating {
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{coin, to_binary, CosmosMsg, WasmMsg};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use super::*;
use crate::mixnodes::transactions::try_remove_mixnode;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::get_bank_send_msg;
use super::*;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_info;
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
#[test]
fn returns_the_tokens_if_mixnode_has_unbonded() {
@@ -523,7 +469,6 @@ mod tests {
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
@@ -549,7 +494,6 @@ mod tests {
Addr::unchecked(owner2),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
let storage_key =
@@ -588,7 +532,6 @@ mod tests {
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
@@ -614,7 +557,6 @@ mod tests {
Addr::unchecked(owner2),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
let storage_key =
@@ -650,7 +592,6 @@ mod tests {
Addr::unchecked(owner),
mix_id,
delegation_coin_new,
None,
)
.unwrap();
@@ -725,7 +666,6 @@ mod tests {
Addr::unchecked(owner),
mix_id,
delegation_coin_new,
None,
)
.unwrap();
@@ -797,7 +737,6 @@ mod tests {
Addr::unchecked(owner),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
assert!(get_bank_send_msg(&res).is_none());
@@ -816,117 +755,13 @@ mod tests {
Decimal::from_atomics(delegation, 0).unwrap()
)
}
#[test]
fn attaches_vesting_contract_track_message_if_tokens_are_returned() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let delegation_coin = coin(delegation, TEST_COIN_DENOM);
let owner = "delegator";
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let vesting_contract = test.vesting_contract();
// for a fresh delegation, nothing was added to the storage either
let res_vesting = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin.clone(),
Some(vesting_contract.clone()),
)
.unwrap();
let storage_key = Delegation::generate_storage_key(
mix_id,
&Addr::unchecked(owner),
Some(vesting_contract.clone()).as_ref(),
);
assert!(delegations_storage::delegations()
.may_load(test.deps().storage, storage_key)
.unwrap()
.is_none());
// and all tokens are returned back to the proxy
let (receiver, sent_amount) = get_bank_send_msg(&res_vesting).unwrap();
assert_eq!(receiver, vesting_contract.as_str());
assert_eq!(sent_amount[0], delegation_coin);
// and we get appropriate track message
let mut found_track = true;
for msg in &res_vesting.messages {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msg.msg
{
found_track = true;
assert_eq!(contract_addr, vesting_contract.as_str());
let expected_msg = to_binary(&VestingContractExecuteMsg::TrackUndelegation {
owner: owner.to_string(),
mix_id,
amount: delegation_coin.clone(),
})
.unwrap();
assert_eq!(&expected_msg, msg);
assert!(funds.is_empty())
}
}
assert!(found_track);
}
#[test]
fn returns_error_for_illegal_proxy() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let delegation_coin = coin(delegation, TEST_COIN_DENOM);
let owner = "delegator";
let dummy_proxy = Addr::unchecked("not-vesting-contract");
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let vesting_contract = test.vesting_contract();
// try to add illegal delegation (with invalid proxy)
let res_other_proxy = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin,
Some(dummy_proxy.clone()),
)
.unwrap_err();
assert_eq!(
res_other_proxy,
MixnetContractError::ProxyIsNotVestingContract {
received: dummy_proxy,
vesting_contract,
}
);
}
}
#[cfg(test)]
mod undelegating {
use cosmwasm_std::{coin, to_binary, CosmosMsg, WasmMsg};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::get_bank_send_msg;
use super::*;
use crate::support::tests::test_helpers::get_bank_send_msg;
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
#[test]
fn doesnt_return_any_tokens_if_it_doesnt_exist() {
@@ -935,7 +770,7 @@ mod tests {
let owner = Addr::unchecked("delegator");
let res = undelegate(test.deps_mut(), 123, owner, mix_id, None).unwrap();
let res = undelegate(test.deps_mut(), 123, owner, mix_id).unwrap();
assert!(get_bank_send_msg(&res).is_none());
}
@@ -950,7 +785,7 @@ mod tests {
// this should never happen in actual code, but if we manually messed something up,
// lets make sure this throws an error
rewards_storage::MIXNODE_REWARDING.remove(test.deps_mut().storage, mix_id);
let res = undelegate(test.deps_mut(), 123, owner, mix_id, None);
let res = undelegate(test.deps_mut(), 123, owner, mix_id);
assert!(matches!(
res,
Err(MixnetContractError::InconsistentState { .. })
@@ -996,8 +831,7 @@ mod tests {
let expected_return = delegation + truncated_reward.u128();
let res =
undelegate(test.deps_mut(), 123, Addr::unchecked(owner), mix_id, None).unwrap();
let res = undelegate(test.deps_mut(), 123, Addr::unchecked(owner), mix_id).unwrap();
let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap();
assert_eq!(receiver, owner);
assert_eq!(sent_amount[0].amount.u128(), expected_return);
@@ -1015,117 +849,19 @@ mod tests {
assert!(rewarding.delegates.is_zero());
assert_eq!(rewarding.unique_delegations, 0);
}
#[test]
fn attaches_vesting_contract_track_message_if_tokens_are_returned() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let delegation_coin = coin(delegation, TEST_COIN_DENOM);
let owner = "delegator";
let vesting_contract = test.vesting_contract();
test.add_immediate_delegation_with_legal_proxy(owner, delegation, mix_id);
let res_vesting = undelegate(
test.deps_mut(),
123,
Addr::unchecked(owner),
mix_id,
Some(vesting_contract.clone()),
)
.unwrap();
let storage_key = Delegation::generate_storage_key(
mix_id,
&Addr::unchecked(owner),
Some(vesting_contract.clone()).as_ref(),
);
assert!(delegations_storage::delegations()
.may_load(test.deps().storage, storage_key)
.unwrap()
.is_none());
// and all tokens are returned back to the proxy
let (receiver, sent_amount) = get_bank_send_msg(&res_vesting).unwrap();
assert_eq!(receiver, vesting_contract.as_str());
assert_eq!(sent_amount[0], delegation_coin);
// and we get appropriate track message
let mut found_track = true;
for msg in &res_vesting.messages {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msg.msg
{
found_track = true;
assert_eq!(contract_addr, vesting_contract.as_str());
let expected_msg = to_binary(&VestingContractExecuteMsg::TrackUndelegation {
owner: owner.to_string(),
mix_id,
amount: delegation_coin.clone(),
})
.unwrap();
assert_eq!(&expected_msg, msg);
assert!(funds.is_empty())
}
}
assert!(found_track);
}
#[test]
fn returns_error_for_illegal_proxy() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let owner = "delegator1";
let vesting_contract = test.vesting_contract();
let dummy_proxy = Addr::unchecked("not-vesting-contract");
test.add_immediate_delegation_with_illegal_proxy(
owner,
delegation,
mix_id,
dummy_proxy.clone(),
);
let res_other_proxy = undelegate(
test.deps_mut(),
123,
Addr::unchecked(owner),
mix_id,
Some(dummy_proxy.clone()),
)
.unwrap_err();
assert_eq!(
res_other_proxy,
MixnetContractError::ProxyIsNotVestingContract {
received: dummy_proxy,
vesting_contract,
}
);
}
}
#[cfg(test)]
mod mixnode_unbonding {
use cosmwasm_std::{coin, to_binary, CosmosMsg, Uint128, WasmMsg};
use super::*;
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::{try_decrease_pledge, try_increase_pledge};
use crate::support::tests::test_helpers::get_bank_send_msg;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Uint128;
use mixnet_contract_common::mixnode::{PendingMixNodeChanges, UnbondedMixnode};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::{_try_decrease_pledge, _try_increase_pledge};
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::get_bank_send_msg;
use super::*;
#[test]
fn returns_hard_error_if_mixnode_doesnt_exist() {
// this should have never happened so hard error MUST be thrown here
@@ -1150,12 +886,10 @@ mod tests {
let pledge = Uint128::new(250_000_000);
let mix_id = test.add_dummy_mixnode(owner, Some(pledge));
_try_increase_pledge(
try_increase_pledge(
test.deps_mut(),
env.clone(),
change.clone(),
Addr::unchecked(owner),
None,
mock_info(owner, &*change.clone()),
)
.unwrap();
@@ -1170,12 +904,11 @@ mod tests {
let pledge = Uint128::new(250_000_000);
let mix_id = test.add_dummy_mixnode(owner, Some(pledge));
_try_decrease_pledge(
try_decrease_pledge(
test.deps_mut(),
env.clone(),
mock_info(owner, &[]),
change[0].clone(),
Addr::unchecked(owner),
None,
)
.unwrap();
@@ -1263,79 +996,6 @@ mod tests {
0
)
}
#[test]
fn attaches_vesting_contract_track_message_if_tokens_are_returned() {
let mut test = TestSetup::new();
let vesting_contract = test.vesting_contract();
let pledge = Uint128::new(250_000_000);
let pledge_coin = coin(250_000_000, TEST_COIN_DENOM);
let owner = "mix-owner1";
let mix_id_vesting = test.add_dummy_mixnode_with_legal_proxy(owner, Some(pledge));
let env = test.env();
let res = unbond_mixnode(test.deps_mut(), &env, 123, mix_id_vesting).unwrap();
assert!(mixnodes_storage::mixnode_bonds()
.may_load(test.deps().storage, mix_id_vesting)
.unwrap()
.is_none());
// and all tokens are returned back to the proxy
let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap();
assert_eq!(receiver, vesting_contract.as_str());
assert_eq!(sent_amount[0], pledge_coin);
// and we get appropriate track message
let mut found_track = true;
for msg in &res.messages {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msg.msg
{
found_track = true;
assert_eq!(contract_addr, vesting_contract.as_str());
let expected_msg = to_binary(&VestingContractExecuteMsg::TrackUnbondMixnode {
owner: owner.to_string(),
amount: pledge_coin.clone(),
})
.unwrap();
assert_eq!(&expected_msg, msg);
assert!(funds.is_empty())
}
}
assert!(found_track);
}
#[test]
fn returns_error_for_illegal_proxy() {
let mut test = TestSetup::new();
let dummy_proxy = Addr::unchecked("not-vesting-contract");
let env = test.env();
let vesting_contract = test.vesting_contract();
let owner = "mix-owner";
let pledge = Uint128::new(250_000_000);
let mix_id_illegal_proxy =
test.add_dummy_mixnode_with_illegal_proxy(owner, Some(pledge), dummy_proxy.clone());
// this is the halting issue that should have never occurred
let res_other_proxy =
unbond_mixnode(test.deps_mut(), &env, 123, mix_id_illegal_proxy).unwrap_err();
assert_eq!(
res_other_proxy,
MixnetContractError::ProxyIsNotVestingContract {
received: dummy_proxy,
vesting_contract,
}
);
}
}
#[cfg(test)]
@@ -1615,11 +1275,9 @@ mod tests {
#[cfg(test)]
mod decreasing_pledge {
use cosmwasm_std::{to_binary, BankMsg, CosmosMsg, Uint128, WasmMsg};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use super::*;
use cosmwasm_std::{BankMsg, CosmosMsg, Uint128};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
#[test]
fn returns_hard_error_if_mixnode_doesnt_exist() {
@@ -1699,64 +1357,6 @@ mod tests {
)
}
#[test]
fn returns_tokens_back_to_the_proxy_if_bonded_with_vesting() {
let mut test = TestSetup::new();
let owner = "mix-owner";
let mix_id = test.add_dummy_mixnode_with_legal_proxy(owner, None);
test.set_pending_pledge_change(mix_id, None);
let vesting_contract = test.vesting_contract();
let amount = test.coin(12345);
let res = decrease_pledge(test.deps_mut(), 123, mix_id, amount.clone()).unwrap();
assert_eq!(res.messages.len(), 2);
assert_eq!(
res.messages[0].msg,
CosmosMsg::Bank(BankMsg::Send {
to_address: vesting_contract.to_string(),
amount: vec![amount],
})
)
}
#[test]
fn attaches_vesting_track_message() {
let mut test = TestSetup::new();
let mix_id_no_proxy = test.add_dummy_mixnode("mix-owner1", None);
test.set_pending_pledge_change(mix_id_no_proxy, None);
let mix_id_proxy = test.add_dummy_mixnode_with_legal_proxy("mix-owner2", None);
test.set_pending_pledge_change(mix_id_proxy, None);
let vesting_contract = test.vesting_contract();
let amount = test.coin(12345);
let res_no_proxy =
decrease_pledge(test.deps_mut(), 123, mix_id_no_proxy, amount.clone()).unwrap();
// nothing was attached (apart from bank message tested in `returns_tokens_back_to_the_owner`)
// because it wasn't done with proxy!
assert_eq!(res_no_proxy.messages.len(), 1);
let res_proxy =
decrease_pledge(test.deps_mut(), 123, mix_id_proxy, amount.clone()).unwrap();
assert_eq!(res_proxy.messages.len(), 2);
assert_eq!(
res_proxy.messages[1].msg,
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: vesting_contract.to_string(),
msg: to_binary(&VestingContractExecuteMsg::TrackDecreasePledge {
owner: "mix-owner2".to_string(),
amount,
})
.unwrap(),
funds: vec![],
})
);
}
#[test]
fn without_any_events_in_between_is_equivalent_to_pledging_the_same_amount_immediately() {
let mut test = TestSetup::new();
+4 -10
View File
@@ -159,11 +159,8 @@ mod tests {
}
fn push_dummy_epoch_action(test: &mut TestSetup) {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action =
PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), test.rng.next_u32());
let env = test.env();
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
@@ -571,11 +568,8 @@ mod tests {
);
// it exists
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action =
PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), test.rng.next_u32());
let env = test.env();
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action.clone()).unwrap();
let expected = PendingEpochEventResponse {
+8 -10
View File
@@ -222,11 +222,10 @@ mod tests {
let env = test.env();
for _ in 0..500 {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action = PendingEpochEventKind::new_undelegate(
Addr::unchecked("foomp"),
test.rng.next_u32(),
);
let id = push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
let expected = EPOCH_EVENT_ID_COUNTER.load(test.deps().storage).unwrap();
assert_eq!(expected, id);
@@ -235,11 +234,10 @@ mod tests {
test.execute_all_pending_events();
for _ in 0..10 {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action = PendingEpochEventKind::new_undelegate(
Addr::unchecked("foomp"),
test.rng.next_u32(),
);
let id = push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
let expected = EPOCH_EVENT_ID_COUNTER.load(test.deps().storage).unwrap();
assert_eq!(expected, id);
+17 -57
View File
@@ -373,18 +373,14 @@ mod tests {
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::Addr;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
fn push_n_dummy_epoch_actions(test: &mut TestSetup, n: usize) {
// if you attempt to undelegate non-existent delegation,
// it will return an empty response, but will not fail
let env = test.env();
for i in 0..n {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: i as MixId,
proxy: None,
};
let dummy_action =
PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), i as MixId);
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
}
@@ -406,7 +402,7 @@ mod tests {
mod performing_pending_epoch_actions {
use super::*;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use cosmwasm_std::{coin, coins, wasm_execute, BankMsg, Empty, SubMsg};
use cosmwasm_std::{coin, coins, BankMsg, Empty, SubMsg};
use mixnet_contract_common::events::{
new_active_set_update_event, new_delegation_on_unbonded_node_event,
new_undelegation_event,
@@ -495,7 +491,6 @@ mod tests {
#[test]
fn catches_all_events_and_messages_from_executed_actions() {
let mut test = TestSetup::new();
let vesting_contract = test.vesting_contract();
let env = test.env();
let legit_mix = test.add_dummy_mixnode("mix-owner", None);
@@ -509,17 +504,15 @@ mod tests {
// delegate to node that doesn't exist,
// we expect to receive BankMsg with tokens being returned,
// and event regarding delegation
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
let non_existent_delegation = PendingEpochEventKind::new_delegate(
Addr::unchecked("foomp"),
123,
coin(123, TEST_COIN_DENOM),
);
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
&None,
123,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
@@ -527,33 +520,6 @@ mod tests {
amount: coins(123, TEST_COIN_DENOM),
}));
// delegation to node that doesn't exist with vesting contract
// we expect the same as above PLUS TrackUndelegation message
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp2"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: Some(vesting_contract.clone()),
};
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp2"),
&Some(vesting_contract.clone()),
123,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
to_address: vesting_contract.clone().into_string(),
amount: coins(123, TEST_COIN_DENOM),
}));
let msg = VestingContractExecuteMsg::TrackUndelegation {
owner: "foomp2".to_string(),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
};
let track_undelegate_message = wasm_execute(vesting_contract, &msg, vec![]).unwrap();
expected_messages.push(SubMsg::new(track_undelegate_message));
// updating active set should only emit events and no cosmos messages
let action_with_event = PendingEpochEventKind::UpdateActiveSetSize { new_size: 50 };
storage::push_new_epoch_event(test.deps_mut().storage, &env, action_with_event)
@@ -561,16 +527,12 @@ mod tests {
expected_events.push(new_active_set_update_event(env.block.height, 50));
// undelegation just returns tokens and emits event
let legit_undelegate = PendingEpochEventKind::Undelegate {
owner: delegator.clone(),
mix_id: legit_mix,
proxy: None,
};
let legit_undelegate =
PendingEpochEventKind::new_undelegate(delegator.clone(), legit_mix);
storage::push_new_epoch_event(test.deps_mut().storage, &env, legit_undelegate).unwrap();
expected_events.push(new_undelegation_event(
env.block.height,
&delegator,
&None,
legit_mix,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
@@ -583,9 +545,9 @@ mod tests {
let mut expected = Response::new().add_events(expected_events);
expected.messages = expected_messages;
assert_eq!(res, expected);
assert_eq!(executed, 4);
assert_eq!(executed, 3);
assert_eq!(
4,
3,
storage::LAST_PROCESSED_EPOCH_EVENT
.load(test.deps().storage)
.unwrap()
@@ -1330,17 +1292,15 @@ mod tests {
let mut expected_messages: Vec<SubMsg<Empty>> = Vec::new();
// epoch event
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
let non_existent_delegation = PendingEpochEventKind::new_delegate(
Addr::unchecked("foomp"),
123,
coin(123, TEST_COIN_DENOM),
);
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
&None,
123,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
@@ -43,12 +43,6 @@ pub(crate) fn rewarding_denom(storage: &dyn Storage) -> Result<String, MixnetCon
.map(|state| state.rewarding_denom)?)
}
pub(crate) fn vesting_contract_address(storage: &dyn Storage) -> Result<Addr, MixnetContractError> {
Ok(CONTRACT_STATE
.load(storage)
.map(|state| state.vesting_contract_address)?)
}
pub(crate) fn initialise_storage(
storage: &mut dyn Storage,
initial_state: ContractState,
+1 -14
View File
@@ -97,7 +97,6 @@ pub(crate) fn save_new_mixnode(
mixnode: MixNode,
cost_params: MixNodeCostParams,
owner: Addr,
proxy: Option<Addr>,
pledge: Coin,
) -> Result<(MixId, Layer), MixnetContractError> {
let layer = assign_layer(storage)?;
@@ -105,15 +104,7 @@ pub(crate) fn save_new_mixnode(
let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id();
let mixnode_rewarding = MixNodeRewarding::initialise_new(cost_params, &pledge, current_epoch)?;
let mixnode_bond = MixNodeBond::new(
mix_id,
owner,
pledge,
layer,
mixnode,
proxy,
env.block.height,
);
let mixnode_bond = MixNodeBond::new(mix_id, owner, pledge, layer, mixnode, env.block.height);
// save mixnode bond data
// note that this implicitly checks for uniqueness on identity key, sphinx key and owner
@@ -411,7 +402,6 @@ pub(crate) mod tests {
mixnode,
cost_params.clone(),
owner.clone(),
None,
pledge.clone(),
)
.unwrap();
@@ -444,7 +434,6 @@ pub(crate) mod tests {
mixnode,
cost_params.clone(),
Addr::unchecked("different-owner"),
None,
pledge.clone(),
);
assert!(res.is_err());
@@ -457,7 +446,6 @@ pub(crate) mod tests {
mixnode,
cost_params.clone(),
owner,
None,
pledge.clone(),
);
assert!(res.is_err());
@@ -471,7 +459,6 @@ pub(crate) mod tests {
mixnode,
cost_params,
Addr::unchecked("different-owner"),
None,
pledge,
);
assert!(res.is_err());
@@ -12,7 +12,6 @@ use nym_contracts_common::signing::Verifier;
pub(crate) fn verify_mixnode_bonding_signature(
deps: Deps<'_>,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
mixnode: MixNode,
cost_params: MixNodeCostParams,
@@ -23,8 +22,7 @@ pub(crate) fn verify_mixnode_bonding_signature(
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
let msg =
construct_mixnode_bonding_sign_payload(nonce, sender, proxy, pledge, mixnode, cost_params);
let msg = construct_mixnode_bonding_sign_payload(nonce, sender, pledge, mixnode, cost_params);
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
+23 -576
View File
@@ -1,8 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
use cosmwasm_std::{coin, Coin, DepsMut, Env, MessageInfo, Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_mixnode_bonding_event, new_mixnode_config_update_event,
@@ -25,8 +24,7 @@ use crate::mixnodes::signature_helpers::verify_mixnode_bonding_signature;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_bonded, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_no_existing_bond,
ensure_no_pending_pledge_changes, ensure_proxy_match, ensure_sent_by_vesting_contract,
validate_pledge,
ensure_no_pending_pledge_changes, validate_pledge,
};
use super::storage;
@@ -61,74 +59,24 @@ pub fn assign_mixnode_layer(
Ok(Response::default())
}
pub fn try_add_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
_try_add_mixnode(
deps,
env,
mix_node,
cost_params,
info.funds,
info.sender,
owner_signature,
None,
)
}
pub fn try_add_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_add_mixnode(
deps,
env,
mix_node,
cost_params,
info.funds,
owner,
owner_signature,
Some(proxy),
)
}
// I'm not entirely sure how to deal with this warning at the current moment
//
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
// so that we could return a better error message if it doesn't match?
#[allow(clippy::too_many_arguments)]
fn _try_add_mixnode(
pub(crate) fn try_add_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mixnode: MixNode,
cost_params: MixNodeCostParams,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the pledge contains any funds of the appropriate denomination
let minimum_pledge = mixnet_params_storage::minimum_mixnode_pledge(deps.storage)?;
let pledge = validate_pledge(pledge, minimum_pledge)?;
let pledge = validate_pledge(info.funds, minimum_pledge)?;
// if the client has an active bonded mixnode or gateway, don't allow bonding
// note that this has to be done explicitly as `UniqueIndex` constraint would not protect us
// against attempting to use different node types (i.e. gateways and mixnodes)
ensure_no_existing_bond(&owner, deps.storage)?;
ensure_no_existing_bond(&info.sender, deps.storage)?;
// there's no need to explicitly check whether there already exists mixnode with the same
// identity or sphinx keys as this is going to be done implicitly when attempting to save
@@ -137,8 +85,7 @@ fn _try_add_mixnode(
// check if this sender actually owns the mixnode by checking the signature
verify_mixnode_bonding_signature(
deps.as_ref(),
owner.clone(),
proxy.clone(),
info.sender.clone(),
pledge.clone(),
mixnode.clone(),
cost_params.clone(),
@@ -146,7 +93,7 @@ fn _try_add_mixnode(
)?;
// update the signing nonce associated with this sender so that the future signature would be made on the new value
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
signing_storage::increment_signing_nonce(deps.storage, info.sender.clone())?;
let node_identity = mixnode.identity_key.clone();
let (node_id, layer) = save_new_mixnode(
@@ -154,14 +101,12 @@ fn _try_add_mixnode(
env,
mixnode,
cost_params,
owner.clone(),
proxy.clone(),
info.sender.clone(),
pledge.clone(),
)?;
Ok(Response::new().add_event(new_mixnode_bonding_event(
&owner,
&proxy,
&info.sender,
&pledge,
&node_identity,
node_id,
@@ -174,43 +119,19 @@ pub fn try_increase_pledge(
env: Env,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_increase_pledge(deps, env, info.funds, info.sender, None)
}
pub fn try_increase_pledge_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_increase_pledge(deps, env, info.funds, owner, Some(proxy))
}
pub fn _try_increase_pledge(
deps: DepsMut<'_>,
env: Env,
increase: Vec<Coin>,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?;
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner: info.sender })?;
let mut pending_changes = mix_details.pending_changes;
let mix_id = mix_details.mix_id();
// increasing pledge is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?;
ensure_bonded(&mix_details.bond_information)?;
ensure_no_pending_pledge_changes(&pending_changes)?;
let rewarding_denom = rewarding_denom(deps.storage)?;
let pledge_increase = validate_pledge(increase, coin(1, rewarding_denom))?;
let pledge_increase = validate_pledge(info.funds, coin(1, rewarding_denom))?;
let cosmos_event = new_pending_pledge_increase_event(mix_id, &pledge_increase);
@@ -232,39 +153,14 @@ pub fn try_decrease_pledge(
info: MessageInfo,
decrease_by: Coin,
) -> Result<Response, MixnetContractError> {
_try_decrease_pledge(deps, env, decrease_by, info.sender, None)
}
pub fn try_decrease_pledge_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
decrease_by: Coin,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_decrease_pledge(deps, env, decrease_by, owner, Some(proxy))
}
pub fn _try_decrease_pledge(
deps: DepsMut<'_>,
env: Env,
decrease_by: Coin,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?;
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner: info.sender })?;
let mut pending_changes = mix_details.pending_changes;
let mix_id = mix_details.mix_id();
// decreasing pledge is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?;
ensure_bonded(&mix_details.bond_information)?;
ensure_no_pending_pledge_changes(&pending_changes)?;
@@ -312,34 +208,12 @@ pub fn _try_decrease_pledge(
Ok(Response::new().add_event(cosmos_event))
}
pub fn try_remove_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_remove_mixnode(deps, env, owner, Some(proxy))
}
pub fn try_remove_mixnode(
pub(crate) fn try_remove_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_remove_mixnode(deps, env, info.sender, None)
}
pub(crate) fn _try_remove_mixnode(
deps: DepsMut<'_>,
env: Env,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
let pending_changes = storage::PENDING_MIXNODE_CHANGES
.may_load(deps.storage, existing_bond.mix_id)?
.unwrap_or_default();
@@ -348,15 +222,12 @@ pub(crate) fn _try_remove_mixnode(
ensure_epoch_in_progress_state(deps.storage)?;
// see if the proxy matches
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
ensure_bonded(&existing_bond)?;
// if there are any pending requests to change the pledge, wait for them to resolve before allowing the unbonding
ensure_no_pending_pledge_changes(&pending_changes)?;
// set `is_unbonding` field
// clippy beta 1.70.0-beta.1 false positive
#[allow(clippy::redundant_clone)]
let mut updated_bond = existing_bond.clone();
updated_bond.is_unbonding = true;
storage::mixnode_bonds().replace(
@@ -375,7 +246,6 @@ pub(crate) fn _try_remove_mixnode(
Ok(
Response::new().add_event(new_pending_mixnode_unbonding_event(
&existing_bond.owner,
&existing_bond.proxy,
existing_bond.identity(),
existing_bond.mix_id,
)),
@@ -387,39 +257,13 @@ pub(crate) fn try_update_mixnode_config(
info: MessageInfo,
new_config: MixNodeConfigUpdate,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_mixnode_config(deps, new_config, owner, None)
}
pub(crate) fn try_update_mixnode_config_on_behalf(
deps: DepsMut<'_>,
info: MessageInfo,
new_config: MixNodeConfigUpdate,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner = deps.api.addr_validate(&owner)?;
let proxy = info.sender;
_try_update_mixnode_config(deps, new_config, owner, Some(proxy))
}
pub(crate) fn _try_update_mixnode_config(
deps: DepsMut<'_>,
new_config: MixNodeConfigUpdate,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
let cfg_update_event =
new_mixnode_config_update_event(existing_bond.mix_id, &owner, &proxy, &new_config);
new_mixnode_config_update_event(existing_bond.mix_id, &info.sender, &new_config);
// clippy beta 1.70.0-beta.1 false positive
#[allow(clippy::redundant_clone)]
let mut updated_bond = existing_bond.clone();
updated_bond.mix_node.host = new_config.host;
updated_bond.mix_node.mix_port = new_config.mix_port;
@@ -442,45 +286,18 @@ pub(crate) fn try_update_mixnode_cost_params(
env: Env,
info: MessageInfo,
new_costs: MixNodeCostParams,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_mixnode_cost_params(deps, env, new_costs, owner, None)
}
pub(crate) fn try_update_mixnode_cost_params_on_behalf(
deps: DepsMut,
env: Env,
info: MessageInfo,
new_costs: MixNodeCostParams,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner = deps.api.addr_validate(&owner)?;
let proxy = info.sender;
_try_update_mixnode_cost_params(deps, env, new_costs, owner, Some(proxy))
}
pub(crate) fn _try_update_mixnode_cost_params(
deps: DepsMut,
env: Env,
new_costs: MixNodeCostParams,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// see if the node still exists
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
// changing cost params is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
ensure_bonded(&existing_bond)?;
let cosmos_event = new_mixnode_pending_cost_params_update_event(
existing_bond.mix_id,
&owner,
&proxy,
&info.sender,
&new_costs,
);
@@ -497,7 +314,7 @@ pub(crate) fn _try_update_mixnode_cost_params(
#[cfg(test)]
pub mod tests {
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Order, StdResult, Uint128};
use cosmwasm_std::{Addr, Order, StdResult, Uint128};
use mixnet_contract_common::mixnode::PendingMixNodeChanges;
use mixnet_contract_common::{EpochState, EpochStatus, ExecuteMsg, LayerDistribution, Percent};
@@ -680,38 +497,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
}
#[test]
fn mixnode_add_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
let (mixnode, sig, _) = test.mixnode_with_signature(owner, None);
let cost_params = fixtures::mix_node_cost_params_fixture();
let res = try_add_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &good_mixnode_pledge()),
mixnode,
cost_params,
owner.to_string(),
sig,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn removing_mixnode_cant_be_performed_if_epoch_transition_is_in_progress() {
let bad_states = vec![
@@ -761,23 +546,6 @@ pub mod tests {
);
let mix_id = test.add_dummy_mixnode(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_remove_mixnode_on_behalf(
test.deps_mut(),
env.clone(),
mock_info(vesting_contract.as_ref(), &[]),
owner.to_string(),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: vesting_contract.into_string(),
})
);
// "normal" unbonding succeeds and unbonding event is pushed to the pending epoch events
let res = try_remove_mixnode(test.deps_mut(), env.clone(), info.clone());
@@ -799,35 +567,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
#[test]
fn mixnode_remove_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let res = try_remove_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[]),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn mixnode_remove_is_not_allowed_if_there_are_pending_pledge_changes() {
let mut test = TestSetup::new();
@@ -908,22 +647,7 @@ pub mod tests {
);
let mix_id = test.add_dummy_mixnode(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_mixnode_config_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
update.clone(),
owner.to_string(),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: vesting_contract.into_string(),
})
);
// "normal" update succeeds
let res = try_update_mixnode_config(test.deps_mut(), info.clone(), update.clone());
assert!(res.is_ok());
@@ -943,41 +667,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
#[test]
fn updating_mixnode_config_with_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let update = MixNodeConfigUpdate {
host: "1.1.1.1:1234".to_string(),
mix_port: 1234,
verloc_port: 1235,
http_api_port: 1236,
version: "v1.2.3".to_string(),
};
let res = try_update_mixnode_config_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
update,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn mixnode_cost_params_cant_be_updated_when_epoch_transition_is_in_progress() {
let bad_states = vec![
@@ -1042,23 +731,7 @@ pub mod tests {
);
let mix_id = test.add_dummy_mixnode(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_mixnode_cost_params_on_behalf(
test.deps_mut(),
env.clone(),
mock_info(vesting_contract.as_ref(), &[]),
update.clone(),
owner.to_string(),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: vesting_contract.into_string(),
})
);
// "normal" update succeeds
let res = try_update_mixnode_cost_params(
test.deps_mut(),
@@ -1099,40 +772,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
#[test]
fn updating_mixnode_cost_params_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let update = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(42).unwrap(),
interval_operating_cost: Coin::new(12345678, TEST_COIN_DENOM),
};
let res = try_update_mixnode_cost_params_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[]),
update,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
let mut test = TestSetup::new();
@@ -1240,69 +879,6 @@ pub mod tests {
)
}
#[test]
fn is_not_allowed_if_theres_proxy_mismatch() {
let mut test = TestSetup::new();
let env = test.env();
let owner_without_proxy = Addr::unchecked("no-proxy");
let owner_with_proxy = Addr::unchecked("with-proxy");
let proxy = Addr::unchecked("proxy");
let wrong_proxy = Addr::unchecked("unrelated-proxy");
test.add_dummy_mixnode(owner_without_proxy.as_str(), None);
test.add_dummy_mixnode_with_illegal_proxy(
owner_with_proxy.as_str(),
None,
proxy.clone(),
);
let res = _try_increase_pledge(
test.deps_mut(),
env.clone(),
Vec::new(),
owner_without_proxy.clone(),
Some(proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: "proxy".to_string(),
})
);
let res = _try_increase_pledge(
test.deps_mut(),
env.clone(),
Vec::new(),
owner_with_proxy.clone(),
None,
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "None".to_string(),
})
);
let res = _try_increase_pledge(
test.deps_mut(),
env,
Vec::new(),
owner_with_proxy.clone(),
Some(wrong_proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "unrelated-proxy".to_string(),
})
)
}
#[test]
fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() {
let mut test = TestSetup::new();
@@ -1457,35 +1033,6 @@ pub mod tests {
}
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let res = try_increase_pledge_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
#[cfg(test)]
@@ -1546,73 +1093,6 @@ pub mod tests {
)
}
#[test]
fn is_not_allowed_if_theres_proxy_mismatch() {
let mut test = TestSetup::new();
let env = test.env();
let owner_without_proxy = Addr::unchecked("no-proxy");
let owner_with_proxy = Addr::unchecked("with-proxy");
let proxy = Addr::unchecked("proxy");
let wrong_proxy = Addr::unchecked("unrelated-proxy");
// 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);
test.add_dummy_mixnode(owner_without_proxy.as_str(), Some(stake));
test.add_dummy_mixnode_with_illegal_proxy(
owner_with_proxy.as_str(),
Some(stake),
proxy.clone(),
);
let res = _try_decrease_pledge(
test.deps_mut(),
env.clone(),
decrease.clone(),
owner_without_proxy.clone(),
Some(proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: "proxy".to_string(),
})
);
let res = _try_decrease_pledge(
test.deps_mut(),
env.clone(),
decrease.clone(),
owner_with_proxy.clone(),
None,
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "None".to_string(),
})
);
let res = _try_decrease_pledge(
test.deps_mut(),
env,
decrease,
owner_with_proxy.clone(),
Some(wrong_proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "unrelated-proxy".to_string(),
})
)
}
#[test]
fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() {
let mut test = TestSetup::new();
@@ -1809,38 +1289,5 @@ pub mod tests {
}
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let stake = Uint128::new(100_000_000_000);
let decrease = test.coin(1000);
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, Some(stake), illegal_proxy.clone());
let res = try_decrease_pledge_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
decrease,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
}
@@ -1,2 +1,9 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::DepsMut;
use mixnet_contract_common::error::MixnetContractError;
pub(crate) fn vesting_purge(deps: DepsMut) -> Result<(), MixnetContractError> {
todo!("ensure no pending")
}
+33 -194
View File
@@ -1,8 +1,19 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{wasm_execute, Addr, DepsMut, Env, MessageInfo, Response};
use super::storage;
use crate::delegations::storage as delegations_storage;
use crate::interval::storage as interval_storage;
use crate::interval::storage::{push_new_epoch_event, push_new_interval_event};
use crate::mixnodes::helpers::get_mixnode_details_by_owner;
use crate::mixnodes::storage as mixnodes_storage;
use crate::rewards::helpers;
use crate::rewards::helpers::update_and_save_last_rewarded;
use crate::support::helpers::{
ensure_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_owner,
AttachSendTokens,
};
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_active_set_update_event, new_mix_rewarding_event,
@@ -16,22 +27,6 @@ use mixnet_contract_common::reward_params::{
IntervalRewardingParamsUpdate, NodeRewardParams, Performance,
};
use mixnet_contract_common::{Delegation, EpochState, MixId};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use crate::delegations::storage as delegations_storage;
use crate::interval::storage as interval_storage;
use crate::interval::storage::{push_new_epoch_event, push_new_interval_event};
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::helpers::get_mixnode_details_by_owner;
use crate::mixnodes::storage as mixnodes_storage;
use crate::rewards::helpers;
use crate::rewards::helpers::update_and_save_last_rewarded;
use crate::support::helpers::{
ensure_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_owner,
ensure_proxy_match, ensure_sent_by_vesting_contract, send_to_proxy_or_owner,
};
use super::storage;
pub(crate) fn try_reward_mixnode(
deps: DepsMut<'_>,
@@ -140,37 +135,16 @@ pub(crate) fn try_withdraw_operator_reward(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_withdraw_operator_reward(deps, info.sender, None)
}
pub(crate) fn try_withdraw_operator_reward_on_behalf(
deps: DepsMut<'_>,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_withdraw_operator_reward(deps, owner, Some(proxy))
}
pub(crate) fn _try_withdraw_operator_reward(
deps: DepsMut<'_>,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// we need to grab all of the node's details so we'd known original pledge alongside
// we need to grab all of the node's details, so we'd known original pledge alongside
// all the earned rewards (and obviously to know if this node even exists and is still
// in the bonded state)
let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())?.ok_or(
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?.ok_or(
MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
owner: info.sender.clone(),
},
)?;
let mix_id = mix_details.mix_id();
ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?;
ensure_bonded(&mix_details.bond_information)?;
let reward = helpers::withdraw_operator_reward(deps.storage, mix_details)?;
@@ -178,26 +152,13 @@ pub(crate) fn _try_withdraw_operator_reward(
// if the reward is zero, don't track or send anything - there's no point
if !reward.amount.is_zero() {
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![reward.clone()]);
response = response.add_message(return_tokens);
if let Some(proxy) = &proxy {
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
// otherwise, we don't care
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
if proxy == vesting_contract {
let msg = VestingContractExecuteMsg::TrackReward {
amount: reward.clone(),
address: owner.clone().into_string(),
};
let track_reward_message = wasm_execute(proxy, &msg, vec![])?;
response = response.add_message(track_reward_message);
}
}
response = response.send_tokens(&info.sender, reward.clone())
}
Ok(response.add_event(new_withdraw_operator_reward_event(
&owner, &proxy, reward, mix_id,
&info.sender,
reward,
mix_id,
)))
}
@@ -205,37 +166,15 @@ pub(crate) fn try_withdraw_delegator_reward(
deps: DepsMut<'_>,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_withdraw_delegator_reward(deps, mix_id, info.sender, None)
}
pub(crate) fn try_withdraw_delegator_reward_on_behalf(
deps: DepsMut<'_>,
info: MessageInfo,
mix_id: MixId,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_withdraw_delegator_reward(deps, mix_id, owner, Some(proxy))
}
pub(crate) fn _try_withdraw_delegator_reward(
deps: DepsMut<'_>,
mix_id: MixId,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// see if the delegation even exists
let storage_key = Delegation::generate_storage_key(mix_id, &owner, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &info.sender, None);
let delegation = match delegations_storage::delegations().may_load(deps.storage, storage_key)? {
None => {
return Err(MixnetContractError::NoMixnodeDelegationFound {
mix_id,
address: owner.into_string(),
proxy: proxy.map(Addr::into_string),
address: info.sender.into_string(),
proxy: None,
});
}
Some(delegation) => delegation,
@@ -257,33 +196,18 @@ pub(crate) fn _try_withdraw_delegator_reward(
_ => (),
};
ensure_proxy_match(&proxy, &delegation.proxy)?;
let reward = helpers::withdraw_delegator_reward(deps.storage, delegation, mix_rewarding)?;
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() {
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![reward.clone()]);
response = response.add_message(return_tokens);
if let Some(proxy) = &proxy {
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
// otherwise, we don't care
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
if proxy == vesting_contract {
let msg = VestingContractExecuteMsg::TrackReward {
amount: reward.clone(),
address: owner.clone().into_string(),
};
let track_reward_message = wasm_execute(proxy, &msg, vec![])?;
response = response.add_message(track_reward_message);
}
}
response = response.send_tokens(&info.sender, reward.clone())
}
Ok(response.add_event(new_withdraw_delegator_reward_event(
&owner, &proxy, reward, mix_id,
&info.sender,
reward,
mix_id,
)))
}
@@ -1405,13 +1329,10 @@ pub mod tests {
#[cfg(test)]
mod withdrawing_delegator_reward {
use cosmwasm_std::{coin, BankMsg, CosmosMsg, Decimal, Uint128};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use crate::interval::pending_events;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::{assert_eq_with_leeway, TestSetup};
use cosmwasm_std::{BankMsg, CosmosMsg, Decimal, Uint128};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use super::*;
@@ -1742,60 +1663,14 @@ pub mod tests {
let accumulated_actual = truncate_reward_amount(accumulated_quad);
assert_eq_with_leeway(total_claimed, accumulated_actual, Uint128::new(6));
}
#[test]
fn fails_for_illegal_proxy() {
let test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let mut test = TestSetup::new();
let mix_id =
test.add_dummy_mixnode("mix-owner1", Some(Uint128::new(1_000_000_000_000)));
let delegator = "delegator";
test.add_immediate_delegation_with_illegal_proxy(
delegator,
100_000_000u128,
mix_id,
illegal_proxy.clone(),
);
// reward the node
test.skip_to_next_epoch_end();
test.force_change_rewarded_set(vec![mix_id]);
test.start_epoch_transition();
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let res = try_withdraw_delegator_reward_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
mix_id,
delegator.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
#[cfg(test)]
mod withdrawing_operator_reward {
use cosmwasm_std::{coin, BankMsg, CosmosMsg, Uint128};
use crate::interval::pending_events;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::TestSetup;
use super::*;
use crate::interval::pending_events;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::{Addr, BankMsg, CosmosMsg, Uint128};
#[test]
fn can_only_be_done_if_bond_exists() {
@@ -1908,42 +1783,6 @@ pub mod tests {
})
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "mix-owner1";
let mix_id = test.add_dummy_mixnode_with_illegal_proxy(
owner,
Some(Uint128::new(1_000_000_000_000)),
illegal_proxy.clone(),
);
// reward the node
test.skip_to_next_epoch_end();
test.force_change_rewarded_set(vec![mix_id]);
test.start_epoch_transition();
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let res = try_withdraw_operator_reward_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
#[cfg(test)]
+10 -174
View File
@@ -2,13 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
use crate::gateways::storage as gateways_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, CosmosMsg, MessageInfo, Response, Storage};
use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::mixnode::PendingMixNodeChanges;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixId, MixNodeBond};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixNodeBond};
// helper trait to attach `Msg` to a response if it's provided
#[allow(dead_code)]
@@ -26,131 +24,16 @@ impl<T> AttachOptionalMessage<T> for Response<T> {
}
}
// another helper trait to remove some duplicate code and consolidate comments regarding
// possible epoch progression halting behaviour
pub(crate) trait VestingTracking
where
Self: Sized,
{
fn maybe_add_track_vesting_undelegation_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
mix_id: MixId,
amount: Coin,
) -> Result<Self, MixnetContractError>;
fn maybe_add_track_vesting_unbond_mixnode_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError>;
fn maybe_add_track_vesting_decrease_mixnode_pledge(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError>;
pub(crate) trait AttachSendTokens {
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self;
}
impl VestingTracking for Response {
fn maybe_add_track_vesting_undelegation_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
mix_id: MixId,
amount: Coin,
) -> Result<Self, MixnetContractError> {
// if there's a proxy set (i.e. the vesting contract), send the track message
if let Some(proxy) = proxy {
let vesting_contract = mixnet_params_storage::vesting_contract_address(storage)?;
// Note: this can INTENTIONALLY cause epoch progression halt if the proxy is not the vesting contract
// But this is fine, since this situation should have NEVER occurred in the first place
// (as all 'on_behalf' methods, including 'DelegateToMixnodeOnBehalf' that got us here,
// explicitly require the proxy to be the vesting contract)
// 'fixing' it would require manually inspecting the problematic event, investigating
// it's cause and manually (presumably via migration) clearing it.
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy,
vesting_contract,
});
}
let msg = VestingContractExecuteMsg::TrackUndelegation {
owner,
mix_id,
amount,
};
let track_undelegate_message = wasm_execute(proxy, &msg, vec![])?;
Ok(self.add_message(track_undelegate_message))
} else {
// there's no proxy so nothing to do
Ok(self)
}
}
fn maybe_add_track_vesting_unbond_mixnode_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError> {
// if there's a proxy set (i.e. the vesting contract), send the track message
if let Some(proxy) = proxy {
let vesting_contract = mixnet_params_storage::vesting_contract_address(storage)?;
// exactly the same possible halting behaviour as in `maybe_add_track_vesting_undelegation_message`.
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy,
vesting_contract,
});
}
let msg = VestingContractExecuteMsg::TrackUnbondMixnode { owner, amount };
let track_unbond_message = wasm_execute(proxy, &msg, vec![])?;
Ok(self.add_message(track_unbond_message))
} else {
// there's no proxy so nothing to do
Ok(self)
}
}
fn maybe_add_track_vesting_decrease_mixnode_pledge(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError> {
if let Some(proxy) = proxy {
let vesting_contract = mixnet_params_storage::vesting_contract_address(storage)?;
// exactly the same possible halting behaviour as in `maybe_add_track_vesting_undelegation_message`.
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy,
vesting_contract,
});
}
let msg = VestingContractExecuteMsg::TrackDecreasePledge { owner, amount };
let track_decrease_pledge_message = wasm_execute(proxy, &msg, vec![])?;
Ok(self.add_message(track_decrease_pledge_message))
} else {
// there's no proxy so nothing to do
Ok(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],
})
}
}
@@ -158,20 +41,6 @@ impl VestingTracking for Response {
// api.debug(&*format!("\n\n\n=========================================\n{}\n=========================================\n\n\n", msg.into()));
// }
/// Attempts to construct a `BankMsg` to send specified tokens to the provided
/// proxy address. If that's unavailable, the `BankMsg` will use the "owner" as the
/// "to_address".
pub(crate) fn send_to_proxy_or_owner(
proxy: &Option<Addr>,
owner: &Addr,
amount: Vec<Coin>,
) -> BankMsg {
BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(owner).to_string(),
amount,
}
}
pub(crate) fn validate_pledge(
mut pledge: Vec<Coin>,
minimum_pledge: Coin,
@@ -337,39 +206,6 @@ pub(crate) fn ensure_is_owner(
Ok(())
}
pub(crate) fn ensure_proxy_match(
actual: &Option<Addr>,
expected: &Option<Addr>,
) -> Result<(), MixnetContractError> {
if actual != expected {
return Err(MixnetContractError::ProxyMismatch {
existing: expected
.as_ref()
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: actual
.as_ref()
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
Ok(())
}
pub(crate) fn ensure_sent_by_vesting_contract(
info: &MessageInfo,
storage: &dyn Storage,
) -> Result<(), MixnetContractError> {
let vesting_contract_address =
crate::mixnet_contract_settings::storage::vesting_contract_address(storage)?;
if info.sender != vesting_contract_address {
Err(MixnetContractError::SenderIsNotVestingContract {
received: info.sender.clone(),
vesting_contract: vesting_contract_address,
})
} else {
Ok(())
}
}
pub(crate) fn ensure_bonded(bond: &MixNodeBond) -> Result<(), MixnetContractError> {
if bond.is_unbonding {
return Err(MixnetContractError::MixnodeIsUnbonding {
@@ -22,7 +22,7 @@ pub(crate) fn valid_bond_gateway_msg(
..tests::fixtures::gateway_fixture()
};
let msg = gateway_bonding_sign_payload(deps, sender, None, gateway.clone(), stake);
let msg = gateway_bonding_sign_payload(deps, sender, gateway.clone(), stake);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let identity_key = keypair.public_key().to_base58_string();
+16 -278
View File
@@ -14,8 +14,7 @@ pub mod test_helpers {
use crate::delegations::storage as delegations_storage;
use crate::delegations::transactions::try_delegate_to_mixnode;
use crate::families::transactions::{try_create_family, try_join_family};
use crate::gateways::storage as gateways_storage;
use crate::gateways::transactions::{try_add_gateway, try_add_gateway_on_behalf};
use crate::gateways::transactions::try_add_gateway;
use crate::interval::transactions::{
perform_pending_epoch_actions, perform_pending_interval_actions, try_begin_epoch_transition,
};
@@ -27,9 +26,7 @@ pub mod test_helpers {
};
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::storage::mixnode_bonds;
use crate::mixnodes::transactions::{
try_add_mixnode, try_add_mixnode_on_behalf, try_remove_mixnode,
};
use crate::mixnodes::transactions::{try_add_mixnode, try_remove_mixnode};
use crate::rewards::queries::{
query_pending_delegator_reward, query_pending_mixnode_operator_reward,
};
@@ -45,7 +42,7 @@ pub mod test_helpers {
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::testing::MockApi;
use cosmwasm_std::testing::MockQuerier;
use cosmwasm_std::{coin, coins, Addr, Api, BankMsg, CosmosMsg, Storage};
use cosmwasm_std::{coin, coins, Addr, BankMsg, CosmosMsg, Storage};
use cosmwasm_std::{Coin, Order};
use cosmwasm_std::{Decimal, Empty, MemoryStorage};
use cosmwasm_std::{Deps, OwnedDeps};
@@ -148,13 +145,6 @@ pub mod test_helpers {
self.owner.clone()
}
pub fn vesting_contract(&self) -> Addr {
mixnet_params_storage::CONTRACT_STATE
.load(self.deps().storage)
.unwrap()
.vesting_contract_address
}
pub fn coin(&self, amount: u128) -> Coin {
coin(amount, rewarding_denom(self.deps().storage).unwrap())
}
@@ -178,7 +168,6 @@ pub mod test_helpers {
&mut self,
family_owner_keys: &identity::KeyPair,
member_node: IdentityKeyRef,
vesting: bool,
) -> MessageSignature {
let identity = family_owner_keys.public_key().to_base58_string();
@@ -195,14 +184,7 @@ pub mod test_helpers {
let nonce = signing_storage::get_signing_nonce(self.deps().storage, owner).unwrap();
let proxy = if vesting {
Some(self.vesting_contract())
} else {
None
};
let msg =
construct_family_join_permit(nonce, family_head, proxy, member_node.to_owned());
let msg = construct_family_join_permit(nonce, family_head, member_node.to_owned());
let sig_bytes = family_owner_keys
.private_key()
@@ -217,13 +199,11 @@ pub mod test_helpers {
member: &str,
member_keys: &identity::KeyPair,
head_keys: &identity::KeyPair,
vesting: bool,
) {
let member_identity = member_keys.public_key().to_base58_string();
let head_identity = head_keys.public_key().to_base58_string();
let join_permit =
self.generate_family_join_permit(head_keys, &member_identity, vesting);
let join_permit = self.generate_family_join_permit(head_keys, &member_identity);
let family_head = FamilyHead::new(head_identity);
try_join_family(
@@ -235,12 +215,13 @@ pub mod test_helpers {
.unwrap();
}
#[allow(dead_code)]
pub fn create_dummy_mixnode_with_new_family(
&mut self,
head: &str,
label: &str,
) -> (MixId, identity::KeyPair) {
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let (mix_id, keys) = self.add_dummy_mixnode_with_keypair(head, None);
try_create_family(self.deps_mut(), mock_info(head, &[]), label.to_string()).unwrap();
(mix_id, keys)
@@ -338,7 +319,7 @@ pub mod test_helpers {
stake: Option<Uint128>,
) -> MessageSignature {
let stake = self.make_mix_pledge(stake);
let msg = mixnode_bonding_sign_payload(self.deps(), owner, None, mixnode, stake);
let msg = mixnode_bonding_sign_payload(self.deps(), owner, mixnode, stake);
ed25519_sign_message(msg, key)
}
@@ -359,13 +340,8 @@ pub mod test_helpers {
..tests::fixtures::mix_node_fixture()
};
let msg = mixnode_bonding_sign_payload(
self.deps(),
owner,
None,
mixnode.clone(),
stake.clone(),
);
let msg =
mixnode_bonding_sign_payload(self.deps(), owner, mixnode.clone(), stake.clone());
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let info = mock_info(owner, &stake);
@@ -389,156 +365,6 @@ pub mod test_helpers {
(current_id_counter + 1, keypair)
}
pub fn add_dummy_mixnode_with_proxy_and_keypair(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> (MixId, identity::KeyPair) {
let stake = self.make_mix_pledge(stake);
let proxy = self.vesting_contract();
let keypair = identity::KeyPair::new(&mut self.rng);
let identity_key = keypair.public_key().to_base58_string();
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
let mixnode = MixNode {
identity_key,
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
};
let msg = mixnode_bonding_sign_payload(
self.deps(),
owner,
Some(proxy.clone()),
mixnode.clone(),
stake.clone(),
);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let info = mock_info(proxy.as_str(), &stake);
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
.may_load(self.deps().storage)
.unwrap()
.unwrap_or_default();
let env = self.env();
try_add_mixnode_on_behalf(
self.deps_mut(),
env,
info,
mixnode,
tests::fixtures::mix_node_cost_params_fixture(),
owner.to_string(),
owner_signature,
)
.unwrap();
// newly added mixnode gets assigned the current counter + 1
(current_id_counter + 1, keypair)
}
pub fn add_dummy_mixnode_with_legal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> MixId {
self.add_dummy_mixnode_with_proxy_and_keypair(owner, stake)
.0
}
pub fn set_illegal_mixnode_proxy(&mut self, mix_id: MixId, proxy: Addr) {
let mut bond_details = mixnodes_storage::mixnode_bonds()
.load(self.deps().storage, mix_id)
.unwrap();
bond_details.proxy = Some(proxy);
mixnodes_storage::mixnode_bonds()
.save(self.deps_mut().storage, mix_id, &bond_details)
.unwrap();
}
pub fn add_dummy_gateway_with_illegal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
proxy: Addr,
) -> IdentityKey {
let gateway_identity = self.add_dummy_gateway_with_legal_proxy(owner, stake);
self.set_illegal_gateway_proxy(&gateway_identity, proxy);
gateway_identity
}
pub fn set_illegal_gateway_proxy(&mut self, gateway_id: &str, proxy: Addr) {
let mut gateway = gateways_storage::gateways()
.load(self.deps().storage, gateway_id)
.unwrap();
gateway.proxy = Some(proxy);
gateways_storage::gateways()
.save(self.deps_mut().storage, gateway_id, &gateway)
.unwrap();
}
pub fn add_dummy_gateway_with_legal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> IdentityKey {
let stake = match stake {
Some(amount) => {
let denom = rewarding_denom(self.deps().storage).unwrap();
Coin { denom, amount }
}
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
};
let keypair = identity::KeyPair::new(&mut self.rng);
let identity_key = keypair.public_key().to_base58_string();
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
let proxy = self.vesting_contract();
let gateway = Gateway {
identity_key,
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::gateway_fixture()
};
let msg = gateway_bonding_sign_payload(
self.deps(),
owner,
Some(proxy.clone()),
gateway.clone(),
vec![stake.clone()],
);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let env = self.env();
let info = mock_info(proxy.as_ref(), &[stake]);
try_add_gateway_on_behalf(
self.deps_mut(),
env,
info,
gateway,
owner.to_string(),
owner_signature,
)
.unwrap();
keypair.public_key().to_base58_string()
}
pub fn add_dummy_mixnode_with_illegal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
proxy: Addr,
) -> MixId {
let mix_id = self.add_dummy_mixnode_with_legal_proxy(owner, stake);
self.set_illegal_mixnode_proxy(mix_id, proxy);
mix_id
}
pub fn mixnode_with_signature(
&mut self,
sender: &str,
@@ -555,8 +381,7 @@ pub mod test_helpers {
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
};
let msg =
mixnode_bonding_sign_payload(self.deps(), sender, None, mixnode.clone(), stake);
let msg = mixnode_bonding_sign_payload(self.deps(), sender, mixnode.clone(), stake);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
(mixnode, owner_signature, keypair)
@@ -579,8 +404,7 @@ pub mod test_helpers {
..tests::fixtures::gateway_fixture()
};
let msg =
gateway_bonding_sign_payload(self.deps(), sender, None, gateway.clone(), stake);
let msg = gateway_bonding_sign_payload(self.deps(), sender, gateway.clone(), stake);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
(gateway, owner_signature)
@@ -625,87 +449,10 @@ pub mod test_helpers {
Addr::unchecked(delegator),
target,
amount,
None,
)
.unwrap();
}
pub fn add_immediate_delegation_with_legal_proxy(
&mut self,
delegator: &str,
amount: impl Into<Uint128>,
target: MixId,
) {
let denom = rewarding_denom(self.deps().storage).unwrap();
let amount = Coin {
denom,
amount: amount.into(),
};
let env = self.env();
let proxy = self.vesting_contract();
pending_events::delegate(
self.deps_mut(),
&env,
env.block.height,
Addr::unchecked(delegator),
target,
amount,
Some(proxy),
)
.unwrap();
}
// to set illegal proxy we have to bypass "normal" flow and put the value
// directly into the storage
pub fn add_immediate_delegation_with_illegal_proxy(
&mut self,
delegator: &str,
amount: impl Into<Uint128>,
target: MixId,
proxy: Addr,
) {
let denom = rewarding_denom(self.deps().storage).unwrap();
let amount = Coin {
denom,
amount: amount.into(),
};
let owner = self.deps.api.addr_validate(delegator).unwrap();
let storage_key = Delegation::generate_storage_key(target, &owner, Some(&proxy));
let mut mix_rewarding = self.mix_rewarding(target);
let mut stored_delegation_amount = amount;
if let Some(existing_delegation) = delegations_storage::delegations()
.may_load(&self.deps.storage, storage_key.clone())
.unwrap()
{
let og_with_reward = mix_rewarding.undelegate(&existing_delegation).unwrap();
stored_delegation_amount.amount += og_with_reward.amount;
}
mix_rewarding
.add_base_delegation(stored_delegation_amount.amount)
.unwrap();
let delegation = Delegation::new(
owner,
target,
mix_rewarding.total_unit_reward,
stored_delegation_amount,
self.env.block.height,
Some(proxy),
);
delegations_storage::delegations()
.save(&mut self.deps.storage, storage_key, &delegation)
.unwrap();
rewards_storage::MIXNODE_REWARDING
.save(&mut self.deps.storage, target, &mix_rewarding)
.unwrap();
}
#[allow(unused)]
pub fn add_delegation(
&mut self,
@@ -724,14 +471,8 @@ pub mod test_helpers {
pub fn remove_immediate_delegation(&mut self, delegator: &str, target: MixId) {
let height = self.env.block.height;
pending_events::undelegate(
self.deps_mut(),
height,
Addr::unchecked(delegator),
target,
None,
)
.unwrap();
pending_events::undelegate(self.deps_mut(), height, Addr::unchecked(delegator), target)
.unwrap();
}
pub fn start_epoch_transition(&mut self) {
@@ -1109,7 +850,6 @@ pub mod test_helpers {
Addr::unchecked(format!("owner{}", i)),
mix_id,
tests::fixtures::good_mixnode_pledge().pop().unwrap(),
None,
)
.unwrap();
}
@@ -1205,7 +945,6 @@ pub mod test_helpers {
pub fn mixnode_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
mixnode: MixNode,
stake: Vec<Coin>,
) -> SignableMixNodeBondingMsg {
@@ -1214,14 +953,13 @@ pub mod test_helpers {
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
let payload = MixnodeBondingPayload::new(mixnode, cost_params);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
let content = ContractMessageContent::new(Addr::unchecked(owner), stake, payload);
SignableMixNodeBondingMsg::new(nonce, content)
}
pub fn gateway_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
gateway: Gateway,
stake: Vec<Coin>,
) -> SignableGatewayBondingMsg {
@@ -1229,7 +967,7 @@ pub mod test_helpers {
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
let payload = GatewayBondingPayload::new(gateway);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
let content = ContractMessageContent::new(Addr::unchecked(owner), stake, payload);
SignableGatewayBondingMsg::new(nonce, content)
}