Skip to main content

pallet_session/
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//! # Session Pallet
19//!
20//! The Session pallet allows validators to manage their session keys, provides a function for
21//! changing the session length, and handles session rotation.
22//!
23//! - [`Config`]
24//! - [`Call`]
25//! - [`Pallet`]
26//!
27//! ## Overview
28//!
29//! ### Terminology
30//! <!-- Original author of paragraph: @gavofyork -->
31//!
32//! - **Session:** A session is a period of time that has a constant set of validators. Validators
33//!   can only join or exit the validator set at a session change. It is measured in block numbers.
34//!   The block where a session is ended is determined by the `ShouldEndSession` trait. When the
35//!   session is ending, a new validator set can be chosen by `OnSessionEnding` implementations.
36//!
37//! - **Session key:** A session key is actually several keys kept together that provide the various
38//!   signing functions required by network authorities/validators in pursuit of their duties.
39//! - **Validator ID:** Every account has an associated validator ID. For some simple staking
40//!   systems, this may just be the same as the account ID. For staking systems using a
41//!   stash/controller model, the validator ID would be the stash account ID of the controller.
42//!
43//! - **Session key configuration process:** Session keys are set using `set_keys` for use not in
44//!   the next session, but the session after next. They are stored in `NextKeys`, a mapping between
45//!   the caller's `ValidatorId` and the session keys provided. `set_keys` allows users to set their
46//!   session key prior to being selected as validator. It is a public call since it uses
47//!   `ensure_signed`, which checks that the origin is a signed account. As such, the account ID of
48//!   the origin stored in `NextKeys` may not necessarily be associated with a block author or a
49//!   validator. The session keys of accounts are removed once their account balance is zero.
50//!
51//! - **Session length:** This pallet does not assume anything about the length of each session.
52//!   Rather, it relies on an implementation of `ShouldEndSession` to dictate a new session's start.
53//!   This pallet provides the `PeriodicSessions` struct for simple periodic sessions.
54//!
55//! - **Session rotation configuration:** Configure as either a 'normal' (rewardable session where
56//!   rewards are applied) or 'exceptional' (slashable) session rotation.
57//!
58//! - **Session rotation process:** At the beginning of each block, the `on_initialize` function
59//!   queries the provided implementation of `ShouldEndSession`. If the session is to end the newly
60//!   activated validator IDs and session keys are taken from storage and passed to the
61//!   `SessionHandler`. The validator set supplied by `SessionManager::new_session` and the
62//!   corresponding session keys, which may have been registered via `set_keys` during the previous
63//!   session, are written to storage where they will wait one session before being passed to the
64//!   `SessionHandler` themselves.
65//!
66//! ### Goals
67//!
68//! The Session pallet is designed to make the following possible:
69//!
70//! - Set session keys of the validator set for upcoming sessions.
71//! - Control the length of sessions.
72//! - Configure and switch between either normal or exceptional session rotations.
73//!
74//! ## Interface
75//!
76//! ### Dispatchable Functions
77//!
78//! - `set_keys` - Set a validator's session keys for upcoming sessions.
79//!
80//! ### Public Functions
81//!
82//! - `rotate_session` - Change to the next session. Register the new authority set. Queue changes
83//!   for next session rotation.
84//! - `disable_index` - Disable a validator by index.
85//! - `disable` - Disable a validator by Validator ID
86//!
87//! ## Usage
88//!
89//! ### Example from the FRAME
90//!
91//! The [Staking pallet](../pallet_staking/index.html) uses the Session pallet to get the validator
92//! set.
93//!
94//! ```
95//! use pallet_session as session;
96//!
97//! fn validators<T: pallet_session::Config>() -> Vec<<T as pallet_session::Config>::ValidatorId> {
98//! 	pallet_session::Validators::<T>::get()
99//! }
100//! # fn main(){}
101//! ```
102//!
103//! ## Related Pallets
104//!
105//! - [Staking](../pallet_staking/index.html)
106
107#![cfg_attr(not(feature = "std"), no_std)]
108
109pub mod disabling;
110#[cfg(feature = "historical")]
111pub mod historical;
112pub mod migrations;
113#[cfg(test)]
114mod mock;
115#[cfg(test)]
116mod tests;
117pub mod weights;
118
119extern crate alloc;
120
121use alloc::{boxed::Box, vec::Vec};
122use codec::{Decode, MaxEncodedLen};
123use core::{
124	marker::PhantomData,
125	ops::{Rem, Sub},
126};
127use disabling::DisablingStrategy;
128use frame_support::{
129	dispatch::DispatchResult,
130	ensure,
131	traits::{
132		fungible::{hold::Mutate as HoldMutate, Inspect, Mutate},
133		Defensive, EstimateNextNewSession, EstimateNextSessionRotation, FindAuthor, Get,
134		OneSessionHandler, ValidatorRegistration, ValidatorSet,
135	},
136	weights::Weight,
137	Parameter,
138};
139use frame_system::pallet_prelude::BlockNumberFor;
140use sp_runtime::{
141	traits::{AtLeast32BitUnsigned, Convert, Member, One, OpaqueKeys, Zero},
142	ConsensusEngineId, DispatchError, KeyTypeId, Permill, RuntimeAppPublic,
143};
144use sp_staking::{offence::OffenceSeverity, SessionIndex};
145
146pub use pallet::*;
147pub use weights::WeightInfo;
148
149#[cfg(any(feature = "try-runtime"))]
150use sp_runtime::TryRuntimeError;
151
152pub(crate) const LOG_TARGET: &str = "runtime::session";
153
154// syntactic sugar for logging.
155#[macro_export]
156macro_rules! log {
157	($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
158		log::$level!(
159			target: crate::LOG_TARGET,
160			concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
161		)
162	};
163}
164
165/// Decides whether the session should be ended.
166pub trait ShouldEndSession<BlockNumber> {
167	/// Return `true` if the session should be ended.
168	fn should_end_session(now: BlockNumber) -> bool;
169}
170
171/// Ends the session after a fixed period of blocks.
172///
173/// The first session will have length of `Offset`, and
174/// the following sessions will have length of `Period`.
175/// This may prove nonsensical if `Offset` >= `Period`.
176pub struct PeriodicSessions<Period, Offset>(PhantomData<(Period, Offset)>);
177
178impl<
179		BlockNumber: Rem<Output = BlockNumber> + Sub<Output = BlockNumber> + Zero + PartialOrd,
180		Period: Get<BlockNumber>,
181		Offset: Get<BlockNumber>,
182	> ShouldEndSession<BlockNumber> for PeriodicSessions<Period, Offset>
183{
184	fn should_end_session(now: BlockNumber) -> bool {
185		let offset = Offset::get();
186		now >= offset && ((now - offset) % Period::get()).is_zero()
187	}
188}
189
190impl<
191		BlockNumber: AtLeast32BitUnsigned + Clone,
192		Period: Get<BlockNumber>,
193		Offset: Get<BlockNumber>,
194	> EstimateNextSessionRotation<BlockNumber> for PeriodicSessions<Period, Offset>
195{
196	fn average_session_length() -> BlockNumber {
197		Period::get()
198	}
199
200	fn estimate_current_session_progress(now: BlockNumber) -> (Option<Permill>, Weight) {
201		let offset = Offset::get();
202		let period = Period::get();
203
204		// NOTE: we add one since we assume that the current block has already elapsed,
205		// i.e. when evaluating the last block in the session the progress should be 100%
206		// (0% is never returned).
207		let progress = if now >= offset {
208			let current = (now - offset) % period.clone() + One::one();
209			Some(Permill::from_rational(current, period))
210		} else {
211			Some(Permill::from_rational(now + One::one(), offset))
212		};
213
214		// Weight note: `estimate_current_session_progress` has no storage reads and trivial
215		// computational overhead. There should be no risk to the chain having this weight value be
216		// zero for now. However, this value of zero was not properly calculated, and so it would be
217		// reasonable to come back here and properly calculate the weight of this function.
218		(progress, Zero::zero())
219	}
220
221	fn estimate_next_session_rotation(now: BlockNumber) -> (Option<BlockNumber>, Weight) {
222		let offset = Offset::get();
223		let period = Period::get();
224
225		let next_session = if now > offset {
226			let block_after_last_session = (now.clone() - offset) % period.clone();
227			if block_after_last_session > Zero::zero() {
228				now.saturating_add(period.saturating_sub(block_after_last_session))
229			} else {
230				// this branch happens when the session is already rotated or will rotate in this
231				// block (depending on being called before or after `session::on_initialize`). Here,
232				// we assume the latter, namely that this is called after `session::on_initialize`,
233				// and thus we add period to it as well.
234				now + period
235			}
236		} else {
237			offset
238		};
239
240		// Weight note: `estimate_next_session_rotation` has no storage reads and trivial
241		// computational overhead. There should be no risk to the chain having this weight value be
242		// zero for now. However, this value of zero was not properly calculated, and so it would be
243		// reasonable to come back here and properly calculate the weight of this function.
244		(Some(next_session), Zero::zero())
245	}
246}
247
248/// A trait for managing creation of new validator set.
249pub trait SessionManager<ValidatorId> {
250	/// Plan a new session, and optionally provide the new validator set.
251	///
252	/// Even if the validator-set is the same as before, if any underlying economic conditions have
253	/// changed (i.e. stake-weights), the new validator set must be returned. This is necessary for
254	/// consensus engines making use of the session pallet to issue a validator-set change so
255	/// misbehavior can be provably associated with the new economic conditions as opposed to the
256	/// old. The returned validator set, if any, will not be applied until `new_index`. `new_index`
257	/// is strictly greater than from previous call.
258	///
259	/// The first session start at index 0.
260	///
261	/// `new_session(session)` is guaranteed to be called before `end_session(session-1)`. In other
262	/// words, a new session must always be planned before an ongoing one can be finished.
263	fn new_session(new_index: SessionIndex) -> Option<Vec<ValidatorId>>;
264	/// Same as `new_session`, but it this should only be called at genesis.
265	///
266	/// The session manager might decide to treat this in a different way. Default impl is simply
267	/// using [`new_session`](Self::new_session).
268	fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<ValidatorId>> {
269		Self::new_session(new_index)
270	}
271	/// End the session.
272	///
273	/// Because the session pallet can queue validator set the ending session can be lower than the
274	/// last new session index.
275	fn end_session(end_index: SessionIndex);
276	/// Start an already planned session.
277	///
278	/// The session start to be used for validation.
279	fn start_session(start_index: SessionIndex);
280}
281
282impl<A> SessionManager<A> for () {
283	fn new_session(_: SessionIndex) -> Option<Vec<A>> {
284		None
285	}
286	fn start_session(_: SessionIndex) {}
287	fn end_session(_: SessionIndex) {}
288}
289
290/// Handler for session life cycle events.
291pub trait SessionHandler<ValidatorId> {
292	/// All the key type ids this session handler can process.
293	///
294	/// The order must be the same as it expects them in
295	/// [`on_new_session`](Self::on_new_session<Ks>) and
296	/// [`on_genesis_session`](Self::on_genesis_session<Ks>).
297	const KEY_TYPE_IDS: &'static [KeyTypeId];
298
299	/// The given validator set will be used for the genesis session.
300	/// It is guaranteed that the given validator set will also be used
301	/// for the second session, therefore the first call to `on_new_session`
302	/// should provide the same validator set.
303	fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(ValidatorId, Ks)]);
304
305	/// Session set has changed; act appropriately. Note that this can be called
306	/// before initialization of your pallet.
307	///
308	/// `changed` is true whenever any of the session keys or underlying economic
309	/// identities or weightings behind `validators` keys has changed. `queued_validators`
310	/// could change without `validators` changing. Example of possible sequent calls:
311	///     Session N: on_new_session(false, unchanged_validators, unchanged_queued_validators)
312	///     Session N + 1: on_new_session(false, unchanged_validators, new_queued_validators)
313	/// 	Session N + 2: on_new_session(true, new_queued_validators, new_queued_validators)
314	fn on_new_session<Ks: OpaqueKeys>(
315		changed: bool,
316		validators: &[(ValidatorId, Ks)],
317		queued_validators: &[(ValidatorId, Ks)],
318	);
319
320	/// A notification for end of the session.
321	///
322	/// Note it is triggered before any [`SessionManager::end_session`] handlers,
323	/// so we can still affect the validator set.
324	fn on_before_session_ending() {}
325
326	/// A validator got disabled. Act accordingly until a new session begins.
327	fn on_disabled(validator_index: u32);
328}
329
330#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
331#[tuple_types_custom_trait_bound(OneSessionHandler<AId>)]
332impl<AId> SessionHandler<AId> for Tuple {
333	for_tuples!(
334		const KEY_TYPE_IDS: &'static [KeyTypeId] = &[ #( <Tuple::Key as RuntimeAppPublic>::ID ),* ];
335	);
336
337	fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(AId, Ks)]) {
338		for_tuples!(
339			#(
340				let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
341					.filter_map(|k|
342						k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
343					)
344				);
345
346				Tuple::on_genesis_session(our_keys);
347			)*
348		)
349	}
350
351	fn on_new_session<Ks: OpaqueKeys>(
352		changed: bool,
353		validators: &[(AId, Ks)],
354		queued_validators: &[(AId, Ks)],
355	) {
356		for_tuples!(
357			#(
358				let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
359					.filter_map(|k|
360						k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
361					));
362				let queued_keys: Box<dyn Iterator<Item=_>> = Box::new(queued_validators.iter()
363					.filter_map(|k|
364						k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
365					));
366				Tuple::on_new_session(changed, our_keys, queued_keys);
367			)*
368		)
369	}
370
371	fn on_before_session_ending() {
372		for_tuples!( #( Tuple::on_before_session_ending(); )* )
373	}
374
375	fn on_disabled(i: u32) {
376		for_tuples!( #( Tuple::on_disabled(i); )* )
377	}
378}
379
380/// `SessionHandler` for tests that use `UintAuthorityId` as `Keys`.
381pub struct TestSessionHandler;
382impl<AId> SessionHandler<AId> for TestSessionHandler {
383	const KEY_TYPE_IDS: &'static [KeyTypeId] = &[sp_runtime::key_types::DUMMY];
384	fn on_genesis_session<Ks: OpaqueKeys>(_: &[(AId, Ks)]) {}
385	fn on_new_session<Ks: OpaqueKeys>(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {}
386	fn on_before_session_ending() {}
387	fn on_disabled(_: u32) {}
388}
389
390/// Interface to the session pallet for session management.
391///
392/// This trait provides a complete interface for managing sessions from external contexts,
393/// such as other pallets or runtime components. It combines session key management with
394/// validator operations and historical session data pruning.
395///
396/// Implemented by `Pallet<T>` when `T: Config + historical::Config`.
397pub trait SessionInterface {
398	/// The validator id type of the session pallet.
399	type ValidatorId: Clone;
400
401	/// The account id type.
402	type AccountId;
403
404	/// The session keys type.
405	type Keys: OpaqueKeys + codec::Decode;
406
407	/// Get the current set of validators.
408	fn validators() -> Vec<Self::ValidatorId>;
409
410	/// Prune historical session data up to the given session index.
411	fn prune_up_to(index: SessionIndex);
412
413	/// Report an offence for a validator.
414	///
415	/// This is used to disable validators directly on the RC until the next validator set.
416	fn report_offence(offender: Self::ValidatorId, severity: OffenceSeverity);
417
418	/// Set session keys for an account.
419	///
420	/// This method is intended for privileged callers (e.g., other pallets receiving validated
421	/// requests via XCM). It bypasses deposit holds and consumer reference tracking, so the
422	/// account does not need to be "live" or have balance on this chain.
423	///
424	/// This method does not validate ownership proof. Callers must verify that the keys belong to
425	/// the account before calling this method.
426	fn set_keys(account: &Self::AccountId, keys: Self::Keys) -> DispatchResult;
427
428	/// Purge session keys for an account.
429	///
430	/// This method is intended for privileged callers (e.g., other pallets receiving validated
431	/// requests via XCM). It bypasses deposit release and consumer reference decrement.
432	fn purge_keys(account: &Self::AccountId) -> DispatchResult;
433
434	/// Weight for setting session keys.
435	fn set_keys_weight() -> Weight;
436
437	/// Weight for purging session keys.
438	fn purge_keys_weight() -> Weight;
439}
440
441#[frame_support::pallet]
442pub mod pallet {
443	use super::*;
444	use frame_support::pallet_prelude::*;
445	use frame_system::pallet_prelude::*;
446
447	/// The in-code storage version.
448	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
449
450	#[pallet::pallet]
451	#[pallet::storage_version(STORAGE_VERSION)]
452	#[pallet::without_storage_info]
453	pub struct Pallet<T>(_);
454
455	#[pallet::config]
456	pub trait Config: frame_system::Config {
457		/// The overarching event type.
458		#[allow(deprecated)]
459		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
460
461		/// A stable ID for a validator.
462		type ValidatorId: Member
463			+ Parameter
464			+ MaybeSerializeDeserialize
465			+ MaxEncodedLen
466			+ TryFrom<Self::AccountId>;
467
468		/// A conversion from account ID to validator ID.
469		///
470		/// It is also a means to check that an account id is eligible to set session keys, through
471		/// being associated with a validator id. To disable this check, use
472		/// [`sp_runtime::traits::ConvertInto`].
473		///
474		/// Its cost must be at most one storage read.
475		type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
476
477		/// Indicator for when to end the session.
478		type ShouldEndSession: ShouldEndSession<BlockNumberFor<Self>>;
479
480		/// Something that can predict the next session rotation. This should typically come from
481		/// the same logical unit that provides [`ShouldEndSession`], yet, it gives a best effort
482		/// estimate. It is helpful to implement [`EstimateNextNewSession`].
483		type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
484
485		/// Handler for managing new session.
486		type SessionManager: SessionManager<Self::ValidatorId>;
487
488		/// Handler when a session has changed.
489		type SessionHandler: SessionHandler<Self::ValidatorId>;
490
491		/// The keys.
492		type Keys: OpaqueKeys + Member + Parameter + MaybeSerializeDeserialize;
493
494		/// `DisablingStragegy` controls how validators are disabled
495		type DisablingStrategy: DisablingStrategy<Self>;
496
497		/// Weight information for extrinsics in this pallet.
498		type WeightInfo: WeightInfo;
499
500		/// The currency type for placing holds when setting keys.
501		type Currency: Mutate<Self::AccountId>
502			+ HoldMutate<Self::AccountId, Reason: From<HoldReason>>;
503
504		/// The amount to be held when setting keys.
505		#[pallet::constant]
506		type KeyDeposit: Get<
507			<<Self as Config>::Currency as Inspect<<Self as frame_system::Config>::AccountId>>::Balance,
508		>;
509	}
510
511	#[pallet::genesis_config]
512	#[derive(frame_support::DefaultNoBound)]
513	pub struct GenesisConfig<T: Config> {
514		/// Initial list of validator at genesis representing by their `(AccountId, ValidatorId,
515		/// Keys)`. These keys will be considered authorities for the first two sessions and they
516		/// will be valid at least until session 2
517		pub keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
518		/// List of (AccountId, ValidatorId, Keys) that will be registered at genesis, but not as
519		/// active validators. These keys are set, together with `keys`, as authority candidates
520		/// for future sessions (enactable from session 2 onwards)
521		pub non_authority_keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
522	}
523
524	#[pallet::genesis_build]
525	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
526		fn build(&self) {
527			if T::SessionHandler::KEY_TYPE_IDS.len() != T::Keys::key_ids().len() {
528				panic!("Number of keys in session handler and session keys does not match");
529			}
530
531			T::SessionHandler::KEY_TYPE_IDS
532				.iter()
533				.zip(T::Keys::key_ids())
534				.enumerate()
535				.for_each(|(i, (sk, kk))| {
536					if sk != kk {
537						panic!(
538							"Session handler and session key expect different key type at index: {}",
539							i,
540						);
541					}
542				});
543
544			for (account, val, keys) in
545				self.keys.iter().chain(self.non_authority_keys.iter()).cloned()
546			{
547				Pallet::<T>::inner_set_keys(&val, keys)
548					.expect("genesis config must not contain duplicates; qed");
549				if frame_system::Pallet::<T>::inc_consumers_without_limit(&account).is_err() {
550					// This will leak a provider reference, however it only happens once (at
551					// genesis) so it's really not a big deal and we assume that the user wants to
552					// do this since it's the only way a non-endowed account can contain a session
553					// key.
554					frame_system::Pallet::<T>::inc_providers(&account);
555				}
556			}
557
558			let initial_validators_0 =
559				T::SessionManager::new_session_genesis(0).unwrap_or_else(|| {
560					frame_support::print(
561						"No initial validator provided by `SessionManager`, use \
562						session config keys to generate initial validator set.",
563					);
564					self.keys.iter().map(|x| x.1.clone()).collect()
565				});
566
567			let initial_validators_1 = T::SessionManager::new_session_genesis(1)
568				.unwrap_or_else(|| initial_validators_0.clone());
569
570			let queued_keys: Vec<_> = initial_validators_1
571				.into_iter()
572				.filter_map(|v| Pallet::<T>::load_keys(&v).map(|k| (v, k)))
573				.collect();
574
575			// Tell everyone about the genesis session keys
576			T::SessionHandler::on_genesis_session::<T::Keys>(&queued_keys);
577
578			Validators::<T>::put(initial_validators_0);
579			QueuedKeys::<T>::put(queued_keys);
580
581			T::SessionManager::start_session(0);
582		}
583	}
584
585	/// A reason for the pallet placing a hold on funds.
586	#[pallet::composite_enum]
587	pub enum HoldReason {
588		// Funds are held when settings keys
589		#[codec(index = 0)]
590		Keys,
591	}
592
593	/// The current set of validators.
594	#[pallet::storage]
595	pub type Validators<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>;
596
597	/// Current index of the session.
598	#[pallet::storage]
599	pub type CurrentIndex<T> = StorageValue<_, SessionIndex, ValueQuery>;
600
601	/// True if the underlying economic identities or weighting behind the validators
602	/// has changed in the queued validator set.
603	#[pallet::storage]
604	pub type QueuedChanged<T> = StorageValue<_, bool, ValueQuery>;
605
606	/// The queued keys for the next session. When the next session begins, these keys
607	/// will be used to determine the validator's session keys.
608	#[pallet::storage]
609	pub type QueuedKeys<T: Config> = StorageValue<_, Vec<(T::ValidatorId, T::Keys)>, ValueQuery>;
610
611	/// Indices of disabled validators.
612	///
613	/// The vec is always kept sorted so that we can find whether a given validator is
614	/// disabled using binary search. It gets cleared when `on_session_ending` returns
615	/// a new set of identities.
616	#[pallet::storage]
617	pub type DisabledValidators<T> = StorageValue<_, Vec<(u32, OffenceSeverity)>, ValueQuery>;
618
619	/// The next session keys for a validator.
620	#[pallet::storage]
621	pub type NextKeys<T: Config> =
622		StorageMap<_, Twox64Concat, T::ValidatorId, T::Keys, OptionQuery>;
623
624	/// The owner of a key. The key is the `KeyTypeId` + the encoded key.
625	#[pallet::storage]
626	pub type KeyOwner<T: Config> =
627		StorageMap<_, Twox64Concat, (KeyTypeId, Vec<u8>), T::ValidatorId, OptionQuery>;
628
629	/// Accounts whose keys were set via `SessionInterface` (external path) without
630	/// incrementing the consumer reference or placing a key deposit. `do_purge_keys`
631	/// only decrements consumers for accounts that were registered through the local
632	/// session pallet.
633	#[pallet::storage]
634	pub type ExternallySetKeys<T: Config> =
635		StorageMap<_, Twox64Concat, T::AccountId, (), OptionQuery>;
636
637	#[pallet::event]
638	#[pallet::generate_deposit(pub(super) fn deposit_event)]
639	pub enum Event<T: Config> {
640		/// New session has happened. Note that the argument is the session index, not the
641		/// block number as the type might suggest.
642		NewSession { session_index: SessionIndex },
643		/// The `NewSession` event in the current block also implies a new validator set to be
644		/// queued.
645		NewQueued,
646		/// Validator has been disabled.
647		ValidatorDisabled { validator: T::ValidatorId },
648		/// Validator has been re-enabled.
649		ValidatorReenabled { validator: T::ValidatorId },
650	}
651
652	/// Error for the session pallet.
653	#[pallet::error]
654	pub enum Error<T> {
655		/// Invalid ownership proof.
656		InvalidProof,
657		/// No associated validator ID for account.
658		NoAssociatedValidatorId,
659		/// Registered duplicate key.
660		DuplicatedKey,
661		/// No keys are associated with this account.
662		NoKeys,
663		/// Key setting account is not live, so it's impossible to associate keys.
664		NoAccount,
665	}
666
667	#[pallet::hooks]
668	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
669		/// Called when a block is initialized. Will rotate session if it is the last
670		/// block of the current session.
671		fn on_initialize(n: BlockNumberFor<T>) -> Weight {
672			if T::ShouldEndSession::should_end_session(n) {
673				Self::rotate_session();
674				T::BlockWeights::get().max_block
675			} else {
676				// NOTE: the non-database part of the weight for `should_end_session(n)` is
677				// included as weight for empty block, the database part is expected to be in
678				// cache.
679				Weight::zero()
680			}
681		}
682
683		#[cfg(feature = "try-runtime")]
684		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
685			Self::do_try_state()
686		}
687	}
688
689	#[pallet::call]
690	impl<T: Config> Pallet<T> {
691		/// Sets the session key(s) of the function caller to `keys`.
692		///
693		/// Allows an account to set its session key prior to becoming a validator.
694		/// This doesn't take effect until the next session.
695		///
696		/// - `origin`: The dispatch origin of this function must be signed.
697		/// - `keys`: The new session keys to set. These are the public keys of all sessions keys
698		///   setup in the runtime.
699		/// - `proof`: The proof that `origin` has access to the private keys of `keys`. See
700		///   [`impl_opaque_keys`](sp_runtime::impl_opaque_keys) for more information about the
701		///   proof format.
702		#[pallet::call_index(0)]
703		#[pallet::weight(T::WeightInfo::set_keys())]
704		pub fn set_keys(origin: OriginFor<T>, keys: T::Keys, proof: Vec<u8>) -> DispatchResult {
705			let who = ensure_signed(origin)?;
706			ensure!(
707				who.using_encoded(|who| keys.ownership_proof_is_valid(who, &proof)),
708				Error::<T>::InvalidProof,
709			);
710
711			Self::do_set_keys(&who, keys)?;
712			Ok(())
713		}
714
715		/// Removes any session key(s) of the function caller.
716		///
717		/// This doesn't take effect until the next session.
718		///
719		/// The dispatch origin of this function must be Signed and the account must be either be
720		/// convertible to a validator ID using the chain's typical addressing system (this usually
721		/// means being a controller account) or directly convertible into a validator ID (which
722		/// usually means being a stash account).
723		#[pallet::call_index(1)]
724		#[pallet::weight(T::WeightInfo::purge_keys())]
725		pub fn purge_keys(origin: OriginFor<T>) -> DispatchResult {
726			let who = ensure_signed(origin)?;
727			Self::do_purge_keys(&who)?;
728			Ok(())
729		}
730	}
731
732	#[cfg(feature = "runtime-benchmarks")]
733	impl<T: Config> Pallet<T> {
734		/// Mint enough funds into `who`, such that they can pay the session key setting deposit.
735		///
736		/// Meant to be used if any pallet's benchmarking code wishes to set session keys, and wants
737		/// to make sure it will succeed.
738		pub fn ensure_can_pay_key_deposit(who: &T::AccountId) -> Result<(), DispatchError> {
739			use frame_support::traits::tokens::{Fortitude, Preservation};
740			let deposit = T::KeyDeposit::get();
741			let has = T::Currency::reducible_balance(who, Preservation::Protect, Fortitude::Force);
742			if let Some(deficit) = deposit.checked_sub(&has) {
743				T::Currency::mint_into(who, deficit.max(T::Currency::minimum_balance()))
744					.map(|_inc| ())
745			} else {
746				Ok(())
747			}
748		}
749	}
750}
751
752impl<T: Config> Pallet<T> {
753	/// Public function to access the current set of validators.
754	pub fn validators() -> Vec<T::ValidatorId> {
755		Validators::<T>::get()
756	}
757
758	/// Public function to access the current session index.
759	pub fn current_index() -> SessionIndex {
760		CurrentIndex::<T>::get()
761	}
762
763	/// Public function to access the queued keys.
764	pub fn queued_keys() -> Vec<(T::ValidatorId, T::Keys)> {
765		QueuedKeys::<T>::get()
766	}
767
768	/// Public function to access the disabled validators.
769	pub fn disabled_validators() -> Vec<u32> {
770		DisabledValidators::<T>::get().iter().map(|(i, _)| *i).collect()
771	}
772
773	/// Move on to next session. Register new validator set and session keys. Changes to the
774	/// validator set have a session of delay to take effect. This allows for equivocation
775	/// punishment after a fork.
776	pub fn rotate_session() {
777		let session_index = CurrentIndex::<T>::get();
778		let changed = QueuedChanged::<T>::get();
779
780		// Inform the session handlers that a session is going to end.
781		T::SessionHandler::on_before_session_ending();
782		T::SessionManager::end_session(session_index);
783		log!(trace, "ending_session {:?}", session_index);
784
785		// Get queued session keys and validators.
786		let session_keys = QueuedKeys::<T>::get();
787		let validators =
788			session_keys.iter().map(|(validator, _)| validator.clone()).collect::<Vec<_>>();
789		Validators::<T>::put(&validators);
790
791		if changed {
792			log!(trace, "resetting disabled validators");
793			// reset disabled validators if active set was changed
794			DisabledValidators::<T>::kill();
795		}
796
797		// Increment session index.
798		let session_index = session_index + 1;
799		CurrentIndex::<T>::put(session_index);
800		T::SessionManager::start_session(session_index);
801		log!(trace, "starting_session {:?}", session_index);
802
803		// Get next validator set.
804		let maybe_next_validators = T::SessionManager::new_session(session_index + 1);
805		log!(
806			trace,
807			"planning_session {:?} with {:?} validators",
808			session_index + 1,
809			maybe_next_validators.as_ref().map(|v| v.len())
810		);
811		let (next_validators, next_identities_changed) =
812			if let Some(validators) = maybe_next_validators {
813				// NOTE: as per the documentation on `OnSessionEnding`, we consider
814				// the validator set as having changed even if the validators are the
815				// same as before, as underlying economic conditions may have changed.
816				Self::deposit_event(Event::<T>::NewQueued);
817				(validators, true)
818			} else {
819				(Validators::<T>::get(), false)
820			};
821
822		// Queue next session keys.
823		let (queued_amalgamated, next_changed) = {
824			// until we are certain there has been a change, iterate the prior
825			// validators along with the current and check for changes
826			let mut changed = next_identities_changed;
827
828			let mut now_session_keys = session_keys.iter();
829			let mut check_next_changed = |keys: &T::Keys| {
830				if changed {
831					return;
832				}
833				// since a new validator set always leads to `changed` starting
834				// as true, we can ensure that `now_session_keys` and `next_validators`
835				// have the same length. this function is called once per iteration.
836				if let Some((_, old_keys)) = now_session_keys.next() {
837					if old_keys != keys {
838						changed = true;
839					}
840				}
841			};
842			let queued_amalgamated =
843				next_validators
844					.into_iter()
845					.filter_map(|a| {
846						let k =
847							Self::load_keys(&a).or_else(|| {
848								log!(warn, "failed to load session key for {:?}, skipping for next session, maybe you need to set session keys for them?", a);
849								None
850							})?;
851						check_next_changed(&k);
852						Some((a, k))
853					})
854					.collect::<Vec<_>>();
855
856			(queued_amalgamated, changed)
857		};
858
859		QueuedKeys::<T>::put(queued_amalgamated.clone());
860		QueuedChanged::<T>::put(next_changed);
861
862		// Record that this happened.
863		Self::deposit_event(Event::NewSession { session_index });
864
865		// Tell everyone about the new session keys.
866		T::SessionHandler::on_new_session::<T::Keys>(changed, &session_keys, &queued_amalgamated);
867	}
868
869	/// Upgrade the key type from some old type to a new type. Supports adding
870	/// and removing key types.
871	///
872	/// This function should be used with extreme care and only during an
873	/// `on_runtime_upgrade` block. Misuse of this function can put your blockchain
874	/// into an unrecoverable state.
875	///
876	/// Care should be taken that the raw versions of the
877	/// added keys are unique for every `ValidatorId, KeyTypeId` combination.
878	/// This is an invariant that the session pallet typically maintains internally.
879	///
880	/// As the actual values of the keys are typically not known at runtime upgrade,
881	/// it's recommended to initialize the keys to a (unique) dummy value with the expectation
882	/// that all validators should invoke `set_keys` before those keys are actually
883	/// required.
884	pub fn upgrade_keys<Old, F>(upgrade: F)
885	where
886		Old: OpaqueKeys + Member + Decode,
887		F: Fn(T::ValidatorId, Old) -> T::Keys,
888	{
889		let old_ids = Old::key_ids();
890		let new_ids = T::Keys::key_ids();
891
892		// Translate NextKeys, and key ownership relations at the same time.
893		NextKeys::<T>::translate::<Old, _>(|val, old_keys| {
894			// Clear all key ownership relations. Typically the overlap should
895			// stay the same, but no guarantees by the upgrade function.
896			for i in old_ids.iter() {
897				Self::clear_key_owner(*i, old_keys.get_raw(*i));
898			}
899
900			let new_keys = upgrade(val.clone(), old_keys);
901
902			// And now set the new ones.
903			for i in new_ids.iter() {
904				Self::put_key_owner(*i, new_keys.get_raw(*i), &val);
905			}
906
907			Some(new_keys)
908		});
909
910		let _ = QueuedKeys::<T>::translate::<Vec<(T::ValidatorId, Old)>, _>(|k| {
911			k.map(|k| {
912				k.into_iter()
913					.map(|(val, old_keys)| (val.clone(), upgrade(val, old_keys)))
914					.collect::<Vec<_>>()
915			})
916		});
917	}
918
919	/// Perform the set_key operation, checking for duplicates. Does not set `Changed`.
920	///
921	/// This ensures that the reference counter in system is incremented appropriately and as such
922	/// must accept an account ID, rather than a validator ID.
923	fn do_set_keys(account: &T::AccountId, keys: T::Keys) -> DispatchResult {
924		let who = T::ValidatorIdOf::convert(account.clone())
925			.ok_or(Error::<T>::NoAssociatedValidatorId)?;
926
927		ensure!(frame_system::Pallet::<T>::can_inc_consumer(account), Error::<T>::NoAccount);
928
929		let old_keys = Self::inner_set_keys(&who, keys)?;
930
931		// Place deposit and increment consumer if this is a new local registration,
932		// or if transitioning from external to local management.
933		// We also clear `ExternallySetKeys` if set.
934		let needs_local_setup =
935			old_keys.is_none() || ExternallySetKeys::<T>::take(account).is_some();
936		if needs_local_setup {
937			let deposit = T::KeyDeposit::get();
938			if !deposit.is_zero() {
939				T::Currency::hold(&HoldReason::Keys.into(), account, deposit)?;
940			}
941
942			let assertion = frame_system::Pallet::<T>::inc_consumers(account).is_ok();
943			debug_assert!(assertion, "can_inc_consumer() returned true; no change since; qed");
944		}
945
946		Ok(())
947	}
948
949	/// Perform the set_key operation, checking for duplicates. Does not set `Changed`.
950	///
951	/// The old keys for this validator are returned, or `None` if there were none.
952	///
953	/// This does not ensure that the reference counter in system is incremented appropriately, it
954	/// must be done by the caller or the keys will be leaked in storage.
955	fn inner_set_keys(
956		who: &T::ValidatorId,
957		keys: T::Keys,
958	) -> Result<Option<T::Keys>, DispatchError> {
959		let old_keys = Self::load_keys(who);
960
961		for id in T::Keys::key_ids() {
962			let key = keys.get_raw(*id);
963
964			// ensure keys are without duplication.
965			ensure!(
966				Self::key_owner(*id, key).map_or(true, |owner| &owner == who),
967				Error::<T>::DuplicatedKey,
968			);
969		}
970
971		for id in T::Keys::key_ids() {
972			let key = keys.get_raw(*id);
973
974			if let Some(old) = old_keys.as_ref().map(|k| k.get_raw(*id)) {
975				if key == old {
976					continue;
977				}
978
979				Self::clear_key_owner(*id, old);
980			}
981
982			Self::put_key_owner(*id, key, who);
983		}
984
985		Self::put_keys(who, &keys);
986		Ok(old_keys)
987	}
988
989	fn do_purge_keys(account: &T::AccountId) -> DispatchResult {
990		let who = T::ValidatorIdOf::convert(account.clone())
991			// `purge_keys` may not have a controller-stash pair any more. If so then we expect the
992			// stash account to be passed in directly and convert that to a `ValidatorId` using the
993			// `TryFrom` trait if supported.
994			.or_else(|| T::ValidatorId::try_from(account.clone()).ok())
995			.ok_or(Error::<T>::NoAssociatedValidatorId)?;
996
997		let old_keys = Self::take_keys(&who).ok_or(Error::<T>::NoKeys)?;
998		for id in T::Keys::key_ids() {
999			let key_data = old_keys.get_raw(*id);
1000			Self::clear_key_owner(*id, key_data);
1001		}
1002
1003		// Use release_all to handle the case where the exact amount might not be available
1004		let _ = T::Currency::release_all(
1005			&HoldReason::Keys.into(),
1006			account,
1007			frame_support::traits::tokens::Precision::BestEffort,
1008		);
1009
1010		if ExternallySetKeys::<T>::take(account).is_none() {
1011			// Consumer was incremented locally via `do_set_keys`, so decrement it.
1012			frame_system::Pallet::<T>::dec_consumers(account);
1013		}
1014
1015		Ok(())
1016	}
1017
1018	pub fn load_keys(v: &T::ValidatorId) -> Option<T::Keys> {
1019		NextKeys::<T>::get(v)
1020	}
1021
1022	fn take_keys(v: &T::ValidatorId) -> Option<T::Keys> {
1023		NextKeys::<T>::take(v)
1024	}
1025
1026	fn put_keys(v: &T::ValidatorId, keys: &T::Keys) {
1027		NextKeys::<T>::insert(v, keys);
1028	}
1029
1030	/// Query the owner of a session key by returning the owner's validator ID.
1031	pub fn key_owner(id: KeyTypeId, key_data: &[u8]) -> Option<T::ValidatorId> {
1032		KeyOwner::<T>::get((id, key_data))
1033	}
1034
1035	fn put_key_owner(id: KeyTypeId, key_data: &[u8], v: &T::ValidatorId) {
1036		KeyOwner::<T>::insert((id, key_data), v)
1037	}
1038
1039	fn clear_key_owner(id: KeyTypeId, key_data: &[u8]) {
1040		KeyOwner::<T>::remove((id, key_data));
1041	}
1042
1043	/// Disable the validator of index `i` with a specified severity,
1044	/// returns `false` if the validator is not found.
1045	///
1046	/// Note: If validator is already disabled, the severity will
1047	/// be updated if the new one is higher.
1048	pub fn disable_index_with_severity(i: u32, severity: OffenceSeverity) -> bool {
1049		if i >= Validators::<T>::decode_len().defensive_unwrap_or(0) as u32 {
1050			return false;
1051		}
1052
1053		DisabledValidators::<T>::mutate(|disabled| {
1054			match disabled.binary_search_by_key(&i, |(index, _)| *index) {
1055				// Validator is already disabled, update severity if the new one is higher
1056				Ok(index) => {
1057					let current_severity = &mut disabled[index].1;
1058					if severity > *current_severity {
1059						log!(
1060							trace,
1061							"updating disablement severity of validator {:?} from {:?} to {:?}",
1062							i,
1063							*current_severity,
1064							severity
1065						);
1066						*current_severity = severity;
1067					}
1068					true
1069				},
1070				// Validator is not disabled, add to `DisabledValidators` and disable it
1071				Err(index) => {
1072					log!(trace, "disabling validator {:?}", i);
1073					Self::deposit_event(Event::ValidatorDisabled {
1074						validator: Validators::<T>::get()[i as usize].clone(),
1075					});
1076					disabled.insert(index, (i, severity));
1077					T::SessionHandler::on_disabled(i);
1078					true
1079				},
1080			}
1081		})
1082	}
1083
1084	/// Disable the validator of index `i` with a default severity (defaults to most severe),
1085	/// returns `false` if the validator is not found.
1086	pub fn disable_index(i: u32) -> bool {
1087		let default_severity = OffenceSeverity::default();
1088		Self::disable_index_with_severity(i, default_severity)
1089	}
1090
1091	/// Re-enable the validator of index `i`, returns `false` if the validator was not disabled.
1092	pub fn reenable_index(i: u32) -> bool {
1093		if i >= Validators::<T>::decode_len().defensive_unwrap_or(0) as u32 {
1094			return false;
1095		}
1096
1097		DisabledValidators::<T>::mutate(|disabled| {
1098			if let Ok(index) = disabled.binary_search_by_key(&i, |(index, _)| *index) {
1099				log!(trace, "reenabling validator {:?}", i);
1100				Self::deposit_event(Event::ValidatorReenabled {
1101					validator: Validators::<T>::get()[i as usize].clone(),
1102				});
1103				disabled.remove(index);
1104				return true;
1105			}
1106			false
1107		})
1108	}
1109
1110	/// Convert a validator ID to an index.
1111	/// (If using with the staking pallet, this would be their *stash* account.)
1112	pub fn validator_id_to_index(id: &T::ValidatorId) -> Option<u32> {
1113		Validators::<T>::get().iter().position(|i| i == id).map(|i| i as u32)
1114	}
1115
1116	/// Report an offence for the given validator and let disabling strategy decide
1117	/// what changes to disabled validators should be made.
1118	pub fn report_offence(validator: T::ValidatorId, severity: OffenceSeverity) {
1119		let decision =
1120			T::DisablingStrategy::decision(&validator, severity, &DisabledValidators::<T>::get());
1121		log!(
1122			debug,
1123			"reporting offence for {:?} with {:?}, decision: {:?}",
1124			validator,
1125			severity,
1126			decision
1127		);
1128
1129		// Disable
1130		if let Some(offender_idx) = decision.disable {
1131			Self::disable_index_with_severity(offender_idx, severity);
1132		}
1133
1134		// Re-enable
1135		if let Some(reenable_idx) = decision.reenable {
1136			Self::reenable_index(reenable_idx);
1137		}
1138	}
1139
1140	#[cfg(any(test, feature = "try-runtime"))]
1141	pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
1142		// Ensure that the validators are sorted
1143		ensure!(
1144			DisabledValidators::<T>::get().windows(2).all(|pair| pair[0].0 <= pair[1].0),
1145			"DisabledValidators is not sorted"
1146		);
1147		Ok(())
1148	}
1149}
1150
1151impl<T: Config> ValidatorRegistration<T::ValidatorId> for Pallet<T> {
1152	fn is_registered(id: &T::ValidatorId) -> bool {
1153		Self::load_keys(id).is_some()
1154	}
1155}
1156
1157impl<T: Config> ValidatorSet<T::AccountId> for Pallet<T> {
1158	type ValidatorId = T::ValidatorId;
1159	type ValidatorIdOf = T::ValidatorIdOf;
1160
1161	fn session_index() -> sp_staking::SessionIndex {
1162		CurrentIndex::<T>::get()
1163	}
1164
1165	fn validators() -> Vec<Self::ValidatorId> {
1166		Validators::<T>::get()
1167	}
1168}
1169
1170impl<T: Config> EstimateNextNewSession<BlockNumberFor<T>> for Pallet<T> {
1171	fn average_session_length() -> BlockNumberFor<T> {
1172		T::NextSessionRotation::average_session_length()
1173	}
1174
1175	/// This session pallet always calls new_session and next_session at the same time, hence we
1176	/// do a simple proxy and pass the function to next rotation.
1177	fn estimate_next_new_session(now: BlockNumberFor<T>) -> (Option<BlockNumberFor<T>>, Weight) {
1178		T::NextSessionRotation::estimate_next_session_rotation(now)
1179	}
1180}
1181
1182impl<T: Config> frame_support::traits::DisabledValidators for Pallet<T> {
1183	fn is_disabled(index: u32) -> bool {
1184		DisabledValidators::<T>::get().binary_search_by_key(&index, |(i, _)| *i).is_ok()
1185	}
1186
1187	fn disabled_validators() -> Vec<u32> {
1188		Self::disabled_validators()
1189	}
1190}
1191
1192#[cfg(feature = "historical")]
1193impl<T: Config + historical::Config> SessionInterface for Pallet<T> {
1194	type ValidatorId = T::ValidatorId;
1195	type AccountId = T::AccountId;
1196	type Keys = T::Keys;
1197
1198	fn validators() -> Vec<Self::ValidatorId> {
1199		Self::validators()
1200	}
1201
1202	fn prune_up_to(index: SessionIndex) {
1203		historical::Pallet::<T>::prune_up_to(index)
1204	}
1205
1206	fn report_offence(offender: Self::ValidatorId, severity: OffenceSeverity) {
1207		Self::report_offence(offender, severity)
1208	}
1209
1210	fn set_keys(account: &Self::AccountId, keys: Self::Keys) -> DispatchResult {
1211		let who = T::ValidatorIdOf::convert(account.clone())
1212			.ok_or(Error::<T>::NoAssociatedValidatorId)?;
1213		let old_keys = Self::inner_set_keys(&who, keys)?;
1214		// Transitioning from local to external: clean up deposit and consumer ref.
1215		if old_keys.is_some() && !ExternallySetKeys::<T>::contains_key(account) {
1216			let _ = T::Currency::release_all(
1217				&HoldReason::Keys.into(),
1218				account,
1219				frame_support::traits::tokens::Precision::BestEffort,
1220			);
1221			frame_system::Pallet::<T>::dec_consumers(account);
1222		}
1223		ExternallySetKeys::<T>::insert(account, ());
1224		Ok(())
1225	}
1226
1227	fn purge_keys(account: &Self::AccountId) -> DispatchResult {
1228		let who = T::ValidatorIdOf::convert(account.clone())
1229			.ok_or(Error::<T>::NoAssociatedValidatorId)?;
1230
1231		let old_keys = Self::take_keys(&who).ok_or(Error::<T>::NoKeys)?;
1232		for id in T::Keys::key_ids() {
1233			let key_data = old_keys.get_raw(*id);
1234			Self::clear_key_owner(*id, key_data);
1235		}
1236		let _ = T::Currency::release_all(
1237			&HoldReason::Keys.into(),
1238			account,
1239			frame_support::traits::tokens::Precision::BestEffort,
1240		);
1241		if ExternallySetKeys::<T>::take(account).is_none() {
1242			frame_system::Pallet::<T>::dec_consumers(account);
1243		}
1244		Ok(())
1245	}
1246
1247	fn set_keys_weight() -> Weight {
1248		T::WeightInfo::set_keys()
1249	}
1250
1251	fn purge_keys_weight() -> Weight {
1252		T::WeightInfo::purge_keys()
1253	}
1254}
1255
1256/// Wraps the author-scraping logic for consensus engines that can recover
1257/// the canonical index of an author. This then transforms it into the
1258/// registering account-ID of that session key index.
1259pub struct FindAccountFromAuthorIndex<T, Inner>(core::marker::PhantomData<(T, Inner)>);
1260
1261impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
1262	for FindAccountFromAuthorIndex<T, Inner>
1263{
1264	fn find_author<'a, I>(digests: I) -> Option<T::ValidatorId>
1265	where
1266		I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
1267	{
1268		let i = Inner::find_author(digests)?;
1269
1270		let validators = Validators::<T>::get();
1271		validators.get(i as usize).cloned()
1272	}
1273}