Migrate pallet-session to the new pallet macro (#9796)

* Migrate pallet-session to the new pallet macro

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Remove old macros

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix

Signed-off-by: koushiro <koushiro.cqx@gmail.com>
This commit is contained in:
Qinxuan Chen
2021-09-28 11:12:57 +08:00
committed by GitHub
parent 6ec82c7a83
commit 7f550d6dd2
15 changed files with 314 additions and 266 deletions
+238 -191
View File
@@ -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()
}
}