Newer
Older
fn force_new_era(origin) {
ensure_root(origin)?;
}
/// Set the offline slash grace period.
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
fn set_offline_slash_grace(origin, #[compact] new: u32) {
ensure_root(origin)?;
OfflineSlashGrace::put(new);
/// Set the validators who cannot be slashed (if any).
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
fn set_invulnerables(origin, validators: Vec<T::AccountId>) {
ensure_root(origin)?;
<Invulnerables<T>>::put(validators);
}
}
impl<T: Trait> Module<T> {
// PUBLIC IMMUTABLES
/// The total balance that can be slashed from a validator controller account as of
/// right now.
pub fn slashable_balance(who: &T::AccountId) -> BalanceOf<T> {
// MUTABLES (DANGEROUS)
/// Update the ledger for a controller. This will also update the stash lock. The lock will
/// will lock the entire funds except paying for further transactions.
fn update_ledger(
controller: &T::AccountId,
ledger: &StakingLedger<T::AccountId, BalanceOf<T>>
) {
T::Currency::set_lock(
STAKING_ID,
&ledger.stash,
ledger.total,
T::BlockNumber::max_value(),
WithdrawReasons::except(WithdrawReason::TransactionPayment),
<Ledger<T>>::insert(controller, ledger);
}
/// Slash a given validator by a specific amount. Removes the slash from the validator's
/// balance by preference, and reduces the nominators' balance if needed.
fn slash_validator(stash: &T::AccountId, slash: BalanceOf<T>) {
// The exposure (backing stake) information of the validator to be slashed.
let exposure = Self::stakers(stash);
// The amount we are actually going to slash (can't be bigger than the validator's total
// exposure)
let slash = slash.min(exposure.total);
// The amount we'll slash from the validator's stash directly.
let own_slash = exposure.own.min(slash);
let (mut imbalance, missing) = T::Currency::slash(stash, own_slash);
let own_slash = own_slash - missing;
// The amount remaining that we can't slash from the validator, that must be taken from the
// nominators.
let rest_slash = slash - own_slash;
if !rest_slash.is_zero() {
// The total to be slashed from the nominators.
let total = exposure.total - exposure.own;
for i in exposure.others.iter() {
let per_u64 = Perbill::from_rational_approximation(i.value, total);
// best effort - not much that can be done on fail.
imbalance.subsume(T::Currency::slash(&i.who, per_u64 * rest_slash).0)
T::Slash::on_unbalanced(imbalance);
/// Actually make a payment to a staker. This uses the currency's reward function
/// to pay the right payee for the given staker account.
fn make_payout(stash: &T::AccountId, amount: BalanceOf<T>) -> Option<PositiveImbalanceOf<T>> {
let dest = Self::payee(stash);
match dest {
RewardDestination::Controller => Self::bonded(stash)
.and_then(|controller|
T::Currency::deposit_into_existing(&controller, amount).ok()
),
RewardDestination::Stash =>
T::Currency::deposit_into_existing(stash, amount).ok(),
RewardDestination::Staked => Self::bonded(stash)
.and_then(|c| Self::ledger(&c).map(|l| (c, l)))
.and_then(|(controller, mut l)| {
l.active += amount;
l.total += amount;
let r = T::Currency::deposit_into_existing(stash, amount).ok();
Self::update_ledger(&controller, &l);
/// Reward a given validator by a specific amount. Add the reward to the validator's, and its
/// nominators' balance, pro-rata based on their exposure, after having removed the validator's
/// pre-payout cut.
fn reward_validator(stash: &T::AccountId, reward: BalanceOf<T>) -> PositiveImbalanceOf<T> {
let off_the_table = reward.min(Self::validators(stash).validator_payment);
let reward = reward - off_the_table;
let mut imbalance = <PositiveImbalanceOf<T>>::zero();
let validator_cut = if reward.is_zero() {
Zero::zero()
} else {
let exposure = Self::stakers(stash);
let total = exposure.total.max(One::one());
let per_u64 = Perbill::from_rational_approximation(i.value, total);
imbalance.maybe_subsume(Self::make_payout(&i.who, per_u64 * reward));
let per_u64 = Perbill::from_rational_approximation(exposure.own, total);
per_u64 * reward
imbalance.maybe_subsume(Self::make_payout(stash, validator_cut + off_the_table));
/// Session has just ended. Provide the validator set for the next session if it's an era-end, along
/// with the exposure of the prior validator set.
fn new_session(session_index: SessionIndex)
-> Option<(Vec<T::AccountId>, Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>)>
{
if ForceNewEra::take() || session_index % T::SessionsPerEra::get() == 0 {
let validators = T::SessionInterface::validators();
let prior = validators.into_iter()
.map(|v| { let e = Self::stakers(&v); (v, e) })
.collect();
Self::new_era(session_index).map(move |new| (new, prior))
}
}
/// The era has changed - enact new staking set.
///
/// NOTE: This always happens immediately before a session change to ensure that new validators
/// get a chance to set their session keys.
fn new_era(start_session_index: SessionIndex) -> Option<Vec<T::AccountId>> {
let rewards = CurrentEraRewards::take();
let now = T::Time::now();
let previous_era_start = <CurrentEraStart<T>>::mutate(|v| {
rstd::mem::replace(v, now.clone())
});
let era_duration = now - previous_era_start;
if !era_duration.is_zero() {
let validators = Self::current_elected();
let validator_len: BalanceOf<T> = (validators.len() as u32).into();
let total_rewarded_stake = Self::slot_stake() * validator_len;
let total_payout = inflation::compute_total_payout(
total_rewarded_stake.clone(),
T::Currency::total_issuance(),
// Era of duration more than u32::MAX is rewarded as u32::MAX.
<BalanceOf<T>>::from(era_duration.saturated_into::<u32>()),
);
let mut total_imbalance = <PositiveImbalanceOf<T>>::zero();
let total_points = rewards.total;
for (v, points) in validators.iter().zip(rewards.rewards.into_iter()) {
if points != 0 {
let reward = multiply_by_rational(total_payout, points, total_points);
total_imbalance.subsume(Self::reward_validator(v, reward));
}
let total_reward = total_imbalance.peek();
Self::deposit_event(RawEvent::Reward(total_reward));
T::Reward::on_unbalanced(total_imbalance);
T::OnRewardMinted::on_dilution(total_reward, total_rewarded_stake);
let current_era = CurrentEra::mutate(|s| { *s += 1; *s });
CurrentEraStartSessionIndex::mutate(|v| {
*v = start_session_index;
});
let bonding_duration = T::BondingDuration::get();
if current_era > bonding_duration {
let first_kept = current_era - bonding_duration;
BondedEras::mutate(|bonded| {
bonded.push((current_era, start_session_index));
// prune out everything that's from before the first-kept index.
let n_to_prune = bonded.iter()
.take_while(|&&(era_idx, _)| era_idx < first_kept)
.count();
bonded.drain(..n_to_prune);
if let Some(&(_, first_session)) = bonded.first() {
T::SessionInterface::prune_historical_up_to(first_session);
}
})
}
let (_slot_stake, maybe_new_validators) = Self::select_validators();
fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> {
Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default()
/// Select a new validator set from the assembled stakers and their role preferences.
///
/// Returns the new `SlotStake` value and a set of newly selected _stash_ IDs.
fn select_validators() -> (BalanceOf<T>, Option<Vec<T::AccountId>>) {
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,
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 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)| {
let item = Exposure { own: s, total: s, ..Default::default() };
exposures.insert(e.clone(), item);
});
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 } );
}
}
}
if cfg!(feature = "equalize") {
let tolerance = 0_u128;
let iterations = 2_usize;
let mut assignments_with_votes = assignments_with_stakes.iter()
.map(|a| (
a.0.clone(), a.1,
a.2.iter()
.map(|e| (e.0.clone(), e.1, to_votes(e.2)))
.collect::<Vec<(T::AccountId, ExtendedBalance, ExtendedBalance)>>()
))
.collect::<Vec<(
T::AccountId,
BalanceOf<T>,
Vec<(T::AccountId, ExtendedBalance, ExtendedBalance)>
)>>();
equalize::<T>(&mut assignments_with_votes, &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 in sessions.
<CurrentElected<T>>::put(&elected_stashes);
(slot_stake, Some(elected_stashes))
} 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.
ForceNewEra::put(true);
/// Remove all associated data of a stash account from the staking system.
///
/// This is called :
/// - Immediately when an account's balance falls below existential deposit.
/// - after a `withdraw_unbond()` call that frees all of a stash's bonded balance.
fn kill_stash(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);
}
/// 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);
});
}
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);
let _ = T::SessionInterface::disable_validator(&stash);
RawEvent::OfflineSlash(stash.clone(), slash)
} else {
RawEvent::OfflineWarning(stash.clone(), slash_count)
};
Self::deposit_event(event);
}
/// Add reward points to validators using their stash account ID.
///
/// Validators are keyed by stash account ID and must be in the current elected set.
///
/// For each element in the iterator the given number of points in u32 is added to the
/// validator, thus duplicates are handled.
///
/// At the end of the era each the total payout will be distributed among validator
/// relatively to their points.
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
///
/// COMPLEXITY: Complexity is `number_of_validator_to_reward x current_elected_len`.
/// If you need to reward lots of validator consider using `reward_by_indices`.
pub fn reward_by_ids(validators_points: impl IntoIterator<Item = (T::AccountId, u32)>) {
CurrentEraRewards::mutate(|rewards| {
let current_elected = <Module<T>>::current_elected();
for (validator, points) in validators_points.into_iter() {
if let Some(index) = current_elected.iter()
.position(|elected| *elected == validator)
{
rewards.add_points_to_index(index as u32, points);
}
}
});
}
/// Add reward points to validators using their validator index.
///
/// For each element in the iterator the given number of points in u32 is added to the
/// validator, thus duplicates are handled.
pub fn reward_by_indices(validators_points: impl IntoIterator<Item = (u32, u32)>) {
// TODO: This can be optimised once #3302 is implemented.
let current_elected_len = <Module<T>>::current_elected().len() as u32;
CurrentEraRewards::mutate(|rewards| {
for (validator_index, points) in validators_points.into_iter() {
if validator_index < current_elected_len {
rewards.add_points_to_index(validator_index, points);
}
}
});
impl<T: Trait> session::OnSessionEnding<T::AccountId> for Module<T> {
fn on_session_ending(_ending: SessionIndex, start_session: SessionIndex) -> Option<Vec<T::AccountId>> {
Self::new_session(start_session - 1).map(|(new, _old)| new)
}
}
impl<T: Trait> OnSessionEnding<T::AccountId, Exposure<T::AccountId, BalanceOf<T>>> for Module<T> {
fn on_session_ending(_ending: SessionIndex, start_session: SessionIndex)
-> Option<(Vec<T::AccountId>, Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>)>
{
Self::new_session(start_session - 1)
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(stash: &T::AccountId) {
Self::kill_stash(stash);
/// 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: Trait + authorship::Trait> authorship::EventHandler<T::AccountId, T::BlockNumber> for Module<T> {
fn note_author(author: T::AccountId) {
}
fn note_uncle(author: T::AccountId, _age: T::BlockNumber) {
Self::reward_by_ids(vec![
(<authorship::Module<T>>::author(), 2),
(author, 1)
])
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
}
}
// This is guarantee not to overflow on whatever values.
// `num` must be inferior to `den` otherwise it will be reduce to `den`.
fn multiply_by_rational<N>(value: N, num: u32, den: u32) -> N
where N: SimpleArithmetic + Clone
{
let num = num.min(den);
let result_divisor_part = value.clone() / den.into() * num.into();
let result_remainder_part = {
let rem = value % den.into();
// Fits into u32 because den is u32 and remainder < den
let rem_u32 = rem.saturated_into::<u32>();
// Multiplication fits into u64 as both term are u32
let rem_part = rem_u32 as u64 * num as u64 / den as u64;
// Result fits into u32 as num < total_points
(rem_part as u32).into()
};
result_divisor_part + result_remainder_part
}
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
/// A `Convert` implementation that finds the stash of the given controller account,
/// if any.
pub struct StashOf<T>(rstd::marker::PhantomData<T>);
impl<T: Trait> Convert<T::AccountId, Option<T::AccountId>> for StashOf<T> {
fn convert(controller: T::AccountId) -> Option<T::AccountId> {
<Module<T>>::ledger(&controller).map(|l| l.stash)
}
}
/// A typed conversion from stash account ID to the current exposure of nominators
/// on that account.
pub struct ExposureOf<T>(rstd::marker::PhantomData<T>);
impl<T: Trait> Convert<T::AccountId, Option<Exposure<T::AccountId, BalanceOf<T>>>>
for ExposureOf<T>
{
fn convert(validator: T::AccountId) -> Option<Exposure<T::AccountId, BalanceOf<T>>> {
Some(<Module<T>>::stakers(&validator))
}
}
impl<T: Trait> SelectInitialValidators<T::AccountId> for Module<T> {
fn select_initial_validators() -> Option<Vec<T::AccountId>> {
<Module<T>>::select_validators().1
}
}