unc_primitives/
epoch_manager.rs

1use crate::challenge::SlashedValidator;
2use crate::num_rational::Rational32;
3use crate::shard_layout::ShardLayout;
4use crate::types::validator_power::{ValidatorPower, ValidatorPowerV1};
5use crate::types::validator_power_and_pledge::{
6    ValidatorPowerAndPledge, ValidatorPowerAndPledgeV1,
7};
8use crate::types::validator_stake::{ValidatorPledge, ValidatorPledgeV1};
9
10use crate::types::{
11    AccountId, Balance, BlockHeightDelta, EpochHeight, EpochId, NumSeats, ProtocolVersion,
12    ValidatorId, ValidatorKickoutReason,
13};
14use crate::validator_mandates::ValidatorMandates;
15use crate::version::PROTOCOL_VERSION;
16use borsh::{BorshDeserialize, BorshSerialize};
17use smart_default::SmartDefault;
18use std::collections::{BTreeMap, HashMap};
19use unc_primitives_core::checked_feature;
20use unc_primitives_core::hash::CryptoHash;
21use unc_primitives_core::types::{BlockHeight, Power};
22
23pub type RngSeed = [u8; 32];
24
25pub const AGGREGATOR_KEY: &[u8] = b"AGGREGATOR";
26
27///
28///
29///
30#[derive(Clone, Eq, Debug, PartialEq)]
31pub struct BlockConfig {
32    /// Number of seats for block producers.
33    pub num_block_producer_seats: NumSeats,
34    /// Number of seats of block producers per each shard.
35    pub num_block_producer_seats_per_shard: Vec<NumSeats>,
36    /// Expected number of hidden validator seats per each shard.
37    pub avg_hidden_validator_seats_per_shard: Vec<NumSeats>,
38    /// Criterion for kicking out block producers.
39    pub block_producer_kickout_threshold: u8,
40    /// Criterion for kicking out chunk producers.
41    pub chunk_producer_kickout_threshold: u8,
42    /// Max ratio of validators that we can kick out in an epoch
43    pub validator_max_kickout_pledge_perc: u8,
44    /// Online minimum threshold below which validator doesn't receive reward.
45    pub online_min_threshold: Rational32,
46    /// Online maximum threshold above which validator gets full reward.
47    pub online_max_threshold: Rational32,
48    /// Pledge threshold for becoming a fisherman.
49    pub fishermen_threshold: Balance,
50    /// The minimum pledge required for staking is last seat price divided by this number.
51    pub minimum_pledge_divisor: u64,
52    /// Threshold of pledge that needs to indicate that they ready for upgrade.
53    pub protocol_upgrade_pledge_threshold: Rational32,
54    /// Shard layout of this epoch, may change from epoch to epoch
55    pub shard_layout: ShardLayout,
56    /// Additional config for validator selection algorithm
57    pub validator_selection_config: ValidatorSelectionConfig,
58}
59/// Epoch config, determines validator assignment for given epoch.
60/// Can change from epoch to epoch depending on the sharding and other parameters, etc.
61#[derive(Clone, Eq, Debug, PartialEq)]
62pub struct EpochConfig {
63    /// Epoch length in block heights.
64    pub epoch_length: BlockHeightDelta,
65    /// Number of seats for block producers.
66    pub num_block_producer_seats: NumSeats,
67    /// Number of seats of block producers per each shard.
68    pub num_block_producer_seats_per_shard: Vec<NumSeats>,
69    /// Expected number of hidden validator seats per each shard.
70    pub avg_hidden_validator_seats_per_shard: Vec<NumSeats>,
71    /// Criterion for kicking out block producers.
72    pub block_producer_kickout_threshold: u8,
73    /// Criterion for kicking out chunk producers.
74    pub chunk_producer_kickout_threshold: u8,
75    /// Max ratio of validators that we can kick out in an epoch
76    pub validator_max_kickout_pledge_perc: u8,
77    /// Online minimum threshold below which validator doesn't receive reward.
78    pub online_min_threshold: Rational32,
79    /// Online maximum threshold above which validator gets full reward.
80    pub online_max_threshold: Rational32,
81    /// Pledge threshold for becoming a fisherman.
82    pub fishermen_threshold: Balance,
83    /// The minimum pledge required for staking is last seat price divided by this number.
84    pub minimum_pledge_divisor: u64,
85    /// Threshold of pledge that needs to indicate that they ready for upgrade.
86    pub protocol_upgrade_pledge_threshold: Rational32,
87    /// Shard layout of this epoch, may change from epoch to epoch
88    pub shard_layout: ShardLayout,
89    /// Additional config for validator selection algorithm
90    pub validator_selection_config: ValidatorSelectionConfig,
91}
92
93#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
94pub struct ShardConfig {
95    pub num_block_producer_seats_per_shard: Vec<NumSeats>,
96    pub avg_hidden_validator_seats_per_shard: Vec<NumSeats>,
97    pub shard_layout: ShardLayout,
98}
99
100impl ShardConfig {
101    pub fn new(epoch_config: EpochConfig) -> Self {
102        Self {
103            num_block_producer_seats_per_shard: epoch_config
104                .num_block_producer_seats_per_shard
105                .clone(),
106            avg_hidden_validator_seats_per_shard: epoch_config
107                .avg_hidden_validator_seats_per_shard
108                .clone(),
109            shard_layout: epoch_config.shard_layout,
110        }
111    }
112}
113
114/// Testing overrides to apply to the EpochConfig returned by the `for_protocol_version`.
115/// All fields should be optional and the default should be a no-op.
116#[derive(Clone, Default)]
117pub struct AllEpochConfigTestOverrides {
118    pub block_producer_kickout_threshold: Option<u8>,
119    pub chunk_producer_kickout_threshold: Option<u8>,
120}
121
122/// AllEpochConfig manages protocol configs that might be changing throughout epochs (hence EpochConfig).
123/// The main function in AllEpochConfig is ::for_protocol_version which takes a protocol version
124/// and returns the EpochConfig that should be used for this protocol version.
125#[derive(Clone)]
126pub struct AllEpochConfig {
127    /// Whether this is for production (i.e., mainnet or testnet). This is a temporary implementation
128    /// to allow us to change protocol config for mainnet and testnet without changing the genesis config
129    use_production_config: bool,
130    /// EpochConfig from genesis
131    genesis_epoch_config: EpochConfig,
132    /// Chain Id. Some parameters are specific to certain chains.
133    chain_id: String,
134
135    /// Testing overrides to apply to the EpochConfig returned by the `for_protocol_version`.
136    test_overrides: AllEpochConfigTestOverrides,
137}
138
139impl AllEpochConfig {
140    pub fn new(
141        use_production_config: bool,
142        genesis_epoch_config: EpochConfig,
143        chain_id: &str,
144    ) -> Self {
145        Self {
146            use_production_config,
147            genesis_epoch_config,
148            chain_id: chain_id.to_string(),
149            test_overrides: AllEpochConfigTestOverrides::default(),
150        }
151    }
152
153    pub fn new_with_test_overrides(
154        use_production_config: bool,
155        genesis_epoch_config: EpochConfig,
156        chain_id: &str,
157        test_overrides: Option<AllEpochConfigTestOverrides>,
158    ) -> Self {
159        Self {
160            use_production_config,
161            genesis_epoch_config,
162            chain_id: chain_id.to_string(),
163            test_overrides: test_overrides.unwrap_or_default(),
164        }
165    }
166
167    pub fn for_protocol_version(&self, protocol_version: ProtocolVersion) -> EpochConfig {
168        let mut config = self.genesis_epoch_config.clone();
169        if !self.use_production_config {
170            return config;
171        }
172
173        Self::config_nightshade(&mut config, protocol_version);
174
175        Self::config_chunk_only_producers(&mut config, &self.chain_id, protocol_version);
176
177        Self::config_max_kickout_pledge(&mut config, protocol_version);
178
179        Self::config_test_overrides(&mut config, &self.test_overrides);
180
181        config
182    }
183
184    fn config_nightshade(config: &mut EpochConfig, _protocol_version: ProtocolVersion) {
185        Self::config_nightshade_impl(config, ShardLayout::v0_single_shard());
186    }
187
188    fn config_nightshade_impl(config: &mut EpochConfig, shard_layout: ShardLayout) {
189        let num_block_producer_seats = config.num_block_producer_seats;
190        config.num_block_producer_seats_per_shard =
191            shard_layout.shard_ids().map(|_| num_block_producer_seats).collect();
192        config.avg_hidden_validator_seats_per_shard = shard_layout.shard_ids().map(|_| 0).collect();
193        config.shard_layout = shard_layout;
194    }
195
196    fn config_chunk_only_producers(
197        config: &mut EpochConfig,
198        chain_id: &str,
199        protocol_version: u32,
200    ) {
201        if checked_feature!("stable", ChunkOnlyProducers, protocol_version) {
202            // On testnet, genesis config set num_block_producer_seats to 200
203            // This is to bring it back to 100 to be the same as on mainnet
204            config.num_block_producer_seats = 100;
205            // Technically, after ChunkOnlyProducers is enabled, this field is no longer used
206            // We still set it here just in case
207            config.num_block_producer_seats_per_shard =
208                config.shard_layout.shard_ids().map(|_| 100).collect();
209            config.block_producer_kickout_threshold = 80;
210            config.chunk_producer_kickout_threshold = 80;
211            config.validator_selection_config.num_chunk_only_producer_seats = 200;
212        }
213
214        // Adjust the number of block and chunk producers for all chains except
215        // mainnet, to make it easier to test the change.
216        if chain_id != unc_primitives_core::chains::MAINNET
217            && checked_feature!("stable", TestnetFewerBlockProducers, protocol_version)
218        {
219            let shard_ids = config.shard_layout.shard_ids();
220            // Decrease the number of block producers from 100 to 20.
221            config.num_block_producer_seats = 20;
222            config.num_block_producer_seats_per_shard =
223                shard_ids.map(|_| config.num_block_producer_seats).collect();
224            // Decrease the number of chunk producers.
225            config.validator_selection_config.num_chunk_only_producer_seats = 100;
226        }
227    }
228
229    fn config_max_kickout_pledge(config: &mut EpochConfig, protocol_version: u32) {
230        if checked_feature!("stable", MaxKickoutPledge, protocol_version) {
231            config.validator_max_kickout_pledge_perc = 30;
232        }
233    }
234
235    fn config_test_overrides(
236        config: &mut EpochConfig,
237        test_overrides: &AllEpochConfigTestOverrides,
238    ) {
239        if let Some(block_producer_kickout_threshold) =
240            test_overrides.block_producer_kickout_threshold
241        {
242            config.block_producer_kickout_threshold = block_producer_kickout_threshold;
243        }
244
245        if let Some(chunk_producer_kickout_threshold) =
246            test_overrides.chunk_producer_kickout_threshold
247        {
248            config.chunk_producer_kickout_threshold = chunk_producer_kickout_threshold;
249        }
250    }
251}
252
253/// Additional configuration parameters for the new validator selection
254/// algorithm.
255#[derive(Debug, Clone, SmartDefault, PartialEq, Eq)]
256pub struct ValidatorSelectionConfig {
257    #[default(300)]
258    pub num_chunk_only_producer_seats: NumSeats,
259    #[default(1)]
260    pub minimum_validators_per_shard: NumSeats,
261    #[default(Rational32::new(160, 1_000_000))]
262    pub minimum_pledge_ratio: Rational32,
263}
264
265pub mod block_summary {
266    use crate::types::validator_power::ValidatorPower;
267    use crate::types::validator_power_and_pledge::{
268        ValidatorPowerAndPledge, ValidatorPowerAndPledgeIter,
269    };
270    use crate::types::validator_stake::ValidatorPledge;
271    use crate::types::{AccountId, ValidatorKickoutReason};
272    use crate::validator_mandates::ValidatorMandates;
273    use borsh::{BorshDeserialize, BorshSerialize};
274    use std::collections::{BTreeMap, HashMap};
275    use unc_primitives_core::hash::CryptoHash;
276    use unc_primitives_core::types::{Balance, Power, ValidatorId};
277
278    #[derive(BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize)]
279    pub enum BlockSummary {
280        V1(BlockSummaryV1),
281    }
282    #[derive(
283        Default, Eq, PartialEq, BorshSerialize, Clone, Debug, BorshDeserialize, serde::Serialize,
284    )]
285    pub struct BlockSummaryV1 {
286        pub this_block_hash: CryptoHash,
287        pub prev_block_hash: CryptoHash,
288        pub random_value: CryptoHash,
289        pub validators: Vec<ValidatorPowerAndPledge>,
290        pub validator_to_index: HashMap<AccountId, ValidatorId>,
291        pub block_producers_settlement: Vec<ValidatorId>,
292        pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
293        pub fishermen: Vec<ValidatorPowerAndPledge>,
294        pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
295        pub power_change: BTreeMap<AccountId, Power>,
296        pub pledge_change: BTreeMap<AccountId, Balance>,
297        pub validator_reward: HashMap<AccountId, Balance>,
298        pub seat_price: Balance,
299        pub minted_amount: Balance,
300        /// Power proposals from the block, only the latest one per account
301        pub all_power_proposals: Vec<ValidatorPower>,
302        /// Pledge proposals from the block, only the latest one per account
303        pub all_pledge_proposals: Vec<ValidatorPledge>,
304        /// Kickout set, includes slashed
305        pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
306        /// Only for validators who met the threshold and didn't get slashed
307        pub validator_mandates: ValidatorMandates,
308    }
309    impl Default for BlockSummary {
310        fn default() -> Self {
311            Self::V1(BlockSummaryV1::default())
312        }
313    }
314    impl BlockSummary {
315        pub fn new(
316            this_block_hash: CryptoHash,
317            prev_block_hash: CryptoHash,
318            random_value: CryptoHash,
319            validators: Vec<ValidatorPowerAndPledge>,
320            validator_to_index: HashMap<AccountId, ValidatorId>,
321            block_producers_settlement: Vec<ValidatorId>,
322            chunk_producers_settlement: Vec<Vec<ValidatorId>>,
323            fishermen: Vec<ValidatorPowerAndPledge>,
324            fishermen_to_index: HashMap<AccountId, ValidatorId>,
325            power_change: BTreeMap<AccountId, Power>,
326            pledge_change: BTreeMap<AccountId, Balance>,
327            validator_reward: HashMap<AccountId, Balance>,
328            seat_price: Balance,
329            minted_amount: Balance,
330            all_power_proposals: Vec<ValidatorPower>,
331            all_pledge_proposals: Vec<ValidatorPledge>,
332            validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
333            validator_mandates: ValidatorMandates,
334        ) -> Self {
335            Self::V1(BlockSummaryV1 {
336                this_block_hash,
337                prev_block_hash,
338                random_value,
339                validators,
340                validator_to_index,
341                block_producers_settlement,
342                chunk_producers_settlement,
343                fishermen,
344                fishermen_to_index,
345                power_change,
346                pledge_change,
347                validator_reward,
348                seat_price,
349                minted_amount,
350                all_power_proposals,
351                all_pledge_proposals,
352                validator_kickout,
353                validator_mandates,
354            })
355        }
356
357        #[inline]
358        pub fn block_hash(&self) -> CryptoHash {
359            match self {
360                Self::V1(v1) => v1.this_block_hash,
361            }
362        }
363        #[inline]
364        pub fn random_value(&self) -> &CryptoHash {
365            match self {
366                Self::V1(v1) => &v1.random_value,
367            }
368        }
369
370        #[inline]
371        pub fn seat_price(&self) -> Balance {
372            match self {
373                Self::V1(v1) => v1.seat_price,
374            }
375        }
376
377        #[inline]
378        pub fn minted_amount(&self) -> Balance {
379            match self {
380                Self::V1(v1) => v1.minted_amount,
381            }
382        }
383
384        #[inline]
385        pub fn block_producers_settlement(&self) -> &[ValidatorId] {
386            match self {
387                Self::V1(v1) => &v1.block_producers_settlement,
388            }
389        }
390
391        #[inline]
392        pub fn chunk_producers_settlement(&self) -> &[Vec<ValidatorId>] {
393            match self {
394                Self::V1(v1) => &v1.chunk_producers_settlement,
395            }
396        }
397
398        #[inline]
399        pub fn validator_kickout(&self) -> &HashMap<AccountId, ValidatorKickoutReason> {
400            match self {
401                Self::V1(v1) => &v1.validator_kickout,
402            }
403        }
404
405        #[inline]
406        pub fn pledge_change(&self) -> &BTreeMap<AccountId, Balance> {
407            match self {
408                Self::V1(v1) => &v1.pledge_change,
409            }
410        }
411
412        #[inline]
413        pub fn power_change(&self) -> &BTreeMap<AccountId, Power> {
414            match self {
415                Self::V1(v1) => &v1.power_change,
416            }
417        }
418
419        #[inline]
420        pub fn validator_reward(&self) -> &HashMap<AccountId, Balance> {
421            match self {
422                Self::V1(v1) => &v1.validator_reward,
423            }
424        }
425
426        #[inline]
427        pub fn validators_iter(&self) -> ValidatorPowerAndPledgeIter {
428            match self {
429                Self::V1(v1) => ValidatorPowerAndPledgeIter::new(&v1.validators),
430            }
431        }
432
433        #[inline]
434        pub fn fishermen_iter(&self) -> ValidatorPowerAndPledgeIter {
435            match self {
436                Self::V1(v1) => ValidatorPowerAndPledgeIter::new(&v1.fishermen),
437            }
438        }
439
440        #[inline]
441        pub fn validator_power(&self, validator_id: u64) -> Power {
442            match self {
443                Self::V1(v1) => v1.validators[validator_id as usize].power(),
444            }
445        }
446
447        #[inline]
448        pub fn validator_stake(&self, validator_id: u64) -> Balance {
449            match self {
450                Self::V1(v1) => v1.validators[validator_id as usize].pledge(),
451            }
452        }
453
454        #[inline]
455        pub fn validator_account_id(&self, validator_id: u64) -> &AccountId {
456            match self {
457                Self::V1(v1) => v1.validators[validator_id as usize].account_id(),
458            }
459        }
460
461        #[inline]
462        pub fn account_is_validator(&self, account_id: &AccountId) -> bool {
463            match self {
464                Self::V1(v1) => v1.validator_to_index.contains_key(account_id),
465            }
466        }
467
468        pub fn get_validator_id(&self, account_id: &AccountId) -> Option<&ValidatorId> {
469            match self {
470                Self::V1(v1) => v1.validator_to_index.get(account_id),
471            }
472        }
473
474        pub fn get_validator_by_account(
475            &self,
476            account_id: &AccountId,
477        ) -> Option<ValidatorPowerAndPledge> {
478            match self {
479                Self::V1(v1) => v1
480                    .validator_to_index
481                    .get(account_id)
482                    .map(|validator_id| v1.validators[*validator_id as usize].clone()),
483            }
484        }
485
486        #[inline]
487        pub fn get_validator(&self, validator_id: u64) -> ValidatorPowerAndPledge {
488            match self {
489                Self::V1(v1) => v1.validators[validator_id as usize].clone(),
490            }
491        }
492
493        #[inline]
494        pub fn account_is_fisherman(&self, account_id: &AccountId) -> bool {
495            match self {
496                Self::V1(v1) => v1.fishermen_to_index.contains_key(account_id),
497            }
498        }
499
500        pub fn get_fisherman_by_account(
501            &self,
502            account_id: &AccountId,
503        ) -> Option<ValidatorPowerAndPledge> {
504            match self {
505                Self::V1(v1) => v1
506                    .fishermen_to_index
507                    .get(account_id)
508                    .map(|validator_id| v1.fishermen[*validator_id as usize].clone()),
509            }
510        }
511
512        #[inline]
513        pub fn get_fisherman(&self, fisherman_id: u64) -> ValidatorPowerAndPledge {
514            match self {
515                Self::V1(v1) => v1.fishermen[fisherman_id as usize].clone(),
516            }
517        }
518
519        #[inline]
520        pub fn validators_len(&self) -> usize {
521            match self {
522                Self::V1(v1) => v1.validators.len(),
523            }
524        }
525
526        pub fn vrf_block_producer(&self, _random_value: &CryptoHash) -> ValidatorId {
527            return 0;
528        }
529    }
530}
531pub mod block_info {
532    use std::collections::{BTreeMap, HashMap};
533
534    pub use super::BlockInfoV1;
535    use super::SlashState;
536    use crate::challenge::SlashedValidator;
537    use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter};
538    use crate::types::validator_power_and_pledge::{
539        ValidatorPowerAndPledge, ValidatorPowerAndPledgeIter,
540    };
541    use crate::types::validator_stake::{ValidatorPledge, ValidatorPledgeIter};
542    use crate::types::{EpochId, ValidatorKickoutReason};
543    use crate::validator_mandates::ValidatorMandates;
544    use borsh::{BorshDeserialize, BorshSerialize};
545    use unc_primitives_core::hash::CryptoHash;
546    use unc_primitives_core::types::{
547        AccountId, Balance, BlockHeight, Power, ProtocolVersion, ValidatorId,
548    };
549
550    /// Information per each block.
551    #[derive(BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize)]
552    pub enum BlockInfo {
553        V1(BlockInfoV1),
554        V2(BlockInfoV2),
555    }
556
557    impl Default for BlockInfo {
558        fn default() -> Self {
559            Self::V2(BlockInfoV2::default())
560        }
561    }
562
563    impl BlockInfo {
564        pub fn new(
565            hash: CryptoHash,
566            height: BlockHeight,
567            last_finalized_height: BlockHeight,
568            last_final_block_hash: CryptoHash,
569            prev_hash: CryptoHash,
570            power_proposals: Vec<ValidatorPower>,
571            pledge_proposals: Vec<ValidatorPledge>,
572            validator_mask: Vec<bool>,
573            slashed: Vec<SlashedValidator>,
574            total_supply: Balance,
575            latest_protocol_version: ProtocolVersion,
576            timestamp_nanosec: u64,
577            // start customized by James Savechives
578            random_value: CryptoHash,
579            validators: Vec<ValidatorPowerAndPledge>,
580            validator_to_index: HashMap<AccountId, ValidatorId>,
581            block_producers_settlement: Vec<ValidatorId>,
582            chunk_producers_settlement: Vec<Vec<ValidatorId>>,
583            fishermen: Vec<ValidatorPowerAndPledge>,
584            fishermen_to_index: HashMap<AccountId, ValidatorId>,
585            power_change: BTreeMap<AccountId, Power>,
586            pledge_change: BTreeMap<AccountId, Balance>,
587            validator_reward: HashMap<AccountId, Balance>,
588            seat_price: Balance,
589            minted_amount: Balance,
590            all_power_proposals: Vec<ValidatorPower>,
591            all_pledge_proposals: Vec<ValidatorPledge>,
592            validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
593            validator_mandates: ValidatorMandates,
594        ) -> Self {
595            Self::V2(BlockInfoV2 {
596                hash,
597                height,
598                last_finalized_height,
599                last_final_block_hash,
600                prev_hash,
601                power_proposals,
602                pledge_proposals,
603                chunk_mask: validator_mask,
604                latest_protocol_version,
605                slashed: slashed
606                    .into_iter()
607                    .map(|s| {
608                        let slash_state = if s.is_double_sign {
609                            SlashState::DoubleSign
610                        } else {
611                            SlashState::Other
612                        };
613                        (s.account_id, slash_state)
614                    })
615                    .collect(),
616                total_supply,
617                epoch_first_block: Default::default(),
618                epoch_id: Default::default(),
619                timestamp_nanosec,
620                // start customized by James Savechives
621                random_value,
622                validators,
623                validator_to_index,
624                block_producers_settlement,
625                chunk_producers_settlement,
626                fishermen,
627                fishermen_to_index,
628                power_change,
629                pledge_change,
630                validator_reward,
631                seat_price,
632                minted_amount,
633                all_power_proposals,
634                all_pledge_proposals,
635                validator_kickout,
636                validator_mandates,
637                // end customized by James Savechives
638            })
639        }
640
641        #[inline]
642        pub fn power_proposals_iter(&self) -> ValidatorPowerIter {
643            match self {
644                Self::V1(v1) => ValidatorPowerIter::v1(&v1.power_proposals),
645                Self::V2(v2) => ValidatorPowerIter::new(&v2.power_proposals),
646            }
647        }
648
649        #[inline]
650        pub fn pledge_proposals_iter(&self) -> ValidatorPledgeIter {
651            match self {
652                Self::V1(v1) => ValidatorPledgeIter::v1(&v1.pledge_proposals),
653                Self::V2(v2) => ValidatorPledgeIter::new(&v2.pledge_proposals),
654            }
655        }
656        #[inline]
657        pub fn hash(&self) -> &CryptoHash {
658            match self {
659                Self::V1(v1) => &v1.hash,
660                Self::V2(v2) => &v2.hash,
661            }
662        }
663        #[inline]
664        pub fn slashed(&self) -> &HashMap<AccountId, SlashState> {
665            match self {
666                Self::V1(v1) => &v1.slashed,
667                Self::V2(v2) => &v2.slashed,
668            }
669        }
670
671        #[inline]
672        pub fn slashed_mut(&mut self) -> &mut HashMap<AccountId, SlashState> {
673            match self {
674                Self::V1(v1) => &mut v1.slashed,
675                Self::V2(v2) => &mut v2.slashed,
676            }
677        }
678
679        #[inline]
680        pub fn height(&self) -> BlockHeight {
681            match self {
682                Self::V1(v1) => v1.height,
683                Self::V2(v2) => v2.height,
684            }
685        }
686
687        #[inline]
688        pub fn last_finalized_height(&self) -> BlockHeight {
689            match self {
690                Self::V1(v1) => v1.last_finalized_height,
691                Self::V2(v2) => v2.last_finalized_height,
692            }
693        }
694
695        #[inline]
696        pub fn last_final_block_hash(&self) -> &CryptoHash {
697            match self {
698                Self::V1(v1) => &v1.last_final_block_hash,
699                Self::V2(v2) => &v2.last_final_block_hash,
700            }
701        }
702
703        #[inline]
704        pub fn prev_hash(&self) -> &CryptoHash {
705            match self {
706                Self::V1(v1) => &v1.prev_hash,
707                Self::V2(v2) => &v2.prev_hash,
708            }
709        }
710
711        #[inline]
712        pub fn epoch_first_block(&self) -> &CryptoHash {
713            match self {
714                Self::V1(v1) => &v1.epoch_first_block,
715                Self::V2(v2) => &v2.epoch_first_block,
716            }
717        }
718
719        #[inline]
720        pub fn epoch_first_block_mut(&mut self) -> &mut CryptoHash {
721            match self {
722                Self::V1(v1) => &mut v1.epoch_first_block,
723                Self::V2(v2) => &mut v2.epoch_first_block,
724            }
725        }
726
727        #[inline]
728        pub fn epoch_id(&self) -> &EpochId {
729            match self {
730                Self::V1(v1) => &v1.epoch_id,
731                Self::V2(v2) => &v2.epoch_id,
732            }
733        }
734
735        #[inline]
736        pub fn epoch_id_mut(&mut self) -> &mut EpochId {
737            match self {
738                Self::V1(v1) => &mut v1.epoch_id,
739                Self::V2(v2) => &mut v2.epoch_id,
740            }
741        }
742
743        #[inline]
744        pub fn chunk_mask(&self) -> &[bool] {
745            match self {
746                Self::V1(v1) => &v1.chunk_mask,
747                Self::V2(v2) => &v2.chunk_mask,
748            }
749        }
750
751        #[inline]
752        pub fn latest_protocol_version(&self) -> &ProtocolVersion {
753            match self {
754                Self::V1(v1) => &v1.latest_protocol_version,
755                Self::V2(v2) => &v2.latest_protocol_version,
756            }
757        }
758
759        #[inline]
760        pub fn total_supply(&self) -> &Balance {
761            match self {
762                Self::V1(v1) => &v1.total_supply,
763                Self::V2(v2) => &v2.total_supply,
764            }
765        }
766
767        #[inline]
768        pub fn timestamp_nanosec(&self) -> &u64 {
769            match self {
770                Self::V1(v1) => &v1.timestamp_nanosec,
771                Self::V2(v2) => &v2.timestamp_nanosec,
772            }
773        }
774
775        /// start customized by James Savechives
776        #[inline]
777        pub fn random_value(&self) -> &CryptoHash {
778            match self {
779                Self::V1(v1) => &v1.random_value,
780                Self::V2(v2) => &v2.random_value,
781            }
782        }
783
784        #[inline]
785        pub fn seat_price(&self) -> Balance {
786            match self {
787                Self::V1(v1) => v1.seat_price,
788                Self::V2(v2) => v2.seat_price,
789            }
790        }
791
792        #[inline]
793        pub fn minted_amount(&self) -> Balance {
794            match self {
795                Self::V1(v1) => v1.minted_amount,
796                Self::V2(v2) => v2.minted_amount,
797            }
798        }
799
800        #[inline]
801        pub fn block_producers_settlement(&self) -> &[ValidatorId] {
802            match self {
803                Self::V1(v1) => &v1.block_producers_settlement,
804                Self::V2(v2) => &v2.block_producers_settlement,
805            }
806        }
807
808        #[inline]
809        pub fn chunk_producers_settlement(&self) -> &[Vec<ValidatorId>] {
810            match self {
811                Self::V1(v1) => &v1.chunk_producers_settlement,
812                Self::V2(v2) => &v2.chunk_producers_settlement,
813            }
814        }
815
816        #[inline]
817        pub fn validator_kickout(&self) -> &HashMap<AccountId, ValidatorKickoutReason> {
818            match self {
819                Self::V1(v1) => &v1.validator_kickout,
820                Self::V2(v2) => &v2.validator_kickout,
821            }
822        }
823
824        #[inline]
825        pub fn pledge_change(&self) -> &BTreeMap<AccountId, Balance> {
826            match self {
827                Self::V1(v1) => &v1.pledge_change,
828                Self::V2(v2) => &v2.pledge_change,
829            }
830        }
831
832        #[inline]
833        pub fn power_change(&self) -> &BTreeMap<AccountId, Power> {
834            match self {
835                Self::V1(v1) => &v1.power_change,
836                Self::V2(v2) => &v2.power_change,
837            }
838        }
839
840        #[inline]
841        pub fn validator_reward(&self) -> &HashMap<AccountId, Balance> {
842            match self {
843                Self::V1(v1) => &v1.validator_reward,
844                Self::V2(v2) => &v2.validator_reward,
845            }
846        }
847
848        #[inline]
849        pub fn validators_iter(&self) -> ValidatorPowerAndPledgeIter {
850            match self {
851                Self::V1(v1) => ValidatorPowerAndPledgeIter::new(&v1.validators),
852                Self::V2(v2) => ValidatorPowerAndPledgeIter::new(&v2.validators),
853            }
854        }
855
856        #[inline]
857        pub fn fishermen_iter(&self) -> ValidatorPowerAndPledgeIter {
858            match self {
859                Self::V1(v1) => ValidatorPowerAndPledgeIter::new(&v1.fishermen),
860                Self::V2(v2) => ValidatorPowerAndPledgeIter::new(&v2.fishermen),
861            }
862        }
863
864        #[inline]
865        pub fn validator_power(&self, validator_id: u64) -> Power {
866            match self {
867                Self::V1(v1) => v1.validators[validator_id as usize].power(),
868                Self::V2(v2) => v2.validators[validator_id as usize].power(),
869            }
870        }
871
872        #[inline]
873        pub fn validator_stake(&self, validator_id: u64) -> Balance {
874            match self {
875                Self::V1(v1) => v1.validators[validator_id as usize].pledge(),
876                Self::V2(v2) => v2.validators[validator_id as usize].pledge(),
877            }
878        }
879
880        #[inline]
881        pub fn validator_account_id(&self, validator_id: u64) -> &AccountId {
882            match self {
883                Self::V1(v1) => v1.validators[validator_id as usize].account_id(),
884                Self::V2(v2) => v2.validators[validator_id as usize].account_id(),
885            }
886        }
887
888        #[inline]
889        pub fn account_is_validator(&self, account_id: &AccountId) -> bool {
890            match self {
891                Self::V1(v1) => v1.validator_to_index.contains_key(account_id),
892                Self::V2(v2) => v2.validator_to_index.contains_key(account_id),
893            }
894        }
895
896        pub fn get_validator_id(&self, account_id: &AccountId) -> Option<&ValidatorId> {
897            match self {
898                Self::V1(v1) => v1.validator_to_index.get(account_id),
899                Self::V2(v2) => v2.validator_to_index.get(account_id),
900            }
901        }
902
903        pub fn get_validator_by_account(
904            &self,
905            account_id: &AccountId,
906        ) -> Option<ValidatorPowerAndPledge> {
907            match self {
908                Self::V1(v1) => v1
909                    .validator_to_index
910                    .get(account_id)
911                    .map(|validator_id| v1.validators[*validator_id as usize].clone()),
912                Self::V2(v2) => v2
913                    .validator_to_index
914                    .get(account_id)
915                    .map(|validator_id| v2.validators[*validator_id as usize].clone()),
916            }
917        }
918
919        #[inline]
920        pub fn get_validator(&self, validator_id: u64) -> ValidatorPowerAndPledge {
921            match self {
922                Self::V1(v1) => v1.validators[validator_id as usize].clone(),
923                Self::V2(v2) => v2.validators[validator_id as usize].clone(),
924            }
925        }
926
927        #[inline]
928        pub fn account_is_fisherman(&self, account_id: &AccountId) -> bool {
929            match self {
930                Self::V1(v1) => v1.fishermen_to_index.contains_key(account_id),
931                Self::V2(v2) => v2.fishermen_to_index.contains_key(account_id),
932            }
933        }
934
935        pub fn get_fisherman_by_account(
936            &self,
937            account_id: &AccountId,
938        ) -> Option<ValidatorPowerAndPledge> {
939            match self {
940                Self::V1(v1) => v1
941                    .fishermen_to_index
942                    .get(account_id)
943                    .map(|validator_id| v1.fishermen[*validator_id as usize].clone()),
944                Self::V2(v2) => v2
945                    .fishermen_to_index
946                    .get(account_id)
947                    .map(|validator_id| v2.fishermen[*validator_id as usize].clone()),
948            }
949        }
950
951        #[inline]
952        pub fn get_fisherman(&self, fisherman_id: u64) -> ValidatorPowerAndPledge {
953            match self {
954                Self::V1(v1) => v1.fishermen[fisherman_id as usize].clone(),
955                Self::V2(v2) => v2.fishermen[fisherman_id as usize].clone(),
956            }
957        }
958
959        #[inline]
960        pub fn validators_len(&self) -> usize {
961            match self {
962                Self::V1(v1) => v1.validators.len(),
963                Self::V2(v2) => v2.validators.len(),
964            }
965        }
966
967        pub fn vrf_block_producer(&self, _random_value: &CryptoHash) -> ValidatorId {
968            return 0;
969        }
970        // end customized by James Savechives
971    }
972
973    // V1 -> V2: Use versioned ValidatorPledge structure in proposals
974    #[derive(
975        Default, BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize,
976    )]
977    pub struct BlockInfoV2 {
978        pub hash: CryptoHash,
979        pub height: BlockHeight,
980        pub last_finalized_height: BlockHeight,
981        pub last_final_block_hash: CryptoHash,
982        pub prev_hash: CryptoHash,
983        pub epoch_first_block: CryptoHash,
984        pub epoch_id: EpochId,
985        pub power_proposals: Vec<ValidatorPower>,
986        pub pledge_proposals: Vec<ValidatorPledge>,
987        pub chunk_mask: Vec<bool>,
988        /// Latest protocol version this validator observes.
989        pub latest_protocol_version: ProtocolVersion,
990        /// Validators slashed since the start of epoch or in previous epoch.
991        pub slashed: HashMap<AccountId, SlashState>,
992        /// Total supply at this block.
993        pub total_supply: Balance,
994        pub timestamp_nanosec: u64,
995        /// start customized by James Savechives
996        pub random_value: CryptoHash,
997        pub validators: Vec<ValidatorPowerAndPledge>,
998        pub validator_to_index: HashMap<AccountId, ValidatorId>,
999        pub block_producers_settlement: Vec<ValidatorId>,
1000        pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1001        pub fishermen: Vec<ValidatorPowerAndPledge>,
1002        pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1003        pub power_change: BTreeMap<AccountId, Power>,
1004        pub pledge_change: BTreeMap<AccountId, Balance>,
1005        pub validator_reward: HashMap<AccountId, Balance>,
1006        pub seat_price: Balance,
1007        pub minted_amount: Balance,
1008        /// Power proposals from the block, only the latest one per account
1009        pub all_power_proposals: Vec<ValidatorPower>,
1010        /// Pledge proposals from the block, only the latest one per account
1011        pub all_pledge_proposals: Vec<ValidatorPledge>,
1012        /// Kickout set, includes slashed
1013        pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1014        /// Only for validators who met the threshold and didn't get slashed
1015        pub validator_mandates: ValidatorMandates,
1016        // end customized by James Savechives
1017    }
1018}
1019
1020/// Information per each block.
1021#[derive(
1022    Default, BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize,
1023)]
1024pub struct BlockInfoV1 {
1025    pub hash: CryptoHash,
1026    pub height: BlockHeight,
1027    pub last_finalized_height: BlockHeight,
1028    pub last_final_block_hash: CryptoHash,
1029    pub prev_hash: CryptoHash,
1030    pub epoch_first_block: CryptoHash,
1031    pub epoch_id: EpochId,
1032    pub power_proposals: Vec<ValidatorPowerV1>,
1033    pub pledge_proposals: Vec<ValidatorPledgeV1>,
1034    pub chunk_mask: Vec<bool>,
1035    /// Latest protocol version this validator observes.
1036    pub latest_protocol_version: ProtocolVersion,
1037    /// Validators slashed since the start of epoch or in previous epoch.
1038    pub slashed: HashMap<AccountId, SlashState>,
1039    /// Total supply at this block.
1040    pub total_supply: Balance,
1041    pub timestamp_nanosec: u64,
1042    /// start customized by James Savechives
1043    pub random_value: CryptoHash,
1044    pub validators: Vec<ValidatorPowerAndPledge>,
1045    pub validator_to_index: HashMap<AccountId, ValidatorId>,
1046    pub block_producers_settlement: Vec<ValidatorId>,
1047    pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1048    pub fishermen: Vec<ValidatorPowerAndPledge>,
1049    pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1050    pub power_change: BTreeMap<AccountId, Power>,
1051    pub pledge_change: BTreeMap<AccountId, Balance>,
1052    pub validator_reward: HashMap<AccountId, Balance>,
1053    pub seat_price: Balance,
1054    pub minted_amount: Balance,
1055    /// Power proposals from the block, only the latest one per account
1056    pub all_power_proposals: Vec<ValidatorPower>,
1057    /// Pledge proposals from the block, only the latest one per account
1058    pub all_pledge_proposals: Vec<ValidatorPledge>,
1059    /// Kickout set, includes slashed
1060    pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1061    /// Only for validators who met the threshold and didn't get slashed
1062    pub validator_mandates: ValidatorMandates,
1063    // end customized by James Savechives
1064}
1065
1066impl BlockInfoV1 {
1067    pub fn new(
1068        hash: CryptoHash,
1069        height: BlockHeight,
1070        last_finalized_height: BlockHeight,
1071        last_final_block_hash: CryptoHash,
1072        prev_hash: CryptoHash,
1073        power_proposals: Vec<ValidatorPowerV1>,
1074        pledge_proposals: Vec<ValidatorPledgeV1>,
1075        validator_mask: Vec<bool>,
1076        slashed: Vec<SlashedValidator>,
1077        total_supply: Balance,
1078        latest_protocol_version: ProtocolVersion,
1079        timestamp_nanosec: u64,
1080        // start customized by James Savechives
1081        random_value: CryptoHash,
1082        validators: Vec<ValidatorPowerAndPledge>,
1083        validator_to_index: HashMap<AccountId, ValidatorId>,
1084        block_producers_settlement: Vec<ValidatorId>,
1085        chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1086        fishermen: Vec<ValidatorPowerAndPledge>,
1087        fishermen_to_index: HashMap<AccountId, ValidatorId>,
1088        power_change: BTreeMap<AccountId, Power>,
1089        pledge_change: BTreeMap<AccountId, Balance>,
1090        validator_reward: HashMap<AccountId, Balance>,
1091        seat_price: Balance,
1092        minted_amount: Balance,
1093        all_power_proposals: Vec<ValidatorPower>,
1094        all_pledge_proposals: Vec<ValidatorPledge>,
1095        validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1096        validator_mandates: ValidatorMandates,
1097        // end customized by James Savechives
1098    ) -> Self {
1099        Self {
1100            hash,
1101            height,
1102            last_finalized_height,
1103            last_final_block_hash,
1104            prev_hash,
1105            power_proposals,
1106            pledge_proposals,
1107            chunk_mask: validator_mask,
1108            latest_protocol_version,
1109            slashed: slashed
1110                .into_iter()
1111                .map(|s| {
1112                    let slash_state =
1113                        if s.is_double_sign { SlashState::DoubleSign } else { SlashState::Other };
1114                    (s.account_id, slash_state)
1115                })
1116                .collect(),
1117            total_supply,
1118            epoch_first_block: Default::default(),
1119            epoch_id: Default::default(),
1120            timestamp_nanosec,
1121            // start customized by James Savechives
1122            random_value,
1123            validators,
1124            validator_to_index,
1125            block_producers_settlement,
1126            chunk_producers_settlement,
1127            fishermen,
1128            fishermen_to_index,
1129            power_change,
1130            pledge_change,
1131            validator_reward,
1132            seat_price,
1133            minted_amount,
1134            all_power_proposals,
1135            all_pledge_proposals,
1136            validator_kickout,
1137            validator_mandates,
1138            // end customized by James Savechives
1139        }
1140    }
1141}
1142
1143#[derive(
1144    Default, BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize,
1145)]
1146pub struct ValidatorWeight(ValidatorId, u64);
1147
1148pub mod epoch_info {
1149    use crate::epoch_manager::ValidatorWeight;
1150    use crate::types::validator_power::ValidatorPower;
1151    use crate::types::{
1152        BlockChunkValidatorStats, ValidatorKickoutReason, ValidatorPowerAndPledgeV1,
1153    };
1154    use crate::validator_mandates::{ValidatorMandates, ValidatorMandatesAssignment};
1155    use crate::version::PROTOCOL_VERSION;
1156    use borsh::{BorshDeserialize, BorshSerialize};
1157    use rand::SeedableRng;
1158    use rand_chacha::ChaCha20Rng;
1159    use smart_default::SmartDefault;
1160    use std::collections::{BTreeMap, HashMap};
1161    use unc_primitives_core::hash::CryptoHash;
1162    use unc_primitives_core::types::{
1163        AccountId, Balance, EpochHeight, Power, ProtocolVersion, ValidatorId,
1164    };
1165
1166    use crate::types::validator_power_and_pledge::{
1167        ValidatorPowerAndPledge, ValidatorPowerAndPledgeIter,
1168    };
1169    use crate::types::validator_stake::ValidatorPledge;
1170    use crate::{epoch_manager::RngSeed, rand::WeightedIndex};
1171    use unc_primitives_core::{
1172        checked_feature,
1173        hash::hash,
1174        types::{BlockHeight, ShardId},
1175    };
1176
1177    pub use super::EpochInfoV1;
1178
1179    /// Information per epoch.
1180    #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize)]
1181    pub enum EpochInfo {
1182        V1(EpochInfoV1),
1183        V2(EpochInfoV2),
1184        V3(EpochInfoV3),
1185        V4(EpochInfoV4),
1186    }
1187
1188    impl Default for EpochInfo {
1189        fn default() -> Self {
1190            Self::V2(EpochInfoV2::default())
1191        }
1192    }
1193
1194    // V1 -> V2: Use versioned ValidatorPledge structure in validators and fishermen
1195    #[derive(
1196        SmartDefault,
1197        BorshSerialize,
1198        BorshDeserialize,
1199        Clone,
1200        Debug,
1201        PartialEq,
1202        Eq,
1203        serde::Serialize,
1204    )]
1205    pub struct EpochInfoV2 {
1206        /// Ordinal of given epoch from genesis.
1207        /// There can be multiple epochs with the same ordinal in case of long forks.
1208        pub epoch_height: EpochHeight,
1209        /// List of current validators.
1210        pub validators: Vec<ValidatorPowerAndPledge>,
1211        /// Validator account id to index in proposals.
1212        pub validator_to_index: HashMap<AccountId, ValidatorId>,
1213        /// Settlement of validators responsible for block production.
1214        pub block_producers_settlement: Vec<ValidatorId>,
1215        /// Per each shard, settlement validators that are responsible.
1216        pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1217        /// Settlement of hidden validators with weights used to determine how many shards they will validate.
1218        pub hidden_validators_settlement: Vec<ValidatorWeight>,
1219        /// List of current fishermen.
1220        pub fishermen: Vec<ValidatorPowerAndPledge>,
1221        /// Fisherman account id to index of proposal.
1222        pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1223        /// New power for validators.
1224        pub power_change: BTreeMap<AccountId, Power>,
1225        /// New pledge for validators.
1226        pub pledge_change: BTreeMap<AccountId, Balance>,
1227        /// Validator reward for the epoch.
1228        pub validator_reward: HashMap<AccountId, Balance>,
1229        /// Validators who are kicked out in this epoch.
1230        pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1231        /// Total minted tokens in the epoch.
1232        pub minted_amount: Balance,
1233        /// Seat price of this epoch.
1234        pub seat_price: Balance,
1235        /// Current protocol version during this epoch.
1236        #[default(PROTOCOL_VERSION)]
1237        pub protocol_version: ProtocolVersion,
1238    }
1239
1240    // V2 -> V3: Structures for randomly selecting validators at each height based on new
1241    // block producer and chunk producer selection algorithm.
1242    #[derive(
1243        SmartDefault,
1244        BorshSerialize,
1245        BorshDeserialize,
1246        Clone,
1247        Debug,
1248        PartialEq,
1249        Eq,
1250        serde::Serialize,
1251    )]
1252    pub struct EpochInfoV3 {
1253        pub epoch_height: EpochHeight,
1254        pub validators: Vec<ValidatorPowerAndPledge>,
1255        pub validator_to_index: HashMap<AccountId, ValidatorId>,
1256        pub block_producers_settlement: Vec<ValidatorId>,
1257        pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1258        pub hidden_validators_settlement: Vec<ValidatorWeight>,
1259        pub fishermen: Vec<ValidatorPowerAndPledge>,
1260        pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1261        pub power_change: BTreeMap<AccountId, Power>,
1262        pub pledge_change: BTreeMap<AccountId, Balance>,
1263        pub validator_reward: HashMap<AccountId, Balance>,
1264        pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1265        pub minted_amount: Balance,
1266        pub seat_price: Balance,
1267        #[default(PROTOCOL_VERSION)]
1268        pub protocol_version: ProtocolVersion,
1269        // stuff for selecting validators at each height
1270        rng_seed: RngSeed,
1271        block_producers_sampler: WeightedIndex,
1272        chunk_producers_sampler: Vec<WeightedIndex>,
1273    }
1274
1275    // V3 -> V4: Add structures and methods for stateless validator assignment.
1276    #[derive(
1277        SmartDefault,
1278        BorshSerialize,
1279        BorshDeserialize,
1280        Clone,
1281        Debug,
1282        PartialEq,
1283        Eq,
1284        serde::Serialize,
1285    )]
1286    pub struct EpochInfoV4 {
1287        pub epoch_height: EpochHeight,
1288        pub validators: Vec<ValidatorPowerAndPledge>,
1289        pub validator_to_index: HashMap<AccountId, ValidatorId>,
1290        pub block_producers_settlement: Vec<ValidatorId>,
1291        pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1292        pub hidden_validators_settlement: Vec<ValidatorWeight>,
1293        pub fishermen: Vec<ValidatorPowerAndPledge>,
1294        pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1295        pub power_change: BTreeMap<AccountId, Power>,
1296        pub pledge_change: BTreeMap<AccountId, Balance>,
1297        pub validator_reward: HashMap<AccountId, Balance>,
1298        pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1299        pub minted_amount: Balance,
1300        pub seat_price: Balance,
1301        #[default(PROTOCOL_VERSION)]
1302        pub protocol_version: ProtocolVersion,
1303        // stuff for selecting validators at each height
1304        rng_seed: RngSeed,
1305        block_producers_sampler: WeightedIndex,
1306        chunk_producers_sampler: Vec<WeightedIndex>,
1307        /// Contains the epoch's validator mandates. Used to sample chunk validators.
1308        validator_mandates: ValidatorMandates,
1309    }
1310
1311    impl EpochInfo {
1312        pub fn new(
1313            epoch_height: EpochHeight,
1314            validators: Vec<ValidatorPowerAndPledge>,
1315            validator_to_index: HashMap<AccountId, ValidatorId>,
1316            block_producers_settlement: Vec<ValidatorId>,
1317            chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1318            hidden_validators_settlement: Vec<ValidatorWeight>,
1319            fishermen: Vec<ValidatorPowerAndPledge>,
1320            fishermen_to_index: HashMap<AccountId, ValidatorId>,
1321            power_change: BTreeMap<AccountId, Power>,
1322            pledge_change: BTreeMap<AccountId, Balance>,
1323            validator_reward: HashMap<AccountId, Balance>,
1324            validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1325            minted_amount: Balance,
1326            seat_price: Balance,
1327            protocol_version: ProtocolVersion,
1328            rng_seed: RngSeed,
1329            validator_mandates: ValidatorMandates,
1330        ) -> Self {
1331            if checked_feature!("stable", AliasValidatorSelectionAlgorithm, protocol_version) {
1332                let power_weights = |ids: &[ValidatorId]| -> WeightedIndex {
1333                    WeightedIndex::new(
1334                        ids.iter()
1335                            .copied()
1336                            .map(|validator_id| validators[validator_id as usize].power())
1337                            .collect(),
1338                    )
1339                };
1340                let block_producers_sampler = power_weights(&block_producers_settlement);
1341                let chunk_producers_sampler =
1342                    chunk_producers_settlement.iter().map(|vs| power_weights(vs)).collect();
1343                if checked_feature!("stable", ChunkValidation, protocol_version) {
1344                    Self::V4(EpochInfoV4 {
1345                        epoch_height,
1346                        validators,
1347                        fishermen,
1348                        validator_to_index,
1349                        block_producers_settlement,
1350                        chunk_producers_settlement,
1351                        hidden_validators_settlement,
1352                        power_change,
1353                        pledge_change,
1354                        validator_reward,
1355                        validator_kickout,
1356                        fishermen_to_index,
1357                        minted_amount,
1358                        seat_price,
1359                        protocol_version,
1360                        rng_seed,
1361                        block_producers_sampler,
1362                        chunk_producers_sampler,
1363                        validator_mandates,
1364                    })
1365                } else {
1366                    Self::V3(EpochInfoV3 {
1367                        epoch_height,
1368                        validators,
1369                        fishermen,
1370                        validator_to_index,
1371                        block_producers_settlement,
1372                        chunk_producers_settlement,
1373                        hidden_validators_settlement,
1374                        power_change,
1375                        pledge_change,
1376                        validator_reward,
1377                        validator_kickout,
1378                        fishermen_to_index,
1379                        minted_amount,
1380                        seat_price,
1381                        protocol_version,
1382                        rng_seed,
1383                        block_producers_sampler,
1384                        chunk_producers_sampler,
1385                    })
1386                }
1387            } else {
1388                Self::V2(EpochInfoV2 {
1389                    epoch_height,
1390                    validators,
1391                    fishermen,
1392                    validator_to_index,
1393                    block_producers_settlement,
1394                    chunk_producers_settlement,
1395                    hidden_validators_settlement,
1396                    power_change,
1397                    pledge_change,
1398                    validator_reward,
1399                    validator_kickout,
1400                    fishermen_to_index,
1401                    minted_amount,
1402                    seat_price,
1403                    protocol_version,
1404                })
1405            }
1406        }
1407
1408        pub fn v0_test() -> Self {
1409            Self::V1(EpochInfoV1 {
1410                epoch_height: 10,
1411                validators: vec![
1412                    ValidatorPowerAndPledgeV1 {
1413                        account_id: "test".parse().unwrap(),
1414                        public_key: "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
1415                            .parse()
1416                            .unwrap(),
1417                        power: 0,
1418                        pledge: 0,
1419                    },
1420                    ValidatorPowerAndPledgeV1 {
1421                        account_id: "validator".parse().unwrap(),
1422                        public_key: "ed25519:9E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
1423                            .parse()
1424                            .unwrap(),
1425                        power: 0,
1426                        pledge: 0,
1427                    },
1428                ],
1429                validator_to_index: HashMap::new(),
1430                block_producers_settlement: vec![0u64, 1u64],
1431                chunk_producers_settlement: vec![vec![0u64, 1u64]],
1432                hidden_validators_settlement: vec![],
1433                fishermen: vec![],
1434                fishermen_to_index: HashMap::new(),
1435                power_change: BTreeMap::new(),
1436                pledge_change: BTreeMap::new(),
1437                validator_reward: HashMap::new(),
1438                validator_kickout: HashMap::new(),
1439                minted_amount: 1,
1440                seat_price: 1,
1441                protocol_version: 1,
1442            })
1443        }
1444
1445        #[inline]
1446        pub fn epoch_height_mut(&mut self) -> &mut EpochHeight {
1447            match self {
1448                Self::V1(v1) => &mut v1.epoch_height,
1449                Self::V2(v2) => &mut v2.epoch_height,
1450                Self::V3(v3) => &mut v3.epoch_height,
1451                Self::V4(v4) => &mut v4.epoch_height,
1452            }
1453        }
1454
1455        #[inline]
1456        pub fn epoch_height(&self) -> EpochHeight {
1457            match self {
1458                Self::V1(v1) => v1.epoch_height,
1459                Self::V2(v2) => v2.epoch_height,
1460                Self::V3(v3) => v3.epoch_height,
1461                Self::V4(v4) => v4.epoch_height,
1462            }
1463        }
1464
1465        #[inline]
1466        pub fn seat_price(&self) -> Balance {
1467            match self {
1468                Self::V1(v1) => v1.seat_price,
1469                Self::V2(v2) => v2.seat_price,
1470                Self::V3(v3) => v3.seat_price,
1471                Self::V4(v4) => v4.seat_price,
1472            }
1473        }
1474
1475        #[inline]
1476        pub fn minted_amount(&self) -> Balance {
1477            match self {
1478                Self::V1(v1) => v1.minted_amount,
1479                Self::V2(v2) => v2.minted_amount,
1480                Self::V3(v3) => v3.minted_amount,
1481                Self::V4(v4) => v4.minted_amount,
1482            }
1483        }
1484
1485        #[inline]
1486        pub fn block_producers_settlement(&self) -> &[ValidatorId] {
1487            match self {
1488                Self::V1(v1) => &v1.block_producers_settlement,
1489                Self::V2(v2) => &v2.block_producers_settlement,
1490                Self::V3(v3) => &v3.block_producers_settlement,
1491                Self::V4(v4) => &v4.block_producers_settlement,
1492            }
1493        }
1494
1495        #[inline]
1496        pub fn chunk_producers_settlement(&self) -> &[Vec<ValidatorId>] {
1497            match self {
1498                Self::V1(v1) => &v1.chunk_producers_settlement,
1499                Self::V2(v2) => &v2.chunk_producers_settlement,
1500                Self::V3(v3) => &v3.chunk_producers_settlement,
1501                Self::V4(v4) => &v4.chunk_producers_settlement,
1502            }
1503        }
1504
1505        #[inline]
1506        pub fn validator_kickout(&self) -> &HashMap<AccountId, ValidatorKickoutReason> {
1507            match self {
1508                Self::V1(v1) => &v1.validator_kickout,
1509                Self::V2(v2) => &v2.validator_kickout,
1510                Self::V3(v3) => &v3.validator_kickout,
1511                Self::V4(v4) => &v4.validator_kickout,
1512            }
1513        }
1514
1515        #[inline]
1516        pub fn protocol_version(&self) -> ProtocolVersion {
1517            match self {
1518                Self::V1(v1) => v1.protocol_version,
1519                Self::V2(v2) => v2.protocol_version,
1520                Self::V3(v3) => v3.protocol_version,
1521                Self::V4(v4) => v4.protocol_version,
1522            }
1523        }
1524
1525        #[inline]
1526        pub fn pledge_change(&self) -> &BTreeMap<AccountId, Balance> {
1527            match self {
1528                Self::V1(v1) => &v1.pledge_change,
1529                Self::V2(v2) => &v2.pledge_change,
1530                Self::V3(v3) => &v3.pledge_change,
1531                Self::V4(v4) => &v4.pledge_change,
1532            }
1533        }
1534
1535        #[inline]
1536        pub fn power_change(&self) -> &BTreeMap<AccountId, Power> {
1537            match self {
1538                Self::V1(v1) => &v1.power_change,
1539                Self::V2(v2) => &v2.power_change,
1540                Self::V3(v3) => &v3.power_change,
1541                Self::V4(v4) => &v4.power_change,
1542            }
1543        }
1544
1545        #[inline]
1546        pub fn validator_reward(&self) -> &HashMap<AccountId, Balance> {
1547            match self {
1548                Self::V1(v1) => &v1.validator_reward,
1549                Self::V2(v2) => &v2.validator_reward,
1550                Self::V3(v3) => &v3.validator_reward,
1551                Self::V4(v4) => &v4.validator_reward,
1552            }
1553        }
1554
1555        #[inline]
1556        pub fn validators_iter(&self) -> ValidatorPowerAndPledgeIter {
1557            match self {
1558                Self::V1(v1) => ValidatorPowerAndPledgeIter::v1(&v1.validators),
1559                Self::V2(v2) => ValidatorPowerAndPledgeIter::new(&v2.validators),
1560                Self::V3(v3) => ValidatorPowerAndPledgeIter::new(&v3.validators),
1561                Self::V4(v4) => ValidatorPowerAndPledgeIter::new(&v4.validators),
1562            }
1563        }
1564
1565        #[inline]
1566        pub fn fishermen_iter(&self) -> ValidatorPowerAndPledgeIter {
1567            match self {
1568                Self::V1(v1) => ValidatorPowerAndPledgeIter::v1(&v1.fishermen),
1569                Self::V2(v2) => ValidatorPowerAndPledgeIter::new(&v2.fishermen),
1570                Self::V3(v3) => ValidatorPowerAndPledgeIter::new(&v3.fishermen),
1571                Self::V4(v4) => ValidatorPowerAndPledgeIter::new(&v4.fishermen),
1572            }
1573        }
1574
1575        #[inline]
1576        pub fn validator_power(&self, validator_id: u64) -> Power {
1577            match self {
1578                Self::V1(v1) => v1.validators[validator_id as usize].power,
1579                Self::V2(v2) => v2.validators[validator_id as usize].power(),
1580                Self::V3(v3) => v3.validators[validator_id as usize].power(),
1581                Self::V4(v4) => v4.validators[validator_id as usize].power(),
1582            }
1583        }
1584
1585        #[inline]
1586        pub fn validator_stake(&self, validator_id: u64) -> Balance {
1587            match self {
1588                Self::V1(v1) => v1.validators[validator_id as usize].pledge,
1589                Self::V2(v2) => v2.validators[validator_id as usize].pledge(),
1590                Self::V3(v3) => v3.validators[validator_id as usize].pledge(),
1591                Self::V4(v4) => v4.validators[validator_id as usize].pledge(),
1592            }
1593        }
1594
1595        #[inline]
1596        pub fn validator_account_id(&self, validator_id: u64) -> &AccountId {
1597            match self {
1598                Self::V1(v1) => &v1.validators[validator_id as usize].account_id,
1599                Self::V2(v2) => v2.validators[validator_id as usize].account_id(),
1600                Self::V3(v3) => v3.validators[validator_id as usize].account_id(),
1601                Self::V4(v4) => v4.validators[validator_id as usize].account_id(),
1602            }
1603        }
1604
1605        #[inline]
1606        pub fn account_is_validator(&self, account_id: &AccountId) -> bool {
1607            match self {
1608                Self::V1(v1) => v1.validator_to_index.contains_key(account_id),
1609                Self::V2(v2) => v2.validator_to_index.contains_key(account_id),
1610                Self::V3(v3) => v3.validator_to_index.contains_key(account_id),
1611                Self::V4(v4) => v4.validator_to_index.contains_key(account_id),
1612            }
1613        }
1614
1615        pub fn get_validator_id(&self, account_id: &AccountId) -> Option<&ValidatorId> {
1616            match self {
1617                Self::V1(v1) => v1.validator_to_index.get(account_id),
1618                Self::V2(v2) => v2.validator_to_index.get(account_id),
1619                Self::V3(v3) => v3.validator_to_index.get(account_id),
1620                Self::V4(v4) => v4.validator_to_index.get(account_id),
1621            }
1622        }
1623
1624        pub fn get_validator_by_account(
1625            &self,
1626            account_id: &AccountId,
1627        ) -> Option<ValidatorPowerAndPledge> {
1628            match self {
1629                Self::V1(v1) => v1.validator_to_index.get(account_id).map(|validator_id| {
1630                    ValidatorPowerAndPledge::V1(v1.validators[*validator_id as usize].clone())
1631                }),
1632                Self::V2(v2) => v2
1633                    .validator_to_index
1634                    .get(account_id)
1635                    .map(|validator_id| v2.validators[*validator_id as usize].clone()),
1636                Self::V3(v3) => v3
1637                    .validator_to_index
1638                    .get(account_id)
1639                    .map(|validator_id| v3.validators[*validator_id as usize].clone()),
1640                Self::V4(v4) => v4
1641                    .validator_to_index
1642                    .get(account_id)
1643                    .map(|validator_id| v4.validators[*validator_id as usize].clone()),
1644            }
1645        }
1646
1647        #[inline]
1648        pub fn get_validator(&self, validator_id: u64) -> ValidatorPowerAndPledge {
1649            match self {
1650                Self::V1(v1) => {
1651                    ValidatorPowerAndPledge::V1(v1.validators[validator_id as usize].clone())
1652                }
1653                Self::V2(v2) => v2.validators[validator_id as usize].clone(),
1654                Self::V3(v3) => v3.validators[validator_id as usize].clone(),
1655                Self::V4(v4) => v4.validators[validator_id as usize].clone(),
1656            }
1657        }
1658
1659        #[inline]
1660        pub fn account_is_fisherman(&self, account_id: &AccountId) -> bool {
1661            match self {
1662                Self::V1(v1) => v1.fishermen_to_index.contains_key(account_id),
1663                Self::V2(v2) => v2.fishermen_to_index.contains_key(account_id),
1664                Self::V3(v3) => v3.fishermen_to_index.contains_key(account_id),
1665                Self::V4(v4) => v4.fishermen_to_index.contains_key(account_id),
1666            }
1667        }
1668
1669        pub fn get_fisherman_by_account(
1670            &self,
1671            account_id: &AccountId,
1672        ) -> Option<ValidatorPowerAndPledge> {
1673            match self {
1674                Self::V1(v1) => v1.fishermen_to_index.get(account_id).map(|validator_id| {
1675                    ValidatorPowerAndPledge::V1(v1.fishermen[*validator_id as usize].clone())
1676                }),
1677                Self::V2(v2) => v2
1678                    .fishermen_to_index
1679                    .get(account_id)
1680                    .map(|validator_id| v2.fishermen[*validator_id as usize].clone()),
1681                Self::V3(v3) => v3
1682                    .fishermen_to_index
1683                    .get(account_id)
1684                    .map(|validator_id| v3.fishermen[*validator_id as usize].clone()),
1685                Self::V4(v4) => v4
1686                    .fishermen_to_index
1687                    .get(account_id)
1688                    .map(|validator_id| v4.fishermen[*validator_id as usize].clone()),
1689            }
1690        }
1691
1692        #[inline]
1693        pub fn get_fisherman(&self, fisherman_id: u64) -> ValidatorPowerAndPledge {
1694            match self {
1695                Self::V1(v1) => {
1696                    ValidatorPowerAndPledge::V1(v1.fishermen[fisherman_id as usize].clone())
1697                }
1698                Self::V2(v2) => v2.fishermen[fisherman_id as usize].clone(),
1699                Self::V3(v3) => v3.fishermen[fisherman_id as usize].clone(),
1700                Self::V4(v4) => v4.fishermen[fisherman_id as usize].clone(),
1701            }
1702        }
1703
1704        #[inline]
1705        pub fn validators_len(&self) -> usize {
1706            match self {
1707                Self::V1(v1) => v1.validators.len(),
1708                Self::V2(v2) => v2.validators.len(),
1709                Self::V3(v3) => v3.validators.len(),
1710                Self::V4(v4) => v4.validators.len(),
1711            }
1712        }
1713
1714        pub fn vrf_block_producer(&self, _random_value: &CryptoHash) -> ValidatorId {
1715            return 0;
1716        }
1717
1718        pub fn sample_block_producer(&self, height: BlockHeight) -> ValidatorId {
1719            match &self {
1720                Self::V1(v1) => {
1721                    let bp_settlement = &v1.block_producers_settlement;
1722                    bp_settlement[(height % (bp_settlement.len() as u64)) as usize]
1723                }
1724                Self::V2(v2) => {
1725                    let bp_settlement = &v2.block_producers_settlement;
1726                    bp_settlement[(height % (bp_settlement.len() as u64)) as usize]
1727                }
1728                Self::V3(v3) => {
1729                    let seed = Self::block_produce_seed(height, &v3.rng_seed);
1730                    v3.block_producers_settlement[v3.block_producers_sampler.sample(seed)]
1731                }
1732                Self::V4(v4) => {
1733                    let seed = Self::block_produce_seed(height, &v4.rng_seed);
1734                    v4.block_producers_settlement[v4.block_producers_sampler.sample(seed)]
1735                }
1736            }
1737        }
1738
1739        pub fn sample_chunk_producer(&self, height: BlockHeight, shard_id: ShardId) -> ValidatorId {
1740            match &self {
1741                Self::V1(v1) => {
1742                    let cp_settlement = &v1.chunk_producers_settlement;
1743                    let shard_cps = &cp_settlement[shard_id as usize];
1744                    shard_cps[(height as u64 % (shard_cps.len() as u64)) as usize]
1745                }
1746                Self::V2(v2) => {
1747                    let cp_settlement = &v2.chunk_producers_settlement;
1748                    let shard_cps = &cp_settlement[shard_id as usize];
1749                    shard_cps[(height as u64 % (shard_cps.len() as u64)) as usize]
1750                }
1751                Self::V3(v3) => {
1752                    let protocol_version = self.protocol_version();
1753                    let seed =
1754                        Self::chunk_produce_seed(protocol_version, &v3.rng_seed, height, shard_id);
1755                    let shard_id = shard_id as usize;
1756                    let sample = v3.chunk_producers_sampler[shard_id].sample(seed);
1757                    v3.chunk_producers_settlement[shard_id][sample]
1758                }
1759                Self::V4(v4) => {
1760                    let protocol_version = self.protocol_version();
1761                    let seed =
1762                        Self::chunk_produce_seed(protocol_version, &v4.rng_seed, height, shard_id);
1763                    let shard_id = shard_id as usize;
1764                    let sample = v4.chunk_producers_sampler[shard_id].sample(seed);
1765                    v4.chunk_producers_settlement[shard_id][sample]
1766                }
1767            }
1768        }
1769
1770        pub fn sample_chunk_validators(&self, height: BlockHeight) -> ValidatorMandatesAssignment {
1771            // Chunk validator assignment was introduced with `V4`.
1772            match &self {
1773                Self::V1(_) => Default::default(),
1774                Self::V2(_) => Default::default(),
1775                Self::V3(_) => Default::default(),
1776                Self::V4(v4) => {
1777                    let mut rng = Self::chunk_validate_rng(&v4.rng_seed, height);
1778                    v4.validator_mandates.sample(&mut rng)
1779                }
1780            }
1781        }
1782
1783        /// 32 bytes from epoch_seed, 8 bytes from height
1784        fn block_produce_seed(height: BlockHeight, seed: &RngSeed) -> [u8; 32] {
1785            let mut buffer = [0u8; 40];
1786            buffer[0..32].copy_from_slice(seed);
1787            buffer[32..40].copy_from_slice(&height.to_le_bytes());
1788            hash(&buffer).0
1789        }
1790
1791        fn chunk_produce_seed(
1792            protocol_version: ProtocolVersion,
1793            seed: &RngSeed,
1794            height: BlockHeight,
1795            shard_id: ShardId,
1796        ) -> [u8; 32] {
1797            if checked_feature!("stable", SynchronizeBlockChunkProduction, protocol_version)
1798                && !checked_feature!("stable", ChunkOnlyProducers, protocol_version)
1799            {
1800                // This is same seed that used for determining block
1801                // producer. This seed does not contain the shard id
1802                // so all shards will be produced by the same
1803                // validator.
1804                Self::block_produce_seed(height, seed)
1805            } else {
1806                // 32 bytes from epoch_seed, 8 bytes from height, 8 bytes from shard_id
1807                let mut buffer = [0u8; 48];
1808                buffer[0..32].copy_from_slice(seed);
1809                buffer[32..40].copy_from_slice(&height.to_le_bytes());
1810                buffer[40..48].copy_from_slice(&shard_id.to_le_bytes());
1811                hash(&buffer).0
1812            }
1813        }
1814
1815        /// Returns a new RNG obtained from combining the provided `seed` and `height`.
1816        ///
1817        /// The returned RNG can be used to shuffle slices via [`rand::seq::SliceRandom`].
1818        fn chunk_validate_rng(seed: &RngSeed, height: BlockHeight) -> ChaCha20Rng {
1819            let mut buffer = [0u8; 40];
1820            buffer[0..32].copy_from_slice(seed);
1821            buffer[32..40].copy_from_slice(&height.to_le_bytes());
1822
1823            // The recommended seed for cryptographic RNG's is `[u8; 32]` and some required traits
1824            // are not implemented for larger seeds, see
1825            // https://docs.rs/rand_core/0.6.2/rand_core/trait.SeedableRng.html#associated-types
1826            // Therefore `buffer` is hashed to obtain a `[u8; 32]`.
1827            let seed = hash(&buffer);
1828            SeedableRng::from_seed(seed.0)
1829        }
1830    }
1831
1832    #[derive(BorshSerialize, BorshDeserialize)]
1833    pub struct EpochSummary {
1834        pub prev_epoch_last_block_hash: CryptoHash,
1835        /// Power proposals from the epoch, only the latest one per account
1836        pub all_power_proposals: Vec<ValidatorPower>,
1837        /// Pledge proposals from the epoch, only the latest one per account
1838        pub all_pledge_proposals: Vec<ValidatorPledge>,
1839        /// Kickout set, includes slashed
1840        pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1841        /// Only for validators who met the threshold and didn't get slashed
1842        pub validator_block_chunk_stats: HashMap<AccountId, BlockChunkValidatorStats>,
1843        /// Protocol version for next epoch.
1844        pub next_version: ProtocolVersion,
1845    }
1846}
1847
1848/// Information per epoch.
1849#[derive(
1850    SmartDefault, BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize,
1851)]
1852pub struct EpochInfoV1 {
1853    /// Ordinal of given epoch from genesis.
1854    /// There can be multiple epochs with the same ordinal in case of long forks.
1855    pub epoch_height: EpochHeight,
1856    /// List of current validators.
1857    pub validators: Vec<ValidatorPowerAndPledgeV1>,
1858    /// Validator account id to index in proposals.
1859    pub validator_to_index: HashMap<AccountId, ValidatorId>,
1860    /// Settlement of validators responsible for block production.
1861    pub block_producers_settlement: Vec<ValidatorId>,
1862    /// Per each shard, settlement validators that are responsible.
1863    pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1864    /// Settlement of hidden validators with weights used to determine how many shards they will validate.
1865    pub hidden_validators_settlement: Vec<ValidatorWeight>,
1866    /// List of current fishermen.
1867    pub fishermen: Vec<ValidatorPowerAndPledgeV1>,
1868    /// Fisherman account id to index of proposal.
1869    pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1870    /// New power for validators.
1871    pub power_change: BTreeMap<AccountId, Power>,
1872    /// New pledge for validators.
1873    pub pledge_change: BTreeMap<AccountId, Balance>,
1874    /// Validator reward for the epoch.
1875    pub validator_reward: HashMap<AccountId, Balance>,
1876    /// Validators who are kicked out in this epoch.
1877    pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1878    /// Total minted tokens in the epoch.
1879    pub minted_amount: Balance,
1880    /// Seat price of this epoch.
1881    pub seat_price: Balance,
1882    /// Current protocol version during this epoch.
1883    #[default(PROTOCOL_VERSION)]
1884    pub protocol_version: ProtocolVersion,
1885}
1886
1887/// State that a slashed validator can be in.
1888#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)]
1889pub enum SlashState {
1890    /// Double Sign, will be partially slashed.
1891    DoubleSign,
1892    /// Malicious behavior but is already slashed (tokens taken away from account).
1893    AlreadySlashed,
1894    /// All other cases (tokens should be entirely slashed),
1895    Other,
1896}
1897
1898#[cfg(feature = "new_epoch_sync")]
1899pub mod epoch_sync {
1900    use crate::block_header::BlockHeader;
1901    use crate::epoch_manager::block_info::BlockInfo;
1902    use crate::epoch_manager::block_summary::{BlockSummary, BlockSummaryV1};
1903    use crate::epoch_manager::epoch_info::EpochInfo;
1904    use crate::errors::epoch_sync::{EpochSyncHashType, EpochSyncInfoError};
1905    use crate::types::EpochId;
1906    use borsh::{BorshDeserialize, BorshSerialize};
1907    use std::collections::{HashMap, HashSet};
1908    use unc_o11y::log_assert;
1909    use unc_primitives_core::hash::CryptoHash;
1910
1911    /// Struct to keep all the info that is transferred for one epoch during Epoch Sync.
1912    #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
1913    pub struct EpochSyncInfo {
1914        /// All block hashes of this epoch. In order of production.
1915        pub all_block_hashes: Vec<CryptoHash>,
1916        /// All headers relevant to epoch sync.
1917        /// Contains epoch headers that need to be saved + supporting headers needed for validation.
1918        /// Probably contains one header from the previous epoch.
1919        /// It refers to `last_final_block` of the first block of the epoch.
1920        /// Also contains first header from the next epoch.
1921        /// It refers to `next_epoch_first_hash`.
1922        pub headers: HashMap<CryptoHash, BlockHeader>,
1923        /// Hashes of headers that need to be validated and saved.
1924        pub headers_to_save: HashSet<CryptoHash>,
1925        /// Hash of the first block of the next epoch.
1926        /// Header of this block contains `epoch_sync_data_hash`.
1927        pub next_epoch_first_hash: CryptoHash,
1928        pub epoch_info: EpochInfo,
1929        pub next_epoch_info: EpochInfo,
1930        pub next_next_epoch_info: EpochInfo,
1931    }
1932
1933    impl EpochSyncInfo {
1934        pub fn get_epoch_id(&self) -> Result<&EpochId, EpochSyncInfoError> {
1935            Ok(self.get_epoch_first_header()?.epoch_id())
1936        }
1937
1938        pub fn get_next_epoch_id(&self) -> Result<&EpochId, EpochSyncInfoError> {
1939            Ok(self
1940                .get_header(self.next_epoch_first_hash, EpochSyncHashType::NextEpochFirstBlock)?
1941                .epoch_id())
1942        }
1943
1944        pub fn get_next_next_epoch_id(&self) -> Result<EpochId, EpochSyncInfoError> {
1945            Ok(EpochId(*self.get_epoch_last_hash()?))
1946        }
1947
1948        pub fn get_epoch_last_hash(&self) -> Result<&CryptoHash, EpochSyncInfoError> {
1949            let epoch_height = self.epoch_info.epoch_height();
1950
1951            self.all_block_hashes.last().ok_or(EpochSyncInfoError::ShortEpoch { epoch_height })
1952        }
1953
1954        pub fn get_epoch_last_header(&self) -> Result<&BlockHeader, EpochSyncInfoError> {
1955            self.get_header(*self.get_epoch_last_hash()?, EpochSyncHashType::LastEpochBlock)
1956        }
1957
1958        pub fn get_epoch_last_finalised_hash(&self) -> Result<&CryptoHash, EpochSyncInfoError> {
1959            Ok(self.get_epoch_last_header()?.last_final_block())
1960        }
1961
1962        pub fn get_epoch_last_finalised_header(&self) -> Result<&BlockHeader, EpochSyncInfoError> {
1963            self.get_header(
1964                *self.get_epoch_last_finalised_hash()?,
1965                EpochSyncHashType::LastFinalBlock,
1966            )
1967        }
1968
1969        pub fn get_epoch_first_hash(&self) -> Result<&CryptoHash, EpochSyncInfoError> {
1970            let epoch_height = self.epoch_info.epoch_height();
1971
1972            self.all_block_hashes.first().ok_or(EpochSyncInfoError::ShortEpoch { epoch_height })
1973        }
1974
1975        pub fn get_epoch_first_header(&self) -> Result<&BlockHeader, EpochSyncInfoError> {
1976            self.get_header(*self.get_epoch_first_hash()?, EpochSyncHashType::FirstEpochBlock)
1977        }
1978
1979        /// Reconstruct BlockInfo for `hash` from information in EpochSyncInfo.
1980        pub fn get_block_info(&self, hash: &CryptoHash) -> Result<BlockInfo, EpochSyncInfoError> {
1981            let epoch_first_header = self.get_epoch_first_header()?;
1982            let header = self.get_header(*hash, EpochSyncHashType::Other)?;
1983
1984            log_assert!(
1985                epoch_first_header.epoch_id() == header.epoch_id(),
1986                "We can only correctly reconstruct headers from this epoch"
1987            );
1988
1989            let last_finalized_height = if *header.last_final_block() == CryptoHash::default() {
1990                0
1991            } else {
1992                let last_finalized_header =
1993                    self.get_header(*header.last_final_block(), EpochSyncHashType::LastFinalBlock)?;
1994                last_finalized_header.height()
1995            };
1996            // start customized by James Savechives
1997
1998            let BlockSummary::V1(BlockSummaryV1 {
1999                random_value: _random_value,
2000                validators,
2001                validator_to_index,
2002                block_producers_settlement,
2003                chunk_producers_settlement,
2004                fishermen,
2005                fishermen_to_index,
2006                power_change,
2007                pledge_change,
2008                validator_reward,
2009                seat_price,
2010                minted_amount,
2011                all_power_proposals,
2012                all_pledge_proposals,
2013                validator_kickout,
2014                validator_mandates,
2015                ..
2016            }) = BlockSummary::default();
2017            // end customized by James Savechives
2018            let mut block_info = BlockInfo::new(
2019                *header.hash(),
2020                header.height(),
2021                last_finalized_height,
2022                *header.last_final_block(),
2023                *header.prev_hash(),
2024                header.prev_validator_power_proposals().collect(),
2025                header.prev_validator_pledge_proposals().collect(),
2026                header.chunk_mask().to_vec(),
2027                vec![],
2028                header.total_supply(),
2029                header.latest_protocol_version(),
2030                header.raw_timestamp(),
2031                // start customized by James Savechives
2032                *header.random_value(),
2033                validators,
2034                validator_to_index,
2035                block_producers_settlement,
2036                chunk_producers_settlement,
2037                fishermen,
2038                fishermen_to_index,
2039                power_change,
2040                pledge_change,
2041                validator_reward,
2042                seat_price,
2043                minted_amount,
2044                all_power_proposals,
2045                all_pledge_proposals,
2046                validator_kickout,
2047                validator_mandates, // end customized by James Savechives
2048            );
2049
2050            *block_info.epoch_id_mut() = epoch_first_header.epoch_id().clone();
2051            *block_info.epoch_first_block_mut() = *epoch_first_header.hash();
2052            Ok(block_info)
2053        }
2054
2055        /// Reconstruct legacy `epoch_sync_data_hash` from `EpochSyncInfo`.
2056        /// `epoch_sync_data_hash` was introduced in `BlockHeaderInnerRestV3`.
2057        /// Using this hash we can verify that `EpochInfo` data provided in `EpochSyncInfo` is correct.
2058        pub fn calculate_epoch_sync_data_hash(&self) -> Result<CryptoHash, EpochSyncInfoError> {
2059            let epoch_height = self.epoch_info.epoch_height();
2060
2061            if self.all_block_hashes.len() < 2 {
2062                return Err(EpochSyncInfoError::ShortEpoch { epoch_height });
2063            }
2064            let epoch_first_block = self.all_block_hashes[0];
2065            let epoch_prev_last_block = self.all_block_hashes[self.all_block_hashes.len() - 2];
2066            let epoch_last_block = self.all_block_hashes[self.all_block_hashes.len() - 1];
2067
2068            Ok(CryptoHash::hash_borsh(&(
2069                self.get_block_info(&epoch_first_block)?,
2070                self.get_block_info(&epoch_prev_last_block)?,
2071                self.get_block_info(&epoch_last_block)?,
2072                &self.epoch_info,
2073                &self.next_epoch_info,
2074                &self.next_next_epoch_info,
2075            )))
2076        }
2077
2078        /// Read legacy `epoch_sync_data_hash` from next epoch first header.
2079        /// `epoch_sync_data_hash` was introduced in `BlockHeaderInnerRestV3`.
2080        /// Using this hash we can verify that `EpochInfo` data provided in `EpochSyncInfo` is correct.
2081        pub fn get_epoch_sync_data_hash(&self) -> Result<Option<CryptoHash>, EpochSyncInfoError> {
2082            let next_epoch_first_header =
2083                self.get_header(self.next_epoch_first_hash, EpochSyncHashType::Other)?;
2084            Ok(next_epoch_first_header.epoch_sync_data_hash())
2085        }
2086
2087        pub fn get_header(
2088            &self,
2089            hash: CryptoHash,
2090            hash_type: EpochSyncHashType,
2091        ) -> Result<&BlockHeader, EpochSyncInfoError> {
2092            self.headers.get(&hash).ok_or(EpochSyncInfoError::HashNotFound {
2093                hash,
2094                hash_type,
2095                epoch_height: self.epoch_info.epoch_height(),
2096            })
2097        }
2098    }
2099}