nym_mixnet_contract_common/
msg.rs

1// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::delegation::OwnerProxySubKey;
5use crate::error::MixnetContractError;
6use crate::families::FamilyHead;
7use crate::gateway::GatewayConfigUpdate;
8use crate::helpers::IntoBaseDecimal;
9use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
10use crate::reward_params::{
11    IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
12};
13use crate::{
14    delegation, ContractStateParams, EpochEventId, IntervalEventId, Layer, LayerAssignment, MixId,
15    Percent,
16};
17use crate::{Gateway, IdentityKey, MixNode};
18use contracts_common::signing::MessageSignature;
19use cosmwasm_std::{Coin, Decimal};
20use schemars::JsonSchema;
21use serde::{Deserialize, Serialize};
22use std::time::Duration;
23
24#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
25#[serde(rename_all = "snake_case")]
26pub struct InstantiateMsg {
27    pub rewarding_validator_address: String,
28    pub vesting_contract_address: String,
29
30    pub rewarding_denom: String,
31    pub epochs_in_interval: u32,
32    pub epoch_duration: Duration,
33    pub initial_rewarding_params: InitialRewardingParams,
34}
35
36#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
37#[serde(rename_all = "snake_case")]
38pub struct InitialRewardingParams {
39    pub initial_reward_pool: Decimal,
40    pub initial_staking_supply: Decimal,
41
42    pub staking_supply_scale_factor: Percent,
43    pub sybil_resistance: Percent,
44    pub active_set_work_factor: Decimal,
45    pub interval_pool_emission: Percent,
46
47    pub rewarded_set_size: u32,
48    pub active_set_size: u32,
49}
50
51impl InitialRewardingParams {
52    pub fn into_rewarding_params(
53        self,
54        epochs_in_interval: u32,
55    ) -> Result<RewardingParams, MixnetContractError> {
56        let epoch_reward_budget = self.initial_reward_pool
57            / epochs_in_interval.into_base_decimal()?
58            * self.interval_pool_emission;
59        let stake_saturation_point =
60            self.initial_staking_supply / self.rewarded_set_size.into_base_decimal()?;
61
62        Ok(RewardingParams {
63            interval: IntervalRewardParams {
64                reward_pool: self.initial_reward_pool,
65                staking_supply: self.initial_staking_supply,
66                staking_supply_scale_factor: self.staking_supply_scale_factor,
67                epoch_reward_budget,
68                stake_saturation_point,
69                sybil_resistance: self.sybil_resistance,
70                active_set_work_factor: self.active_set_work_factor,
71                interval_pool_emission: self.interval_pool_emission,
72            },
73            rewarded_set_size: self.rewarded_set_size,
74            active_set_size: self.active_set_size,
75        })
76    }
77}
78
79#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
80#[serde(rename_all = "snake_case")]
81pub enum ExecuteMsg {
82    AssignNodeLayer {
83        mix_id: MixId,
84        layer: Layer,
85    },
86    // Families
87    /// Only owner of the node can crate the family with node as head
88    CreateFamily {
89        label: String,
90    },
91    /// Family head needs to sign the joining node IdentityKey
92    JoinFamily {
93        join_permit: MessageSignature,
94        family_head: FamilyHead,
95    },
96    LeaveFamily {
97        family_head: FamilyHead,
98    },
99    KickFamilyMember {
100        member: IdentityKey,
101    },
102    CreateFamilyOnBehalf {
103        owner_address: String,
104        label: String,
105    },
106    /// Family head needs to sign the joining node IdentityKey, MixNode needs to provide its signature proving that it wants to join the family
107    JoinFamilyOnBehalf {
108        member_address: String,
109        join_permit: MessageSignature,
110        family_head: FamilyHead,
111    },
112    LeaveFamilyOnBehalf {
113        member_address: String,
114        family_head: FamilyHead,
115    },
116    KickFamilyMemberOnBehalf {
117        head_address: String,
118        member: IdentityKey,
119    },
120
121    // state/sys-params-related
122    UpdateRewardingValidatorAddress {
123        address: String,
124    },
125    UpdateContractStateParams {
126        updated_parameters: ContractStateParams,
127    },
128    UpdateActiveSetSize {
129        active_set_size: u32,
130        force_immediately: bool,
131    },
132    UpdateRewardingParams {
133        updated_params: IntervalRewardingParamsUpdate,
134        force_immediately: bool,
135    },
136    UpdateIntervalConfig {
137        epochs_in_interval: u32,
138        epoch_duration_secs: u64,
139        force_immediately: bool,
140    },
141    BeginEpochTransition {},
142    AdvanceCurrentEpoch {
143        new_rewarded_set: Vec<LayerAssignment>,
144        // families_in_layer: HashMap<String, Layer>,
145        expected_active_set_size: u32,
146    },
147    ReconcileEpochEvents {
148        limit: Option<u32>,
149    },
150
151    // mixnode-related:
152    BondMixnode {
153        mix_node: MixNode,
154        cost_params: MixNodeCostParams,
155        owner_signature: MessageSignature,
156    },
157    BondMixnodeOnBehalf {
158        mix_node: MixNode,
159        cost_params: MixNodeCostParams,
160        owner_signature: MessageSignature,
161        owner: String,
162    },
163    PledgeMore {},
164    PledgeMoreOnBehalf {
165        owner: String,
166    },
167    DecreasePledge {
168        decrease_by: Coin,
169    },
170    DecreasePledgeOnBehalf {
171        owner: String,
172        decrease_by: Coin,
173    },
174    UnbondMixnode {},
175    UnbondMixnodeOnBehalf {
176        owner: String,
177    },
178    UpdateMixnodeCostParams {
179        new_costs: MixNodeCostParams,
180    },
181    UpdateMixnodeCostParamsOnBehalf {
182        new_costs: MixNodeCostParams,
183        owner: String,
184    },
185    UpdateMixnodeConfig {
186        new_config: MixNodeConfigUpdate,
187    },
188    UpdateMixnodeConfigOnBehalf {
189        new_config: MixNodeConfigUpdate,
190        owner: String,
191    },
192
193    // gateway-related:
194    BondGateway {
195        gateway: Gateway,
196        owner_signature: MessageSignature,
197    },
198    BondGatewayOnBehalf {
199        gateway: Gateway,
200        owner: String,
201        owner_signature: MessageSignature,
202    },
203    UnbondGateway {},
204    UnbondGatewayOnBehalf {
205        owner: String,
206    },
207    UpdateGatewayConfig {
208        new_config: GatewayConfigUpdate,
209    },
210    UpdateGatewayConfigOnBehalf {
211        new_config: GatewayConfigUpdate,
212        owner: String,
213    },
214
215    // delegation-related:
216    DelegateToMixnode {
217        mix_id: MixId,
218    },
219    DelegateToMixnodeOnBehalf {
220        mix_id: MixId,
221        delegate: String,
222    },
223    UndelegateFromMixnode {
224        mix_id: MixId,
225    },
226    UndelegateFromMixnodeOnBehalf {
227        mix_id: MixId,
228        delegate: String,
229    },
230
231    // reward-related
232    RewardMixnode {
233        mix_id: MixId,
234        performance: Performance,
235    },
236    WithdrawOperatorReward {},
237    WithdrawOperatorRewardOnBehalf {
238        owner: String,
239    },
240    WithdrawDelegatorReward {
241        mix_id: MixId,
242    },
243    WithdrawDelegatorRewardOnBehalf {
244        mix_id: MixId,
245        owner: String,
246    },
247
248    // testing-only
249    #[cfg(feature = "contract-testing")]
250    TestingResolveAllPendingEvents {
251        limit: Option<u32>,
252    },
253}
254
255impl ExecuteMsg {
256    pub fn default_memo(&self) -> String {
257        match self {
258            ExecuteMsg::AssignNodeLayer { mix_id, layer } => {
259                format!("assigning mix {mix_id} for layer {layer:?}")
260            }
261            ExecuteMsg::CreateFamily { .. } => "crating node family with".to_string(),
262            ExecuteMsg::JoinFamily { family_head, .. } => {
263                format!("joining family {family_head}")
264            }
265            ExecuteMsg::LeaveFamily { family_head, .. } => {
266                format!("leaving family {family_head}")
267            }
268            ExecuteMsg::KickFamilyMember { member, .. } => {
269                format!("kicking {member} from family")
270            }
271            ExecuteMsg::CreateFamilyOnBehalf { .. } => "crating node family with".to_string(),
272            ExecuteMsg::JoinFamilyOnBehalf { family_head, .. } => {
273                format!("joining family {family_head}")
274            }
275            ExecuteMsg::LeaveFamilyOnBehalf { family_head, .. } => {
276                format!("leaving family {family_head}")
277            }
278            ExecuteMsg::KickFamilyMemberOnBehalf { member, .. } => {
279                format!("kicking {member} from family")
280            }
281            ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
282                format!("updating rewarding validator to {address}")
283            }
284            ExecuteMsg::UpdateContractStateParams { .. } => {
285                "updating mixnet state parameters".into()
286            }
287            ExecuteMsg::UpdateActiveSetSize {
288                active_set_size,
289                force_immediately,
290            } => format!(
291                "updating active set size to {active_set_size}. forced: {force_immediately}"
292            ),
293            ExecuteMsg::UpdateRewardingParams {
294                force_immediately, ..
295            } => format!("updating mixnet rewarding parameters. forced: {force_immediately}"),
296            ExecuteMsg::UpdateIntervalConfig {
297                force_immediately, ..
298            } => format!("updating mixnet interval configuration. forced: {force_immediately}"),
299            ExecuteMsg::BeginEpochTransition {} => "beginning epoch transition".into(),
300            ExecuteMsg::AdvanceCurrentEpoch { .. } => "advancing current epoch".into(),
301            ExecuteMsg::ReconcileEpochEvents { .. } => "reconciling epoch events".into(),
302            ExecuteMsg::BondMixnode { mix_node, .. } => {
303                format!("bonding mixnode {}", mix_node.identity_key)
304            }
305            ExecuteMsg::BondMixnodeOnBehalf { mix_node, .. } => {
306                format!("bonding mixnode {} on behalf", mix_node.identity_key)
307            }
308            ExecuteMsg::PledgeMore {} => "pledging additional tokens".into(),
309            ExecuteMsg::PledgeMoreOnBehalf { .. } => "pledging additional tokens on behalf".into(),
310            ExecuteMsg::DecreasePledge { .. } => "decreasing mixnode pledge".into(),
311            ExecuteMsg::DecreasePledgeOnBehalf { .. } => {
312                "decreasing mixnode pledge on behalf".into()
313            }
314            ExecuteMsg::UnbondMixnode { .. } => "unbonding mixnode".into(),
315            ExecuteMsg::UnbondMixnodeOnBehalf { .. } => "unbonding mixnode on behalf".into(),
316            ExecuteMsg::UpdateMixnodeCostParams { .. } => "updating mixnode cost parameters".into(),
317            ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { .. } => {
318                "updating mixnode cost parameters on behalf".into()
319            }
320            ExecuteMsg::UpdateMixnodeConfig { .. } => "updating mixnode configuration".into(),
321            ExecuteMsg::UpdateMixnodeConfigOnBehalf { .. } => {
322                "updating mixnode configuration on behalf".into()
323            }
324            ExecuteMsg::BondGateway { gateway, .. } => {
325                format!("bonding gateway {}", gateway.identity_key)
326            }
327            ExecuteMsg::BondGatewayOnBehalf { gateway, .. } => {
328                format!("bonding gateway {} on behalf", gateway.identity_key)
329            }
330            ExecuteMsg::UnbondGateway { .. } => "unbonding gateway".into(),
331            ExecuteMsg::UnbondGatewayOnBehalf { .. } => "unbonding gateway on behalf".into(),
332            ExecuteMsg::UpdateGatewayConfig { .. } => "updating gateway configuration".into(),
333            ExecuteMsg::UpdateGatewayConfigOnBehalf { .. } => {
334                "updating gateway configuration on behalf".into()
335            }
336            ExecuteMsg::DelegateToMixnode { mix_id } => format!("delegating to mixnode {mix_id}"),
337            ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, .. } => {
338                format!("delegating to mixnode {mix_id} on behalf")
339            }
340            ExecuteMsg::UndelegateFromMixnode { mix_id } => {
341                format!("removing delegation from mixnode {mix_id}")
342            }
343            ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, .. } => {
344                format!("removing delegation from mixnode {mix_id} on behalf")
345            }
346            ExecuteMsg::RewardMixnode {
347                mix_id,
348                performance,
349            } => format!("rewarding mixnode {mix_id} for performance {performance}"),
350            ExecuteMsg::WithdrawOperatorReward { .. } => "withdrawing operator reward".into(),
351            ExecuteMsg::WithdrawOperatorRewardOnBehalf { .. } => {
352                "withdrawing operator reward on behalf".into()
353            }
354            ExecuteMsg::WithdrawDelegatorReward { mix_id } => {
355                format!("withdrawing delegator reward from mixnode {mix_id}")
356            }
357            ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, .. } => {
358                format!("withdrawing delegator reward from mixnode {mix_id} on behalf")
359            }
360            #[cfg(feature = "contract-testing")]
361            ExecuteMsg::TestingResolveAllPendingEvents { .. } => {
362                "resolving all pending events".into()
363            }
364        }
365    }
366}
367
368#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
369#[serde(rename_all = "snake_case")]
370pub enum QueryMsg {
371    // families
372    GetAllFamiliesPaged {
373        limit: Option<u32>,
374        start_after: Option<String>,
375    },
376    GetAllMembersPaged {
377        limit: Option<u32>,
378        start_after: Option<String>,
379    },
380    GetFamilyByHead {
381        head: String,
382    },
383    GetFamilyByLabel {
384        label: String,
385    },
386    GetFamilyMembersByHead {
387        head: String,
388    },
389    GetFamilyMembersByLabel {
390        label: String,
391    },
392    // state/sys-params-related
393    GetContractVersion {},
394    #[serde(rename = "get_cw2_contract_version")]
395    GetCW2ContractVersion {},
396    GetRewardingValidatorAddress {},
397    GetStateParams {},
398    GetState {},
399    GetRewardingParams {},
400    GetEpochStatus {},
401    GetCurrentIntervalDetails {},
402    GetRewardedSet {
403        limit: Option<u32>,
404        start_after: Option<MixId>,
405    },
406
407    // mixnode-related:
408    GetMixNodeBonds {
409        limit: Option<u32>,
410        start_after: Option<MixId>,
411    },
412    GetMixNodesDetailed {
413        limit: Option<u32>,
414        start_after: Option<MixId>,
415    },
416    GetUnbondedMixNodes {
417        limit: Option<u32>,
418        start_after: Option<MixId>,
419    },
420    GetUnbondedMixNodesByOwner {
421        owner: String,
422        limit: Option<u32>,
423        start_after: Option<MixId>,
424    },
425    GetUnbondedMixNodesByIdentityKey {
426        identity_key: String,
427        limit: Option<u32>,
428        start_after: Option<MixId>,
429    },
430    GetOwnedMixnode {
431        address: String,
432    },
433    GetMixnodeDetails {
434        mix_id: MixId,
435    },
436    GetMixnodeRewardingDetails {
437        mix_id: MixId,
438    },
439    GetStakeSaturation {
440        mix_id: MixId,
441    },
442    GetUnbondedMixNodeInformation {
443        mix_id: MixId,
444    },
445    GetBondedMixnodeDetailsByIdentity {
446        mix_identity: IdentityKey,
447    },
448    GetLayerDistribution {},
449    // gateway-related:
450    GetGateways {
451        start_after: Option<IdentityKey>,
452        limit: Option<u32>,
453    },
454    GetGatewayBond {
455        identity: IdentityKey,
456    },
457    GetOwnedGateway {
458        address: String,
459    },
460
461    // delegation-related:
462    // gets all [paged] delegations associated with particular mixnode
463    GetMixnodeDelegations {
464        mix_id: MixId,
465        // since `start_after` is user-provided input, we can't use `Addr` as we
466        // can't guarantee it's validated.
467        start_after: Option<String>,
468        limit: Option<u32>,
469    },
470    // gets all [paged] delegations associated with particular delegator
471    GetDelegatorDelegations {
472        // since `delegator` is user-provided input, we can't use `Addr` as we
473        // can't guarantee it's validated.
474        delegator: String,
475        start_after: Option<(MixId, OwnerProxySubKey)>,
476        limit: Option<u32>,
477    },
478    // gets delegation associated with particular mixnode, delegator pair
479    GetDelegationDetails {
480        mix_id: MixId,
481        delegator: String,
482        proxy: Option<String>,
483    },
484    // gets all delegations in the system
485    GetAllDelegations {
486        start_after: Option<delegation::StorageKey>,
487        limit: Option<u32>,
488    },
489
490    // rewards related
491    GetPendingOperatorReward {
492        address: String,
493    },
494    GetPendingMixNodeOperatorReward {
495        mix_id: MixId,
496    },
497    GetPendingDelegatorReward {
498        address: String,
499        mix_id: MixId,
500        proxy: Option<String>,
501    },
502    // given the provided performance, estimate the reward at the end of the current epoch
503    GetEstimatedCurrentEpochOperatorReward {
504        mix_id: MixId,
505        estimated_performance: Performance,
506    },
507    GetEstimatedCurrentEpochDelegatorReward {
508        address: String,
509        mix_id: MixId,
510        proxy: Option<String>,
511        estimated_performance: Performance,
512    },
513
514    // interval-related
515    GetPendingEpochEvents {
516        limit: Option<u32>,
517        start_after: Option<u32>,
518    },
519    GetPendingIntervalEvents {
520        limit: Option<u32>,
521        start_after: Option<u32>,
522    },
523    GetPendingEpochEvent {
524        event_id: EpochEventId,
525    },
526    GetPendingIntervalEvent {
527        event_id: IntervalEventId,
528    },
529    GetNumberOfPendingEvents {},
530
531    // signing-related
532    GetSigningNonce {
533        address: String,
534    },
535}
536
537#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
538#[serde(rename_all = "snake_case")]
539pub struct MigrateMsg {
540    pub vesting_contract_address: Option<String>,
541}