|
|
|
@@ -15,14 +15,14 @@
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
|
|
//! # Session Module
|
|
|
|
|
//! # Session Pallet
|
|
|
|
|
//!
|
|
|
|
|
//! The Session module allows validators to manage their session keys, provides a function for
|
|
|
|
|
//! The Session pallet allows validators to manage their session keys, provides a function for
|
|
|
|
|
//! changing the session length, and handles session rotation.
|
|
|
|
|
//!
|
|
|
|
|
//! - [`Config`]
|
|
|
|
|
//! - [`Call`]
|
|
|
|
|
//! - [`Module`]
|
|
|
|
|
//! - [`Pallet`]
|
|
|
|
|
//!
|
|
|
|
|
//! ## Overview
|
|
|
|
|
//!
|
|
|
|
@@ -95,12 +95,12 @@
|
|
|
|
|
//! use pallet_session as session;
|
|
|
|
|
//!
|
|
|
|
|
//! fn validators<T: pallet_session::Config>() -> Vec<<T as pallet_session::Config>::ValidatorId> {
|
|
|
|
|
//! <pallet_session::Module<T>>::validators()
|
|
|
|
|
//! <pallet_session::Pallet<T>>::validators()
|
|
|
|
|
//! }
|
|
|
|
|
//! # fn main(){}
|
|
|
|
|
//! ```
|
|
|
|
|
//!
|
|
|
|
|
//! ## Related Modules
|
|
|
|
|
//! ## Related Pallets
|
|
|
|
|
//!
|
|
|
|
|
//! - [Staking](../pallet_staking/index.html)
|
|
|
|
|
|
|
|
|
@@ -114,22 +114,9 @@ mod mock;
|
|
|
|
|
mod tests;
|
|
|
|
|
pub mod weights;
|
|
|
|
|
|
|
|
|
|
use codec::{Decode, MaxEncodedLen};
|
|
|
|
|
use frame_support::{
|
|
|
|
|
decl_error, decl_event, decl_module, decl_storage,
|
|
|
|
|
dispatch::{self, DispatchError, DispatchResult},
|
|
|
|
|
ensure,
|
|
|
|
|
traits::{
|
|
|
|
|
EstimateNextNewSession, EstimateNextSessionRotation, FindAuthor, Get, OneSessionHandler,
|
|
|
|
|
ValidatorRegistration, ValidatorSet,
|
|
|
|
|
},
|
|
|
|
|
weights::Weight,
|
|
|
|
|
ConsensusEngineId, Parameter,
|
|
|
|
|
};
|
|
|
|
|
use frame_system::ensure_signed;
|
|
|
|
|
use sp_runtime::{
|
|
|
|
|
traits::{AtLeast32BitUnsigned, Convert, Member, One, OpaqueKeys, Zero},
|
|
|
|
|
KeyTypeId, Perbill, Permill, RuntimeAppPublic,
|
|
|
|
|
ConsensusEngineId, KeyTypeId, Perbill, Permill, RuntimeAppPublic,
|
|
|
|
|
};
|
|
|
|
|
use sp_staking::SessionIndex;
|
|
|
|
|
use sp_std::{
|
|
|
|
@@ -137,6 +124,20 @@ use sp_std::{
|
|
|
|
|
ops::{Rem, Sub},
|
|
|
|
|
prelude::*,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use frame_support::{
|
|
|
|
|
codec::{Decode, MaxEncodedLen},
|
|
|
|
|
dispatch::{DispatchError, DispatchResult},
|
|
|
|
|
ensure,
|
|
|
|
|
traits::{
|
|
|
|
|
EstimateNextNewSession, EstimateNextSessionRotation, FindAuthor, Get, OneSessionHandler,
|
|
|
|
|
StorageVersion, ValidatorRegistration, ValidatorSet,
|
|
|
|
|
},
|
|
|
|
|
weights::Weight,
|
|
|
|
|
Parameter,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub use pallet::*;
|
|
|
|
|
pub use weights::WeightInfo;
|
|
|
|
|
|
|
|
|
|
/// Decides whether the session should be ended.
|
|
|
|
@@ -228,7 +229,7 @@ pub trait SessionManager<ValidatorId> {
|
|
|
|
|
///
|
|
|
|
|
/// Even if the validator-set is the same as before, if any underlying economic conditions have
|
|
|
|
|
/// changed (i.e. stake-weights), the new validator set must be returned. This is necessary for
|
|
|
|
|
/// consensus engines making use of the session module to issue a validator-set change so
|
|
|
|
|
/// consensus engines making use of the session pallet to issue a validator-set change so
|
|
|
|
|
/// misbehavior can be provably associated with the new economic conditions as opposed to the
|
|
|
|
|
/// old. The returned validator set, if any, will not be applied until `new_index`. `new_index`
|
|
|
|
|
/// is strictly greater than from previous call.
|
|
|
|
@@ -280,7 +281,7 @@ pub trait SessionHandler<ValidatorId> {
|
|
|
|
|
fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(ValidatorId, Ks)]);
|
|
|
|
|
|
|
|
|
|
/// Session set has changed; act appropriately. Note that this can be called
|
|
|
|
|
/// before initialization of your module.
|
|
|
|
|
/// before initialization of your pallet.
|
|
|
|
|
///
|
|
|
|
|
/// `changed` is true whenever any of the session keys or underlying economic
|
|
|
|
|
/// identities or weightings behind those keys has changed.
|
|
|
|
@@ -356,86 +357,83 @@ impl<AId> SessionHandler<AId> for TestSessionHandler {
|
|
|
|
|
fn on_disabled(_: usize) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> ValidatorRegistration<T::ValidatorId> for Module<T> {
|
|
|
|
|
fn is_registered(id: &T::ValidatorId) -> bool {
|
|
|
|
|
Self::load_keys(id).is_some()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[frame_support::pallet]
|
|
|
|
|
pub mod pallet {
|
|
|
|
|
use super::*;
|
|
|
|
|
use frame_support::pallet_prelude::*;
|
|
|
|
|
use frame_system::pallet_prelude::*;
|
|
|
|
|
|
|
|
|
|
pub trait Config: frame_system::Config {
|
|
|
|
|
/// The overarching event type.
|
|
|
|
|
type Event: From<Event> + Into<<Self as frame_system::Config>::Event>;
|
|
|
|
|
/// The current storage version.
|
|
|
|
|
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
|
|
|
|
|
|
|
|
|
|
/// A stable ID for a validator.
|
|
|
|
|
type ValidatorId: Member + Parameter + MaxEncodedLen;
|
|
|
|
|
#[pallet::pallet]
|
|
|
|
|
#[pallet::generate_store(pub(super) trait Store)]
|
|
|
|
|
#[pallet::storage_version(STORAGE_VERSION)]
|
|
|
|
|
pub struct Pallet<T>(_);
|
|
|
|
|
|
|
|
|
|
/// A conversion from account ID to validator ID.
|
|
|
|
|
///
|
|
|
|
|
/// Its cost must be at most one storage read.
|
|
|
|
|
type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
|
|
|
|
|
#[pallet::config]
|
|
|
|
|
pub trait Config: frame_system::Config {
|
|
|
|
|
/// The overarching event type.
|
|
|
|
|
type Event: From<Event> + IsType<<Self as frame_system::Config>::Event>;
|
|
|
|
|
|
|
|
|
|
/// Indicator for when to end the session.
|
|
|
|
|
type ShouldEndSession: ShouldEndSession<Self::BlockNumber>;
|
|
|
|
|
/// A stable ID for a validator.
|
|
|
|
|
type ValidatorId: Member + Parameter + MaybeSerializeDeserialize + MaxEncodedLen;
|
|
|
|
|
|
|
|
|
|
/// Something that can predict the next session rotation. This should typically come from the
|
|
|
|
|
/// same logical unit that provides [`ShouldEndSession`], yet, it gives a best effort estimate.
|
|
|
|
|
/// It is helpful to implement [`EstimateNextNewSession`].
|
|
|
|
|
type NextSessionRotation: EstimateNextSessionRotation<Self::BlockNumber>;
|
|
|
|
|
|
|
|
|
|
/// Handler for managing new session.
|
|
|
|
|
type SessionManager: SessionManager<Self::ValidatorId>;
|
|
|
|
|
|
|
|
|
|
/// Handler when a session has changed.
|
|
|
|
|
type SessionHandler: SessionHandler<Self::ValidatorId>;
|
|
|
|
|
|
|
|
|
|
/// The keys.
|
|
|
|
|
type Keys: OpaqueKeys + Member + Parameter + Default;
|
|
|
|
|
|
|
|
|
|
/// The fraction of validators set that is safe to be disabled.
|
|
|
|
|
///
|
|
|
|
|
/// After the threshold is reached `disabled` method starts to return true,
|
|
|
|
|
/// which in combination with `pallet_staking` forces a new era.
|
|
|
|
|
type DisabledValidatorsThreshold: Get<Perbill>;
|
|
|
|
|
|
|
|
|
|
/// Weight information for extrinsics in this pallet.
|
|
|
|
|
type WeightInfo: WeightInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decl_storage! {
|
|
|
|
|
trait Store for Module<T: Config> as Session {
|
|
|
|
|
/// The current set of validators.
|
|
|
|
|
Validators get(fn validators): Vec<T::ValidatorId>;
|
|
|
|
|
|
|
|
|
|
/// Current index of the session.
|
|
|
|
|
CurrentIndex get(fn current_index): SessionIndex;
|
|
|
|
|
|
|
|
|
|
/// True if the underlying economic identities or weighting behind the validators
|
|
|
|
|
/// has changed in the queued validator set.
|
|
|
|
|
QueuedChanged: bool;
|
|
|
|
|
|
|
|
|
|
/// The queued keys for the next session. When the next session begins, these keys
|
|
|
|
|
/// will be used to determine the validator's session keys.
|
|
|
|
|
QueuedKeys get(fn queued_keys): Vec<(T::ValidatorId, T::Keys)>;
|
|
|
|
|
|
|
|
|
|
/// Indices of disabled validators.
|
|
|
|
|
/// A conversion from account ID to validator ID.
|
|
|
|
|
///
|
|
|
|
|
/// The set is cleared when `on_session_ending` returns a new set of identities.
|
|
|
|
|
DisabledValidators get(fn disabled_validators): Vec<u32>;
|
|
|
|
|
/// Its cost must be at most one storage read.
|
|
|
|
|
type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
|
|
|
|
|
|
|
|
|
|
/// The next session keys for a validator.
|
|
|
|
|
NextKeys: map hasher(twox_64_concat) T::ValidatorId => Option<T::Keys>;
|
|
|
|
|
/// Indicator for when to end the session.
|
|
|
|
|
type ShouldEndSession: ShouldEndSession<Self::BlockNumber>;
|
|
|
|
|
|
|
|
|
|
/// The owner of a key. The key is the `KeyTypeId` + the encoded key.
|
|
|
|
|
KeyOwner: map hasher(twox_64_concat) (KeyTypeId, Vec<u8>) => Option<T::ValidatorId>;
|
|
|
|
|
/// Something that can predict the next session rotation. This should typically come from
|
|
|
|
|
/// the same logical unit that provides [`ShouldEndSession`], yet, it gives a best effort
|
|
|
|
|
/// estimate. It is helpful to implement [`EstimateNextNewSession`].
|
|
|
|
|
type NextSessionRotation: EstimateNextSessionRotation<Self::BlockNumber>;
|
|
|
|
|
|
|
|
|
|
/// Handler for managing new session.
|
|
|
|
|
type SessionManager: SessionManager<Self::ValidatorId>;
|
|
|
|
|
|
|
|
|
|
/// Handler when a session has changed.
|
|
|
|
|
type SessionHandler: SessionHandler<Self::ValidatorId>;
|
|
|
|
|
|
|
|
|
|
/// The keys.
|
|
|
|
|
type Keys: OpaqueKeys + Member + Parameter + Default + MaybeSerializeDeserialize;
|
|
|
|
|
|
|
|
|
|
/// The fraction of validators set that is safe to be disabled.
|
|
|
|
|
///
|
|
|
|
|
/// After the threshold is reached `disabled` method starts to return true,
|
|
|
|
|
/// which in combination with `pallet_staking` forces a new era.
|
|
|
|
|
type DisabledValidatorsThreshold: Get<Perbill>;
|
|
|
|
|
|
|
|
|
|
/// Weight information for extrinsics in this pallet.
|
|
|
|
|
type WeightInfo: WeightInfo;
|
|
|
|
|
}
|
|
|
|
|
add_extra_genesis {
|
|
|
|
|
config(keys): Vec<(T::AccountId, T::ValidatorId, T::Keys)>;
|
|
|
|
|
build(|config: &GenesisConfig<T>| {
|
|
|
|
|
|
|
|
|
|
#[pallet::genesis_config]
|
|
|
|
|
pub struct GenesisConfig<T: Config> {
|
|
|
|
|
pub keys: Vec<(T::AccountId, T::ValidatorId, T::Keys)>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "std")]
|
|
|
|
|
impl<T: Config> Default for GenesisConfig<T> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self { keys: Default::default() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pallet::genesis_build]
|
|
|
|
|
impl<T: Config> GenesisBuild<T> 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()
|
|
|
|
|
T::SessionHandler::KEY_TYPE_IDS
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(T::Keys::key_ids())
|
|
|
|
|
.enumerate()
|
|
|
|
|
.for_each(|(i, (sk, kk))| {
|
|
|
|
|
if sk != kk {
|
|
|
|
|
panic!(
|
|
|
|
@@ -445,8 +443,8 @@ decl_storage! {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (account, val, keys) in config.keys.iter().cloned() {
|
|
|
|
|
<Module<T>>::inner_set_keys(&val, keys)
|
|
|
|
|
for (account, val, keys) in self.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(&account).is_err() {
|
|
|
|
|
// This will leak a provider reference, however it only happens once (at
|
|
|
|
@@ -457,25 +455,30 @@ decl_storage! {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
config.keys.iter().map(|x| x.1.clone()).collect()
|
|
|
|
|
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()
|
|
|
|
|
});
|
|
|
|
|
assert!(!initial_validators_0.is_empty(), "Empty validator set for session 0 in genesis block!");
|
|
|
|
|
assert!(
|
|
|
|
|
!initial_validators_0.is_empty(),
|
|
|
|
|
"Empty validator set for session 0 in genesis block!"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let initial_validators_1 = T::SessionManager::new_session_genesis(1)
|
|
|
|
|
.unwrap_or_else(|| initial_validators_0.clone());
|
|
|
|
|
assert!(!initial_validators_1.is_empty(), "Empty validator set for session 1 in genesis block!");
|
|
|
|
|
assert!(
|
|
|
|
|
!initial_validators_1.is_empty(),
|
|
|
|
|
"Empty validator set for session 1 in genesis block!"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let queued_keys: Vec<_> = initial_validators_1
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.map(|v| (
|
|
|
|
|
v.clone(),
|
|
|
|
|
<Module<T>>::load_keys(&v).unwrap_or_default(),
|
|
|
|
|
))
|
|
|
|
|
.map(|v| (v.clone(), <Pallet<T>>::load_keys(&v).unwrap_or_default()))
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
// Tell everyone about the genesis session keys
|
|
|
|
@@ -485,21 +488,62 @@ decl_storage! {
|
|
|
|
|
<QueuedKeys<T>>::put(queued_keys);
|
|
|
|
|
|
|
|
|
|
T::SessionManager::start_session(0);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decl_event!(
|
|
|
|
|
/// The current set of validators.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
#[pallet::getter(fn validators)]
|
|
|
|
|
pub type Validators<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>;
|
|
|
|
|
|
|
|
|
|
/// Current index of the session.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
#[pallet::getter(fn current_index)]
|
|
|
|
|
pub type CurrentIndex<T> = StorageValue<_, SessionIndex, ValueQuery>;
|
|
|
|
|
|
|
|
|
|
/// True if the underlying economic identities or weighting behind the validators
|
|
|
|
|
/// has changed in the queued validator set.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
pub type QueuedChanged<T> = StorageValue<_, bool, ValueQuery>;
|
|
|
|
|
|
|
|
|
|
/// The queued keys for the next session. When the next session begins, these keys
|
|
|
|
|
/// will be used to determine the validator's session keys.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
#[pallet::getter(fn queued_keys)]
|
|
|
|
|
pub type QueuedKeys<T: Config> = StorageValue<_, Vec<(T::ValidatorId, T::Keys)>, ValueQuery>;
|
|
|
|
|
|
|
|
|
|
/// Indices of disabled validators.
|
|
|
|
|
///
|
|
|
|
|
/// The set is cleared when `on_session_ending` returns a new set of identities.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
#[pallet::getter(fn disabled_validators)]
|
|
|
|
|
pub type DisabledValidators<T> = StorageValue<_, Vec<u32>, ValueQuery>;
|
|
|
|
|
|
|
|
|
|
/// The next session keys for a validator.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
pub type NextKeys<T: Config> =
|
|
|
|
|
StorageMap<_, Twox64Concat, T::ValidatorId, T::Keys, OptionQuery>;
|
|
|
|
|
|
|
|
|
|
/// The owner of a key. The key is the `KeyTypeId` + the encoded key.
|
|
|
|
|
#[pallet::storage]
|
|
|
|
|
pub type KeyOwner<T: Config> =
|
|
|
|
|
StorageMap<_, Twox64Concat, (KeyTypeId, Vec<u8>), T::ValidatorId, OptionQuery>;
|
|
|
|
|
|
|
|
|
|
#[pallet::event]
|
|
|
|
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
|
|
|
|
pub enum Event {
|
|
|
|
|
/// New session has happened. Note that the argument is the \[session_index\], not the
|
|
|
|
|
/// block number as the type might suggest.
|
|
|
|
|
NewSession(SessionIndex),
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
decl_error! {
|
|
|
|
|
/// Error for the session module.
|
|
|
|
|
pub enum Error for Module<T: Config> {
|
|
|
|
|
/// Old name generated by `decl_event`.
|
|
|
|
|
#[deprecated(note = "use `Event` instead")]
|
|
|
|
|
pub type RawEvent = Event;
|
|
|
|
|
|
|
|
|
|
/// Error for the session pallet.
|
|
|
|
|
#[pallet::error]
|
|
|
|
|
pub enum Error<T> {
|
|
|
|
|
/// Invalid ownership proof.
|
|
|
|
|
InvalidProof,
|
|
|
|
|
/// No associated validator ID for account.
|
|
|
|
@@ -511,57 +555,9 @@ decl_error! {
|
|
|
|
|
/// Key setting account is not live, so it's impossible to associate keys.
|
|
|
|
|
NoAccount,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decl_module! {
|
|
|
|
|
pub struct Module<T: Config> for enum Call where origin: T::Origin {
|
|
|
|
|
type Error = Error<T>;
|
|
|
|
|
|
|
|
|
|
fn deposit_event() = default;
|
|
|
|
|
|
|
|
|
|
/// Sets the session key(s) of the function caller to `keys`.
|
|
|
|
|
/// Allows an account to set its session key prior to becoming a validator.
|
|
|
|
|
/// This doesn't take effect until the next session.
|
|
|
|
|
///
|
|
|
|
|
/// The dispatch origin of this function must be signed.
|
|
|
|
|
///
|
|
|
|
|
/// # <weight>
|
|
|
|
|
/// - Complexity: `O(1)`
|
|
|
|
|
/// Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed.
|
|
|
|
|
/// - DbReads: `origin account`, `T::ValidatorIdOf`, `NextKeys`
|
|
|
|
|
/// - DbWrites: `origin account`, `NextKeys`
|
|
|
|
|
/// - DbReads per key id: `KeyOwner`
|
|
|
|
|
/// - DbWrites per key id: `KeyOwner`
|
|
|
|
|
/// # </weight>
|
|
|
|
|
#[weight = T::WeightInfo::set_keys()]
|
|
|
|
|
pub fn set_keys(origin, keys: T::Keys, proof: Vec<u8>) -> dispatch::DispatchResult {
|
|
|
|
|
let who = ensure_signed(origin)?;
|
|
|
|
|
|
|
|
|
|
ensure!(keys.ownership_proof_is_valid(&proof), Error::<T>::InvalidProof);
|
|
|
|
|
|
|
|
|
|
Self::do_set_keys(&who, keys)?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Removes any session key(s) of the function caller.
|
|
|
|
|
/// This doesn't take effect until the next session.
|
|
|
|
|
///
|
|
|
|
|
/// The dispatch origin of this function must be signed.
|
|
|
|
|
///
|
|
|
|
|
/// # <weight>
|
|
|
|
|
/// - Complexity: `O(1)` in number of key types.
|
|
|
|
|
/// Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed.
|
|
|
|
|
/// - DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account`
|
|
|
|
|
/// - DbWrites: `NextKeys`, `origin account`
|
|
|
|
|
/// - DbWrites per key id: `KeyOwner`
|
|
|
|
|
/// # </weight>
|
|
|
|
|
#[weight = T::WeightInfo::purge_keys()]
|
|
|
|
|
pub fn purge_keys(origin) {
|
|
|
|
|
let who = ensure_signed(origin)?;
|
|
|
|
|
Self::do_purge_keys(&who)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pallet::hooks]
|
|
|
|
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
|
|
|
|
/// Called when a block is initialized. Will rotate session if it is the last
|
|
|
|
|
/// block of the current session.
|
|
|
|
|
fn on_initialize(n: T::BlockNumber) -> Weight {
|
|
|
|
@@ -576,17 +572,62 @@ decl_module! {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pallet::call]
|
|
|
|
|
impl<T: Config> Pallet<T> {
|
|
|
|
|
/// Sets the session key(s) of the function caller to `keys`.
|
|
|
|
|
/// Allows an account to set its session key prior to becoming a validator.
|
|
|
|
|
/// This doesn't take effect until the next session.
|
|
|
|
|
///
|
|
|
|
|
/// The dispatch origin of this function must be signed.
|
|
|
|
|
///
|
|
|
|
|
/// # <weight>
|
|
|
|
|
/// - Complexity: `O(1)`. Actual cost depends on the number of length of
|
|
|
|
|
/// `T::Keys::key_ids()` which is fixed.
|
|
|
|
|
/// - DbReads: `origin account`, `T::ValidatorIdOf`, `NextKeys`
|
|
|
|
|
/// - DbWrites: `origin account`, `NextKeys`
|
|
|
|
|
/// - DbReads per key id: `KeyOwner`
|
|
|
|
|
/// - DbWrites per key id: `KeyOwner`
|
|
|
|
|
/// # </weight>
|
|
|
|
|
#[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!(keys.ownership_proof_is_valid(&proof), Error::<T>::InvalidProof);
|
|
|
|
|
|
|
|
|
|
Self::do_set_keys(&who, keys)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Removes any session key(s) of the function caller.
|
|
|
|
|
/// This doesn't take effect until the next session.
|
|
|
|
|
///
|
|
|
|
|
/// The dispatch origin of this function must be signed.
|
|
|
|
|
///
|
|
|
|
|
/// # <weight>
|
|
|
|
|
/// - Complexity: `O(1)` in number of key types. Actual cost depends on the number of length
|
|
|
|
|
/// of `T::Keys::key_ids()` which is fixed.
|
|
|
|
|
/// - DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account`
|
|
|
|
|
/// - DbWrites: `NextKeys`, `origin account`
|
|
|
|
|
/// - DbWrites per key id: `KeyOwner`
|
|
|
|
|
/// # </weight>
|
|
|
|
|
#[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(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> Module<T> {
|
|
|
|
|
impl<T: Config> Pallet<T> {
|
|
|
|
|
/// Move on to next session. Register new validator set and session keys. Changes to the
|
|
|
|
|
/// validator set have a session of delay to take effect. This allows for equivocation
|
|
|
|
|
/// punishment after a fork.
|
|
|
|
|
pub fn rotate_session() {
|
|
|
|
|
let session_index = CurrentIndex::get();
|
|
|
|
|
let session_index = <CurrentIndex<T>>::get();
|
|
|
|
|
log::trace!(target: "runtime::session", "rotating session {:?}", session_index);
|
|
|
|
|
|
|
|
|
|
let changed = QueuedChanged::get();
|
|
|
|
|
let changed = <QueuedChanged<T>>::get();
|
|
|
|
|
|
|
|
|
|
// Inform the session handlers that a session is going to end.
|
|
|
|
|
T::SessionHandler::on_before_session_ending();
|
|
|
|
@@ -600,12 +641,12 @@ impl<T: Config> Module<T> {
|
|
|
|
|
|
|
|
|
|
if changed {
|
|
|
|
|
// reset disabled validators
|
|
|
|
|
DisabledValidators::take();
|
|
|
|
|
<DisabledValidators<T>>::take();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Increment session index.
|
|
|
|
|
let session_index = session_index + 1;
|
|
|
|
|
CurrentIndex::put(session_index);
|
|
|
|
|
<CurrentIndex<T>>::put(session_index);
|
|
|
|
|
|
|
|
|
|
T::SessionManager::start_session(session_index);
|
|
|
|
|
|
|
|
|
@@ -655,7 +696,7 @@ impl<T: Config> Module<T> {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
<QueuedKeys<T>>::put(queued_amalgamated.clone());
|
|
|
|
|
QueuedChanged::put(next_changed);
|
|
|
|
|
<QueuedChanged<T>>::put(next_changed);
|
|
|
|
|
|
|
|
|
|
// Record that this happened.
|
|
|
|
|
Self::deposit_event(Event::NewSession(session_index));
|
|
|
|
@@ -669,7 +710,7 @@ impl<T: Config> Module<T> {
|
|
|
|
|
/// Returns `true` if this causes a `DisabledValidatorsThreshold` of validators
|
|
|
|
|
/// to be already disabled.
|
|
|
|
|
pub fn disable_index(i: usize) -> bool {
|
|
|
|
|
let (fire_event, threshold_reached) = DisabledValidators::mutate(|disabled| {
|
|
|
|
|
let (fire_event, threshold_reached) = <DisabledValidators<T>>::mutate(|disabled| {
|
|
|
|
|
let i = i as u32;
|
|
|
|
|
if let Err(index) = disabled.binary_search(&i) {
|
|
|
|
|
let count = <Validators<T>>::decode_len().unwrap_or(0) as u32;
|
|
|
|
@@ -688,12 +729,12 @@ impl<T: Config> Module<T> {
|
|
|
|
|
threshold_reached
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Disable the validator identified by `c`. (If using with the staking module,
|
|
|
|
|
/// Disable the validator identified by `c`. (If using with the staking pallet,
|
|
|
|
|
/// this would be their *stash* account.)
|
|
|
|
|
///
|
|
|
|
|
/// Returns `Ok(true)` if more than `DisabledValidatorsThreshold` validators in current
|
|
|
|
|
/// session is already disabled.
|
|
|
|
|
/// If used with the staking module it allows to force a new era in such case.
|
|
|
|
|
/// If used with the staking pallet it allows to force a new era in such case.
|
|
|
|
|
pub fn disable(c: &T::ValidatorId) -> sp_std::result::Result<bool, ()> {
|
|
|
|
|
Self::validators()
|
|
|
|
|
.iter()
|
|
|
|
@@ -711,7 +752,7 @@ impl<T: Config> Module<T> {
|
|
|
|
|
///
|
|
|
|
|
/// Care should be taken that the raw versions of the
|
|
|
|
|
/// added keys are unique for every `ValidatorId, KeyTypeId` combination.
|
|
|
|
|
/// This is an invariant that the session module typically maintains internally.
|
|
|
|
|
/// This is an invariant that the session pallet typically maintains internally.
|
|
|
|
|
///
|
|
|
|
|
/// As the actual values of the keys are typically not known at runtime upgrade,
|
|
|
|
|
/// it's recommended to initialize the keys to a (unique) dummy value with the expectation
|
|
|
|
@@ -756,7 +797,7 @@ impl<T: Config> Module<T> {
|
|
|
|
|
///
|
|
|
|
|
/// This ensures that the reference counter in system is incremented appropriately and as such
|
|
|
|
|
/// must accept an account ID, rather than a validator ID.
|
|
|
|
|
fn do_set_keys(account: &T::AccountId, keys: T::Keys) -> dispatch::DispatchResult {
|
|
|
|
|
fn do_set_keys(account: &T::AccountId, keys: T::Keys) -> DispatchResult {
|
|
|
|
|
let who = T::ValidatorIdOf::convert(account.clone())
|
|
|
|
|
.ok_or(Error::<T>::NoAssociatedValidatorId)?;
|
|
|
|
|
|
|
|
|
@@ -850,16 +891,40 @@ impl<T: Config> Module<T> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> ValidatorSet<T::AccountId> for Module<T> {
|
|
|
|
|
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 {
|
|
|
|
|
Module::<T>::current_index()
|
|
|
|
|
Pallet::<T>::current_index()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn validators() -> Vec<Self::ValidatorId> {
|
|
|
|
|
Module::<T>::validators()
|
|
|
|
|
Pallet::<T>::validators()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> EstimateNextNewSession<T::BlockNumber> for Pallet<T> {
|
|
|
|
|
fn average_session_length() -> T::BlockNumber {
|
|
|
|
|
T::NextSessionRotation::average_session_length()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// This session pallet always calls new_session and next_session at the same time, hence we
|
|
|
|
|
/// do a simple proxy and pass the function to next rotation.
|
|
|
|
|
fn estimate_next_new_session(now: T::BlockNumber) -> (Option<T::BlockNumber>, Weight) {
|
|
|
|
|
T::NextSessionRotation::estimate_next_session_rotation(now)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> frame_support::traits::DisabledValidators for Pallet<T> {
|
|
|
|
|
fn is_disabled(index: u32) -> bool {
|
|
|
|
|
<Pallet<T>>::disabled_validators().binary_search(&index).is_ok()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -877,25 +942,7 @@ impl<T: Config, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
|
|
|
|
|
{
|
|
|
|
|
let i = Inner::find_author(digests)?;
|
|
|
|
|
|
|
|
|
|
let validators = <Module<T>>::validators();
|
|
|
|
|
let validators = <Pallet<T>>::validators();
|
|
|
|
|
validators.get(i as usize).map(|k| k.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> EstimateNextNewSession<T::BlockNumber> for Module<T> {
|
|
|
|
|
fn average_session_length() -> T::BlockNumber {
|
|
|
|
|
T::NextSessionRotation::average_session_length()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// This session module always calls new_session and next_session at the same time, hence we
|
|
|
|
|
/// do a simple proxy and pass the function to next rotation.
|
|
|
|
|
fn estimate_next_new_session(now: T::BlockNumber) -> (Option<T::BlockNumber>, Weight) {
|
|
|
|
|
T::NextSessionRotation::estimate_next_session_rotation(now)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Config> frame_support::traits::DisabledValidators for Module<T> {
|
|
|
|
|
fn is_disabled(index: u32) -> bool {
|
|
|
|
|
<Module<T>>::disabled_validators().binary_search(&index).is_ok()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|