radix_engine_interface/blueprints/consensus_manager/
invocations.rs

1use crate::blueprints::component::*;
2use crate::blueprints::resource::*;
3use crate::internal_prelude::*;
4use radix_common::crypto::Secp256k1PublicKey;
5use radix_common::data::manifest::model::ManifestAddressReservation;
6use radix_common::math::{traits::*, Decimal};
7use radix_common::prelude::ManifestBucket;
8use radix_common::prelude::CONSENSUS_MANAGER_PACKAGE;
9use radix_common::time::{Instant, TimeComparisonOperator};
10use radix_common::types::*;
11use sbor::rust::fmt::Debug;
12use sbor::rust::string::String;
13use sbor::rust::vec::Vec;
14
15pub const CONSENSUS_MANAGER_BLUEPRINT: &str = "ConsensusManager";
16pub const VALIDATOR_BLUEPRINT: &str = "Validator";
17
18define_type_marker!(Some(CONSENSUS_MANAGER_PACKAGE), ConsensusManager);
19define_type_marker!(Some(CONSENSUS_MANAGER_PACKAGE), Validator);
20
21pub const CONSENSUS_MANAGER_CREATE_IDENT: &str = "create";
22
23#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
24pub struct ConsensusManagerCreateInput {
25    pub validator_owner_token_address: GlobalAddressReservation,
26    pub component_address: GlobalAddressReservation,
27    pub initial_epoch: Epoch,
28    pub initial_config: ConsensusManagerConfig,
29    pub initial_time_ms: i64,
30    pub initial_current_leader: Option<ValidatorIndex>,
31}
32
33#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
34pub struct ConsensusManagerCreateManifestInput {
35    pub validator_owner_token_address: ManifestAddressReservation,
36    pub component_address: ManifestAddressReservation,
37    pub initial_epoch: Epoch,
38    pub initial_config: ConsensusManagerConfig,
39    pub initial_time_ms: i64,
40    pub initial_current_leader: Option<ValidatorIndex>,
41}
42
43#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
44pub struct ConsensusManagerConfig {
45    pub max_validators: u32,
46    pub epoch_change_condition: EpochChangeCondition,
47    pub num_unstake_epochs: u64,
48    pub total_emission_xrd_per_epoch: Decimal,
49    /// The proportion of proposals a validator needs to complete in an epoch to get emissions
50    /// Should be between 0 and 1
51    pub min_validator_reliability: Decimal,
52    pub num_owner_stake_units_unlock_epochs: u64,
53    pub num_fee_increase_delay_epochs: u64,
54
55    pub validator_creation_usd_cost: Decimal,
56}
57
58impl ConsensusManagerConfig {
59    pub fn mainnet_genesis() -> Self {
60        Self {
61            max_validators: 100,
62            epoch_change_condition: EpochChangeCondition {
63                min_round_count: 500,
64                max_round_count: 3000,
65                target_duration_millis: 300000,
66            },
67            num_unstake_epochs: 2016,
68            total_emission_xrd_per_epoch: dec!("2853.881278538812785388"),
69            min_validator_reliability: Decimal::one(),
70            num_owner_stake_units_unlock_epochs: 8064,
71            num_fee_increase_delay_epochs: 4032,
72            validator_creation_usd_cost: dec!(1000),
73        }
74    }
75
76    pub fn test_default() -> Self {
77        ConsensusManagerConfig {
78            max_validators: 10,
79            epoch_change_condition: EpochChangeCondition {
80                min_round_count: 1,
81                max_round_count: 1,
82                target_duration_millis: 0,
83            },
84            num_unstake_epochs: 1,
85            total_emission_xrd_per_epoch: Decimal::one(),
86            min_validator_reliability: Decimal::one(),
87            num_owner_stake_units_unlock_epochs: 2,
88            num_fee_increase_delay_epochs: 1,
89            validator_creation_usd_cost: dec!(100),
90        }
91    }
92
93    pub fn with_max_validators(mut self, new_value: u32) -> Self {
94        self.max_validators = new_value;
95        self
96    }
97
98    pub fn with_epoch_change_condition(mut self, new_value: EpochChangeCondition) -> Self {
99        self.epoch_change_condition = new_value;
100        self
101    }
102
103    pub fn with_num_unstake_epochs(mut self, new_value: u64) -> Self {
104        self.num_unstake_epochs = new_value;
105        self
106    }
107
108    pub fn with_total_emission_xrd_per_epoch(mut self, new_value: Decimal) -> Self {
109        self.total_emission_xrd_per_epoch = new_value;
110        self
111    }
112
113    pub fn with_min_validator_reliability(mut self, new_value: Decimal) -> Self {
114        self.min_validator_reliability = new_value;
115        self
116    }
117
118    pub fn with_num_owner_stake_units_unlock_epochs(mut self, new_value: u64) -> Self {
119        self.num_owner_stake_units_unlock_epochs = new_value;
120        self
121    }
122
123    pub fn with_num_fee_increase_delay_epochs(mut self, new_value: u64) -> Self {
124        self.num_fee_increase_delay_epochs = new_value;
125        self
126    }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Default, ScryptoSbor, ManifestSbor)]
130pub struct EpochChangeCondition {
131    /// A minimum number of rounds that *must* happen in an epoch.
132    /// The timestamp will not drive the epoch progression until at least this number of rounds is
133    /// reached (i.e. if an actual number of rounds after [`duration_millis`] is less than this
134    /// value, the epoch change will wait until this value is reached).
135    pub min_round_count: u64,
136
137    /// A maximum number of rounds that *can* happen in an epoch.
138    /// If an actual number of rounds reaches this value before [`duration_millis`], then the
139    /// timestamp no longer drives the epoch progression (i.e. the epoch change will happen right
140    /// away, to prevent more than [`max_round_count`] rounds).
141    pub max_round_count: u64,
142
143    /// An "ideal" duration of an epoch, which should be applied if the number of epochs is within
144    /// the `min_round_count..max_round_count` range.
145    /// Note: the range exists in order to limit the amount of damage that can be done by
146    /// semi-byzantine purposeful clock drift attacks.
147    pub target_duration_millis: u64,
148}
149
150pub enum EpochChangeOutcome {
151    NoChange,
152    Change {
153        next_epoch_effective_start_millis: i64,
154    },
155}
156
157impl EpochChangeCondition {
158    /// Determines whether this condition is met by the given actual state.
159    /// See the condition's field definitions for exact rules.
160    pub fn should_epoch_change(
161        &self,
162        effective_start: i64,
163        current_time: i64,
164        round: Round,
165    ) -> EpochChangeOutcome {
166        let epoch_duration_millis =
167            // The application invariants in `check_non_decreasing_and_update_timestamps`
168            // ensures that current_time > effective_start, and genesis should ensure
169            // effective_start > 0.
170            // This is just a sanity-check to avoid overflow if something invaraint fails.
171            if current_time >= 0 && effective_start >= 0 && current_time > effective_start {
172                (current_time - effective_start) as u64
173            } else {
174                0
175            };
176        if self.is_change_criterion_met(epoch_duration_millis, round) {
177            // The following aims to prevent small systematic drift in the epoch length each epoch,
178            // due to overheads / time noticing end of epoch.
179            // If the actual epoch length is sufficiently close to the target epoch length, we just
180            // pretend the effective epoch length was actually the target epoch length.
181            let next_epoch_effective_start_millis =
182                if self.is_actual_duration_close_to_target(epoch_duration_millis) {
183                    effective_start.saturating_add_unsigned(self.target_duration_millis)
184                } else {
185                    current_time
186                };
187            EpochChangeOutcome::Change {
188                next_epoch_effective_start_millis,
189            }
190        } else {
191            EpochChangeOutcome::NoChange
192        }
193    }
194
195    fn is_actual_duration_close_to_target(&self, actual_duration_millis: u64) -> bool {
196        let bounds_are_compatible_with_calculation =
197            actual_duration_millis >= 1000 && self.target_duration_millis >= 1000;
198        if !bounds_are_compatible_with_calculation {
199            // Need to avoid issues with divide by zero etc
200            return false;
201        }
202        // NOTE: Decimal arithmetic operation safe unwrap.
203        // No realistic chance to overflow.
204        // 100 years in ms is less than 2^35
205        let proportion_difference = (Decimal::from(actual_duration_millis)
206            .checked_sub(self.target_duration_millis)
207            .expect("Overflow"))
208        .checked_div(self.target_duration_millis)
209        .expect("Overflow");
210        proportion_difference <= dec!("0.1")
211    }
212
213    fn is_change_criterion_met(&self, duration_millis: u64, round: Round) -> bool {
214        if round.number() >= self.max_round_count {
215            true
216        } else if round.number() < self.min_round_count {
217            false
218        } else {
219            duration_millis >= self.target_duration_millis
220        }
221    }
222}
223
224pub type ConsensusManagerCreateOutput = ();
225
226pub const CONSENSUS_MANAGER_GET_CURRENT_EPOCH_IDENT: &str = "get_current_epoch";
227
228#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
229pub struct ConsensusManagerGetCurrentEpochInput;
230
231pub type ConsensusManagerGetCurrentEpochManifestInput = ConsensusManagerGetCurrentEpochInput;
232
233pub type ConsensusManagerGetCurrentEpochOutput = Epoch;
234
235pub const CONSENSUS_MANAGER_START_IDENT: &str = "start";
236
237#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
238pub struct ConsensusManagerStartInput {}
239
240pub type ConsensusManagerStartManifestInput = ConsensusManagerStartInput;
241
242pub type ConsensusManagerStartOutput = ();
243
244#[derive(Copy, Clone, Debug, Eq, PartialEq, Sbor)]
245#[sbor(type_name = "TimePrecision")]
246pub enum TimePrecisionV1 {
247    Minute,
248}
249
250#[derive(Copy, Clone, Debug, Eq, PartialEq, Sbor)]
251#[sbor(type_name = "TimePrecision")]
252pub enum TimePrecisionV2 {
253    Minute,
254    Second,
255}
256
257pub type TimePrecision = TimePrecisionV2;
258
259pub const CONSENSUS_MANAGER_GET_CURRENT_TIME_IDENT: &str = "get_current_time";
260
261#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
262#[sbor(type_name = "ConsensusManagerGetCurrentTimeInput")]
263pub struct ConsensusManagerGetCurrentTimeInputV1 {
264    pub precision: TimePrecisionV1,
265}
266
267pub type ConsensusManagerGetCurrentTimeManifestInputV1 = ConsensusManagerGetCurrentTimeInputV1;
268
269#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
270#[sbor(type_name = "ConsensusManagerGetCurrentTimeInput")]
271pub struct ConsensusManagerGetCurrentTimeInputV2 {
272    pub precision: TimePrecisionV2,
273}
274
275pub type ConsensusManagerGetCurrentTimeManifestInputV2 = ConsensusManagerGetCurrentTimeInputV2;
276
277pub type ConsensusManagerGetCurrentTimeOutput = Instant;
278
279pub const CONSENSUS_MANAGER_COMPARE_CURRENT_TIME_IDENT: &str = "compare_current_time";
280
281#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
282#[sbor(type_name = "ConsensusManagerCompareCurrentTimeInput")]
283pub struct ConsensusManagerCompareCurrentTimeInputV1 {
284    pub instant: Instant,
285    pub precision: TimePrecisionV1,
286    pub operator: TimeComparisonOperator,
287}
288
289pub type ConsensusManagerCompareCurrentTimeManifestInputV1 =
290    ConsensusManagerCompareCurrentTimeInputV1;
291
292#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
293#[sbor(type_name = "ConsensusManagerCompareCurrentTimeInput")]
294pub struct ConsensusManagerCompareCurrentTimeInputV2 {
295    pub instant: Instant,
296    pub precision: TimePrecisionV2,
297    pub operator: TimeComparisonOperator,
298}
299
300pub type ConsensusManagerCompareCurrentTimeManifestInputV2 =
301    ConsensusManagerCompareCurrentTimeInputV2;
302
303pub type ConsensusManagerCompareCurrentTimeOutput = bool;
304
305pub const CONSENSUS_MANAGER_NEXT_ROUND_IDENT: &str = "next_round";
306
307#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
308pub struct ConsensusManagerNextRoundInput {
309    /// Current round number.
310    /// Please note that in case of liveness breaks, this number may be different than previous
311    /// reported `round + 1`. Such gaps are considered "round leader's fault" and are penalized
312    /// on emission, according to leader reliability statistics (see `LeaderProposalHistory`).
313    pub round: Round,
314
315    /// Current millisecond timestamp of the proposer.
316    pub proposer_timestamp_ms: i64,
317
318    /// A captured history of leader proposal reliability since the previously reported round.
319    // TODO(post-babylon): we should change the approach here, so that the Engine drives the
320    // leader rotation, and the Node only informs it on round success/fallback/miss (in order to
321    // avoid certain byzantine quorum behaviors). The entire `leader_proposal_history` information
322    // will then no longer be required.
323    pub leader_proposal_history: LeaderProposalHistory,
324}
325
326pub type ConsensusManagerNextRoundManifestInput = ConsensusManagerNextRoundInput;
327
328impl ConsensusManagerNextRoundInput {
329    /// Creates a "next round" input for a regular (happy-path, in terms of consensus) round
330    /// progression, i.e. no missed proposals, no fallback rounds.
331    /// Please note that the current round's number passed here should be an immediate successor of
332    /// the previously reported round.
333    pub fn successful(
334        current_round: Round,
335        current_leader: ValidatorIndex,
336        current_timestamp_ms: i64,
337    ) -> Self {
338        Self {
339            round: current_round,
340            proposer_timestamp_ms: current_timestamp_ms,
341            leader_proposal_history: LeaderProposalHistory {
342                gap_round_leaders: Vec::new(),
343                current_leader,
344                is_fallback: false,
345            },
346        }
347    }
348}
349
350#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
351pub struct LeaderProposalHistory {
352    /// The validators which were leaders of the "gap" rounds (i.e. those that were not reported to
353    /// the consensus manager since the previous call; see `ConsensusManagerNextRoundInput::round`).
354    /// This list will contain exactly `current_call.round - previous_call.round - 1` elements; in
355    /// theory, this makes `ConsensusManagerNextRoundInput::round` field redundant (i.e. computable),
356    /// but this relation can be used for an extra consistency check.
357    /// The validators on this list should be penalized during emissions at the end of the current
358    /// epoch.
359    pub gap_round_leaders: Vec<ValidatorIndex>,
360
361    /// The leader of the current round.
362    pub current_leader: ValidatorIndex,
363
364    /// Whether the current round was conducted in a "fallback" mode (i.e. indicating a fault
365    /// of the current leader).
366    /// When `true`, the `current_leader` should be penalized during emissions in the same way as
367    /// `gap_round_leaders`.
368    /// When `false`, the `current_leader` is considered to have made this round's proposal
369    /// successfully.
370    pub is_fallback: bool,
371}
372
373pub type ConsensusManagerNextRoundOutput = ();
374
375pub const CONSENSUS_MANAGER_CREATE_VALIDATOR_IDENT: &str = "create_validator";
376
377#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
378pub struct ConsensusManagerCreateValidatorInput {
379    pub key: Secp256k1PublicKey,
380    pub fee_factor: Decimal,
381    pub xrd_payment: Bucket,
382}
383
384#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
385pub struct ConsensusManagerCreateValidatorManifestInput {
386    pub key: Secp256k1PublicKey,
387    pub fee_factor: Decimal,
388    pub xrd_payment: ManifestBucket,
389}
390
391pub type ConsensusManagerCreateValidatorOutput = (Global<ValidatorMarker>, Bucket, Bucket);
392
393pub const VALIDATOR_REGISTER_IDENT: &str = "register";
394
395#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
396pub struct ValidatorRegisterInput {}
397
398pub type ValidatorRegisterManifestInput = ValidatorRegisterInput;
399
400pub type ValidatorRegisterOutput = ();
401
402pub const VALIDATOR_UNREGISTER_IDENT: &str = "unregister";
403
404#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
405pub struct ValidatorUnregisterInput {}
406
407pub type ValidatorUnregisterManifestInput = ValidatorUnregisterInput;
408
409pub type ValidatorUnregisterOutput = ();
410
411pub const VALIDATOR_STAKE_AS_OWNER_IDENT: &str = "stake_as_owner";
412
413#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
414pub struct ValidatorStakeAsOwnerInput {
415    pub stake: Bucket,
416}
417
418#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
419pub struct ValidatorStakeAsOwnerManifestInput {
420    pub stake: ManifestBucket,
421}
422
423pub type ValidatorStakeAsOwnerOutput = Bucket;
424
425pub const VALIDATOR_STAKE_IDENT: &str = "stake";
426#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
427pub struct ValidatorStakeInput {
428    pub stake: Bucket,
429}
430
431#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
432pub struct ValidatorStakeManifestInput {
433    pub stake: ManifestBucket,
434}
435
436pub type ValidatorStakeOutput = Bucket;
437
438pub const VALIDATOR_UNSTAKE_IDENT: &str = "unstake";
439#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
440pub struct ValidatorUnstakeInput {
441    pub stake_unit_bucket: Bucket,
442}
443
444#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
445pub struct ValidatorUnstakeManifestInput {
446    pub stake_unit_bucket: ManifestBucket,
447}
448
449pub type ValidatorUnstakeOutput = Bucket;
450
451pub const VALIDATOR_CLAIM_XRD_IDENT: &str = "claim_xrd";
452#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
453pub struct ValidatorClaimXrdInput {
454    pub bucket: Bucket,
455}
456
457#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
458pub struct ValidatorClaimXrdManifestInput {
459    pub bucket: ManifestBucket,
460}
461
462pub type ValidatorClaimXrdOutput = Bucket;
463
464pub const VALIDATOR_UPDATE_KEY_IDENT: &str = "update_key";
465
466#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
467pub struct ValidatorUpdateKeyInput {
468    pub key: Secp256k1PublicKey,
469}
470
471pub type ValidatorUpdateKeyManifestInput = ValidatorUpdateKeyInput;
472
473pub type ValidatorUpdateKeyOutput = ();
474
475pub const VALIDATOR_UPDATE_FEE_IDENT: &str = "update_fee";
476
477#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
478pub struct ValidatorUpdateFeeInput {
479    /// A fraction of the effective emission amount which gets transferred to the validator's owner.
480    /// Must be within `[0.0, 1.0]`.
481    pub new_fee_factor: Decimal,
482}
483
484pub type ValidatorUpdateFeeManifestInput = ValidatorUpdateFeeInput;
485
486pub type ValidatorUpdateFeeOutput = ();
487
488pub const VALIDATOR_UPDATE_ACCEPT_DELEGATED_STAKE_IDENT: &str = "update_accept_delegated_stake";
489
490#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
491pub struct ValidatorUpdateAcceptDelegatedStakeInput {
492    pub accept_delegated_stake: bool,
493}
494
495pub type ValidatorUpdateAcceptDelegatedStakeManifestInput =
496    ValidatorUpdateAcceptDelegatedStakeInput;
497
498pub type ValidatorUpdateAcceptDelegatedStakeOutput = ();
499
500pub const VALIDATOR_ACCEPTS_DELEGATED_STAKE_IDENT: &str = "accepts_delegated_stake";
501
502#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
503pub struct ValidatorAcceptsDelegatedStakeInput {}
504
505pub type ValidatorAcceptsDelegatedStakeManifestInput = ValidatorAcceptsDelegatedStakeInput;
506
507pub type ValidatorAcceptsDelegatedStakeOutput = bool;
508
509pub const VALIDATOR_TOTAL_STAKE_XRD_AMOUNT_IDENT: &str = "total_stake_xrd_amount";
510
511#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
512pub struct ValidatorTotalStakeXrdAmountInput {}
513
514pub type ValidatorTotalStakeXrdAmountManifestInput = ValidatorTotalStakeXrdAmountInput;
515
516pub type ValidatorTotalStakeXrdAmountOutput = Decimal;
517
518pub const VALIDATOR_TOTAL_STAKE_UNIT_SUPPLY_IDENT: &str = "total_stake_unit_supply";
519
520#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
521pub struct ValidatorTotalStakeUnitSupplyInput {}
522
523pub type ValidatorTotalStakeUnitSupplyManifestInput = ValidatorTotalStakeUnitSupplyInput;
524
525pub type ValidatorTotalStakeUnitSupplyOutput = Decimal;
526
527pub const VALIDATOR_GET_REDEMPTION_VALUE_IDENT: &str = "get_redemption_value";
528
529#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
530pub struct ValidatorGetRedemptionValueInput {
531    pub amount_of_stake_units: Decimal,
532}
533
534pub type ValidatorGetRedemptionValueManifestInput = ValidatorGetRedemptionValueInput;
535
536pub type ValidatorGetRedemptionValueOutput = Decimal;
537
538pub const VALIDATOR_SIGNAL_PROTOCOL_UPDATE_READINESS_IDENT: &str =
539    "signal_protocol_update_readiness";
540
541#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
542pub struct ValidatorSignalProtocolUpdateReadinessInput {
543    pub vote: String,
544}
545
546pub type ValidatorSignalProtocolUpdateReadinessManifestInput =
547    ValidatorSignalProtocolUpdateReadinessInput;
548
549pub type ValidatorSignalProtocolUpdateReadinessOutput = ();
550
551pub const VALIDATOR_GET_PROTOCOL_UPDATE_READINESS_IDENT: &str = "get_protocol_update_readiness";
552
553#[derive(Debug, Clone, Eq, PartialEq, Sbor)]
554pub struct ValidatorGetProtocolUpdateReadinessInput {}
555
556pub type ValidatorGetProtocolUpdateReadinessManifestInput =
557    ValidatorGetProtocolUpdateReadinessInput;
558
559pub type ValidatorGetProtocolUpdateReadinessOutput = Option<String>;
560
561pub const VALIDATOR_APPLY_EMISSION_IDENT: &str = "apply_emission";
562
563#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
564pub struct ValidatorApplyEmissionInput {
565    /// A bucket with the emitted XRDs for this validator.
566    /// The validator should subtract the configured fee from this amount.
567    pub xrd_bucket: Bucket,
568    /// The *concluded* epoch's number. Informational-only.
569    pub epoch: Epoch,
570    /// A number of proposals successfully made by this validator during the emission period.
571    pub proposals_made: u64,
572    /// A number of proposals missed by this validator during the emission period.
573    pub proposals_missed: u64,
574}
575
576#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
577pub struct ValidatorApplyEmissionManifestInput {
578    /// A bucket with the emitted XRDs for this validator.
579    /// The validator should subtract the configured fee from this amount.
580    pub xrd_bucket: ManifestBucket,
581    /// The *concluded* epoch's number. Informational-only.
582    pub epoch: Epoch,
583    /// A number of proposals successfully made by this validator during the emission period.
584    pub proposals_made: u64,
585    /// A number of proposals missed by this validator during the emission period.
586    pub proposals_missed: u64,
587}
588
589pub type ValidatorApplyEmissionOutput = ();
590
591pub const VALIDATOR_APPLY_REWARD_IDENT: &str = "apply_reward";
592
593#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
594pub struct ValidatorApplyRewardInput {
595    /// A bucket with the rewarded XRDs (from transaction fees) for this validator.
596    pub xrd_bucket: Bucket,
597    /// The *concluded* epoch's number. Informational-only.
598    pub epoch: Epoch,
599}
600
601#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
602pub struct ValidatorApplyRewardManifestInput {
603    /// A bucket with the rewarded XRDs (from transaction fees) for this validator.
604    pub xrd_bucket: ManifestBucket,
605    /// The *concluded* epoch's number. Informational-only.
606    pub epoch: Epoch,
607}
608
609pub type ValidatorApplyRewardOutput = ();
610
611pub const VALIDATOR_LOCK_OWNER_STAKE_UNITS_IDENT: &str = "lock_owner_stake_units";
612
613#[derive(Debug, Eq, PartialEq, ScryptoSbor)]
614pub struct ValidatorLockOwnerStakeUnitsInput {
615    pub stake_unit_bucket: Bucket,
616}
617
618#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
619pub struct ValidatorLockOwnerStakeUnitsManifestInput {
620    pub stake_unit_bucket: ManifestBucket,
621}
622
623pub type ValidatorLockOwnerStakeUnitsOutput = ();
624
625pub const VALIDATOR_START_UNLOCK_OWNER_STAKE_UNITS_IDENT: &str = "start_unlock_owner_stake_units";
626
627#[derive(Debug, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
628pub struct ValidatorStartUnlockOwnerStakeUnitsInput {
629    pub requested_stake_unit_amount: Decimal,
630}
631
632pub type ValidatorStartUnlockOwnerStakeUnitsManifestInput =
633    ValidatorStartUnlockOwnerStakeUnitsInput;
634
635pub type ValidatorStartUnlockOwnerStakeUnitsOutput = ();
636
637pub const VALIDATOR_FINISH_UNLOCK_OWNER_STAKE_UNITS_IDENT: &str = "finish_unlock_owner_stake_units";
638
639#[derive(Debug, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
640pub struct ValidatorFinishUnlockOwnerStakeUnitsInput {}
641
642pub type ValidatorFinishUnlockOwnerStakeUnitsManifestInput =
643    ValidatorFinishUnlockOwnerStakeUnitsInput;
644
645pub type ValidatorFinishUnlockOwnerStakeUnitsOutput = Bucket;