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		fn on_finalize(block_number: BlockNumberFor<T>) {
130			// check for scheduled pending authority set changes
131			if let Some(pending_change) = PendingChange::<T>::get() {
132				// emit signal if we're at the block that scheduled the change
133				if block_number == pending_change.scheduled_at {
134					let next_authorities = pending_change.next_authorities.to_vec();
135					if let Some(median) = pending_change.forced {
136						Self::deposit_log(ConsensusLog::ForcedChange(
137							median,
138							ScheduledChange { delay: pending_change.delay, next_authorities },
139						))
140					} else {
141						Self::deposit_log(ConsensusLog::ScheduledChange(ScheduledChange {
142							delay: pending_change.delay,
143							next_authorities,
144						}));
145					}
146				}
147
148				// enact the change if we've reached the enacting block
149				if block_number == pending_change.scheduled_at + pending_change.delay {
150					Authorities::<T>::put(&pending_change.next_authorities);
151					Self::deposit_event(Event::NewAuthorities {
152						authority_set: pending_change.next_authorities.into_inner(),
153					});
154					PendingChange::<T>::kill();
155				}
156			}
157
158			// check for scheduled pending state changes
159			match State::<T>::get() {
160				StoredState::PendingPause { scheduled_at, delay } => {
161					// signal change to pause
162					if block_number == scheduled_at {
163						Self::deposit_log(ConsensusLog::Pause(delay));
164					}
165
166					// enact change to paused state
167					if block_number == scheduled_at + delay {
168						State::<T>::put(StoredState::Paused);
169						Self::deposit_event(Event::Paused);
170					}
171				},
172				StoredState::PendingResume { scheduled_at, delay } => {
173					// signal change to resume
174					if block_number == scheduled_at {
175						Self::deposit_log(ConsensusLog::Resume(delay));
176					}
177
178					// enact change to live state
179					if block_number == scheduled_at + delay {
180						State::<T>::put(StoredState::Live);
181						Self::deposit_event(Event::Resumed);
182					}
183				},
184				_ => {},
185			}
186		}
187	}
188
189	#[pallet::call]
190	impl<T: Config> Pallet<T> {
191		/// Report voter equivocation/misbehavior. This method will verify the
192		/// equivocation proof and validate the given key ownership proof
193		/// against the extracted offender. If both are valid, the offence
194		/// will be reported.
195		#[pallet::call_index(0)]
196		#[pallet::weight(T::WeightInfo::report_equivocation(
197			key_owner_proof.validator_count(),
198			T::MaxNominators::get(),
199		))]
200		pub fn report_equivocation(
201			origin: OriginFor<T>,
202			equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>,
203			key_owner_proof: T::KeyOwnerProof,
204		) -> DispatchResultWithPostInfo {
205			let reporter = ensure_signed(origin)?;
206
207			T::EquivocationReportSystem::process_evidence(
208				Some(reporter),
209				(*equivocation_proof, key_owner_proof),
210			)?;
211			// Waive the fee since the report is valid and beneficial
212			Ok(Pays::No.into())
213		}
214
215		/// Report voter equivocation/misbehavior. This method will verify the
216		/// equivocation proof and validate the given key ownership proof
217		/// against the extracted offender. If both are valid, the offence
218		/// will be reported.
219		///
220		/// This extrinsic must be called unsigned and it is expected that only
221		/// block authors will call it (validated in `ValidateUnsigned`), as such
222		/// if the block author is defined it will be defined as the equivocation
223		/// reporter.
224		#[pallet::call_index(1)]
225		#[pallet::weight(T::WeightInfo::report_equivocation(
226			key_owner_proof.validator_count(),
227			T::MaxNominators::get(),
228		))]
229		pub fn report_equivocation_unsigned(
230			origin: OriginFor<T>,
231			equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>,
232			key_owner_proof: T::KeyOwnerProof,
233		) -> DispatchResultWithPostInfo {
234			ensure_none(origin)?;
235
236			T::EquivocationReportSystem::process_evidence(
237				None,
238				(*equivocation_proof, key_owner_proof),
239			)?;
240			Ok(Pays::No.into())
241		}
242
243		/// Note that the current authority set of the GRANDPA finality gadget has stalled.
244		///
245		/// This will trigger a forced authority set change at the beginning of the next session, to
246		/// be enacted `delay` blocks after that. The `delay` should be high enough to safely assume
247		/// that the block signalling the forced change will not be re-orged e.g. 1000 blocks.
248		/// The block production rate (which may be slowed down because of finality lagging) should
249		/// be taken into account when choosing the `delay`. The GRANDPA voters based on the new
250		/// authority will start voting on top of `best_finalized_block_number` for new finalized
251		/// blocks. `best_finalized_block_number` should be the highest of the latest finalized
252		/// block of all validators of the new authority set.
253		///
254		/// Only callable by root.
255		#[pallet::call_index(2)]
256		#[pallet::weight(T::WeightInfo::note_stalled())]
257		pub fn note_stalled(
258			origin: OriginFor<T>,
259			delay: BlockNumberFor<T>,
260			best_finalized_block_number: BlockNumberFor<T>,
261		) -> DispatchResult {
262			ensure_root(origin)?;
263
264			Self::on_stalled(delay, best_finalized_block_number);
265			Ok(())
266		}
267	}
268
269	#[pallet::event]
270	#[pallet::generate_deposit(fn deposit_event)]
271	pub enum Event {
272		/// New authority set has been applied.
273		NewAuthorities { authority_set: AuthorityList },
274		/// Current authority set has been paused.
275		Paused,
276		/// Current authority set has been resumed.
277		Resumed,
278	}
279
280	#[pallet::error]
281	pub enum Error<T> {
282		/// Attempt to signal GRANDPA pause when the authority set isn't live
283		/// (either paused or already pending pause).
284		PauseFailed,
285		/// Attempt to signal GRANDPA resume when the authority set isn't paused
286		/// (either live or already pending resume).
287		ResumeFailed,
288		/// Attempt to signal GRANDPA change with one already pending.
289		ChangePending,
290		/// Cannot signal forced change so soon after last.
291		TooSoon,
292		/// A key ownership proof provided as part of an equivocation report is invalid.
293		InvalidKeyOwnershipProof,
294		/// An equivocation proof provided as part of an equivocation report is invalid.
295		InvalidEquivocationProof,
296		/// A given equivocation report is valid but already previously reported.
297		DuplicateOffenceReport,
298	}
299
300	#[pallet::type_value]
301	pub fn DefaultForState<T: Config>() -> StoredState<BlockNumberFor<T>> {
302		StoredState::Live
303	}
304
305	/// State of the current authority set.
306	#[pallet::storage]
307	pub type State<T: Config> =
308		StorageValue<_, StoredState<BlockNumberFor<T>>, ValueQuery, DefaultForState<T>>;
309
310	/// Pending change: (signaled at, scheduled change).
311	#[pallet::storage]
312	pub type PendingChange<T: Config> =
313		StorageValue<_, StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>>;
314
315	/// next block number where we can force a change.
316	#[pallet::storage]
317	pub type NextForced<T: Config> = StorageValue<_, BlockNumberFor<T>>;
318
319	/// `true` if we are currently stalled.
320	#[pallet::storage]
321	pub type Stalled<T: Config> = StorageValue<_, (BlockNumberFor<T>, BlockNumberFor<T>)>;
322
323	/// The number of changes (both in terms of keys and underlying economic responsibilities)
324	/// in the "set" of Grandpa validators from genesis.
325	#[pallet::storage]
326	pub type CurrentSetId<T: Config> = StorageValue<_, SetId, ValueQuery>;
327
328	/// A mapping from grandpa set ID to the index of the *most recent* session for which its
329	/// members were responsible.
330	///
331	/// This is only used for validating equivocation proofs. An equivocation proof must
332	/// contains a key-ownership proof for a given session, therefore we need a way to tie
333	/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
334	/// was the owner of a given key on a given session, and what the active set ID was
335	/// during that session.
336	///
337	/// TWOX-NOTE: `SetId` is not under user control.
338	#[pallet::storage]
339	pub type SetIdSession<T: Config> = StorageMap<_, Twox64Concat, SetId, SessionIndex>;
340
341	/// The current list of authorities.
342	#[pallet::storage]
343	pub type Authorities<T: Config> =
344		StorageValue<_, BoundedAuthorityList<T::MaxAuthorities>, ValueQuery>;
345
346	#[derive(frame_support::DefaultNoBound)]
347	#[pallet::genesis_config]
348	pub struct GenesisConfig<T: Config> {
349		pub authorities: AuthorityList,
350		#[serde(skip)]
351		pub _config: core::marker::PhantomData<T>,
352	}
353
354	#[pallet::genesis_build]
355	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
356		fn build(&self) {
357			CurrentSetId::<T>::put(SetId::default());
358			Pallet::<T>::initialize(self.authorities.clone())
359		}
360	}
361
362	#[pallet::validate_unsigned]
363	impl<T: Config> ValidateUnsigned for Pallet<T> {
364		type Call = Call<T>;
365
366		fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
367			Self::validate_unsigned(source, call)
368		}
369
370		fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
371			Self::pre_dispatch(call)
372		}
373	}
374}
375
376pub trait WeightInfo {
377	fn report_equivocation(validator_count: u32, max_nominators_per_validator: u32) -> Weight;
378	fn note_stalled() -> Weight;
379}
380
381/// Bounded version of `AuthorityList`, `Limit` being the bound
382pub type BoundedAuthorityList<Limit> = WeakBoundedVec<(AuthorityId, AuthorityWeight), Limit>;
383
384/// A stored pending change.
385/// `Limit` is the bound for `next_authorities`
386#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
387#[codec(mel_bound(N: MaxEncodedLen, Limit: Get<u32>))]
388#[scale_info(skip_type_params(Limit))]
389pub struct StoredPendingChange<N, Limit> {
390	/// The block number this was scheduled at.
391	pub scheduled_at: N,
392	/// The delay in blocks until it will be applied.
393	pub delay: N,
394	/// The next authority set, weakly bounded in size by `Limit`.
395	pub next_authorities: BoundedAuthorityList<Limit>,
396	/// If defined it means the change was forced and the given block number
397	/// indicates the median last finalized block when the change was signaled.
398	pub forced: Option<N>,
399}
400
401/// Current state of the GRANDPA authority set. State transitions must happen in
402/// the same order of states defined below, e.g. `Paused` implies a prior
403/// `PendingPause`.
404#[derive(Decode, Encode, TypeInfo, MaxEncodedLen)]
405#[cfg_attr(test, derive(Debug, PartialEq))]
406pub enum StoredState<N> {
407	/// The current authority set is live, and GRANDPA is enabled.
408	Live,
409	/// There is a pending pause event which will be enacted at the given block
410	/// height.
411	PendingPause {
412		/// Block at which the intention to pause was scheduled.
413		scheduled_at: N,
414		/// Number of blocks after which the change will be enacted.
415		delay: N,
416	},
417	/// The current GRANDPA authority set is paused.
418	Paused,
419	/// There is a pending resume event which will be enacted at the given block
420	/// height.
421	PendingResume {
422		/// Block at which the intention to resume was scheduled.
423		scheduled_at: N,
424		/// Number of blocks after which the change will be enacted.
425		delay: N,
426	},
427}
428
429impl<T: Config> Pallet<T> {
430	/// State of the current authority set.
431	pub fn state() -> StoredState<BlockNumberFor<T>> {
432		State::<T>::get()
433	}
434
435	/// Pending change: (signaled at, scheduled change).
436	pub fn pending_change() -> Option<StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>> {
437		PendingChange::<T>::get()
438	}
439
440	/// next block number where we can force a change.
441	pub fn next_forced() -> Option<BlockNumberFor<T>> {
442		NextForced::<T>::get()
443	}
444
445	/// `true` if we are currently stalled.
446	pub fn stalled() -> Option<(BlockNumberFor<T>, BlockNumberFor<T>)> {
447		Stalled::<T>::get()
448	}
449
450	/// The number of changes (both in terms of keys and underlying economic responsibilities)
451	/// in the "set" of Grandpa validators from genesis.
452	pub fn current_set_id() -> SetId {
453		CurrentSetId::<T>::get()
454	}
455
456	/// A mapping from grandpa set ID to the index of the *most recent* session for which its
457	/// members were responsible.
458	///
459	/// This is only used for validating equivocation proofs. An equivocation proof must
460	/// contains a key-ownership proof for a given session, therefore we need a way to tie
461	/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
462	/// was the owner of a given key on a given session, and what the active set ID was
463	/// during that session.
464	pub fn session_for_set(set_id: SetId) -> Option<SessionIndex> {
465		SetIdSession::<T>::get(set_id)
466	}
467
468	/// Get the current set of authorities, along with their respective weights.
469	pub fn grandpa_authorities() -> AuthorityList {
470		Authorities::<T>::get().into_inner()
471	}
472
473	/// Schedule GRANDPA to pause starting in the given number of blocks.
474	/// Cannot be done when already paused.
475	pub fn schedule_pause(in_blocks: BlockNumberFor<T>) -> DispatchResult {
476		if let StoredState::Live = State::<T>::get() {
477			let scheduled_at = frame_system::Pallet::<T>::block_number();
478			State::<T>::put(StoredState::PendingPause { delay: in_blocks, scheduled_at });
479
480			Ok(())
481		} else {
482			Err(Error::<T>::PauseFailed.into())
483		}
484	}
485
486	/// Schedule a resume of GRANDPA after pausing.
487	pub fn schedule_resume(in_blocks: BlockNumberFor<T>) -> DispatchResult {
488		if let StoredState::Paused = State::<T>::get() {
489			let scheduled_at = frame_system::Pallet::<T>::block_number();
490			State::<T>::put(StoredState::PendingResume { delay: in_blocks, scheduled_at });
491
492			Ok(())
493		} else {
494			Err(Error::<T>::ResumeFailed.into())
495		}
496	}
497
498	/// Schedule a change in the authorities.
499	///
500	/// The change will be applied at the end of execution of the block
501	/// `in_blocks` after the current block. This value may be 0, in which
502	/// case the change is applied at the end of the current block.
503	///
504	/// If the `forced` parameter is defined, this indicates that the current
505	/// set has been synchronously determined to be offline and that after
506	/// `in_blocks` the given change should be applied. The given block number
507	/// indicates the median last finalized block number and it should be used
508	/// as the canon block when starting the new grandpa voter.
509	///
510	/// No change should be signaled while any change is pending. Returns
511	/// an error if a change is already pending.
512	pub fn schedule_change(
513		next_authorities: AuthorityList,
514		in_blocks: BlockNumberFor<T>,
515		forced: Option<BlockNumberFor<T>>,
516	) -> DispatchResult {
517		if !PendingChange::<T>::exists() {
518			let scheduled_at = frame_system::Pallet::<T>::block_number();
519
520			if forced.is_some() {
521				if NextForced::<T>::get().map_or(false, |next| next > scheduled_at) {
522					return Err(Error::<T>::TooSoon.into())
523				}
524
525				// only allow the next forced change when twice the window has passed since
526				// this one.
527				NextForced::<T>::put(scheduled_at + in_blocks * 2u32.into());
528			}
529
530			let next_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
531				next_authorities,
532				Some(
533					"Warning: The number of authorities given is too big. \
534					A runtime configuration adjustment may be needed.",
535				),
536			);
537
538			PendingChange::<T>::put(StoredPendingChange {
539				delay: in_blocks,
540				scheduled_at,
541				next_authorities,
542				forced,
543			});
544
545			Ok(())
546		} else {
547			Err(Error::<T>::ChangePending.into())
548		}
549	}
550
551	/// Deposit one of this module's logs.
552	fn deposit_log(log: ConsensusLog<BlockNumberFor<T>>) {
553		let log = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode());
554		frame_system::Pallet::<T>::deposit_log(log);
555	}
556
557	// Perform module initialization, abstracted so that it can be called either through genesis
558	// config builder or through `on_genesis_session`.
559	fn initialize(authorities: AuthorityList) {
560		if !authorities.is_empty() {
561			assert!(Self::grandpa_authorities().is_empty(), "Authorities are already initialized!");
562			Authorities::<T>::put(
563				&BoundedAuthorityList::<T::MaxAuthorities>::try_from(authorities).expect(
564					"Grandpa: `Config::MaxAuthorities` is smaller than the number of genesis authorities!",
565				),
566			);
567		}
568
569		// NOTE: initialize first session of first set. this is necessary for
570		// the genesis set and session since we only update the set -> session
571		// mapping whenever a new session starts, i.e. through `on_new_session`.
572		SetIdSession::<T>::insert(0, 0);
573	}
574
575	/// Submits an extrinsic to report an equivocation. This method will create
576	/// an unsigned extrinsic with a call to `report_equivocation_unsigned` and
577	/// will push the transaction to the pool. Only useful in an offchain
578	/// context.
579	pub fn submit_unsigned_equivocation_report(
580		equivocation_proof: EquivocationProof<T::Hash, BlockNumberFor<T>>,
581		key_owner_proof: T::KeyOwnerProof,
582	) -> Option<()> {
583		T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok()
584	}
585
586	fn on_stalled(further_wait: BlockNumberFor<T>, median: BlockNumberFor<T>) {
587		// when we record old authority sets we could try to figure out _who_
588		// failed. until then, we can't meaningfully guard against
589		// `next == last` the way that normal session changes do.
590		Stalled::<T>::put((further_wait, median));
591	}
592}
593
594impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
595	type Public = AuthorityId;
596}
597
598impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T>
599where
600	T: pallet_session::Config,
601{
602	type Key = AuthorityId;
603
604	fn on_genesis_session<'a, I: 'a>(validators: I)
605	where
606		I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
607	{
608		let authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
609		Self::initialize(authorities);
610	}
611
612	fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
613	where
614		I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
615	{
616		// Always issue a change if `session` says that the validators have changed.
617		// Even if their session keys are the same as before, the underlying economic
618		// identities have changed.
619		let current_set_id = if changed || Stalled::<T>::exists() {
620			let next_authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
621
622			let res = if let Some((further_wait, median)) = Stalled::<T>::take() {
623				Self::schedule_change(next_authorities, further_wait, Some(median))
624			} else {
625				Self::schedule_change(next_authorities, Zero::zero(), None)
626			};
627
628			if res.is_ok() {
629				let current_set_id = CurrentSetId::<T>::mutate(|s| {
630					*s += 1;
631					*s
632				});
633
634				let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1);
635				if current_set_id >= max_set_id_session_entries {
636					SetIdSession::<T>::remove(current_set_id - max_set_id_session_entries);
637				}
638
639				current_set_id
640			} else {
641				// either the session module signalled that the validators have changed
642				// or the set was stalled. but since we didn't successfully schedule
643				// an authority set change we do not increment the set id.
644				CurrentSetId::<T>::get()
645			}
646		} else {
647			// nothing's changed, neither economic conditions nor session keys. update the pointer
648			// of the current set.
649			CurrentSetId::<T>::get()
650		};
651
652		// update the mapping to note that the current set corresponds to the
653		// latest equivalent session (i.e. now).
654		let session_index = pallet_session::Pallet::<T>::current_index();
655		SetIdSession::<T>::insert(current_set_id, &session_index);
656	}
657
658	fn on_disabled(i: u32) {
659		Self::deposit_log(ConsensusLog::OnDisabled(i as u64))
660	}
661}