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