referrerpolicy=no-referrer-when-downgrade

pallet_grandpa/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! GRANDPA Consensus module for runtime.
19//!
20//! This manages the GRANDPA authority set ready for the native code.
21//! These authorities are only for GRANDPA finality, not for consensus overall.
22//!
23//! In the future, it will also handle misbehavior reports, and on-chain
24//! finality notifications.
25//!
26//! For full integration with GRANDPA, the `GrandpaApi` should be implemented.
27//! The necessary items are re-exported via the `fg_primitives` crate.
28
29#![cfg_attr(not(feature = "std"), no_std)]
30
31extern crate alloc;
32
33// Re-export since this is necessary for `impl_apis` in runtime.
34pub 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	/// The in-code storage version.
79	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		/// The event type of this module.
88		#[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		/// Weights for this pallet.
94		type WeightInfo: WeightInfo;
95
96		/// Max Authorities in use
97		#[pallet::constant]
98		type MaxAuthorities: Get<u32>;
99
100		/// The maximum number of nominators for each validator.
101		#[pallet::constant]
102		type MaxNominators: Get<u32>;
103
104		/// The maximum number of entries to keep in the set id to session index mapping.
105		///
106		/// Since the `SetIdSession` map is only used for validating equivocations this
107		/// value should relate to the bonding duration of whatever staking system is
108		/// being used (if any). If equivocation handling is not enabled then this value
109		/// can be zero.
110		#[pallet::constant]
111		type MaxSetIdSessionEntries: Get<u64>;
112
113		/// The proof of key ownership, used for validating equivocation reports
114		/// The proof include the session index and validator count of the
115		/// session at which the equivocation occurred.
116		type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount;
117
118		/// The equivocation handling subsystem, defines methods to check/report an
119		/// offence and for submitting a transaction to report an equivocation
120		/// (from an offchain context).
121		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			// check for scheduled pending authority set changes
136			if let Some(pending_change) = PendingChange::<T>::get() {
137				// emit signal if we're at the block that scheduled the change
138				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				// enact the change if we've reached the enacting block
154				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			// check for scheduled pending state changes
164			match State::<T>::get() {
165				StoredState::PendingPause { scheduled_at, delay } => {
166					// signal change to pause
167					if block_number == scheduled_at {
168						Self::deposit_log(ConsensusLog::Pause(delay));
169					}
170
171					// enact change to paused state
172					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					// signal change to resume
179					if block_number == scheduled_at {
180						Self::deposit_log(ConsensusLog::Resume(delay));
181					}
182
183					// enact change to live state
184					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		/// Report voter equivocation/misbehavior. This method will verify the
197		/// equivocation proof and validate the given key ownership proof
198		/// against the extracted offender. If both are valid, the offence
199		/// will be reported.
200		#[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			// Waive the fee since the report is valid and beneficial
217			Ok(Pays::No.into())
218		}
219
220		/// Report voter equivocation/misbehavior. This method will verify the
221		/// equivocation proof and validate the given key ownership proof
222		/// against the extracted offender. If both are valid, the offence
223		/// will be reported.
224		///
225		/// This extrinsic must be called unsigned and it is expected that only
226		/// block authors will call it (validated in `ValidateUnsigned`), as such
227		/// if the block author is defined it will be defined as the equivocation
228		/// reporter.
229		#[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		/// Note that the current authority set of the GRANDPA finality gadget has stalled.
249		///
250		/// This will trigger a forced authority set change at the beginning of the next session, to
251		/// be enacted `delay` blocks after that. The `delay` should be high enough to safely assume
252		/// that the block signalling the forced change will not be re-orged e.g. 1000 blocks.
253		/// The block production rate (which may be slowed down because of finality lagging) should
254		/// be taken into account when choosing the `delay`. The GRANDPA voters based on the new
255		/// authority will start voting on top of `best_finalized_block_number` for new finalized
256		/// blocks. `best_finalized_block_number` should be the highest of the latest finalized
257		/// block of all validators of the new authority set.
258		///
259		/// Only callable by root.
260		#[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		/// New authority set has been applied.
278		NewAuthorities { authority_set: AuthorityList },
279		/// Current authority set has been paused.
280		Paused,
281		/// Current authority set has been resumed.
282		Resumed,
283	}
284
285	#[pallet::error]
286	pub enum Error<T> {
287		/// Attempt to signal GRANDPA pause when the authority set isn't live
288		/// (either paused or already pending pause).
289		PauseFailed,
290		/// Attempt to signal GRANDPA resume when the authority set isn't paused
291		/// (either live or already pending resume).
292		ResumeFailed,
293		/// Attempt to signal GRANDPA change with one already pending.
294		ChangePending,
295		/// Cannot signal forced change so soon after last.
296		TooSoon,
297		/// A key ownership proof provided as part of an equivocation report is invalid.
298		InvalidKeyOwnershipProof,
299		/// An equivocation proof provided as part of an equivocation report is invalid.
300		InvalidEquivocationProof,
301		/// A given equivocation report is valid but already previously reported.
302		DuplicateOffenceReport,
303	}
304
305	#[pallet::type_value]
306	pub fn DefaultForState<T: Config>() -> StoredState<BlockNumberFor<T>> {
307		StoredState::Live
308	}
309
310	/// State of the current authority set.
311	#[pallet::storage]
312	pub type State<T: Config> =
313		StorageValue<_, StoredState<BlockNumberFor<T>>, ValueQuery, DefaultForState<T>>;
314
315	/// Pending change: (signaled at, scheduled change).
316	#[pallet::storage]
317	pub type PendingChange<T: Config> =
318		StorageValue<_, StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>>;
319
320	/// next block number where we can force a change.
321	#[pallet::storage]
322	pub type NextForced<T: Config> = StorageValue<_, BlockNumberFor<T>>;
323
324	/// `true` if we are currently stalled.
325	#[pallet::storage]
326	pub type Stalled<T: Config> = StorageValue<_, (BlockNumberFor<T>, BlockNumberFor<T>)>;
327
328	/// The number of changes (both in terms of keys and underlying economic responsibilities)
329	/// in the "set" of Grandpa validators from genesis.
330	#[pallet::storage]
331	pub type CurrentSetId<T: Config> = StorageValue<_, SetId, ValueQuery>;
332
333	/// A mapping from grandpa set ID to the index of the *most recent* session for which its
334	/// members were responsible.
335	///
336	/// This is only used for validating equivocation proofs. An equivocation proof must
337	/// contains a key-ownership proof for a given session, therefore we need a way to tie
338	/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
339	/// was the owner of a given key on a given session, and what the active set ID was
340	/// during that session.
341	///
342	/// TWOX-NOTE: `SetId` is not under user control.
343	#[pallet::storage]
344	pub type SetIdSession<T: Config> = StorageMap<_, Twox64Concat, SetId, SessionIndex>;
345
346	/// The current list of authorities.
347	#[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
387/// Bounded version of `AuthorityList`, `Limit` being the bound
388pub type BoundedAuthorityList<Limit> = WeakBoundedVec<(AuthorityId, AuthorityWeight), Limit>;
389
390/// A stored pending change.
391/// `Limit` is the bound for `next_authorities`
392#[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	/// The block number this was scheduled at.
397	pub scheduled_at: N,
398	/// The delay in blocks until it will be applied.
399	pub delay: N,
400	/// The next authority set, weakly bounded in size by `Limit`.
401	pub next_authorities: BoundedAuthorityList<Limit>,
402	/// If defined it means the change was forced and the given block number
403	/// indicates the median last finalized block when the change was signaled.
404	pub forced: Option<N>,
405}
406
407/// Current state of the GRANDPA authority set. State transitions must happen in
408/// the same order of states defined below, e.g. `Paused` implies a prior
409/// `PendingPause`.
410#[derive(Decode, Encode, TypeInfo, MaxEncodedLen)]
411#[cfg_attr(test, derive(Debug, PartialEq))]
412pub enum StoredState<N> {
413	/// The current authority set is live, and GRANDPA is enabled.
414	Live,
415	/// There is a pending pause event which will be enacted at the given block
416	/// height.
417	PendingPause {
418		/// Block at which the intention to pause was scheduled.
419		scheduled_at: N,
420		/// Number of blocks after which the change will be enacted.
421		delay: N,
422	},
423	/// The current GRANDPA authority set is paused.
424	Paused,
425	/// There is a pending resume event which will be enacted at the given block
426	/// height.
427	PendingResume {
428		/// Block at which the intention to resume was scheduled.
429		scheduled_at: N,
430		/// Number of blocks after which the change will be enacted.
431		delay: N,
432	},
433}
434
435impl<T: Config> Pallet<T> {
436	/// State of the current authority set.
437	pub fn state() -> StoredState<BlockNumberFor<T>> {
438		State::<T>::get()
439	}
440
441	/// Pending change: (signaled at, scheduled change).
442	pub fn pending_change() -> Option<StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>> {
443		PendingChange::<T>::get()
444	}
445
446	/// next block number where we can force a change.
447	pub fn next_forced() -> Option<BlockNumberFor<T>> {
448		NextForced::<T>::get()
449	}
450
451	/// `true` if we are currently stalled.
452	pub fn stalled() -> Option<(BlockNumberFor<T>, BlockNumberFor<T>)> {
453		Stalled::<T>::get()
454	}
455
456	/// The number of changes (both in terms of keys and underlying economic responsibilities)
457	/// in the "set" of Grandpa validators from genesis.
458	pub fn current_set_id() -> SetId {
459		CurrentSetId::<T>::get()
460	}
461
462	/// A mapping from grandpa set ID to the index of the *most recent* session for which its
463	/// members were responsible.
464	///
465	/// This is only used for validating equivocation proofs. An equivocation proof must
466	/// contains a key-ownership proof for a given session, therefore we need a way to tie
467	/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
468	/// was the owner of a given key on a given session, and what the active set ID was
469	/// during that session.
470	pub fn session_for_set(set_id: SetId) -> Option<SessionIndex> {
471		SetIdSession::<T>::get(set_id)
472	}
473
474	/// Get the current set of authorities, along with their respective weights.
475	pub fn grandpa_authorities() -> AuthorityList {
476		Authorities::<T>::get().into_inner()
477	}
478
479	/// Schedule GRANDPA to pause starting in the given number of blocks.
480	/// Cannot be done when already paused.
481	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	/// Schedule a resume of GRANDPA after pausing.
493	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	/// Schedule a change in the authorities.
505	///
506	/// The change will be applied at the end of execution of the block
507	/// `in_blocks` after the current block. This value may be 0, in which
508	/// case the change is applied at the end of the current block.
509	///
510	/// If the `forced` parameter is defined, this indicates that the current
511	/// set has been synchronously determined to be offline and that after
512	/// `in_blocks` the given change should be applied. The given block number
513	/// indicates the median last finalized block number and it should be used
514	/// as the canon block when starting the new grandpa voter.
515	///
516	/// No change should be signaled while any change is pending. Returns
517	/// an error if a change is already pending.
518	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				// only allow the next forced change when twice the window has passed since
532				// this one.
533				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	/// Deposit one of this module's logs.
558	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	// Perform module initialization, abstracted so that it can be called either through genesis
564	// config builder or through `on_genesis_session`.
565	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		// NOTE: initialize first session of first set. this is necessary for
576		// the genesis set and session since we only update the set -> session
577		// mapping whenever a new session starts, i.e. through `on_new_session`.
578		SetIdSession::<T>::insert(0, 0);
579	}
580
581	/// Submits an extrinsic to report an equivocation. This method will create
582	/// an unsigned extrinsic with a call to `report_equivocation_unsigned` and
583	/// will push the transaction to the pool. Only useful in an offchain
584	/// context.
585	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		// when we record old authority sets we could try to figure out _who_
594		// failed. until then, we can't meaningfully guard against
595		// `next == last` the way that normal session changes do.
596		Stalled::<T>::put((further_wait, median));
597	}
598}
599
600#[cfg(any(feature = "try-runtime", test))]
601impl<T: Config> Pallet<T> {
602	/// Ensure the correctness of the state of this pallet.
603	///
604	/// This should be valid before or after each state transition of this pallet.
605	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	/// # Invariants
613	///
614	/// * `SetIdSession` must contain an entry for `CurrentSetId`.
615	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	/// # Invariants
627	///
628	/// * No entry in `SetIdSession` should have a set ID greater than `CurrentSetId`.
629	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		// Always issue a change if `session` says that the validators have changed.
668		// Even if their session keys are the same as before, the underlying economic
669		// identities have changed.
670		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		// update the mapping to note that the current set corresponds to the
694		// latest equivalent session (i.e. now).
695		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}