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#[derive(Clone, Eq, Debug, PartialEq)]
31pub struct BlockConfig {
32 pub num_block_producer_seats: NumSeats,
34 pub num_block_producer_seats_per_shard: Vec<NumSeats>,
36 pub avg_hidden_validator_seats_per_shard: Vec<NumSeats>,
38 pub block_producer_kickout_threshold: u8,
40 pub chunk_producer_kickout_threshold: u8,
42 pub validator_max_kickout_pledge_perc: u8,
44 pub online_min_threshold: Rational32,
46 pub online_max_threshold: Rational32,
48 pub fishermen_threshold: Balance,
50 pub minimum_pledge_divisor: u64,
52 pub protocol_upgrade_pledge_threshold: Rational32,
54 pub shard_layout: ShardLayout,
56 pub validator_selection_config: ValidatorSelectionConfig,
58}
59#[derive(Clone, Eq, Debug, PartialEq)]
62pub struct EpochConfig {
63 pub epoch_length: BlockHeightDelta,
65 pub num_block_producer_seats: NumSeats,
67 pub num_block_producer_seats_per_shard: Vec<NumSeats>,
69 pub avg_hidden_validator_seats_per_shard: Vec<NumSeats>,
71 pub block_producer_kickout_threshold: u8,
73 pub chunk_producer_kickout_threshold: u8,
75 pub validator_max_kickout_pledge_perc: u8,
77 pub online_min_threshold: Rational32,
79 pub online_max_threshold: Rational32,
81 pub fishermen_threshold: Balance,
83 pub minimum_pledge_divisor: u64,
85 pub protocol_upgrade_pledge_threshold: Rational32,
87 pub shard_layout: ShardLayout,
89 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#[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#[derive(Clone)]
126pub struct AllEpochConfig {
127 use_production_config: bool,
130 genesis_epoch_config: EpochConfig,
132 chain_id: String,
134
135 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 config.num_block_producer_seats = 100;
205 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 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 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 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#[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 pub all_power_proposals: Vec<ValidatorPower>,
302 pub all_pledge_proposals: Vec<ValidatorPledge>,
304 pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
306 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 #[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 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 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 })
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 #[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 }
972
973 #[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 pub latest_protocol_version: ProtocolVersion,
990 pub slashed: HashMap<AccountId, SlashState>,
992 pub total_supply: Balance,
994 pub timestamp_nanosec: u64,
995 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 pub all_power_proposals: Vec<ValidatorPower>,
1010 pub all_pledge_proposals: Vec<ValidatorPledge>,
1012 pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1014 pub validator_mandates: ValidatorMandates,
1016 }
1018}
1019
1020#[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 pub latest_protocol_version: ProtocolVersion,
1037 pub slashed: HashMap<AccountId, SlashState>,
1039 pub total_supply: Balance,
1041 pub timestamp_nanosec: u64,
1042 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 pub all_power_proposals: Vec<ValidatorPower>,
1057 pub all_pledge_proposals: Vec<ValidatorPledge>,
1059 pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1061 pub validator_mandates: ValidatorMandates,
1063 }
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 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 ) -> 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 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 }
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 #[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 #[derive(
1196 SmartDefault,
1197 BorshSerialize,
1198 BorshDeserialize,
1199 Clone,
1200 Debug,
1201 PartialEq,
1202 Eq,
1203 serde::Serialize,
1204 )]
1205 pub struct EpochInfoV2 {
1206 pub epoch_height: EpochHeight,
1209 pub validators: Vec<ValidatorPowerAndPledge>,
1211 pub validator_to_index: HashMap<AccountId, ValidatorId>,
1213 pub block_producers_settlement: Vec<ValidatorId>,
1215 pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1217 pub hidden_validators_settlement: Vec<ValidatorWeight>,
1219 pub fishermen: Vec<ValidatorPowerAndPledge>,
1221 pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1223 pub power_change: BTreeMap<AccountId, Power>,
1225 pub pledge_change: BTreeMap<AccountId, Balance>,
1227 pub validator_reward: HashMap<AccountId, Balance>,
1229 pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1231 pub minted_amount: Balance,
1233 pub seat_price: Balance,
1235 #[default(PROTOCOL_VERSION)]
1237 pub protocol_version: ProtocolVersion,
1238 }
1239
1240 #[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 rng_seed: RngSeed,
1271 block_producers_sampler: WeightedIndex,
1272 chunk_producers_sampler: Vec<WeightedIndex>,
1273 }
1274
1275 #[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 rng_seed: RngSeed,
1305 block_producers_sampler: WeightedIndex,
1306 chunk_producers_sampler: Vec<WeightedIndex>,
1307 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 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 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 Self::block_produce_seed(height, seed)
1805 } else {
1806 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 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 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 pub all_power_proposals: Vec<ValidatorPower>,
1837 pub all_pledge_proposals: Vec<ValidatorPledge>,
1839 pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1841 pub validator_block_chunk_stats: HashMap<AccountId, BlockChunkValidatorStats>,
1843 pub next_version: ProtocolVersion,
1845 }
1846}
1847
1848#[derive(
1850 SmartDefault, BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize,
1851)]
1852pub struct EpochInfoV1 {
1853 pub epoch_height: EpochHeight,
1856 pub validators: Vec<ValidatorPowerAndPledgeV1>,
1858 pub validator_to_index: HashMap<AccountId, ValidatorId>,
1860 pub block_producers_settlement: Vec<ValidatorId>,
1862 pub chunk_producers_settlement: Vec<Vec<ValidatorId>>,
1864 pub hidden_validators_settlement: Vec<ValidatorWeight>,
1866 pub fishermen: Vec<ValidatorPowerAndPledgeV1>,
1868 pub fishermen_to_index: HashMap<AccountId, ValidatorId>,
1870 pub power_change: BTreeMap<AccountId, Power>,
1872 pub pledge_change: BTreeMap<AccountId, Balance>,
1874 pub validator_reward: HashMap<AccountId, Balance>,
1876 pub validator_kickout: HashMap<AccountId, ValidatorKickoutReason>,
1878 pub minted_amount: Balance,
1880 pub seat_price: Balance,
1882 #[default(PROTOCOL_VERSION)]
1884 pub protocol_version: ProtocolVersion,
1885}
1886
1887#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)]
1889pub enum SlashState {
1890 DoubleSign,
1892 AlreadySlashed,
1894 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 #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
1913 pub struct EpochSyncInfo {
1914 pub all_block_hashes: Vec<CryptoHash>,
1916 pub headers: HashMap<CryptoHash, BlockHeader>,
1923 pub headers_to_save: HashSet<CryptoHash>,
1925 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 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 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 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 *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, );
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 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 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}