1#![cfg_attr(not(feature = "std"), no_std)]
22#![warn(unused_must_use, unsafe_code, unused_variables, unused_must_use)]
23
24extern crate alloc;
25
26use alloc::{boxed::Box, vec, vec::Vec};
27use codec::{Decode, Encode};
28use frame_support::{
29 dispatch::{DispatchResultWithPostInfo, Pays},
30 ensure,
31 traits::{ConstU32, DisabledValidators, FindAuthor, Get, OnTimestampSet, OneSessionHandler},
32 weights::Weight,
33 BoundedVec, WeakBoundedVec,
34};
35use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor};
36use sp_consensus_babe::{
37 digests::{NextConfigDescriptor, NextEpochDescriptor, PreDigest},
38 AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch,
39 EquivocationProof, Randomness as BabeRandomness, Slot, BABE_ENGINE_ID, RANDOMNESS_LENGTH,
40 RANDOMNESS_VRF_CONTEXT,
41};
42use sp_core::crypto::Wraps;
43use sp_runtime::{
44 generic::DigestItem,
45 traits::{IsMember, One, SaturatedConversion, Saturating, Zero},
46 ConsensusEngineId, Permill,
47};
48use sp_session::{GetSessionNumber, GetValidatorCount};
49use sp_staking::{offence::OffenceReportSystem, SessionIndex};
50
51pub use sp_consensus_babe::AuthorityId;
52
53const LOG_TARGET: &str = "runtime::babe";
54
55mod default_weights;
56mod equivocation;
57mod randomness;
58
59#[cfg(any(feature = "runtime-benchmarks", test))]
60mod benchmarking;
61#[cfg(all(feature = "std", test))]
62mod mock;
63#[cfg(all(feature = "std", test))]
64mod tests;
65
66pub use equivocation::{EquivocationOffence, EquivocationReportSystem};
67#[allow(deprecated)]
68pub use randomness::CurrentBlockRandomness;
69pub use randomness::{
70 ParentBlockRandomness, RandomnessFromOneEpochAgo, RandomnessFromTwoEpochsAgo,
71};
72
73pub use pallet::*;
74
75pub trait WeightInfo {
76 fn plan_config_change() -> Weight;
77 fn report_equivocation(validator_count: u32, max_nominators_per_validator: u32) -> Weight;
78}
79
80pub trait EpochChangeTrigger {
82 fn trigger<T: Config>(now: BlockNumberFor<T>);
85}
86
87pub struct ExternalTrigger;
90
91impl EpochChangeTrigger for ExternalTrigger {
92 fn trigger<T: Config>(_: BlockNumberFor<T>) {} }
94
95pub struct SameAuthoritiesForever;
98
99impl EpochChangeTrigger for SameAuthoritiesForever {
100 fn trigger<T: Config>(now: BlockNumberFor<T>) {
101 if Pallet::<T>::should_epoch_change(now) {
102 let authorities = Authorities::<T>::get();
103 let next_authorities = authorities.clone();
104
105 Pallet::<T>::enact_epoch_change(authorities, next_authorities, None);
106 }
107 }
108}
109
110const UNDER_CONSTRUCTION_SEGMENT_LENGTH: u32 = 256;
111
112#[frame_support::pallet]
113pub mod pallet {
114 use super::*;
115 use frame_support::pallet_prelude::*;
116 use frame_system::pallet_prelude::*;
117
118 #[pallet::pallet]
120 pub struct Pallet<T>(_);
121
122 #[pallet::config]
123 #[pallet::disable_frame_system_supertrait_check]
124 pub trait Config: pallet_timestamp::Config {
125 #[pallet::constant]
129 type EpochDuration: Get<u64>;
130
131 #[pallet::constant]
137 type ExpectedBlockTime: Get<Self::Moment>;
138
139 type EpochChangeTrigger: EpochChangeTrigger;
145
146 type DisabledValidators: DisabledValidators;
150
151 type WeightInfo: WeightInfo;
153
154 #[pallet::constant]
156 type MaxAuthorities: Get<u32>;
157
158 #[pallet::constant]
160 type MaxNominators: Get<u32>;
161
162 type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount;
166
167 type EquivocationReportSystem: OffenceReportSystem<
171 Option<Self::AccountId>,
172 (EquivocationProof<HeaderFor<Self>>, Self::KeyOwnerProof),
173 >;
174 }
175
176 #[pallet::error]
177 pub enum Error<T> {
178 InvalidEquivocationProof,
180 InvalidKeyOwnershipProof,
182 DuplicateOffenceReport,
184 InvalidConfiguration,
186 }
187
188 #[pallet::storage]
190 pub type EpochIndex<T> = StorageValue<_, u64, ValueQuery>;
191
192 #[pallet::storage]
194 pub type Authorities<T: Config> = StorageValue<
195 _,
196 WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities>,
197 ValueQuery,
198 >;
199
200 #[pallet::storage]
203 pub type GenesisSlot<T> = StorageValue<_, Slot, ValueQuery>;
204
205 #[pallet::storage]
207 pub type CurrentSlot<T> = StorageValue<_, Slot, ValueQuery>;
208
209 #[pallet::storage]
223 pub type Randomness<T> = StorageValue<_, BabeRandomness, ValueQuery>;
224
225 #[pallet::storage]
227 pub type PendingEpochConfigChange<T> = StorageValue<_, NextConfigDescriptor>;
228
229 #[pallet::storage]
231 pub type NextRandomness<T> = StorageValue<_, BabeRandomness, ValueQuery>;
232
233 #[pallet::storage]
235 pub type NextAuthorities<T: Config> = StorageValue<
236 _,
237 WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities>,
238 ValueQuery,
239 >;
240
241 #[pallet::storage]
251 pub type SegmentIndex<T> = StorageValue<_, u32, ValueQuery>;
252
253 #[pallet::storage]
255 pub type UnderConstruction<T: Config> = StorageMap<
256 _,
257 Twox64Concat,
258 u32,
259 BoundedVec<BabeRandomness, ConstU32<UNDER_CONSTRUCTION_SEGMENT_LENGTH>>,
260 ValueQuery,
261 >;
262
263 #[pallet::storage]
266 pub type Initialized<T> = StorageValue<_, Option<PreDigest>>;
267
268 #[pallet::storage]
273 pub type AuthorVrfRandomness<T> = StorageValue<_, Option<BabeRandomness>, ValueQuery>;
274
275 #[pallet::storage]
281 pub type EpochStart<T: Config> =
282 StorageValue<_, (BlockNumberFor<T>, BlockNumberFor<T>), ValueQuery>;
283
284 #[pallet::storage]
290 pub type Lateness<T: Config> = StorageValue<_, BlockNumberFor<T>, ValueQuery>;
291
292 #[pallet::storage]
295 pub type EpochConfig<T> = StorageValue<_, BabeEpochConfiguration>;
296
297 #[pallet::storage]
300 pub type NextEpochConfig<T> = StorageValue<_, BabeEpochConfiguration>;
301
302 #[pallet::storage]
311 pub type SkippedEpochs<T> =
312 StorageValue<_, BoundedVec<(u64, SessionIndex), ConstU32<100>>, ValueQuery>;
313
314 #[derive(frame_support::DefaultNoBound)]
315 #[pallet::genesis_config]
316 pub struct GenesisConfig<T: Config> {
317 pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
318 pub epoch_config: BabeEpochConfiguration,
319 #[serde(skip)]
320 pub _config: core::marker::PhantomData<T>,
321 }
322
323 #[pallet::genesis_build]
324 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
325 fn build(&self) {
326 SegmentIndex::<T>::put(0);
327 Pallet::<T>::initialize_genesis_authorities(&self.authorities);
328 EpochConfig::<T>::put(&self.epoch_config);
329 }
330 }
331
332 #[pallet::hooks]
333 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
334 fn on_initialize(now: BlockNumberFor<T>) -> Weight {
336 Self::initialize(now);
337 Weight::zero()
338 }
339
340 #[cfg(feature = "try-runtime")]
341 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
342 Self::do_try_state()
343 }
344
345 fn on_finalize(_now: BlockNumberFor<T>) {
347 if let Some(pre_digest) = Initialized::<T>::take().flatten() {
353 let authority_index = pre_digest.authority_index();
354
355 if T::DisabledValidators::is_disabled(authority_index) {
356 panic!(
357 "Validator with index {:?} is disabled and should not be attempting to author blocks.",
358 authority_index,
359 );
360 }
361
362 if let Some(signature) = pre_digest.vrf_signature() {
363 let randomness: Option<BabeRandomness> = Authorities::<T>::get()
364 .get(authority_index as usize)
365 .and_then(|(authority, _)| {
366 let public = authority.as_inner_ref();
367 let transcript = sp_consensus_babe::make_vrf_transcript(
368 &Randomness::<T>::get(),
369 CurrentSlot::<T>::get(),
370 EpochIndex::<T>::get(),
371 );
372
373 debug_assert!({
377 use sp_core::crypto::VrfPublic;
378 public.vrf_verify(&transcript.clone().into_sign_data(), &signature)
379 });
380
381 public
382 .make_bytes(
383 RANDOMNESS_VRF_CONTEXT,
384 &transcript,
385 &signature.pre_output,
386 )
387 .ok()
388 });
389
390 if let Some(randomness) = pre_digest.is_primary().then(|| randomness).flatten()
391 {
392 Self::deposit_randomness(&randomness);
393 }
394
395 AuthorVrfRandomness::<T>::put(randomness);
396 }
397 }
398
399 Lateness::<T>::kill();
401 }
402 }
403
404 #[pallet::call]
405 impl<T: Config> Pallet<T> {
406 #[pallet::call_index(0)]
411 #[pallet::weight(<T as Config>::WeightInfo::report_equivocation(
412 key_owner_proof.validator_count(),
413 T::MaxNominators::get(),
414 ))]
415 pub fn report_equivocation(
416 origin: OriginFor<T>,
417 equivocation_proof: Box<EquivocationProof<HeaderFor<T>>>,
418 key_owner_proof: T::KeyOwnerProof,
419 ) -> DispatchResultWithPostInfo {
420 let reporter = ensure_signed(origin)?;
421 T::EquivocationReportSystem::process_evidence(
422 Some(reporter),
423 (*equivocation_proof, key_owner_proof),
424 )?;
425 Ok(Pays::No.into())
427 }
428
429 #[pallet::call_index(1)]
438 #[pallet::weight(<T as Config>::WeightInfo::report_equivocation(
439 key_owner_proof.validator_count(),
440 T::MaxNominators::get(),
441 ))]
442 pub fn report_equivocation_unsigned(
443 origin: OriginFor<T>,
444 equivocation_proof: Box<EquivocationProof<HeaderFor<T>>>,
445 key_owner_proof: T::KeyOwnerProof,
446 ) -> DispatchResultWithPostInfo {
447 ensure_none(origin)?;
448 T::EquivocationReportSystem::process_evidence(
449 None,
450 (*equivocation_proof, key_owner_proof),
451 )?;
452 Ok(Pays::No.into())
453 }
454
455 #[pallet::call_index(2)]
460 #[pallet::weight(<T as Config>::WeightInfo::plan_config_change())]
461 pub fn plan_config_change(
462 origin: OriginFor<T>,
463 config: NextConfigDescriptor,
464 ) -> DispatchResult {
465 ensure_root(origin)?;
466 match config {
467 NextConfigDescriptor::V1 { c, allowed_slots } => {
468 ensure!(
469 (c.0 != 0 || allowed_slots != AllowedSlots::PrimarySlots) && c.1 != 0,
470 Error::<T>::InvalidConfiguration
471 );
472 },
473 }
474 PendingEpochConfigChange::<T>::put(config);
475 Ok(())
476 }
477 }
478
479 #[pallet::validate_unsigned]
480 impl<T: Config> ValidateUnsigned for Pallet<T> {
481 type Call = Call<T>;
482 fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
483 Self::validate_unsigned(source, call)
484 }
485
486 fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
487 Self::pre_dispatch(call)
488 }
489 }
490}
491
492impl<T: Config> FindAuthor<u32> for Pallet<T> {
493 fn find_author<'a, I>(digests: I) -> Option<u32>
494 where
495 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
496 {
497 for (id, mut data) in digests.into_iter() {
498 if id == BABE_ENGINE_ID {
499 let pre_digest: PreDigest = PreDigest::decode(&mut data).ok()?;
500 return Some(pre_digest.authority_index());
501 }
502 }
503
504 None
505 }
506}
507
508impl<T: Config> IsMember<AuthorityId> for Pallet<T> {
509 fn is_member(authority_id: &AuthorityId) -> bool {
510 Authorities::<T>::get().iter().any(|id| &id.0 == authority_id)
511 }
512}
513
514impl<T: Config> pallet_session::ShouldEndSession<BlockNumberFor<T>> for Pallet<T> {
515 fn should_end_session(now: BlockNumberFor<T>) -> bool {
516 Self::initialize(now);
521 Self::should_epoch_change(now)
522 }
523}
524
525impl<T: Config> Pallet<T> {
526 pub fn epoch_index() -> u64 {
528 EpochIndex::<T>::get()
529 }
530 pub fn authorities() -> WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities> {
532 Authorities::<T>::get()
533 }
534 pub fn genesis_slot() -> Slot {
536 GenesisSlot::<T>::get()
537 }
538 pub fn current_slot() -> Slot {
540 CurrentSlot::<T>::get()
541 }
542 pub fn randomness() -> BabeRandomness {
544 Randomness::<T>::get()
545 }
546 pub fn initialized() -> Option<Option<PreDigest>> {
548 Initialized::<T>::get()
549 }
550 pub fn author_vrf_randomness() -> Option<BabeRandomness> {
552 AuthorVrfRandomness::<T>::get()
553 }
554 pub fn lateness() -> BlockNumberFor<T> {
556 Lateness::<T>::get()
557 }
558 pub fn epoch_config() -> Option<BabeEpochConfiguration> {
560 EpochConfig::<T>::get()
561 }
562 pub fn skipped_epochs() -> BoundedVec<(u64, SessionIndex), ConstU32<100>> {
564 SkippedEpochs::<T>::get()
565 }
566
567 pub fn slot_duration() -> T::Moment {
569 <T as pallet_timestamp::Config>::MinimumPeriod::get().saturating_mul(2u32.into())
572 }
573
574 pub fn should_epoch_change(now: BlockNumberFor<T>) -> bool {
577 now != One::one() && {
586 let diff = CurrentSlot::<T>::get().saturating_sub(Self::current_epoch_start());
587 *diff >= T::EpochDuration::get()
588 }
589 }
590
591 pub fn next_expected_epoch_change(now: BlockNumberFor<T>) -> Option<BlockNumberFor<T>> {
607 let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get());
608 next_slot.checked_sub(*CurrentSlot::<T>::get()).map(|slots_remaining| {
609 let blocks_remaining: BlockNumberFor<T> = slots_remaining.saturated_into();
611 now.saturating_add(blocks_remaining)
612 })
613 }
614
615 pub fn enact_epoch_change(
623 authorities: WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities>,
624 next_authorities: WeakBoundedVec<(AuthorityId, BabeAuthorityWeight), T::MaxAuthorities>,
625 session_index: Option<SessionIndex>,
626 ) {
627 debug_assert!(Initialized::<T>::get().is_some());
630
631 if authorities.is_empty() {
632 log::warn!(target: LOG_TARGET, "Ignoring empty epoch change.");
633 return;
634 }
635
636 let epoch_index = sp_consensus_babe::epoch_index(
645 CurrentSlot::<T>::get(),
646 GenesisSlot::<T>::get(),
647 T::EpochDuration::get(),
648 );
649
650 let current_epoch_index = EpochIndex::<T>::get();
651 if current_epoch_index.saturating_add(1) != epoch_index {
652 if let Some(session_index) = session_index {
655 SkippedEpochs::<T>::mutate(|skipped_epochs| {
656 if epoch_index < session_index as u64 {
657 log::warn!(
658 target: LOG_TARGET,
659 "Current epoch index {} is lower than session index {}.",
660 epoch_index,
661 session_index,
662 );
663
664 return;
665 }
666
667 if skipped_epochs.is_full() {
668 skipped_epochs.remove(0);
672 }
673
674 skipped_epochs.force_push((epoch_index, session_index));
675 })
676 }
677 }
678
679 EpochIndex::<T>::put(epoch_index);
680 Authorities::<T>::put(authorities);
681
682 let next_epoch_index = epoch_index
684 .checked_add(1)
685 .expect("epoch indices will never reach 2^64 before the death of the universe; qed");
686
687 let randomness = Self::randomness_change_epoch(next_epoch_index);
690 Randomness::<T>::put(randomness);
691
692 NextAuthorities::<T>::put(&next_authorities);
694
695 EpochStart::<T>::mutate(|(previous_epoch_start_block, current_epoch_start_block)| {
697 *previous_epoch_start_block = core::mem::take(current_epoch_start_block);
698 *current_epoch_start_block = <frame_system::Pallet<T>>::block_number();
699 });
700
701 let next_randomness = NextRandomness::<T>::get();
704
705 let next_epoch = NextEpochDescriptor {
706 authorities: next_authorities.into_inner(),
707 randomness: next_randomness,
708 };
709 Self::deposit_consensus(ConsensusLog::NextEpochData(next_epoch));
710
711 if let Some(next_config) = NextEpochConfig::<T>::get() {
712 EpochConfig::<T>::put(next_config);
713 }
714
715 if let Some(pending_epoch_config_change) = PendingEpochConfigChange::<T>::take() {
716 let next_epoch_config: BabeEpochConfiguration =
717 pending_epoch_config_change.clone().into();
718 NextEpochConfig::<T>::put(next_epoch_config);
719
720 Self::deposit_consensus(ConsensusLog::NextConfigData(pending_epoch_config_change));
721 }
722 }
723
724 pub fn current_epoch_start() -> Slot {
729 sp_consensus_babe::epoch_start_slot(
730 EpochIndex::<T>::get(),
731 GenesisSlot::<T>::get(),
732 T::EpochDuration::get(),
733 )
734 }
735
736 pub fn current_epoch() -> Epoch {
738 Epoch {
739 epoch_index: EpochIndex::<T>::get(),
740 start_slot: Self::current_epoch_start(),
741 duration: T::EpochDuration::get(),
742 authorities: Authorities::<T>::get().into_inner(),
743 randomness: Randomness::<T>::get(),
744 config: EpochConfig::<T>::get()
745 .expect("EpochConfig is initialized in genesis; we never `take` or `kill` it; qed"),
746 }
747 }
748
749 pub fn next_epoch() -> Epoch {
752 let next_epoch_index = EpochIndex::<T>::get().checked_add(1).expect(
753 "epoch index is u64; it is always only incremented by one; \
754 if u64 is not enough we should crash for safety; qed.",
755 );
756
757 let start_slot = sp_consensus_babe::epoch_start_slot(
758 next_epoch_index,
759 GenesisSlot::<T>::get(),
760 T::EpochDuration::get(),
761 );
762
763 Epoch {
764 epoch_index: next_epoch_index,
765 start_slot,
766 duration: T::EpochDuration::get(),
767 authorities: NextAuthorities::<T>::get().into_inner(),
768 randomness: NextRandomness::<T>::get(),
769 config: NextEpochConfig::<T>::get().unwrap_or_else(|| {
770 EpochConfig::<T>::get().expect(
771 "EpochConfig is initialized in genesis; we never `take` or `kill` it; qed",
772 )
773 }),
774 }
775 }
776
777 fn deposit_consensus<U: Encode>(new: U) {
778 let log = DigestItem::Consensus(BABE_ENGINE_ID, new.encode());
779 <frame_system::Pallet<T>>::deposit_log(log)
780 }
781
782 fn deposit_randomness(randomness: &BabeRandomness) {
783 let segment_idx = SegmentIndex::<T>::get();
784 let mut segment = UnderConstruction::<T>::get(&segment_idx);
785 if segment.try_push(*randomness).is_ok() {
786 UnderConstruction::<T>::insert(&segment_idx, &segment);
788 } else {
789 let segment_idx = segment_idx + 1;
791 let bounded_randomness =
792 BoundedVec::<_, ConstU32<UNDER_CONSTRUCTION_SEGMENT_LENGTH>>::try_from(vec![
793 *randomness,
794 ])
795 .expect("UNDER_CONSTRUCTION_SEGMENT_LENGTH >= 1");
796 UnderConstruction::<T>::insert(&segment_idx, bounded_randomness);
797 SegmentIndex::<T>::put(&segment_idx);
798 }
799 }
800
801 fn initialize_genesis_authorities(authorities: &[(AuthorityId, BabeAuthorityWeight)]) {
802 if !authorities.is_empty() {
803 assert!(Authorities::<T>::get().is_empty(), "Authorities are already initialized!");
804 let bounded_authorities =
805 WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.to_vec())
806 .expect("Initial number of authorities should be lower than T::MaxAuthorities");
807 Authorities::<T>::put(&bounded_authorities);
808 NextAuthorities::<T>::put(&bounded_authorities);
809 }
810 }
811
812 fn initialize_genesis_epoch(genesis_slot: Slot) {
813 GenesisSlot::<T>::put(genesis_slot);
814 debug_assert_ne!(*GenesisSlot::<T>::get(), 0);
815
816 let next = NextEpochDescriptor {
820 authorities: Authorities::<T>::get().into_inner(),
821 randomness: Randomness::<T>::get(),
822 };
823
824 Self::deposit_consensus(ConsensusLog::NextEpochData(next));
825 }
826
827 fn initialize(now: BlockNumberFor<T>) {
828 let initialized = Initialized::<T>::get().is_some();
831 if initialized {
832 return;
833 }
834
835 let pre_digest =
836 <frame_system::Pallet<T>>::digest()
837 .logs
838 .iter()
839 .filter_map(|s| s.as_pre_runtime())
840 .filter_map(|(id, mut data)| {
841 if id == BABE_ENGINE_ID {
842 PreDigest::decode(&mut data).ok()
843 } else {
844 None
845 }
846 })
847 .next();
848
849 if let Some(ref pre_digest) = pre_digest {
850 let current_slot = pre_digest.slot();
852
853 if *GenesisSlot::<T>::get() == 0 {
857 Self::initialize_genesis_epoch(current_slot)
858 }
859
860 let lateness = current_slot.saturating_sub(CurrentSlot::<T>::get() + 1);
862 let lateness = BlockNumberFor::<T>::from(*lateness as u32);
863
864 Lateness::<T>::put(lateness);
865 CurrentSlot::<T>::put(current_slot);
866 }
867
868 Initialized::<T>::put(pre_digest);
869
870 T::EpochChangeTrigger::trigger::<T>(now);
872 }
873
874 fn randomness_change_epoch(next_epoch_index: u64) -> BabeRandomness {
877 let this_randomness = NextRandomness::<T>::get();
878 let segment_idx: u32 = SegmentIndex::<T>::mutate(|s| core::mem::replace(s, 0));
879
880 let rho_size = (segment_idx.saturating_add(1) * UNDER_CONSTRUCTION_SEGMENT_LENGTH) as usize;
882
883 let next_randomness = compute_randomness(
884 this_randomness,
885 next_epoch_index,
886 (0..segment_idx).flat_map(|i| UnderConstruction::<T>::take(&i)),
887 Some(rho_size),
888 );
889 NextRandomness::<T>::put(&next_randomness);
890 this_randomness
891 }
892
893 pub(crate) fn session_index_for_epoch(epoch_index: u64) -> SessionIndex {
900 let skipped_epochs = SkippedEpochs::<T>::get();
901 match skipped_epochs.binary_search_by_key(&epoch_index, |(epoch_index, _)| *epoch_index) {
902 Ok(index) => skipped_epochs[index].1,
904 Err(0) => epoch_index.saturated_into::<u32>(),
907 Err(index) => {
909 let closest_skipped_epoch = skipped_epochs[index - 1];
912
913 let skipped_epochs = closest_skipped_epoch.0 - closest_skipped_epoch.1 as u64;
918 epoch_index.saturating_sub(skipped_epochs).saturated_into::<u32>()
919 },
920 }
921 }
922
923 pub fn submit_unsigned_equivocation_report(
928 equivocation_proof: EquivocationProof<HeaderFor<T>>,
929 key_owner_proof: T::KeyOwnerProof,
930 ) -> Option<()> {
931 T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok()
932 }
933}
934
935#[cfg(any(feature = "try-runtime", test))]
936impl<T: Config> Pallet<T> {
937 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
951 use frame_support::ensure;
952
953 ensure!(
954 Authorities::<T>::decode_len().unwrap_or(0) as u32 <= T::MaxAuthorities::get(),
955 "Authorities length exceeds MaxAuthorities"
956 );
957
958 ensure!(
959 NextAuthorities::<T>::decode_len().unwrap_or(0) as u32 <= T::MaxAuthorities::get(),
960 "NextAuthorities length exceeds MaxAuthorities"
961 );
962
963 ensure!(
964 EpochConfig::<T>::get().is_some(),
965 "EpochConfig must be initialized and never be None"
966 );
967
968 let genesis_slot = *GenesisSlot::<T>::get();
969 ensure!(*CurrentSlot::<T>::get() >= genesis_slot, "CurrentSlot must be >= GenesisSlot");
970
971 let skipped_epochs = SkippedEpochs::<T>::get();
972 ensure!(
973 skipped_epochs.windows(2).all(|w| w[0].0 < w[1].0),
974 "SkippedEpochs must be sorted in ascending order by epoch index"
975 );
976 ensure!(
977 skipped_epochs
978 .iter()
979 .all(|(epoch_index, session_index)| *epoch_index >= *session_index as u64),
980 "SkippedEpochs entry has epoch_index < session_index"
981 );
982
983 let (previous_epoch_start, current_epoch_start) = EpochStart::<T>::get();
984 ensure!(
985 previous_epoch_start <= current_epoch_start,
986 "EpochStart previous epoch start must be <= current epoch start"
987 );
988
989 Ok(())
990 }
991}
992
993impl<T: Config> OnTimestampSet<T::Moment> for Pallet<T> {
994 fn on_timestamp_set(moment: T::Moment) {
995 let slot_duration = Self::slot_duration();
996 assert!(!slot_duration.is_zero(), "Babe slot duration cannot be zero.");
997
998 let timestamp_slot = moment / slot_duration;
999 let timestamp_slot = Slot::from(timestamp_slot.saturated_into::<u64>());
1000
1001 assert_eq!(
1002 CurrentSlot::<T>::get(),
1003 timestamp_slot,
1004 "Timestamp slot must match `CurrentSlot`"
1005 );
1006 }
1007}
1008
1009impl<T: Config> frame_support::traits::EstimateNextSessionRotation<BlockNumberFor<T>>
1010 for Pallet<T>
1011{
1012 fn average_session_length() -> BlockNumberFor<T> {
1013 T::EpochDuration::get().saturated_into()
1014 }
1015
1016 fn estimate_current_session_progress(_now: BlockNumberFor<T>) -> (Option<Permill>, Weight) {
1017 let elapsed = CurrentSlot::<T>::get().saturating_sub(Self::current_epoch_start()) + 1;
1018
1019 (
1020 Some(Permill::from_rational(*elapsed, T::EpochDuration::get())),
1021 T::DbWeight::get().reads(3),
1023 )
1024 }
1025
1026 fn estimate_next_session_rotation(
1027 now: BlockNumberFor<T>,
1028 ) -> (Option<BlockNumberFor<T>>, Weight) {
1029 (
1030 Self::next_expected_epoch_change(now),
1031 T::DbWeight::get().reads(3),
1033 )
1034 }
1035}
1036
1037impl<T: Config> frame_support::traits::Lateness<BlockNumberFor<T>> for Pallet<T> {
1038 fn lateness(&self) -> BlockNumberFor<T> {
1039 Lateness::<T>::get()
1040 }
1041}
1042
1043impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
1044 type Public = AuthorityId;
1045}
1046
1047impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T>
1048where
1049 T: pallet_session::Config,
1050{
1051 type Key = AuthorityId;
1052
1053 fn on_genesis_session<'a, I: 'a>(validators: I)
1054 where
1055 I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
1056 {
1057 let authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
1058 Self::initialize_genesis_authorities(&authorities);
1059 }
1060
1061 fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I)
1062 where
1063 I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
1064 {
1065 let authorities = validators.map(|(_account, k)| (k, 1)).collect::<Vec<_>>();
1066 let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
1067 authorities,
1068 Some(
1069 "Warning: The session has more validators than expected. \
1070 A runtime configuration adjustment may be needed.",
1071 ),
1072 );
1073
1074 let next_authorities = queued_validators.map(|(_account, k)| (k, 1)).collect::<Vec<_>>();
1075 let next_bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
1076 next_authorities,
1077 Some(
1078 "Warning: The session has more queued validators than expected. \
1079 A runtime configuration adjustment may be needed.",
1080 ),
1081 );
1082
1083 let session_index = <pallet_session::Pallet<T>>::current_index();
1084
1085 Self::enact_epoch_change(bounded_authorities, next_bounded_authorities, Some(session_index))
1086 }
1087
1088 fn on_disabled(i: u32) {
1089 Self::deposit_consensus(ConsensusLog::OnDisabled(i))
1090 }
1091}
1092
1093fn compute_randomness(
1098 last_epoch_randomness: BabeRandomness,
1099 epoch_index: u64,
1100 rho: impl Iterator<Item = BabeRandomness>,
1101 rho_size_hint: Option<usize>,
1102) -> BabeRandomness {
1103 let mut s = Vec::with_capacity(40 + rho_size_hint.unwrap_or(0) * RANDOMNESS_LENGTH);
1104 s.extend_from_slice(&last_epoch_randomness);
1105 s.extend_from_slice(&epoch_index.to_le_bytes());
1106
1107 for vrf_output in rho {
1108 s.extend_from_slice(&vrf_output[..]);
1109 }
1110
1111 sp_io::hashing::blake2_256(&s)
1112}
1113
1114pub mod migrations {
1115 use super::*;
1116 use frame_support::pallet_prelude::{StorageValue, ValueQuery};
1117
1118 pub trait BabePalletPrefix: Config {
1120 fn pallet_prefix() -> &'static str;
1121 }
1122
1123 struct __OldNextEpochConfig<T>(core::marker::PhantomData<T>);
1124 impl<T: BabePalletPrefix> frame_support::traits::StorageInstance for __OldNextEpochConfig<T> {
1125 fn pallet_prefix() -> &'static str {
1126 T::pallet_prefix()
1127 }
1128 const STORAGE_PREFIX: &'static str = "NextEpochConfig";
1129 }
1130
1131 type OldNextEpochConfig<T> =
1132 StorageValue<__OldNextEpochConfig<T>, Option<NextConfigDescriptor>, ValueQuery>;
1133
1134 pub fn add_epoch_configuration<T: BabePalletPrefix>(
1137 epoch_config: BabeEpochConfiguration,
1138 ) -> Weight {
1139 let mut writes = 0;
1140 let mut reads = 0;
1141
1142 if let Some(pending_change) = OldNextEpochConfig::<T>::get() {
1143 PendingEpochConfigChange::<T>::put(pending_change);
1144
1145 writes += 1;
1146 }
1147
1148 reads += 1;
1149
1150 OldNextEpochConfig::<T>::kill();
1151
1152 EpochConfig::<T>::put(epoch_config.clone());
1153 NextEpochConfig::<T>::put(epoch_config);
1154
1155 writes += 3;
1156
1157 T::DbWeight::get().reads_writes(reads, writes)
1158 }
1159}