referrerpolicy=no-referrer-when-downgrade

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