Skip to content

Commit

Permalink
Hooks & Slash functionality update
Browse files Browse the repository at this point in the history
Signed-off-by: R.Rajeshkumar <rajesh@nodle.com>
  • Loading branch information
R.Rajeshkumar committed Jun 20, 2022
1 parent 1f7d3a1 commit 4a9a724
Show file tree
Hide file tree
Showing 7 changed files with 2,149 additions and 6,083 deletions.
280 changes: 279 additions & 1 deletion pallets/staking/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ use super::{
SessionValidatorReward, SlashRewardProportion, Staked, Store, Total,
};
// use crate::slashing;
use crate::types::{ValidatorSnapshot, ValidatorSnapshotOf};
use crate::{
slashing::SlashParams,
types::{ValidatorSnapshot, ValidatorSnapshotOf},
};
use frame_support::{
pallet_prelude::*,
traits::{Currency, Get, Imbalance, OnUnbalanced},
Expand Down Expand Up @@ -57,6 +60,135 @@ impl<T: Config> OnUnbalanced<NegativeImbalanceOf<T>> for Pallet<T> {
}
}

/// Add reward points to block authors:
/// * 20 points to the block producer for producing a (non-uncle) block in the relay chain,
/// * 2 points to the block producer for each reference to a previously unreferenced uncle, and
/// * 1 point to the producer of each referenced uncle block.
impl<T> pallet_authorship::EventHandler<T::AccountId, T::BlockNumber> for Pallet<T>
where
T: Config + pallet_authorship::Config + pallet_session::Config,
{
fn note_author(author: T::AccountId) {
log::trace!("note_author:[{:#?}] - Author[{:#?}]", line!(), author);
Self::reward_by_ids(vec![(author, 20)])
}
fn note_uncle(uncle_author: T::AccountId, _age: T::BlockNumber) {
log::trace!("note_uncle:[{:#?}] - uncle_author[{:#?}]", line!(), uncle_author);
if let Some(block_author) = <pallet_authorship::Pallet<T>>::author() {
Self::reward_by_ids(vec![(block_author, 2), (uncle_author, 1)])
} else {
log::error!("block author not set, this should never happen");
}
}
}
/// In this implementation `new_session(session)` must be called before `end_session(session-1)`
/// i.e. the new session must be planned before the ending of the previous session.
///
/// Once the first new_session is planned, all session must start and then end in order.
impl<T: Config> pallet_session::SessionManager<T::AccountId> for Pallet<T> {
fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
log::trace!("new_session:[{:#?}] - Sess-idx[{:#?}]", line!(), new_index);

let current_block_number = system::Pallet::<T>::block_number();

// select top collator validators for next round
let (validator_count, total_staked) = match Self::select_session_validators(new_index) {
Ok((validator_count, total_staked)) => (validator_count, total_staked),
Err(_) => return None,
};

// snapshot total stake
<Staked<T>>::insert(new_index, <Total<T>>::get());

Self::deposit_event(Event::NewSession(
current_block_number,
new_index,
validator_count,
total_staked,
));

log::debug!(
"new_session:[{:#?}] - Event::NewSession(SI[{}],VC[{}],TS[{:#?}])",
line!(),
new_index,
validator_count,
total_staked,
);

Some(Self::selected_validators().to_vec())
}
fn start_session(start_index: SessionIndex) {
log::trace!("start_session:[{:#?}] - Sess-idx[{:#?}]", line!(), start_index);

<ActiveSession<T>>::put(start_index);

let bonding_duration = T::BondedDuration::get();

<BondedSessions<T>>::mutate(|bonded| {
let _ = match bonded.try_push(start_index) {
Err(_) => {
log::error!(
"start_session:[{:#?}] - Error, Could be BondedSessions Overflow",
line!()
);
return ();
}
Ok(_) => (),
};

if start_index > bonding_duration {
let first_kept = start_index - bonding_duration;

// prune out everything that's from before the first-kept index.
let n_to_prune = bonded
.to_vec()
.iter()
.take_while(|&&session_idx| session_idx < first_kept)
.count();

for prune_session in bonded.to_vec().drain(..n_to_prune) {
// Clear the DB cached state of last session
Self::clear_session_information(prune_session);
}

if let Some(&first_session) = bonded.first() {
T::SessionInterface::prune_historical_up_to(first_session);
}
}
});

// execute all delayed validator exits
Self::execute_delayed_validator_exits(start_index);

// Handle the unapplied deferd slashes
Self::apply_unapplied_slashes(start_index);

log::trace!("start_session:[{:#?}] - Exit!!! Sess-idx[{:#?}]", line!(), start_index);
}
fn end_session(end_index: SessionIndex) {
log::trace!("end_session:[{:#?}] - Sess-idx[{:#?}]", line!(), end_index);

if Self::active_session() == end_index {
let payout = Self::session_accumulated_balance(end_index);

// Set ending session reward.
<SessionValidatorReward<T>>::insert(&end_index, payout);

// pay all stakers for T::BondedDuration rounds ago
Self::pay_stakers(end_index);

// Clear the DB cached state of last session
Self::clear_session_information(Self::active_session());
} else {
log::error!(
"end_session:[{:#?}] - Something wrong (CSI[{}], ESI[{}])",
line!(),
Self::active_session(),
end_index,
);
}
}
}
/// Means for interacting with a specialized version of the `session` trait.
///
/// This is needed because `Staking` sets the `ValidatorIdOf` of the `pallet_session::Config`
Expand Down Expand Up @@ -99,3 +231,149 @@ where
<pallet_session::historical::Pallet<T>>::prune_up_to(up_to);
}
}

