removed all on_behalf mixnet contract methods
This commit is contained in:
@@ -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 {
|
||||
|
||||
-1
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user