1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
use frame_system::pallet_prelude::BlockNumberFor;
use log::debug;
use pallet_session::SessionManager;
use core_primitives::{EraManager, FinalityCommitteeManager, SessionCommittee};
use sp_staking::{EraIndex, SessionIndex};
use sp_std::{marker::PhantomData, vec::Vec};
use crate::{
pallet::{Config, Pallet, SessionValidatorBlockCount},
traits::EraInfoProvider,
LOG_TARGET,
};
/// We assume that block `B` ends session nr `S`, and current era index is `E`.
///
/// 1. Block `B` initialized
/// 2. `End_session(S)` is called
/// * Based on block count we might mark the session for a given validator as underperformed
/// * We update rewards and clear block count for the session `S`.
/// 3. `Start_session(S + 1)` is called.
/// * If session `S+1` starts a new era,
/// we populate totals and unban all validators whose ban expired.
/// * If session `S+1` % `clean_session_counter_delay`
/// == 0, we clean up underperformed session counter.
/// * `Clean_session_counter_delay` is read from pallet's storage
/// 4. `New_session(S + 2)` is called.
/// * If session `S+2` starts a new era, we emit fresh bans events
/// * We rotate the validators for session`S + 2`
/// using the information about reserved and non-reserved validators.
impl<T> pallet_authorship::EventHandler<T::AccountId, BlockNumberFor<T>> for Pallet<T>
where
T: Config,
{
fn note_author(validator: T::AccountId) {
SessionValidatorBlockCount::<T>::mutate(&validator, |count| {
*count = count.saturating_add(1);
});
}
}
/// SessionManager that also fires EraManager functions. It is responsible for rotation of the committee,
/// bans and rewards logic.
///
/// The order of the calls is as follows:
/// First call is always from the inner SessionManager then the call to EraManager fn if applicable.
/// * New session is planned:
/// 1. Inner T::new_session invoked
/// 2. If session starts era EM::on_new_era invoked
/// 3. Logic related to new session from this pallet is invoked
/// * Session ends:
/// 1. Inner T::end_session invoked
/// 2. Logic related to new session from this pallet is invoked
/// * Session starts:
/// 1. Inner T::start_session invoked
/// 2. Logic related to the new session from this pallet is invoked
/// 3. If session starts era EM::new_era_start invoked
/// 4. If session starts era logic related to new era from this pallet is invoked
///
/// In the runtime we set EM to pallet_elections and T to combination of staking and historical_session.
pub struct SessionAndEraManager<E, EM, T, C>(PhantomData<(E, EM, T, C)>)
where
E: EraInfoProvider,
EM: EraManager,
T: SessionManager<C::AccountId>,
C: Config;
impl<E, EM, T, C> SessionAndEraManager<E, EM, T, C>
where
E: EraInfoProvider,
EM: EraManager,
T: SessionManager<C::AccountId>,
C: Config,
{
fn session_starts_era(session: SessionIndex) -> Option<EraIndex> {
let active_era = match E::active_era() {
Some(ae) => ae,
// no active era, session can't start it
_ => return None,
};
if Self::is_start_of_the_era(active_era, session) {
return Some(active_era);
}
None
}
fn session_starts_next_era(session: SessionIndex) -> Option<EraIndex> {
let active_era = match E::active_era() {
Some(ae) => ae.saturating_add(1),
// no active era, session can't start it
_ => return None,
};
if Self::is_start_of_the_era(active_era, session) {
return Some(active_era);
}
None
}
fn is_start_of_the_era(era: EraIndex, session: SessionIndex) -> bool {
if let Some(era_start_index) = E::era_start_session_index(era) {
return era_start_index == session;
}
false
}
}
impl<E, EM, T, C> SessionManager<C::AccountId> for SessionAndEraManager<E, EM, T, C>
where
E: EraInfoProvider,
EM: EraManager,
T: SessionManager<C::AccountId>,
C: Config,
{
fn new_session(new_index: SessionIndex) -> Option<Vec<C::AccountId>> {
T::new_session(new_index);
if let Some(era) = Self::session_starts_next_era(new_index) {
EM::on_new_era(era);
Pallet::<C>::emit_fresh_bans_event();
}
let SessionCommittee {
finality_committee,
block_producers,
} = Pallet::<C>::rotate_committee(new_index)?;
// Notify about elected next session finality committee
C::FinalityCommitteeManager::on_next_session_finality_committee(finality_committee);
Some(block_producers)
}
fn end_session(end_index: SessionIndex) {
T::end_session(end_index);
Pallet::<C>::adjust_rewards_for_session();
Pallet::<C>::calculate_underperforming_validators();
// clear block count after calculating stats for underperforming validators, as they use
// SessionValidatorBlockCount for that
let result = SessionValidatorBlockCount::<C>::clear(u32::MAX, None);
debug!(
target: LOG_TARGET,
"Result of clearing the `SessionValidatorBlockCount`, {:?}",
result.deconstruct()
);
}
fn start_session(start_index: SessionIndex) {
T::start_session(start_index);
Pallet::<C>::clear_underperformance_session_counter(start_index);
if let Some(era) = Self::session_starts_era(start_index) {
Pallet::<C>::update_validator_total_rewards(era);
Pallet::<C>::clear_expired_bans(era);
}
}
}