1#![cfg_attr(not(feature = "std"), no_std)]
30
31extern crate alloc;
32
33pub use sp_consensus_grandpa::{
35 self as fg_primitives, AuthorityId, AuthorityList, AuthorityWeight,
36};
37
38use alloc::{boxed::Box, vec::Vec};
39use codec::{Decode, Encode, MaxEncodedLen};
40use frame_support::{
41 dispatch::{DispatchResultWithPostInfo, Pays},
42 pallet_prelude::Get,
43 traits::OneSessionHandler,
44 weights::Weight,
45 WeakBoundedVec,
46};
47use frame_system::pallet_prelude::BlockNumberFor;
48use scale_info::TypeInfo;
49use sp_consensus_grandpa::{
50 ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_ENGINE_ID,
51 RUNTIME_LOG_TARGET as LOG_TARGET,
52};
53use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult};
54use sp_session::{GetSessionNumber, GetValidatorCount};
55use sp_staking::{offence::OffenceReportSystem, SessionIndex};
56
57mod default_weights;
58mod equivocation;
59pub mod migrations;
60
61#[cfg(any(feature = "runtime-benchmarks", test))]
62mod benchmarking;
63#[cfg(all(feature = "std", test))]
64mod mock;
65#[cfg(all(feature = "std", test))]
66mod tests;
67
68pub use equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot};
69
70pub use pallet::*;
71
72#[frame_support::pallet]
73pub mod pallet {
74 use super::*;
75 use frame_support::{dispatch::DispatchResult, pallet_prelude::*};
76 use frame_system::pallet_prelude::*;
77
78 const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
80
81 #[pallet::pallet]
82 #[pallet::storage_version(STORAGE_VERSION)]
83 pub struct Pallet<T>(_);
84
85 #[pallet::config]
86 pub trait Config: frame_system::Config {
87 #[allow(deprecated)]
89 type RuntimeEvent: From<Event>
90 + Into<<Self as frame_system::Config>::RuntimeEvent>
91 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
92
93 type WeightInfo: WeightInfo;
95
96 #[pallet::constant]
98 type MaxAuthorities: Get<u32>;
99
100 #[pallet::constant]
102 type MaxNominators: Get<u32>;
103
104 #[pallet::constant]
111 type MaxSetIdSessionEntries: Get<u64>;
112
113 type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount;
117
118 type EquivocationReportSystem: OffenceReportSystem<
122 Option<Self::AccountId>,
123 (EquivocationProof<Self::Hash, BlockNumberFor<Self>>, Self::KeyOwnerProof),
124 >;
125 }
126
127 #[pallet::hooks]
128 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
129 #[cfg(feature = "try-runtime")]
130 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
131 Self::do_try_state()
132 }
133
134 fn on_finalize(block_number: BlockNumberFor<T>) {
135 if let Some(pending_change) = PendingChange::<T>::get() {
137 if block_number == pending_change.scheduled_at {
139 let next_authorities = pending_change.next_authorities.to_vec();
140 if let Some(median) = pending_change.forced {
141 Self::deposit_log(ConsensusLog::ForcedChange(
142 median,
143 ScheduledChange { delay: pending_change.delay, next_authorities },
144 ))
145 } else {
146 Self::deposit_log(ConsensusLog::ScheduledChange(ScheduledChange {
147 delay: pending_change.delay,
148 next_authorities,
149 }));
150 }
151 }
152
153 if block_number == pending_change.scheduled_at + pending_change.delay {
155 Authorities::<T>::put(&pending_change.next_authorities);
156 Self::deposit_event(Event::NewAuthorities {
157 authority_set: pending_change.next_authorities.into_inner(),
158 });
159 PendingChange::<T>::kill();
160 }
161 }
162
163 match State::<T>::get() {
165 StoredState::PendingPause { scheduled_at, delay } => {
166 if block_number == scheduled_at {
168 Self::deposit_log(ConsensusLog::Pause(delay));
169 }
170
171 if block_number == scheduled_at + delay {
173 State::<T>::put(StoredState::Paused);
174 Self::deposit_event(Event::Paused);
175 }
176 },
177 StoredState::PendingResume { scheduled_at, delay } => {
178 if block_number == scheduled_at {
180 Self::deposit_log(ConsensusLog::Resume(delay));
181 }
182
183 if block_number == scheduled_at + delay {
185 State::<T>::put(StoredState::Live);
186 Self::deposit_event(Event::Resumed);
187 }
188 },
189 _ => {},
190 }
191 }
192 }
193
194 #[pallet::call]
195 impl<T: Config> Pallet<T> {
196 #[pallet::call_index(0)]
201 #[pallet::weight(T::WeightInfo::report_equivocation(
202 key_owner_proof.validator_count(),
203 T::MaxNominators::get(),
204 ))]
205 pub fn report_equivocation(
206 origin: OriginFor<T>,
207 equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>,
208 key_owner_proof: T::KeyOwnerProof,
209 ) -> DispatchResultWithPostInfo {
210 let reporter = ensure_signed(origin)?;
211
212 T::EquivocationReportSystem::process_evidence(
213 Some(reporter),
214 (*equivocation_proof, key_owner_proof),
215 )?;
216 Ok(Pays::No.into())
218 }
219
220 #[pallet::call_index(1)]
230 #[pallet::weight(T::WeightInfo::report_equivocation(
231 key_owner_proof.validator_count(),
232 T::MaxNominators::get(),
233 ))]
234 pub fn report_equivocation_unsigned(
235 origin: OriginFor<T>,
236 equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>,
237 key_owner_proof: T::KeyOwnerProof,
238 ) -> DispatchResultWithPostInfo {
239 ensure_none(origin)?;
240
241 T::EquivocationReportSystem::process_evidence(
242 None,
243 (*equivocation_proof, key_owner_proof),
244 )?;
245 Ok(Pays::No.into())
246 }
247
248 #[pallet::call_index(2)]
261 #[pallet::weight(T::WeightInfo::note_stalled())]
262 pub fn note_stalled(
263 origin: OriginFor<T>,
264 delay: BlockNumberFor<T>,
265 best_finalized_block_number: BlockNumberFor<T>,
266 ) -> DispatchResult {
267 ensure_root(origin)?;
268
269 Self::on_stalled(delay, best_finalized_block_number);
270 Ok(())
271 }
272 }
273
274 #[pallet::event]
275 #[pallet::generate_deposit(fn deposit_event)]
276 pub enum Event {
277 NewAuthorities { authority_set: AuthorityList },
279 Paused,
281 Resumed,
283 }
284
285 #[pallet::error]
286 pub enum Error<T> {
287 PauseFailed,
290 ResumeFailed,
293 ChangePending,
295 TooSoon,
297 InvalidKeyOwnershipProof,
299 InvalidEquivocationProof,
301 DuplicateOffenceReport,
303 }
304
305 #[pallet::type_value]
306 pub fn DefaultForState<T: Config>() -> StoredState<BlockNumberFor<T>> {
307 StoredState::Live
308 }
309
310 #[pallet::storage]
312 pub type State<T: Config> =
313 StorageValue<_, StoredState<BlockNumberFor<T>>, ValueQuery, DefaultForState<T>>;
314
315 #[pallet::storage]
317 pub type PendingChange<T: Config> =
318 StorageValue<_, StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>>;
319
320 #[pallet::storage]
322 pub type NextForced<T: Config> = StorageValue<_, BlockNumberFor<T>>;
323
324 #[pallet::storage]
326 pub type Stalled<T: Config> = StorageValue<_, (BlockNumberFor<T>, BlockNumberFor<T>)>;
327
328 #[pallet::storage]
331 pub type CurrentSetId<T: Config> = StorageValue<_, SetId, ValueQuery>;
332
333 #[pallet::storage]
344 pub type SetIdSession<T: Config> = StorageMap<_, Twox64Concat, SetId, SessionIndex>;
345
346 #[pallet::storage]
348 pub type Authorities<T: Config> =
349 StorageValue<_, BoundedAuthorityList<T::MaxAuthorities>, ValueQuery>;
350
351 #[derive(frame_support::DefaultNoBound)]
352 #[pallet::genesis_config]
353 pub struct GenesisConfig<T: Config> {
354 pub authorities: AuthorityList,
355 #[serde(skip)]
356 pub _config: core::marker::PhantomData<T>,
357 }
358
359 #[pallet::genesis_build]
360 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
361 fn build(&self) {
362 CurrentSetId::<T>::put(SetId::default());
363 Pallet::<T>::initialize(self.authorities.clone())
364 }
365 }
366
367 #[allow(deprecated)]
368 #[pallet::validate_unsigned]
369 impl<T: Config> ValidateUnsigned for Pallet<T> {
370 type Call = Call<T>;
371
372 fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
373 Self::validate_unsigned(source, call)
374 }
375
376 fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
377 Self::pre_dispatch(call)
378 }
379 }
380}
381
382pub trait WeightInfo {
383 fn report_equivocation(validator_count: u32, max_nominators_per_validator: u32) -> Weight;
384 fn note_stalled() -> Weight;
385}
386
387pub type BoundedAuthorityList<Limit> = WeakBoundedVec<(AuthorityId, AuthorityWeight), Limit>;
389
390#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
393#[codec(mel_bound(N: MaxEncodedLen, Limit: Get<u32>))]
394#[scale_info(skip_type_params(Limit))]
395pub struct StoredPendingChange<N, Limit> {
396 pub scheduled_at: N,
398 pub delay: N,
400 pub next_authorities: BoundedAuthorityList<Limit>,
402 pub forced: Option<N>,
405}
406
407#[derive(Decode, Encode, TypeInfo, MaxEncodedLen)]
411#[cfg_attr(test, derive(Debug, PartialEq))]
412pub enum StoredState<N> {
413 Live,
415 PendingPause {
418 scheduled_at: N,
420 delay: N,
422 },
423 Paused,
425 PendingResume {
428 scheduled_at: N,
430 delay: N,
432 },
433}
434
435impl<T: Config> Pallet<T> {
436 pub fn state() -> StoredState<BlockNumberFor<T>> {
438 State::<T>::get()
439 }
440
441 pub fn pending_change() -> Option<StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>> {
443 PendingChange::<T>::get()
444 }
445
446 pub fn next_forced() -> Option<BlockNumberFor<T>> {
448 NextForced::<T>::get()
449 }
450
451 pub fn stalled() -> Option<(BlockNumberFor<T>, BlockNumberFor<T>)> {
453 Stalled::<T>::get()
454 }
455
456 pub fn current_set_id() -> SetId {
459 CurrentSetId::<T>::get()
460 }
461
462 pub fn session_for_set(set_id: SetId) -> Option<SessionIndex> {
471 SetIdSession::<T>::get(set_id)
472 }
473
474 pub fn grandpa_authorities() -> AuthorityList {
476 Authorities::<T>::get().into_inner()
477 }
478
479 pub fn schedule_pause(in_blocks: BlockNumberFor<T>) -> DispatchResult {
482 if let StoredState::Live = State::<T>::get() {
483 let scheduled_at = frame_system::Pallet::<T>::block_number();
484 State::<T>::put(StoredState::PendingPause { delay: in_blocks, scheduled_at });
485
486 Ok(())
487 } else {
488 Err(Error::<T>::PauseFailed.into())
489 }
490 }
491
492 pub fn schedule_resume(in_blocks: BlockNumberFor<T>) -> DispatchResult {
494 if let StoredState::Paused = State::<T>::get() {
495 let scheduled_at = frame_system::Pallet::<T>::block_number();
496 State::<T>::put(StoredState::PendingResume { delay: in_blocks, scheduled_at });
497
498 Ok(())
499 } else {
500 Err(Error::<T>::ResumeFailed.into())
501 }
502 }
503
504 pub fn schedule_change(
519 next_authorities: AuthorityList,
520 in_blocks: BlockNumberFor<T>,
521 forced: Option<BlockNumberFor<T>>,
522 ) -> DispatchResult {
523 if !PendingChange::<T>::exists() {
524 let scheduled_at = frame_system::Pallet::<T>::block_number();
525
526 if forced.is_some() {
527 if NextForced::<T>::get().map_or(false, |next| next > scheduled_at) {
528 return Err(Error::<T>::TooSoon.into());
529 }
530
531 NextForced::<T>::put(scheduled_at + in_blocks * 2u32.into());
534 }
535
536 let next_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
537 next_authorities,
538 Some(
539 "Warning: The number of authorities given is too big. \
540 A runtime configuration adjustment may be needed.",
541 ),
542 );
543
544 PendingChange::<T>::put(StoredPendingChange {
545 delay: in_blocks,
546 scheduled_at,
547 next_authorities,
548 forced,
549 });
550
551 Ok(())
552 } else {
553 Err(Error::<T>::ChangePending.into())
554 }
555 }
556
557 fn deposit_log(log: ConsensusLog<BlockNumberFor<T>>) {
559 let log = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode());
560 frame_system::Pallet::<T>::deposit_log(log);
561 }
562
563 fn initialize(authorities: AuthorityList) {
566 if !authorities.is_empty() {
567 assert!(Self::grandpa_authorities().is_empty(), "Authorities are already initialized!");
568 Authorities::<T>::put(
569 &BoundedAuthorityList::<T::MaxAuthorities>::try_from(authorities).expect(
570 "Grandpa: `Config::MaxAuthorities` is smaller than the number of genesis authorities!",
571 ),
572 );
573 }
574
575 SetIdSession::<T>::insert(0, 0);
579 }
580
581 pub fn submit_unsigned_equivocation_report(
586 equivocation_proof: EquivocationProof<T::Hash, BlockNumberFor<T>>,
587 key_owner_proof: T::KeyOwnerProof,
588 ) -> Option<()> {
589 T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok()
590 }
591
592 fn on_stalled(further_wait: BlockNumberFor<T>, median: BlockNumberFor<T>) {
593 Stalled::<T>::put((further_wait, median));
597 }
598}
599
600#[cfg(any(feature = "try-runtime", test))]
601impl<T: Config> Pallet<T> {
602 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
606 Self::try_state_current_set_id()?;
607 Self::try_state_set_id_session_entries()?;
608
609 Ok(())
610 }
611
612 fn try_state_current_set_id() -> Result<(), sp_runtime::TryRuntimeError> {
616 let current_set_id = CurrentSetId::<T>::get();
617
618 frame_support::ensure!(
619 SetIdSession::<T>::get(current_set_id).is_some(),
620 "`SetIdSession` must contain an entry for `CurrentSetId`"
621 );
622
623 Ok(())
624 }
625
626 fn try_state_set_id_session_entries() -> Result<(), sp_runtime::TryRuntimeError> {
630 let current_set_id = CurrentSetId::<T>::get();
631
632 for (set_id, _) in SetIdSession::<T>::iter() {
633 frame_support::ensure!(
634 set_id <= current_set_id,
635 "`SetIdSession` contains an entry with a set ID greater than `CurrentSetId`"
636 );
637 }
638
639 Ok(())
640 }
641}
642
643impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
644 type Public = AuthorityId;
645}
646
647impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T>
648where
649 T: pallet_session::Config,
650{
651 type Key = AuthorityId;
652
653 fn on_genesis_session<'a, I: 'a>(validators: I)
654 where
655 I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
656 {
657 let authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
658 Self::initialize(authorities);
659 }
660
661 fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
662 where
663 I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
664 {
665 let mut current_set_id = CurrentSetId::<T>::get();
666
667 if changed || Stalled::<T>::exists() {
671 let next_authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
672
673 let res = match Stalled::<T>::get() {
674 Some((further_wait, median)) => {
675 Self::schedule_change(next_authorities, further_wait, Some(median))
676 },
677 None => Self::schedule_change(next_authorities, Zero::zero(), None),
678 };
679
680 if res.is_ok() {
681 Stalled::<T>::kill();
682
683 current_set_id += 1;
684 CurrentSetId::<T>::set(current_set_id);
685
686 let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1);
687 if current_set_id >= max_set_id_session_entries {
688 SetIdSession::<T>::remove(current_set_id - max_set_id_session_entries);
689 }
690 }
691 }
692
693 let session_index = pallet_session::Pallet::<T>::current_index();
696 SetIdSession::<T>::insert(current_set_id, &session_index);
697 }
698
699 fn on_disabled(i: u32) {
700 Self::deposit_log(ConsensusLog::OnDisabled(i as u64))
701 }
702}