impl<T: Config> historical::SessionManager<T::AccountId, ValidatorSnapshot<T, T::MaxNominatorsPerValidator>>
for Pallet<T>
{
fn new_session(
new_index: SessionIndex,
) -> Option<Vec<(T::AccountId, ValidatorSnapshot<T, T::MaxNominatorsPerValidator>)>> {
<Self as pallet_session::SessionManager<_>>::new_session(new_index).map(|validators| {
validators
.into_iter()
.map(|v| {
let validator_inst = Self::at_stake(new_index, &v);
(v, validator_inst)
})
.collect()
})
}
fn start_session(start_index: SessionIndex) {
<Self as pallet_session::SessionManager<_>>::start_session(start_index)
}
fn end_session(end_index: SessionIndex) {
<Self as pallet_session::SessionManager<_>>::end_session(end_index)
}
}

/// This is intended to be used with `FilterHistoricalOffences`.
impl<T: Config> OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>, Weight> for Pallet<T>
where
T: pallet_session::Config<ValidatorId = <T as frame_system::Config>::AccountId>,
T: pallet_session::historical::Config<
FullIdentification = ValidatorSnapshot<T, T::MaxNominatorsPerValidator>,
FullIdentificationOf = ValidatorSnapshotOf<T, T::MaxNominatorsPerValidator>,
>,
T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Config>::AccountId>,
T::SessionManager: pallet_session::SessionManager<<T as frame_system::Config>::AccountId>,
T::ValidatorIdOf: Convert<<T as frame_system::Config>::AccountId, Option<<T as frame_system::Config>::AccountId>>,
{
fn on_offence(
offenders: &[OffenceDetails<T::AccountId, pallet_session::historical::IdentificationTuple<T>>],
slash_fraction: &[Perbill],
slash_session: SessionIndex,
disable_strategy: DisableStrategy,
) -> Weight {
log::trace!(
"on_offence:[{:#?}] - Sess-idx [{:#?}] | Slash-Frac [{:#?}]",
line!(),
slash_session,
slash_fraction,
);

let reward_proportion = <SlashRewardProportion<T>>::get();
let mut consumed_weight: Weight = 0;
let mut add_db_reads_writes = |reads, writes| {
consumed_weight = consumed_weight.saturating_add(T::DbWeight::get().reads_writes(reads, writes));
};

let active_session = Self::active_session();
add_db_reads_writes(1, 0);

let window_start = active_session.saturating_sub(T::BondedDuration::get());
let slash_defer_duration = T::SlashDeferDuration::get();

let invulnerables = Self::invulnerables();
add_db_reads_writes(1, 0);

log::trace!("on_offence:[{:#?}] - Invulnerables[{:#?}]", line!(), invulnerables,);

for (details, slash_fraction) in offenders.iter().zip(slash_fraction) {
let (controller, exposure) = &details.offender;

// Skip if the validator is invulnerable.
if invulnerables.contains(controller) {
continue;
}

let slash_param: SlashParams<T, T::MaxNominatorsPerValidator> = SlashParams {
controller: controller.clone(),
slash: *slash_fraction,
exposure: exposure.clone(),
slash_session,
window_start,
now: active_session,
reward_proportion,
disable_strategy,
};

match slash_param.compute_slash() {
Err(err) => {
log::error!("on_offence:[{:#?}] - compute_slash Err[{:#?}]", line!(), err,);
}
Ok(None) => {
log::trace!("on_offence:[{:#?}] - NOP", line!(),);
add_db_reads_writes(4 /* fetch_spans */, 5 /* kick_out_if_recent */);
}
Ok(Some(mut unapplied)) => {
let nominators_len = unapplied.others.len() as u64;
let reporters_len = details.reporters.to_vec().len() as u64;

{
let upper_bound = 1 /* Validator/NominatorSlashInEra */ + 2 /* fetch_spans */;
let rw = upper_bound + nominators_len * upper_bound;
add_db_reads_writes(rw, rw);
}

unapplied.reporters =
<BoundedVec<T::AccountId, T::MaxSlashReporters>>::try_from(details.reporters.clone())
.expect("OnOffenceHandler Reporters Overflow Error");

if slash_defer_duration == 0 {
// apply right away.
unapplied.apply_slash();

let slash_cost = (6, 5);
let reward_cost = (2, 2);
add_db_reads_writes(
(1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len,
(1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len,
);
} else {
// defer to end of some `slash_defer_duration` from now.
let apply_at = active_session.saturating_add(slash_defer_duration);

let unapplied_for_event = unapplied.clone();

<Self as Store>::UnappliedSlashes::mutate(apply_at, move |for_later| {
match for_later.try_push(unapplied) {
Err(_) => {
log::error!("on_offence:[{:#?}] - UnappliedSlashes Overflow", line!());
}
Ok(_) => {}
}
});

<Pallet<T>>::deposit_event(Event::DeferredUnappliedSlash(
active_session,
unapplied_for_event.validator,
));

add_db_reads_writes(1, 1);
}
}
}
}
consumed_weight
}
}
Loading

0 comments on commit 4a9a724

Please sign in to comment.