Newer
Older
/// get a chance to set their session keys.
fn new_era() {
// Payout
let reward = <CurrentEraReward<T>>::take();
if !reward.is_zero() {
let validators = Self::current_elected();
for v in validators.iter() {
Self::reward_validator(v, reward);
}
Self::deposit_event(RawEvent::Reward(reward));
let len = validators.len() as u32; // validators length can never overflow u64
let len: BalanceOf<T> = len.into();
let total_minted = reward * len;
let total_rewarded_stake = Self::slot_stake() * len;
T::OnRewardMinted::on_dilution(total_minted, total_rewarded_stake);
}
// Increment current era.
<CurrentEra<T>>::put(&(<CurrentEra<T>>::get() + One::one()));
// Enact era length change.
if let Some(next_spe) = Self::next_sessions_per_era() {
if next_spe != Self::sessions_per_era() {
<SessionsPerEra<T>>::put(&next_spe);
<LastEraLengthChange<T>>::put(&<session::Module<T>>::current_index());
let slot_stake = Self::select_validators();
// Update the balances for rewarding according to the stakes.
<CurrentSessionReward<T>>::put(Self::session_reward() * slot_stake);
}
fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> {
Self::bonded(stash).and_then(Self::ledger).map(|l| l.total).unwrap_or_default()
}
/// Select a new validator set from the assembled stakers and their role preferences.
///
fn select_validators() -> BalanceOf<T> {
let maybe_elected_set = elect::<T, _, _, _>(
Self::validator_count() as usize,
Self::minimum_validator_count().max(1) as usize,
<Validators<T>>::enumerate(),
<Nominators<T>>::enumerate(),
Self::slashable_balance_of,
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
if let Some(elected_set) = maybe_elected_set {
let elected_stashes = elected_set.0;
let assignments = elected_set.1;
// helper closure.
let to_balance = |b: ExtendedBalance| <T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(b);
let to_votes = |b: BalanceOf<T>| <T::CurrencyToVote as Convert<BalanceOf<T>, u64>>::convert(b) as ExtendedBalance;
// The return value of this is safe to be converted to u64.
// The original balance, `b` is within the scope of u64. It is just extended to u128
// to be properly multiplied by a ratio, which will lead to another value
// less than u64 for sure. The result can then be safely passed to `to_balance`.
// For now the backward convert is used. A simple `TryFrom<u64>` is also safe.
let ratio_of = |b, p| (p as ExtendedBalance).saturating_mul(to_votes(b)) / ACCURACY;
// Compute the actual stake from nominator's ratio.
let mut assignments_with_stakes = assignments.iter().map(|(n, a)|(
n.clone(),
Self::slashable_balance_of(n),
a.iter().map(|(acc, r)| (
acc.clone(),
*r,
to_balance(ratio_of(Self::slashable_balance_of(n), *r)),
))
.collect::<Vec<Assignment<T>>>()
)).collect::<Vec<(T::AccountId, BalanceOf<T>, Vec<Assignment<T>>)>>();
// update elected candidate exposures.
let mut exposures = <ExpoMap<T>>::new();
elected_stashes
.iter()
.map(|e| (e, Self::slashable_balance_of(e)))
.for_each(|(e, s)| {
exposures.insert(e.clone(), Exposure { own: s, total: s, ..Default::default() });
});
for (n, _, assignment) in &assignments_with_stakes {
for (c, _, s) in assignment {
if let Some(expo) = exposures.get_mut(c) {
// NOTE: simple example where this saturates:
// candidate with max_value stake. 1 nominator with max_value stake.
// Nuked. Sadly there is not much that we can do about this.
// See this test: phragmen_should_not_overflow_xxx()
expo.total = expo.total.saturating_add(*s);
expo.others.push( IndividualExposure { who: n.clone(), value: *s } );
}
}
}
// This optimization will most likely be only applied off-chain.
let do_equalise = false;
if do_equalise {
let tolerance = 10 as u128;
let iterations = 10 as usize;
phragmen::equalize::<T>(&mut assignments_with_stakes, &mut exposures, tolerance, iterations);
}
// Clear Stakers and reduce their slash_count.
for v in Self::current_elected().iter() {
<Stakers<T>>::remove(v);
let slash_count = <SlashCount<T>>::take(v);
if slash_count > 1 {
<SlashCount<T>>::insert(v, slash_count - 1);
}
// Populate Stakers and figure out the minimum stake behind a slot.
let mut slot_stake = BalanceOf::<T>::max_value();
for (c, e) in exposures.iter() {
if e.total < slot_stake {
slot_stake = e.total;
<Stakers<T>>::insert(c.clone(), e.clone());
}
<SlotStake<T>>::put(&slot_stake);
// Set the new validator set.
<CurrentElected<T>>::put(&elected_stashes);
<session::Module<T>>::set_validators(
&elected_stashes.into_iter().map(|s| Self::bonded(s).unwrap_or_default()).collect::<Vec<_>>()
);
slot_stake
} else {
// There were not enough candidates for even our minimal level of functionality.
// This is bad.
// We should probably disable all functionality except for block production
// and let the chain keep producing blocks until we can decide on a sufficiently
// substantial set.
/// Call when a validator is determined to be offline. `count` is the
/// number of offenses the validator has committed.
///
/// NOTE: This is called with the controller (not the stash) account id.
pub fn on_offline_validator(controller: T::AccountId, count: usize) {
if let Some(l) = Self::ledger(&controller) {
let stash = l.stash;
// Early exit if validator is invulnerable.
if Self::invulnerables().contains(&stash) {
return
}
let slash_count = Self::slash_count(&stash);
let new_slash_count = slash_count + count as u32;
<SlashCount<T>>::insert(&stash, new_slash_count);
let grace = Self::offline_slash_grace();
if RECENT_OFFLINE_COUNT > 0 {
let item = (stash.clone(), <system::Module<T>>::block_number(), count as u32);
<RecentlyOffline<T>>::mutate(|v| if v.len() >= RECENT_OFFLINE_COUNT {
let index = v.iter()
.enumerate()
.min_by_key(|(_, (_, block, _))| block)
.expect("v is non-empty; qed")
.0;
v[index] = item;
} else {
v.push(item);
});
}
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
let prefs = Self::validators(&stash);
let unstake_threshold = prefs.unstake_threshold.min(MAX_UNSTAKE_THRESHOLD);
let max_slashes = grace + unstake_threshold;
let event = if new_slash_count > max_slashes {
let slash_exposure = Self::stakers(&stash).total;
let offline_slash_base = Self::offline_slash() * slash_exposure;
// They're bailing.
let slash = offline_slash_base
// Multiply slash_mantissa by 2^(unstake_threshold with upper bound)
.checked_shl(unstake_threshold)
.map(|x| x.min(slash_exposure))
.unwrap_or(slash_exposure);
let _ = Self::slash_validator(&stash, slash);
<Validators<T>>::remove(&stash);
let _ = Self::apply_force_new_era(false);
RawEvent::OfflineSlash(stash.clone(), slash)
} else {
RawEvent::OfflineWarning(stash.clone(), slash_count)
};
Self::deposit_event(event);
}
impl<T: Trait> OnSessionChange<T::Moment> for Module<T> {
fn on_session_change(elapsed: T::Moment, should_reward: bool) {
Self::new_session(elapsed, should_reward);
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(stash: &T::AccountId) {
if let Some(controller) = <Bonded<T>>::take(stash) {
<Ledger<T>>::remove(&controller);
}
<Payee<T>>::remove(stash);
<SlashCount<T>>::remove(stash);
<Validators<T>>::remove(stash);
<Nominators<T>>::remove(stash);
impl<T: Trait> consensus::OnOfflineReport<Vec<u32>> for Module<T> {
fn handle_report(reported_indices: Vec<u32>) {
for validator_index in reported_indices {
let v = <session::Module<T>>::validators()[validator_index as usize].clone();
Self::on_offline_validator(v, 1);
}