// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
//! The session info pallet provides information about validator sets
//! from prior sessions needed for approvals and disputes.
//!
//! See .
use crate::{
configuration, paras, scheduler, shared,
util::{take_active_subset, take_active_subset_and_inactive},
};
use frame_support::{
pallet_prelude::*,
traits::{OneSessionHandler, ValidatorSet, ValidatorSetWithIdentification},
};
use primitives::{AssignmentId, AuthorityDiscoveryId, ExecutorParams, SessionIndex, SessionInfo};
use sp_std::vec::Vec;
pub use pallet::*;
pub mod migration;
#[cfg(test)]
mod tests;
/// A type for representing the validator account id in a session.
pub type AccountId = <::ValidatorSet as ValidatorSet<
::AccountId,
>>::ValidatorId;
/// A tuple of `(AccountId, Identification)` where `Identification`
/// is the full identification of `AccountId`.
pub type IdentificationTuple = (
AccountId,
<::ValidatorSet as ValidatorSetWithIdentification<
::AccountId,
>>::Identification,
);
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
#[pallet::storage_version(migration::STORAGE_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet(_);
#[pallet::config]
pub trait Config:
frame_system::Config
+ configuration::Config
+ shared::Config
+ paras::Config
+ scheduler::Config
+ AuthorityDiscoveryConfig
{
/// A type for retrieving `AccountId`s of the validators in the current session.
/// These are stash keys of the validators.
/// It's used for rewards and slashing. `Identification` is only needed for slashing.
type ValidatorSet: ValidatorSetWithIdentification;
}
/// Assignment keys for the current session.
/// Note that this API is private due to it being prone to 'off-by-one' at session boundaries.
/// When in doubt, use `Sessions` API instead.
#[pallet::storage]
pub(super) type AssignmentKeysUnsafe =
StorageValue<_, Vec, ValueQuery>;
/// The earliest session for which previous session info is stored.
#[pallet::storage]
#[pallet::getter(fn earliest_stored_session)]
pub(crate) type EarliestStoredSession = StorageValue<_, SessionIndex, ValueQuery>;
/// Session information in a rolling window.
/// Should have an entry in range `EarliestStoredSession..=CurrentSessionIndex`.
/// Does not have any entries before the session index in the first session change notification.
#[pallet::storage]
#[pallet::getter(fn session_info)]
pub(crate) type Sessions = StorageMap<_, Identity, SessionIndex, SessionInfo>;
/// The validator account keys of the validators actively participating in parachain consensus.
// We do not store this in `SessionInfo` to avoid leaking the `AccountId` type to the client,
// which would complicate the migration process if we are to change it in the future.
#[pallet::storage]
#[pallet::getter(fn account_keys)]
pub(crate) type AccountKeys =
StorageMap<_, Identity, SessionIndex, Vec>>;
/// Executor parameter set for a given session index
#[pallet::storage]
#[pallet::getter(fn session_executor_params)]
pub(crate) type SessionExecutorParams =
StorageMap<_, Identity, SessionIndex, ExecutorParams>;
}
/// An abstraction for the authority discovery pallet
/// to help with mock testing.
pub trait AuthorityDiscoveryConfig {
/// Retrieve authority identifiers of the current authority set in canonical ordering.
fn authorities() -> Vec;
}
impl AuthorityDiscoveryConfig for T {
fn authorities() -> Vec {
>::current_authorities().to_vec()
}
}
impl Pallet {
/// Handle an incoming session change.
pub(crate) fn initializer_on_new_session(
notification: &crate::initializer::SessionChangeNotification,
) {
let config = >::config();
let dispute_period = config.dispute_period;
let validators = notification.validators.clone().into();
let discovery_keys = ::authorities();
let assignment_keys = AssignmentKeysUnsafe::::get();
let active_set = >::active_validator_indices();
let validator_groups = >::validator_groups().into();
let n_cores = >::availability_cores().len() as u32;
let zeroth_delay_tranche_width = config.zeroth_delay_tranche_width;
let relay_vrf_modulo_samples = config.relay_vrf_modulo_samples;
let n_delay_tranches = config.n_delay_tranches;
let no_show_slots = config.no_show_slots;
let needed_approvals = config.needed_approvals;
let new_session_index = notification.session_index;
let random_seed = notification.random_seed;
let old_earliest_stored_session = EarliestStoredSession::::get();
let new_earliest_stored_session = new_session_index.saturating_sub(dispute_period);
let new_earliest_stored_session =
core::cmp::max(new_earliest_stored_session, old_earliest_stored_session);
// remove all entries from `Sessions` from the previous value up to the new value
// avoid a potentially heavy loop when introduced on a live chain
if old_earliest_stored_session != 0 || Sessions::::get(0).is_some() {
for idx in old_earliest_stored_session..new_earliest_stored_session {
Sessions::::remove(&idx);
// Idx will be missing for a few sessions after the runtime upgrade.
// But it shouldn'be be a problem.
AccountKeys::::remove(&idx);
SessionExecutorParams::::remove(&idx);
}
// update `EarliestStoredSession` based on `config.dispute_period`
EarliestStoredSession::::set(new_earliest_stored_session);
} else {
// just introduced on a live chain
EarliestStoredSession::::set(new_session_index);
}
// The validator set is guaranteed to be of the current session
// because we delay `on_new_session` till the end of the block.
let account_ids = T::ValidatorSet::validators();
let active_account_ids = take_active_subset(&active_set, &account_ids);
AccountKeys::::insert(&new_session_index, &active_account_ids);
// create a new entry in `Sessions` with information about the current session
let new_session_info = SessionInfo {
validators, // these are from the notification and are thus already correct.
discovery_keys: take_active_subset_and_inactive(&active_set, &discovery_keys),
assignment_keys: take_active_subset(&active_set, &assignment_keys),
validator_groups,
n_cores,
zeroth_delay_tranche_width,
relay_vrf_modulo_samples,
n_delay_tranches,
no_show_slots,
needed_approvals,
active_validator_indices: active_set,
random_seed,
dispute_period,
};
Sessions::::insert(&new_session_index, &new_session_info);
SessionExecutorParams::::insert(&new_session_index, config.executor_params);
}
/// Called by the initializer to initialize the session info pallet.
pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight {
Weight::zero()
}
/// Called by the initializer to finalize the session info pallet.
pub(crate) fn initializer_finalize() {}
}
impl sp_runtime::BoundToRuntimeAppPublic for Pallet {
type Public = AssignmentId;
}
impl OneSessionHandler for Pallet {
type Key = AssignmentId;
fn on_genesis_session<'a, I: 'a>(_validators: I)
where
I: Iterator- ,
{
}
fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, _queued: I)
where
I: Iterator
- ,
{
let assignment_keys: Vec<_> = validators.map(|(_, v)| v).collect();
AssignmentKeysUnsafe::::set(assignment_keys);
}
fn on_disabled(_i: u32) {}
}