#![cfg_attr(not(feature = "std"), no_std)]
pub mod disabling;
#[cfg(feature = "historical")]
pub mod historical;
pub mod migrations;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod weights;
extern crate alloc;
use alloc::{boxed::Box, vec::Vec};
use codec::{Decode, MaxEncodedLen};
use core::{
marker::PhantomData,
ops::{Rem, Sub},
};
use disabling::DisablingStrategy;
use frame_support::{
dispatch::DispatchResult,
ensure,
traits::{
fungible::{hold::Mutate as HoldMutate, Inspect, Mutate},
Defensive, EstimateNextNewSession, EstimateNextSessionRotation, FindAuthor, Get,
OneSessionHandler, ValidatorRegistration, ValidatorSet,
},
weights::Weight,
Parameter,
};
use frame_system::pallet_prelude::BlockNumberFor;
use sp_runtime::{
traits::{AtLeast32BitUnsigned, Convert, Member, One, OpaqueKeys, Zero},
ConsensusEngineId, DispatchError, KeyTypeId, Permill, RuntimeAppPublic,
};
use sp_staking::{offence::OffenceSeverity, SessionIndex};
pub use pallet::*;
pub use weights::WeightInfo;
#[cfg(any(feature = "try-runtime"))]
use sp_runtime::TryRuntimeError;
pub(crate) const LOG_TARGET: &str = "runtime::session";
#[macro_export]
macro_rules! log {
($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
log::$level!(
target: crate::LOG_TARGET,
concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
)
};
}
pub trait ShouldEndSession<BlockNumber> {
fn should_end_session(now: BlockNumber) -> bool;
}
pub struct PeriodicSessions<Period, Offset>(PhantomData<(Period, Offset)>);
impl<
BlockNumber: Rem<Output = BlockNumber> + Sub<Output = BlockNumber> + Zero + PartialOrd,
Period: Get<BlockNumber>,
Offset: Get<BlockNumber>,
> ShouldEndSession<BlockNumber> for PeriodicSessions<Period, Offset>
{
fn should_end_session(now: BlockNumber) -> bool {
let offset = Offset::get();
now >= offset && ((now - offset) % Period::get()).is_zero()
}
}
impl<
BlockNumber: AtLeast32BitUnsigned + Clone,
Period: Get<BlockNumber>,
Offset: Get<BlockNumber>,
> EstimateNextSessionRotation<BlockNumber> for PeriodicSessions<Period, Offset>
{
fn average_session_length() -> BlockNumber {
Period::get()
}
fn estimate_current_session_progress(now: BlockNumber) -> (Option<Permill>, Weight) {
let offset = Offset::get();
let period = Period::get();
let progress = if now >= offset {
let current = (now - offset) % period.clone() + One::one();
Some(Permill::from_rational(current, period))
} else {
Some(Permill::from_rational(now + One::one(), offset))
};
(progress, Zero::zero())
}
fn estimate_next_session_rotation(now: BlockNumber) -> (Option<BlockNumber>, Weight) {
let offset = Offset::get();
let period = Period::get();
let next_session = if now > offset {
let block_after_last_session = (now.clone() - offset) % period.clone();
if block_after_last_session > Zero::zero() {
now.saturating_add(period.saturating_sub(block_after_last_session))
} else {
now + period
}
} else {
offset
};
(Some(next_session), Zero::zero())
}
}
pub trait SessionManager<ValidatorId> {
fn new_session(new_index: SessionIndex) -> Option<Vec<ValidatorId>>;
fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<ValidatorId>> {
Self::new_session(new_index)
}
fn end_session(end_index: SessionIndex);
fn start_session(start_index: SessionIndex);
}
impl<A> SessionManager<A> for () {
fn new_session(_: SessionIndex) -> Option<Vec<A>> {
None
}
fn start_session(_: SessionIndex) {}
fn end_session(_: SessionIndex) {}
}
pub trait SessionHandler<ValidatorId> {
const KEY_TYPE_IDS: &'static [KeyTypeId];
fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(ValidatorId, Ks)]);
fn on_new_session<Ks: OpaqueKeys>(
changed: bool,
validators: &[(ValidatorId, Ks)],
queued_validators: &[(ValidatorId, Ks)],
);
fn on_before_session_ending() {}
fn on_disabled(validator_index: u32);
}
#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
#[tuple_types_custom_trait_bound(OneSessionHandler<AId>)]
impl<AId> SessionHandler<AId> for Tuple {
for_tuples!(
const KEY_TYPE_IDS: &'static [KeyTypeId] = &[ #( <Tuple::Key as RuntimeAppPublic>::ID ),* ];
);
fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(AId, Ks)]) {
for_tuples!(
#(
let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
.filter_map(|k|
k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
)
);
Tuple::on_genesis_session(our_keys);
)*
)
}
fn on_new_session<Ks: OpaqueKeys>(
changed: bool,
validators: &[(AId, Ks)],
queued_validators: &[(AId, Ks)],
) {
for_tuples!(
#(
let our_keys: Box<dyn Iterator<Item=_>> = Box::new(validators.iter()
.filter_map(|k|
k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
));
let queued_keys: Box<dyn Iterator<Item=_>> = Box::new(queued_validators.iter()
.filter_map(|k|
k.1.get::<Tuple::Key>(<Tuple::Key as RuntimeAppPublic>::ID).map(|k1| (&k.0, k1))
));
Tuple::on_new_session(changed, our_keys, queued_keys);
)*
)
}
fn on_before_session_ending() {
for_tuples!( #( Tuple::on_before_session_ending(); )* )
}
fn on_disabled(i: u32) {
for_tuples!( #( Tuple::on_disabled(i); )* )
}
}
pub struct TestSessionHandler;
impl<AId> SessionHandler<AId> for TestSessionHandler {
const KEY_TYPE_IDS: &'static [KeyTypeId] = &[sp_runtime::key_types::DUMMY];
fn on_genesis_session<Ks: OpaqueKeys>(_: &[(AId, Ks)]) {}
fn on_new_session<Ks: OpaqueKeys>(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {}
fn on_before_session_ending() {}
fn on_disabled(_: u32) {}
}
pub trait SessionInterface {
type ValidatorId: Clone;
type AccountId;
type Keys: OpaqueKeys + codec::Decode;
fn validators() -> Vec<Self::ValidatorId>;
fn prune_up_to(index: SessionIndex);
fn report_offence(offender: Self::ValidatorId, severity: OffenceSeverity);
fn set_keys(account: &Self::AccountId, keys: Self::Keys) -> DispatchResult;
fn purge_keys(account: &Self::AccountId) -> DispatchResult;
fn set_keys_weight() -> Weight;
fn purge_keys_weight() -> Weight;
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type ValidatorId: Member
+ Parameter
+ MaybeSerializeDeserialize
+ MaxEncodedLen
+ TryFrom<Self::AccountId>;
type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
type ShouldEndSession: ShouldEndSession<BlockNumberFor<Self>>;
type NextSessionRotation: EstimateNextSessionRotation<BlockNumberFor<Self>>;
type SessionManager: SessionManager<Self::ValidatorId>;
type SessionHandler: SessionHandler<Self::ValidatorId>;
type Keys: OpaqueKeys + Member + Parameter + MaybeSerializeDeserialize;
type DisablingStrategy: DisablingStrategy<Self>;
type WeightInfo: WeightInfo;
type Currency: Mutate<Self::AccountId>
+ HoldMutate<Self::AccountId, Reason: From<HoldReason>>;
#[pallet::constant]
type KeyDeposit: Get<
<<Self as Config>::Currency as Inspect<<Self as frame_system::Config>::AccountId>>::Balance,
>;
}
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
pub non_authority_keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
if T::SessionHandler::KEY_TYPE_IDS.len() != T::Keys::key_ids().len() {
panic!("Number of keys in session handler and session keys does not match");
}
T::SessionHandler::KEY_TYPE_IDS
.iter()
.zip(T::Keys::key_ids())
.enumerate()
.for_each(|(i, (sk, kk))| {
if sk != kk {
panic!(
"Session handler and session key expect different key type at index: {}",
i,
);
}
});
for (account, val, keys) in
self.keys.iter().chain(self.non_authority_keys.iter()).cloned()
{
Pallet::<T>::inner_set_keys(&val, keys)
.expect("genesis config must not contain duplicates; qed");
if frame_system::Pallet::<T>::inc_consumers_without_limit(&account).is_err() {
frame_system::Pallet::<T>::inc_providers(&account);
}
}
let initial_validators_0 =
T::SessionManager::new_session_genesis(0).unwrap_or_else(|| {
frame_support::print(
"No initial validator provided by `SessionManager`, use \
session config keys to generate initial validator set.",
);
self.keys.iter().map(|x| x.1.clone()).collect()
});
let initial_validators_1 = T::SessionManager::new_session_genesis(1)
.unwrap_or_else(|| initial_validators_0.clone());
let queued_keys: Vec<_> = initial_validators_1
.into_iter()
.filter_map(|v| Pallet::<T>::load_keys(&v).map(|k| (v, k)))
.collect();
T::SessionHandler::on_genesis_session::<T::Keys>(&queued_keys);
Validators::<T>::put(initial_validators_0);
QueuedKeys::<T>::put(queued_keys);
T::SessionManager::start_session(0);
}
}
#[pallet::composite_enum]
pub enum HoldReason {
#[codec(index = 0)]
Keys,
}
#[pallet::storage]
pub type Validators<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>;
#[pallet::storage]
pub type CurrentIndex<T> = StorageValue<_, SessionIndex, ValueQuery>;
#[pallet::storage]
pub type QueuedChanged<T> = StorageValue<_, bool, ValueQuery>;
#[pallet::storage]
pub type QueuedKeys<T: Config> = StorageValue<_, Vec<(T::ValidatorId, T::Keys)>, ValueQuery>;
#[pallet::storage]
pub type DisabledValidators<T> = StorageValue<_, Vec<(u32, OffenceSeverity)>, ValueQuery>;
#[pallet::storage]
pub type NextKeys<T: Config> =
StorageMap<_, Twox64Concat, T::ValidatorId, T::Keys, OptionQuery>;
#[pallet::storage]
pub type KeyOwner<T: Config> =
StorageMap<_, Twox64Concat, (KeyTypeId, Vec<u8>), T::ValidatorId, OptionQuery>;
#[pallet::storage]
pub type ExternallySetKeys<T: Config> =
StorageMap<_, Twox64Concat, T::AccountId, (), OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
NewSession { session_index: SessionIndex },
NewQueued,
ValidatorDisabled { validator: T::ValidatorId },
ValidatorReenabled { validator: T::ValidatorId },
}
#[pallet::error]
pub enum Error<T> {
InvalidProof,
NoAssociatedValidatorId,
DuplicatedKey,
NoKeys,
NoAccount,
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(n: BlockNumberFor<T>) -> Weight {
if T::ShouldEndSession::should_end_session(n) {
Self::rotate_session();
T::BlockWeights::get().max_block
} else {
Weight::zero()
}
}
#[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
Self::do_try_state()
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::set_keys())]
pub fn set_keys(origin: OriginFor<T>, keys: T::Keys, proof: Vec<u8>) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(
who.using_encoded(|who| keys.ownership_proof_is_valid(who, &proof)),
Error::<T>::InvalidProof,
);
Self::do_set_keys(&who, keys)?;
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::purge_keys())]
pub fn purge_keys(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::do_purge_keys(&who)?;
Ok(())
}
}
#[cfg(feature = "runtime-benchmarks")]
impl<T: Config> Pallet<T> {
pub fn ensure_can_pay_key_deposit(who: &T::AccountId) -> Result<(), DispatchError> {
use frame_support::traits::tokens::{Fortitude, Preservation};
let deposit = T::KeyDeposit::get();
let has = T::Currency::reducible_balance(who, Preservation::Protect, Fortitude::Force);
if let Some(deficit) = deposit.checked_sub(&has) {
T::Currency::mint_into(who, deficit.max(T::Currency::minimum_balance()))
.map(|_inc| ())
} else {
Ok(())
}
}
}
}
impl<T: Config> Pallet<T> {
pub fn validators() -> Vec<T::ValidatorId> {
Validators::<T>::get()
}
pub fn current_index() -> SessionIndex {
CurrentIndex::<T>::get()
}
pub fn queued_keys() -> Vec<(T::ValidatorId, T::Keys)> {
QueuedKeys::<T>::get()
}
pub fn disabled_validators() -> Vec<u32> {
DisabledValidators::<T>::get().iter().map(|(i, _)| *i).collect()
}
pub fn rotate_session() {
let session_index = CurrentIndex::<T>::get();
let changed = QueuedChanged::<T>::get();
T::SessionHandler::on_before_session_ending();
T::SessionManager::end_session(session_index);
log!(trace, "ending_session {:?}", session_index);
let session_keys = QueuedKeys::<T>::get();
let validators =
session_keys.iter().map(|(validator, _)| validator.clone()).collect::<Vec<_>>();
Validators::<T>::put(&validators);
if changed {
log!(trace, "resetting disabled validators");
DisabledValidators::<T>::kill();
}
let session_index = session_index + 1;
CurrentIndex::<T>::put(session_index);
T::SessionManager::start_session(session_index);
log!(trace, "starting_session {:?}", session_index);
let maybe_next_validators = T::SessionManager::new_session(session_index + 1);
log!(
trace,
"planning_session {:?} with {:?} validators",
session_index + 1,
maybe_next_validators.as_ref().map(|v| v.len())
);
let (next_validators, next_identities_changed) =
if let Some(validators) = maybe_next_validators {
Self::deposit_event(Event::<T>::NewQueued);
(validators, true)
} else {
(Validators::<T>::get(), false)
};
let (queued_amalgamated, next_changed) = {
let mut changed = next_identities_changed;
let mut now_session_keys = session_keys.iter();
let mut check_next_changed = |keys: &T::Keys| {
if changed {
return;
}
if let Some((_, old_keys)) = now_session_keys.next() {
if old_keys != keys {
changed = true;
}
}
};
let queued_amalgamated =
next_validators
.into_iter()
.filter_map(|a| {
let k =
Self::load_keys(&a).or_else(|| {
log!(warn, "failed to load session key for {:?}, skipping for next session, maybe you need to set session keys for them?", a);
None
})?;
check_next_changed(&k);
Some((a, k))
})
.collect::<Vec<_>>();
(queued_amalgamated, changed)
};
QueuedKeys::<T>::put(queued_amalgamated.clone());
QueuedChanged::<T>::put(next_changed);
Self::deposit_event(Event::NewSession { session_index });
T::SessionHandler::on_new_session::<T::Keys>(changed, &session_keys, &queued_amalgamated);
}
pub fn upgrade_keys<Old, F>(upgrade: F)
where
Old: OpaqueKeys + Member + Decode,
F: Fn(T::ValidatorId, Old) -> T::Keys,
{
let old_ids = Old::key_ids();
let new_ids = T::Keys::key_ids();
NextKeys::<T>::translate::<Old, _>(|val, old_keys| {
for i in old_ids.iter() {
Self::clear_key_owner(*i, old_keys.get_raw(*i));
}
let new_keys = upgrade(val.clone(), old_keys);
for i in new_ids.iter() {
Self::put_key_owner(*i, new_keys.get_raw(*i), &val);
}
Some(new_keys)
});
let _ = QueuedKeys::<T>::translate::<Vec<(T::ValidatorId, Old)>, _>(|k| {
k.map(|k| {
k.into_iter()
.map(|(val, old_keys)| (val.clone(), upgrade(val, old_keys)))
.collect::<Vec<_>>()
})
});
}
fn do_set_keys(account: &T::AccountId, keys: T::Keys) -> DispatchResult {
let who = T::ValidatorIdOf::convert(account.clone())
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
ensure!(frame_system::Pallet::<T>::can_inc_consumer(account), Error::<T>::NoAccount);
let old_keys = Self::inner_set_keys(&who, keys)?;
let needs_local_setup =
old_keys.is_none() || ExternallySetKeys::<T>::take(account).is_some();
if needs_local_setup {
let deposit = T::KeyDeposit::get();
if !deposit.is_zero() {
T::Currency::hold(&HoldReason::Keys.into(), account, deposit)?;
}
let assertion = frame_system::Pallet::<T>::inc_consumers(account).is_ok();
debug_assert!(assertion, "can_inc_consumer() returned true; no change since; qed");
}
Ok(())
}
fn inner_set_keys(
who: &T::ValidatorId,
keys: T::Keys,
) -> Result<Option<T::Keys>, DispatchError> {
let old_keys = Self::load_keys(who);
for id in T::Keys::key_ids() {
let key = keys.get_raw(*id);
ensure!(
Self::key_owner(*id, key).map_or(true, |owner| &owner == who),
Error::<T>::DuplicatedKey,
);
}
for id in T::Keys::key_ids() {
let key = keys.get_raw(*id);
if let Some(old) = old_keys.as_ref().map(|k| k.get_raw(*id)) {
if key == old {
continue;
}
Self::clear_key_owner(*id, old);
}
Self::put_key_owner(*id, key, who);
}
Self::put_keys(who, &keys);
Ok(old_keys)
}
fn do_purge_keys(account: &T::AccountId) -> DispatchResult {
let who = T::ValidatorIdOf::convert(account.clone())
.or_else(|| T::ValidatorId::try_from(account.clone()).ok())
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
let old_keys = Self::take_keys(&who).ok_or(Error::<T>::NoKeys)?;
for id in T::Keys::key_ids() {
let key_data = old_keys.get_raw(*id);
Self::clear_key_owner(*id, key_data);
}
let _ = T::Currency::release_all(
&HoldReason::Keys.into(),
account,
frame_support::traits::tokens::Precision::BestEffort,
);
if ExternallySetKeys::<T>::take(account).is_none() {
frame_system::Pallet::<T>::dec_consumers(account);
}
Ok(())
}
pub fn load_keys(v: &T::ValidatorId) -> Option<T::Keys> {
NextKeys::<T>::get(v)
}
fn take_keys(v: &T::ValidatorId) -> Option<T::Keys> {
NextKeys::<T>::take(v)
}
fn put_keys(v: &T::ValidatorId, keys: &T::Keys) {
NextKeys::<T>::insert(v, keys);
}
pub fn key_owner(id: KeyTypeId, key_data: &[u8]) -> Option<T::ValidatorId> {
KeyOwner::<T>::get((id, key_data))
}
fn put_key_owner(id: KeyTypeId, key_data: &[u8], v: &T::ValidatorId) {
KeyOwner::<T>::insert((id, key_data), v)
}
fn clear_key_owner(id: KeyTypeId, key_data: &[u8]) {
KeyOwner::<T>::remove((id, key_data));
}
pub fn disable_index_with_severity(i: u32, severity: OffenceSeverity) -> bool {
if i >= Validators::<T>::decode_len().defensive_unwrap_or(0) as u32 {
return false;
}
DisabledValidators::<T>::mutate(|disabled| {
match disabled.binary_search_by_key(&i, |(index, _)| *index) {
Ok(index) => {
let current_severity = &mut disabled[index].1;
if severity > *current_severity {
log!(
trace,
"updating disablement severity of validator {:?} from {:?} to {:?}",
i,
*current_severity,
severity
);
*current_severity = severity;
}
true
},
Err(index) => {
log!(trace, "disabling validator {:?}", i);
Self::deposit_event(Event::ValidatorDisabled {
validator: Validators::<T>::get()[i as usize].clone(),
});
disabled.insert(index, (i, severity));
T::SessionHandler::on_disabled(i);
true
},
}
})
}
pub fn disable_index(i: u32) -> bool {
let default_severity = OffenceSeverity::default();
Self::disable_index_with_severity(i, default_severity)
}
pub fn reenable_index(i: u32) -> bool {
if i >= Validators::<T>::decode_len().defensive_unwrap_or(0) as u32 {
return false;
}
DisabledValidators::<T>::mutate(|disabled| {
if let Ok(index) = disabled.binary_search_by_key(&i, |(index, _)| *index) {
log!(trace, "reenabling validator {:?}", i);
Self::deposit_event(Event::ValidatorReenabled {
validator: Validators::<T>::get()[i as usize].clone(),
});
disabled.remove(index);
return true;
}
false
})
}
pub fn validator_id_to_index(id: &T::ValidatorId) -> Option<u32> {
Validators::<T>::get().iter().position(|i| i == id).map(|i| i as u32)
}
pub fn report_offence(validator: T::ValidatorId, severity: OffenceSeverity) {
let decision =
T::DisablingStrategy::decision(&validator, severity, &DisabledValidators::<T>::get());
log!(
debug,
"reporting offence for {:?} with {:?}, decision: {:?}",
validator,
severity,
decision
);
if let Some(offender_idx) = decision.disable {
Self::disable_index_with_severity(offender_idx, severity);
}
if let Some(reenable_idx) = decision.reenable {
Self::reenable_index(reenable_idx);
}
}
#[cfg(any(test, feature = "try-runtime"))]
pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
ensure!(
DisabledValidators::<T>::get().windows(2).all(|pair| pair[0].0 <= pair[1].0),
"DisabledValidators is not sorted"
);
Ok(())
}
}
impl<T: Config> ValidatorRegistration<T::ValidatorId> for Pallet<T> {
fn is_registered(id: &T::ValidatorId) -> bool {
Self::load_keys(id).is_some()
}
}
impl<T: Config> ValidatorSet<T::AccountId> for Pallet<T> {
type ValidatorId = T::ValidatorId;
type ValidatorIdOf = T::ValidatorIdOf;
fn session_index() -> sp_staking::SessionIndex {
CurrentIndex::<T>::get()
}
fn validators() -> Vec<Self::ValidatorId> {
Validators::<T>::get()
}
}
impl<T: Config> EstimateNextNewSession<BlockNumberFor<T>> for Pallet<T> {
fn average_session_length() -> BlockNumberFor<T> {
T::NextSessionRotation::average_session_length()
}
fn estimate_next_new_session(now: BlockNumberFor<T>) -> (Option<BlockNumberFor<T>>, Weight) {
T::NextSessionRotation::estimate_next_session_rotation(now)
}
}
impl<T: Config> frame_support::traits::DisabledValidators for Pallet<T> {
fn is_disabled(index: u32) -> bool {
DisabledValidators::<T>::get().binary_search_by_key(&index, |(i, _)| *i).is_ok()
}
fn disabled_validators() -> Vec<u32> {
Self::disabled_validators()
}
}
#[cfg(feature = "historical")]
impl<T: Config + historical::Config> SessionInterface for Pallet<T> {
type ValidatorId = T::ValidatorId;
type AccountId = T::AccountId;
type Keys = T::Keys;
fn validators() -> Vec<Self::ValidatorId> {
Self::validators()
}
fn prune_up_to(index: SessionIndex) {
historical::Pallet::<T>::prune_up_to(index)
}
fn report_offence(offender: Self::ValidatorId, severity: OffenceSeverity) {
Self::report_offence(offender, severity)
}
fn set_keys(account: &Self::AccountId, keys: Self::Keys) -> DispatchResult {
let who = T::ValidatorIdOf::convert(account.clone())
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
let old_keys = Self::inner_set_keys(&who, keys)?;
if old_keys.is_some() && !ExternallySetKeys::<T>::contains_key(account) {
let _ = T::Currency::release_all(
&HoldReason::Keys.into(),
account,
frame_support::traits::tokens::Precision::BestEffort,
);
frame_system::Pallet::<T>::dec_consumers(account);
}
ExternallySetKeys::<T>::insert(account, ());
Ok(())
}
fn purge_keys(account: &Self::AccountId) -> DispatchResult {
let who = T::ValidatorIdOf::convert(account.clone())
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
let old_keys = Self::take_keys(&who).ok_or(Error::<T>::NoKeys)?;
for id in T::Keys::key_ids() {
let key_data = old_keys.get_raw(*id);
Self::clear_key_owner(*id, key_data);
}
let _ = T::Currency::release_all(
&HoldReason::Keys.into(),
account,
frame_support::traits::tokens::Precision::BestEffort,
);
if ExternallySetKeys::<T>::take(account).is_none() {
frame_system::Pallet::<T>::dec_consumers(account);
}
Ok(())
}
fn set_keys_weight() -> Weight {
T::WeightInfo::set_keys()
}
fn purge_keys_weight() -> Weight {
T::WeightInfo::purge_keys()
}
}
pub struct FindAccountFromAuthorIndex<T, Inner>(core::marker::PhantomData<(T, Inner)>);
impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
for FindAccountFromAuthorIndex<T, Inner>
{
fn find_author<'a, I>(digests: I) -> Option<T::ValidatorId>
where
I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
{
let i = Inner::find_author(digests)?;
let validators = Validators::<T>::get();
validators.get(i as usize).cloned()
}
}