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		Defensive, EstimateNextNewSession, EstimateNextSessionRotation, FindAuthor, Get,
133		OneSessionHandler, ValidatorRegistration, ValidatorSet,
134	},
135	weights::Weight,
136	Parameter,
137};
138use frame_system::pallet_prelude::BlockNumberFor;
139use sp_runtime::{
140	traits::{AtLeast32BitUnsigned, Convert, Member, One, OpaqueKeys, Zero},
141	ConsensusEngineId, DispatchError, KeyTypeId, Permill, RuntimeAppPublic,
142};
143use sp_staking::{offence::OffenceSeverity, SessionIndex};
144
145pub use pallet::*;
146pub use weights::WeightInfo;
147
148#[cfg(any(feature = "try-runtime"))]
149use sp_runtime::TryRuntimeError;
150
151pub(crate) const LOG_TARGET: &str = "runtime::session";
152
153// syntactic sugar for logging.
154#[macro_export]
155macro_rules! log {
156	($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
157		log::$level!(
158			target: crate::LOG_TARGET,
159			concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
160		)
161	};
162}
163
164/// Decides whether the session should be ended.
165pub trait ShouldEndSession<BlockNumber> {
166	/// Return `true` if the session should be ended.
167	fn should_end_session(now: BlockNumber) -> bool;
168}
169
170/// Ends the session after a fixed period of blocks.
171///
172/// The first session will have length of `Offset`, and
173/// the following sessions will have length of `Period`.
174/// This may prove nonsensical if `Offset` >= `Period`.
175pub struct PeriodicSessions<Period, Offset>(PhantomData<(Period, Offset)>);
176
177impl<
178		BlockNumber: Rem<Output = BlockNumber> + Sub<Output = BlockNumber> + Zero + PartialOrd,
179		Period: Get<BlockNumber>,
180		Offset: Get<BlockNumber>,
181	> ShouldEndSession<BlockNumber> for PeriodicSessions<Period, Offset>
182{
183	fn should_end_session(now: BlockNumber) -> bool {
184		let offset = Offset::get();
185		now >= offset && ((now - offset) % Period::get()).is_zero()
186	}
187}
188
189impl<
190		BlockNumber: AtLeast32BitUnsigned + Clone,
191		Period: Get<BlockNumber>,
192		Offset: Get<BlockNumber>,
193	> EstimateNextSessionRotation<BlockNumber> for PeriodicSessions<Period, Offset>
194{
195	fn average_session_length() -> BlockNumber {
196		Period::get()
197	}
198
199	fn estimate_current_session_progress(now: BlockNumber) -> (Option<Permill>, Weight) {
200		let offset = Offset::get();
201		let period = Period::get();
202
203		// NOTE: we add one since we assume that the current block has already elapsed,
204		// i.e. when evaluating the last block in the session the progress should be 100%
205		// (0% is never returned).
206		let progress = if now >= offset {
207			let current = (now - offset) % period.clone() + One::one();
208			Some(Permill::from_rational(current, period))
209		} else {
210			Some(Permill::from_rational(now + One::one(), offset))
211		};
212
213		// Weight note: `estimate_current_session_progress` has no storage reads and trivial
214		// computational overhead. There should be no risk to the chain having this weight value be
215		// zero for now. However, this value of zero was not properly calculated, and so it would be
216		// reasonable to come back here and properly calculate the weight of this function.
217		(progress, Zero::zero())
218	}
219
220	fn estimate_next_session_rotation(now: BlockNumber) -> (Option<BlockNumber>, Weight) {
221		let offset = Offset::get();
222		let period = Period::get();
223
224		let next_session = if now > offset {
225			let block_after_last_session = (now.clone() - offset) % period.clone();
226			if block_after_last_session > Zero::zero() {
227				now.saturating_add(period.saturating_sub(block_after_last_session))
228			} else {
229				// this branch happens when the session is already rotated or will rotate in this
230				// block (depending on being called before or after `session::on_initialize`). Here,
231				// we assume the latter, namely that this is called after `session::on_initialize`,
232				// and thus we add period to it as well.
233				now + period
234			}
235		} else {
236			offset
237		};
238
239		// Weight note: `estimate_next_session_rotation` has no storage reads and trivial
240		// computational overhead. There should be no risk to the chain having this weight value be
241		// zero for now. However, this value of zero was not properly calculated, and so it would be
242		// reasonable to come back here and properly calculate the weight of this function.
243		(Some(next_session), Zero::zero())
244	}
245}
246
247/// A trait for managing creation of new validator set.
248pub trait SessionManager<ValidatorId> {
249	/// Plan a new session, and optionally provide the new validator set.
250	///
251	/// Even if the validator-set is the same as before, if any underlying economic conditions have
252	/// changed (i.e. stake-weights), the new validator set must be returned. This is necessary for
253	/// consensus engines making use of the session pallet to issue a validator-set change so
254	/// misbehavior can be provably associated with the new economic conditions as opposed to the
255	/// old. The returned validator set, if any, will not be applied until `new_index`. `new_index`
256	/// is strictly greater than from previous call.
257	///
258	/// The first session start at index 0.
259	///
260	/// `new_session(session)` is guaranteed to be called before `end_session(session-1)`. In other
261	/// words, a new session must always be planned before an ongoing one can be finished.
262	fn new_session(new_index: SessionIndex) -> Option<Vec<ValidatorId>>;
263	/// Same as `new_session`, but it this should only be called at genesis.
264	///
265	/// The session manager might decide to treat this in a different way. Default impl is simply
266	/// using [`new_session`](Self::new_session).
267	fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<ValidatorId>> {
268		Self::new_session(new_index)
269	}
270	/// End the session.
271	///
272	/// Because the session pallet can queue validator set the ending session can be lower than the
273	/// last new session index.
274	fn end_session(end_index: SessionIndex);
275	/// Start an already planned session.
276	///
277	/// The session start to be used for validation.
278	fn start_session(start_index: SessionIndex);
279}
280
281impl<A> SessionManager<A> for () {
282	fn new_session(_: SessionIndex) -> Option<Vec<A>> {
283		None
284	}
285	fn start_session(_: SessionIndex) {}
286	fn end_session(_: SessionIndex) {}
287}
288
289/// Handler for session life cycle events.
290pub trait SessionHandler<ValidatorId> {
291	/// All the key type ids this session handler can process.
292	///
293	/// The order must be the same as it expects them in
294	/// [`on_new_session`](Self::on_new_session<Ks>) and
295	/// [`on_genesis_session`](Self::on_genesis_session<Ks>).
296	const KEY_TYPE_IDS: &'static [KeyTypeId];
297
298	/// The given validator set will be used for the genesis session.
299	/// It is guaranteed that the given validator set will also be used
300	/// for the second session, therefore the first call to `on_new_session`
301	/// should provide the same validator set.
302	fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(ValidatorId, Ks)]);
303
304	/// Session set has changed; act appropriately. Note that this can be called
305	/// before initialization of your pallet.
306	///
307	/// `changed` is true whenever any of the session keys or underlying economic
308	/// identities or weightings behind `validators` keys has changed. `queued_validators`
309	/// could change without `validators` changing. Example of possible sequent calls:
310	///     Session N: on_new_session(false, unchanged_validators, unchanged_queued_validators)
311	///     Session N + 1: on_new_session(false, unchanged_validators, new_queued_validators)
312	/// 	Session N + 2: on_new_session(true, new_queued_validators, new_queued_validators)
313	fn on_new_session<Ks: OpaqueKeys>(
314		changed: bool,
315		validators: &[(ValidatorId, Ks)],
316		queued_validators: &[(ValidatorId, Ks)],
317	);
318
319	/// A notification for end of the session.
320	///
321	/// Note it is triggered before any [`SessionManager::end_session`] handlers,
322	/// so we can still affect the validator set.
323	fn on_before_session_ending() {}
324
325	/// A validator got disabled. Act accordingly until a new session begins.
326	fn on_disabled(validator_index: u32);
327}
328
329#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
330#[tuple_types_custom_trait_bound(OneSessionHandler<AId>)]
331impl<AId> SessionHandler<AId> for Tuple {
332	for_tuples!(
333		const KEY_TYPE_IDS: &'static [KeyTypeId] = &[ #( <Tuple::Key as RuntimeAppPublic>::ID ),* ];
334	);
335
336	fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(AId, Ks)]) {
337		for_tuples!(
338			#(
339				let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
340					.filter_map(|k|
341						k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
342					)
343				);
344
345				Tuple::on_genesis_session(our_keys);
346			)*
347		)
348	}
349
350	fn on_new_session<Ks: OpaqueKeys>(
351		changed: bool,
352		validators: &[(AId, Ks)],
353		queued_validators: &[(AId, Ks)],
354	) {
355		for_tuples!(
356			#(
357				let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
358					.filter_map(|k|
359						k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
360					));
361				let queued_keys: Box<dyn Iterator<Item=_>> = Box::new(queued_validators.iter()
362					.filter_map(|k|
363						k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
364					));
365				Tuple::on_new_session(changed, our_keys, queued_keys);
366			)*
367		)
368	}
369
370	fn on_before_session_ending() {
371		for_tuples!( #( Tuple::on_before_session_ending(); )* )
372	}
373
374	fn on_disabled(i: u32) {
375		for_tuples!( #( Tuple::on_disabled(i); )* )
376	}
377}
378
379/// `SessionHandler` for tests that use `UintAuthorityId` as `Keys`.
380pub struct TestSessionHandler;
381impl<AId> SessionHandler<AId> for TestSessionHandler {
382	const KEY_TYPE_IDS: &'static [KeyTypeId] = &[sp_runtime::key_types::DUMMY];
383	fn on_genesis_session<Ks: OpaqueKeys>(_: &[(AId, Ks)]) {}
384	fn on_new_session<Ks: OpaqueKeys>(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {}
385	fn on_before_session_ending() {}
386	fn on_disabled(_: u32) {}
387}
388
389#[frame_support::pallet]
390pub mod pallet {
391	use super::*;
392	use frame_support::pallet_prelude::*;
393	use frame_system::pallet_prelude::*;
394
395	/// The in-code storage version.
396	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
397
398	#[pallet::pallet]
399	#[pallet::storage_version(STORAGE_VERSION)]
400	#[pallet::without_storage_info]
401	pub struct Pallet<T>(_);
402
403	#[pallet::config]
404	pub trait Config: frame_system::Config {
405		/// The overarching event type.
406		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
407
408		/// A stable ID for a validator.
409		type ValidatorId: Member
410			+ Parameter
411			+ MaybeSerializeDeserialize
412			+ MaxEncodedLen
413			+ TryFrom<Self::AccountId>;
414
415		/// A conversion from account ID to validator ID.
416		///
417		/// Its cost must be at most one storage read.
418		type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
419
420		/// Indicator for when to end the session.
421		type ShouldEndSession: ShouldEndSession<BlockNumberFor<Self>>;
422
423		/// Something that can predict the next session rotation. This should typically come from
424		/// the same logical unit that provides [`ShouldEndSession`], yet, it gives a best effort
425		/// estimate. It is helpful to implement [`EstimateNextNewSession`].
426		type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
427
428		/// Handler for managing new session.
429		type SessionManager: SessionManager<Self::ValidatorId>;
430
431		/// Handler when a session has changed.
432		type SessionHandler: SessionHandler<Self::ValidatorId>;
433
434		/// The keys.
435		type Keys: OpaqueKeys + Member + Parameter + MaybeSerializeDeserialize;
436
437		/// `DisablingStragegy` controls how validators are disabled
438		type DisablingStrategy: DisablingStrategy<Self>;
439
440		/// Weight information for extrinsics in this pallet.
441		type WeightInfo: WeightInfo;
442	}
443
444	#[pallet::genesis_config]
445	#[derive(frame_support::DefaultNoBound)]
446	pub struct GenesisConfig<T: Config> {
447		/// Initial list of validator at genesis representing by their `(AccountId, ValidatorId,
448		/// Keys)`. These keys will be considered authorities for the first two sessions and they
449		/// will be valid at least until session 2
450		pub keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
451		/// List of (AccountId, ValidatorId, Keys) that will be registered at genesis, but not as
452		/// active validators. These keys are set, together with `keys`, as authority candidates
453		/// for future sessions (enactable from session 2 onwards)
454		pub non_authority_keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
455	}
456
457	#[pallet::genesis_build]
458	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
459		fn build(&self) {
460			if T::SessionHandler::KEY_TYPE_IDS.len() != T::Keys::key_ids().len() {
461				panic!("Number of keys in session handler and session keys does not match");
462			}
463
464			T::SessionHandler::KEY_TYPE_IDS
465				.iter()
466				.zip(T::Keys::key_ids())
467				.enumerate()
468				.for_each(|(i, (sk, kk))| {
469					if sk != kk {
470						panic!(
471							"Session handler and session key expect different key type at index: {}",
472							i,
473						);
474					}
475				});
476
477			for (account, val, keys) in
478				self.keys.iter().chain(self.non_authority_keys.iter()).cloned()
479			{
480				Pallet::<T>::inner_set_keys(&val, keys)
481					.expect("genesis config must not contain duplicates; qed");
482				if frame_system::Pallet::<T>::inc_consumers_without_limit(&account).is_err() {
483					// This will leak a provider reference, however it only happens once (at
484					// genesis) so it's really not a big deal and we assume that the user wants to
485					// do this since it's the only way a non-endowed account can contain a session
486					// key.
487					frame_system::Pallet::<T>::inc_providers(&account);
488				}
489			}
490
491			let initial_validators_0 =
492				T::SessionManager::new_session_genesis(0).unwrap_or_else(|| {
493					frame_support::print(
494						"No initial validator provided by `SessionManager`, use \
495						session config keys to generate initial validator set.",
496					);
497					self.keys.iter().map(|x| x.1.clone()).collect()
498				});
499
500			let initial_validators_1 = T::SessionManager::new_session_genesis(1)
501				.unwrap_or_else(|| initial_validators_0.clone());
502
503			let queued_keys: Vec<_> = initial_validators_1
504				.into_iter()
505				.filter_map(|v| Pallet::<T>::load_keys(&v).map(|k| (v, k)))
506				.collect();
507
508			// Tell everyone about the genesis session keys
509			T::SessionHandler::on_genesis_session::<T::Keys>(&queued_keys);
510
511			Validators::<T>::put(initial_validators_0);
512			QueuedKeys::<T>::put(queued_keys);
513
514			T::SessionManager::start_session(0);
515		}
516	}
517
518	/// The current set of validators.
519	#[pallet::storage]
520	pub type Validators<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>;
521
522	/// Current index of the session.
523	#[pallet::storage]
524	pub type CurrentIndex<T> = StorageValue<_, SessionIndex, ValueQuery>;
525
526	/// True if the underlying economic identities or weighting behind the validators
527	/// has changed in the queued validator set.
528	#[pallet::storage]
529	pub type QueuedChanged<T> = StorageValue<_, bool, ValueQuery>;
530
531	/// The queued keys for the next session. When the next session begins, these keys
532	/// will be used to determine the validator's session keys.
533	#[pallet::storage]
534	pub type QueuedKeys<T: Config> = StorageValue<_, Vec<(T::ValidatorId, T::Keys)>, ValueQuery>;
535
536	/// Indices of disabled validators.
537	///
538	/// The vec is always kept sorted so that we can find whether a given validator is
539	/// disabled using binary search. It gets cleared when `on_session_ending` returns
540	/// a new set of identities.
541	#[pallet::storage]
542	pub type DisabledValidators<T> = StorageValue<_, Vec<(u32, OffenceSeverity)>, ValueQuery>;
543
544	/// The next session keys for a validator.
545	#[pallet::storage]
546	pub type NextKeys<T: Config> =
547		StorageMap<_, Twox64Concat, T::ValidatorId, T::Keys, OptionQuery>;
548
549	/// The owner of a key. The key is the `KeyTypeId` + the encoded key.
550	#[pallet::storage]
551	pub type KeyOwner<T: Config> =
552		StorageMap<_, Twox64Concat, (KeyTypeId, Vec<u8>), T::ValidatorId, OptionQuery>;
553
554	#[pallet::event]
555	#[pallet::generate_deposit(pub(super) fn deposit_event)]
556	pub enum Event<T: Config> {
557		/// New session has happened. Note that the argument is the session index, not the
558		/// block number as the type might suggest.
559		NewSession { session_index: SessionIndex },
560		/// Validator has been disabled.
561		ValidatorDisabled { validator: T::ValidatorId },
562		/// Validator has been re-enabled.
563		ValidatorReenabled { validator: T::ValidatorId },
564	}
565
566	/// Error for the session pallet.
567	#[pallet::error]
568	pub enum Error<T> {
569		/// Invalid ownership proof.
570		InvalidProof,
571		/// No associated validator ID for account.
572		NoAssociatedValidatorId,
573		/// Registered duplicate key.
574		DuplicatedKey,
575		/// No keys are associated with this account.
576		NoKeys,
577		/// Key setting account is not live, so it's impossible to associate keys.
578		NoAccount,
579	}
580
581	#[pallet::hooks]
582	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
583		/// Called when a block is initialized. Will rotate session if it is the last
584		/// block of the current session.
585		fn on_initialize(n: BlockNumberFor<T>) -> Weight {
586			if T::ShouldEndSession::should_end_session(n) {
587				Self::rotate_session();
588				T::BlockWeights::get().max_block
589			} else {
590				// NOTE: the non-database part of the weight for `should_end_session(n)` is
591				// included as weight for empty block, the database part is expected to be in
592				// cache.
593				Weight::zero()
594			}
595		}
596
597		#[cfg(feature = "try-runtime")]
598		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
599			Self::do_try_state()
600		}
601	}
602
603	#[pallet::call]
604	impl<T: Config> Pallet<T> {
605		/// Sets the session key(s) of the function caller to `keys`.
606		/// Allows an account to set its session key prior to becoming a validator.
607		/// This doesn't take effect until the next session.
608		///
609		/// The dispatch origin of this function must be signed.
610		///
611		/// ## Complexity
612		/// - `O(1)`. Actual cost depends on the number of length of `T::Keys::key_ids()` which is
613		///   fixed.
614		#[pallet::call_index(0)]
615		#[pallet::weight(T::WeightInfo::set_keys())]
616		pub fn set_keys(origin: OriginFor<T>, keys: T::Keys, proof: Vec<u8>) -> DispatchResult {
617			let who = ensure_signed(origin)?;
618			ensure!(keys.ownership_proof_is_valid(&proof), Error::<T>::InvalidProof);
619
620			Self::do_set_keys(&who, keys)?;
621			Ok(())
622		}
623
624		/// Removes any session key(s) of the function caller.
625		///
626		/// This doesn't take effect until the next session.
627		///
628		/// The dispatch origin of this function must be Signed and the account must be either be
629		/// convertible to a validator ID using the chain's typical addressing system (this usually
630		/// means being a controller account) or directly convertible into a validator ID (which
631		/// usually means being a stash account).
632		///
633		/// ## Complexity
634		/// - `O(1)` in number of key types. Actual cost depends on the number of length of
635		///   `T::Keys::key_ids()` which is fixed.
636		#[pallet::call_index(1)]
637		#[pallet::weight(T::WeightInfo::purge_keys())]
638		pub fn purge_keys(origin: OriginFor<T>) -> DispatchResult {
639			let who = ensure_signed(origin)?;
640			Self::do_purge_keys(&who)?;
641			Ok(())
642		}
643	}
644}
645
646impl<T: Config> Pallet<T> {
647	/// Public function to access the current set of validators.
648	pub fn validators() -> Vec<T::ValidatorId> {
649		Validators::<T>::get()
650	}
651
652	/// Public function to access the current session index.
653	pub fn current_index() -> SessionIndex {
654		CurrentIndex::<T>::get()
655	}
656
657	/// Public function to access the queued keys.
658	pub fn queued_keys() -> Vec<(T::ValidatorId, T::Keys)> {
659		QueuedKeys::<T>::get()
660	}
661
662	/// Public function to access the disabled validators.
663	pub fn disabled_validators() -> Vec<u32> {
664		DisabledValidators::<T>::get().iter().map(|(i, _)| *i).collect()
665	}
666
667	/// Move on to next session. Register new validator set and session keys. Changes to the
668	/// validator set have a session of delay to take effect. This allows for equivocation
669	/// punishment after a fork.
670	pub fn rotate_session() {
671		let session_index = CurrentIndex::<T>::get();
672		let changed = QueuedChanged::<T>::get();
673
674		// Inform the session handlers that a session is going to end.
675		T::SessionHandler::on_before_session_ending();
676		T::SessionManager::end_session(session_index);
677		log!(trace, "ending_session {:?}", session_index);
678
679		// Get queued session keys and validators.
680		let session_keys = QueuedKeys::<T>::get();
681		let validators =
682			session_keys.iter().map(|(validator, _)| validator.clone()).collect::<Vec<_>>();
683		Validators::<T>::put(&validators);
684
685		if changed {
686			log!(trace, "resetting disabled validators");
687			// reset disabled validators if active set was changed
688			DisabledValidators::<T>::take();
689		}
690
691		// Increment session index.
692		let session_index = session_index + 1;
693		CurrentIndex::<T>::put(session_index);
694		T::SessionManager::start_session(session_index);
695		log!(trace, "starting_session {:?}", session_index);
696
697		// Get next validator set.
698		let maybe_next_validators = T::SessionManager::new_session(session_index + 1);
699		log!(
700			trace,
701			"planning_session {:?} with {:?} validators",
702			session_index + 1,
703			maybe_next_validators.as_ref().map(|v| v.len())
704		);
705		let (next_validators, next_identities_changed) =
706			if let Some(validators) = maybe_next_validators {
707				// NOTE: as per the documentation on `OnSessionEnding`, we consider
708				// the validator set as having changed even if the validators are the
709				// same as before, as underlying economic conditions may have changed.
710				(validators, true)
711			} else {
712				(Validators::<T>::get(), false)
713			};
714
715		// Queue next session keys.
716		let (queued_amalgamated, next_changed) = {
717			// until we are certain there has been a change, iterate the prior
718			// validators along with the current and check for changes
719			let mut changed = next_identities_changed;
720
721			let mut now_session_keys = session_keys.iter();
722			let mut check_next_changed = |keys: &T::Keys| {
723				if changed {
724					return;
725				}
726				// since a new validator set always leads to `changed` starting
727				// as true, we can ensure that `now_session_keys` and `next_validators`
728				// have the same length. this function is called once per iteration.
729				if let Some((_, old_keys)) = now_session_keys.next() {
730					if old_keys != keys {
731						changed = true;
732					}
733				}
734			};
735			let queued_amalgamated = next_validators
736				.into_iter()
737				.filter_map(|a| {
738					let k = Self::load_keys(&a)?;
739					check_next_changed(&k);
740					Some((a, k))
741				})
742				.collect::<Vec<_>>();
743
744			(queued_amalgamated, changed)
745		};
746
747		QueuedKeys::<T>::put(queued_amalgamated.clone());
748		QueuedChanged::<T>::put(next_changed);
749
750		// Record that this happened.
751		Self::deposit_event(Event::NewSession { session_index });
752
753		// Tell everyone about the new session keys.
754		T::SessionHandler::on_new_session::<T::Keys>(changed, &session_keys, &queued_amalgamated);
755	}
756
757	/// Upgrade the key type from some old type to a new type. Supports adding
758	/// and removing key types.
759	///
760	/// This function should be used with extreme care and only during an
761	/// `on_runtime_upgrade` block. Misuse of this function can put your blockchain
762	/// into an unrecoverable state.
763	///
764	/// Care should be taken that the raw versions of the
765	/// added keys are unique for every `ValidatorId, KeyTypeId` combination.
766	/// This is an invariant that the session pallet typically maintains internally.
767	///
768	/// As the actual values of the keys are typically not known at runtime upgrade,
769	/// it's recommended to initialize the keys to a (unique) dummy value with the expectation
770	/// that all validators should invoke `set_keys` before those keys are actually
771	/// required.
772	pub fn upgrade_keys<Old, F>(upgrade: F)
773	where
774		Old: OpaqueKeys + Member + Decode,
775		F: Fn(T::ValidatorId, Old) -> T::Keys,
776	{
777		let old_ids = Old::key_ids();
778		let new_ids = T::Keys::key_ids();
779
780		// Translate NextKeys, and key ownership relations at the same time.
781		NextKeys::<T>::translate::<Old, _>(|val, old_keys| {
782			// Clear all key ownership relations. Typically the overlap should
783			// stay the same, but no guarantees by the upgrade function.
784			for i in old_ids.iter() {
785				Self::clear_key_owner(*i, old_keys.get_raw(*i));
786			}
787
788			let new_keys = upgrade(val.clone(), old_keys);
789
790			// And now set the new ones.
791			for i in new_ids.iter() {
792				Self::put_key_owner(*i, new_keys.get_raw(*i), &val);
793			}
794
795			Some(new_keys)
796		});
797
798		let _ = QueuedKeys::<T>::translate::<Vec<(T::ValidatorId, Old)>, _>(|k| {
799			k.map(|k| {
800				k.into_iter()
801					.map(|(val, old_keys)| (val.clone(), upgrade(val, old_keys)))
802					.collect::<Vec<_>>()
803			})
804		});
805	}
806
807	/// Perform the set_key operation, checking for duplicates. Does not set `Changed`.
808	///
809	/// This ensures that the reference counter in system is incremented appropriately and as such
810	/// must accept an account ID, rather than a validator ID.
811	fn do_set_keys(account: &T::AccountId, keys: T::Keys) -> DispatchResult {
812		let who = T::ValidatorIdOf::convert(account.clone())
813			.ok_or(Error::<T>::NoAssociatedValidatorId)?;
814
815		ensure!(frame_system::Pallet::<T>::can_inc_consumer(account), Error::<T>::NoAccount);
816		let old_keys = Self::inner_set_keys(&who, keys)?;
817		if old_keys.is_none() {
818			let assertion = frame_system::Pallet::<T>::inc_consumers(account).is_ok();
819			debug_assert!(assertion, "can_inc_consumer() returned true; no change since; qed");
820		}
821
822		Ok(())
823	}
824
825	/// Perform the set_key operation, checking for duplicates. Does not set `Changed`.
826	///
827	/// The old keys for this validator are returned, or `None` if there were none.
828	///
829	/// This does not ensure that the reference counter in system is incremented appropriately, it
830	/// must be done by the caller or the keys will be leaked in storage.
831	fn inner_set_keys(
832		who: &T::ValidatorId,
833		keys: T::Keys,
834	) -> Result<Option<T::Keys>, DispatchError> {
835		let old_keys = Self::load_keys(who);
836
837		for id in T::Keys::key_ids() {
838			let key = keys.get_raw(*id);
839
840			// ensure keys are without duplication.
841			ensure!(
842				Self::key_owner(*id, key).map_or(true, |owner| &owner == who),
843				Error::<T>::DuplicatedKey,
844			);
845		}
846
847		for id in T::Keys::key_ids() {
848			let key = keys.get_raw(*id);
849
850			if let Some(old) = old_keys.as_ref().map(|k| k.get_raw(*id)) {
851				if key == old {
852					continue
853				}
854
855				Self::clear_key_owner(*id, old);
856			}
857
858			Self::put_key_owner(*id, key, who);
859		}
860
861		Self::put_keys(who, &keys);
862		Ok(old_keys)
863	}
864
865	fn do_purge_keys(account: &T::AccountId) -> DispatchResult {
866		let who = T::ValidatorIdOf::convert(account.clone())
867			// `purge_keys` may not have a controller-stash pair any more. If so then we expect the
868			// stash account to be passed in directly and convert that to a `ValidatorId` using the
869			// `TryFrom` trait if supported.
870			.or_else(|| T::ValidatorId::try_from(account.clone()).ok())
871			.ok_or(Error::<T>::NoAssociatedValidatorId)?;
872
873		let old_keys = Self::take_keys(&who).ok_or(Error::<T>::NoKeys)?;
874		for id in T::Keys::key_ids() {
875			let key_data = old_keys.get_raw(*id);
876			Self::clear_key_owner(*id, key_data);
877		}
878		frame_system::Pallet::<T>::dec_consumers(account);
879
880		Ok(())
881	}
882
883	fn load_keys(v: &T::ValidatorId) -> Option<T::Keys> {
884		NextKeys::<T>::get(v)
885	}
886
887	fn take_keys(v: &T::ValidatorId) -> Option<T::Keys> {
888		NextKeys::<T>::take(v)
889	}
890
891	fn put_keys(v: &T::ValidatorId, keys: &T::Keys) {
892		NextKeys::<T>::insert(v, keys);
893	}
894
895	/// Query the owner of a session key by returning the owner's validator ID.
896	pub fn key_owner(id: KeyTypeId, key_data: &[u8]) -> Option<T::ValidatorId> {
897		KeyOwner::<T>::get((id, key_data))
898	}
899
900	fn put_key_owner(id: KeyTypeId, key_data: &[u8], v: &T::ValidatorId) {
901		KeyOwner::<T>::insert((id, key_data), v)
902	}
903
904	fn clear_key_owner(id: KeyTypeId, key_data: &[u8]) {
905		KeyOwner::<T>::remove((id, key_data));
906	}
907
908	/// Disable the validator of index `i` with a specified severity,
909	/// returns `false` if the validator is not found.
910	///
911	/// Note: If validator is already disabled, the severity will
912	/// be updated if the new one is higher.
913	pub fn disable_index_with_severity(i: u32, severity: OffenceSeverity) -> bool {
914		if i >= Validators::<T>::decode_len().defensive_unwrap_or(0) as u32 {
915			return false;
916		}
917
918		DisabledValidators::<T>::mutate(|disabled| {
919			match disabled.binary_search_by_key(&i, |(index, _)| *index) {
920				// Validator is already disabled, update severity if the new one is higher
921				Ok(index) => {
922					let current_severity = &mut disabled[index].1;
923					if severity > *current_severity {
924						log!(
925							trace,
926							"updating disablement severity of validator {:?} from {:?} to {:?}",
927							i,
928							*current_severity,
929							severity
930						);
931						*current_severity = severity;
932					}
933					true
934				},
935				// Validator is not disabled, add to `DisabledValidators` and disable it
936				Err(index) => {
937					log!(trace, "disabling validator {:?}", i);
938					Self::deposit_event(Event::ValidatorDisabled {
939						validator: Validators::<T>::get()[index as usize].clone(),
940					});
941					disabled.insert(index, (i, severity));
942					T::SessionHandler::on_disabled(i);
943					true
944				},
945			}
946		})
947	}
948
949	/// Disable the validator of index `i` with a default severity (defaults to most severe),
950	/// returns `false` if the validator is not found.
951	pub fn disable_index(i: u32) -> bool {
952		let default_severity = OffenceSeverity::default();
953		Self::disable_index_with_severity(i, default_severity)
954	}
955
956	/// Disable the validator identified by `c`. (If using with the staking pallet,
957	/// this would be their *stash* account.)
958	///
959	/// Returns `false` either if the validator could not be found or it was already
960	/// disabled.
961	pub fn disable(c: &T::ValidatorId) -> bool {
962		Validators::<T>::get()
963			.iter()
964			.position(|i| i == c)
965			.map(|i| Self::disable_index(i as u32))
966			.unwrap_or(false)
967	}
968
969	/// Re-enable the validator of index `i`, returns `false` if the validator was not disabled.
970	pub fn reenable_index(i: u32) -> bool {
971		if i >= Validators::<T>::decode_len().defensive_unwrap_or(0) as u32 {
972			return false;
973		}
974
975		DisabledValidators::<T>::mutate(|disabled| {
976			if let Ok(index) = disabled.binary_search_by_key(&i, |(index, _)| *index) {
977				log!(trace, "reenabling validator {:?}", i);
978				Self::deposit_event(Event::ValidatorReenabled {
979					validator: Validators::<T>::get()[index as usize].clone(),
980				});
981				disabled.remove(index);
982				return true;
983			}
984			false
985		})
986	}
987
988	/// Convert a validator ID to an index.
989	/// (If using with the staking pallet, this would be their *stash* account.)
990	pub fn validator_id_to_index(id: &T::ValidatorId) -> Option<u32> {
991		Validators::<T>::get().iter().position(|i| i == id).map(|i| i as u32)
992	}
993
994	/// Report an offence for the given validator and let disabling strategy decide
995	/// what changes to disabled validators should be made.
996	pub fn report_offence(validator: T::ValidatorId, severity: OffenceSeverity) {
997		log!(trace, "reporting offence for {:?} with {:?}", validator, severity);
998		let decision =
999			T::DisablingStrategy::decision(&validator, severity, &DisabledValidators::<T>::get());
1000
1001		// Disable
1002		if let Some(offender_idx) = decision.disable {
1003			Self::disable_index_with_severity(offender_idx, severity);
1004		}
1005
1006		// Re-enable
1007		if let Some(reenable_idx) = decision.reenable {
1008			Self::reenable_index(reenable_idx);
1009		}
1010	}
1011
1012	#[cfg(any(test, feature = "try-runtime"))]
1013	pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
1014		// Ensure that the validators are sorted
1015		ensure!(
1016			DisabledValidators::<T>::get().windows(2).all(|pair| pair[0].0 <= pair[1].0),
1017			"DisabledValidators is not sorted"
1018		);
1019		Ok(())
1020	}
1021}
1022
1023impl<T: Config> ValidatorRegistration<T::ValidatorId> for Pallet<T> {
1024	fn is_registered(id: &T::ValidatorId) -> bool {
1025		Self::load_keys(id).is_some()
1026	}
1027}
1028
1029impl<T: Config> ValidatorSet<T::AccountId> for Pallet<T> {
1030	type ValidatorId = T::ValidatorId;
1031	type ValidatorIdOf = T::ValidatorIdOf;
1032
1033	fn session_index() -> sp_staking::SessionIndex {
1034		CurrentIndex::<T>::get()
1035	}
1036
1037	fn validators() -> Vec<Self::ValidatorId> {
1038		Validators::<T>::get()
1039	}
1040}
1041
1042impl<T: Config> EstimateNextNewSession<BlockNumberFor<T>> for Pallet<T> {
1043	fn average_session_length() -> BlockNumberFor<T> {
1044		T::NextSessionRotation::average_session_length()
1045	}
1046
1047	/// This session pallet always calls new_session and next_session at the same time, hence we
1048	/// do a simple proxy and pass the function to next rotation.
1049	fn estimate_next_new_session(now: BlockNumberFor<T>) -> (Option<BlockNumberFor<T>>, Weight) {
1050		T::NextSessionRotation::estimate_next_session_rotation(now)
1051	}
1052}
1053
1054impl<T: Config> frame_support::traits::DisabledValidators for Pallet<T> {
1055	fn is_disabled(index: u32) -> bool {
1056		DisabledValidators::<T>::get().binary_search_by_key(&index, |(i, _)| *i).is_ok()
1057	}
1058
1059	fn disabled_validators() -> Vec<u32> {
1060		Self::disabled_validators()
1061	}
1062}
1063
1064/// Wraps the author-scraping logic for consensus engines that can recover
1065/// the canonical index of an author. This then transforms it into the
1066/// registering account-ID of that session key index.
1067pub struct FindAccountFromAuthorIndex<T, Inner>(core::marker::PhantomData<(T, Inner)>);
1068
1069impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
1070	for FindAccountFromAuthorIndex<T, Inner>
1071{
1072	fn find_author<'a, I>(digests: I) -> Option<T::ValidatorId>
1073	where
1074		I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
1075	{
1076		let i = Inner::find_author(digests)?;
1077
1078		let validators = Validators::<T>::get();
1079		validators.get(i as usize).cloned()
1080	}
1081}