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 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 pub min_round_count: u64,
136
137 pub max_round_count: u64,
142
143 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 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 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 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 return false;
201 }
202 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 pub round: Round,
314
315 pub proposer_timestamp_ms: i64,
317
318 pub leader_proposal_history: LeaderProposalHistory,
324}
325
326pub type ConsensusManagerNextRoundManifestInput = ConsensusManagerNextRoundInput;
327
328impl ConsensusManagerNextRoundInput {
329 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 pub gap_round_leaders: Vec<ValidatorIndex>,
360
361 pub current_leader: ValidatorIndex,
363
364 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 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 pub xrd_bucket: Bucket,
568 pub epoch: Epoch,
570 pub proposals_made: u64,
572 pub proposals_missed: u64,
574}
575
576#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
577pub struct ValidatorApplyEmissionManifestInput {
578 pub xrd_bucket: ManifestBucket,
581 pub epoch: Epoch,
583 pub proposals_made: u64,
585 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 pub xrd_bucket: Bucket,
597 pub epoch: Epoch,
599}
600
601#[derive(Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe)]
602pub struct ValidatorApplyRewardManifestInput {
603 pub xrd_bucket: ManifestBucket,
605 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;