Newer
Older
ensure!(
Self::era_election_status().is_closed() || Self::payee(&nominator_ledger.stash) != RewardDestination::Staked,
Error::<T>::CallNotAllowed,
);
nominator_ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth));
match nominator_ledger.claimed_rewards.binary_search(&era) {
Ok(_) => Err(Error::<T>::AlreadyClaimed)?,
Err(pos) => nominator_ledger.claimed_rewards.insert(pos, era),
<Ledger<T>>::insert(&ctrl, &nominator_ledger);
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
let mut reward = Perbill::zero();
let era_reward_points = <ErasRewardPoints<T>>::get(&era);
for (validator, nominator_index) in validators.into_iter() {
let commission = Self::eras_validator_prefs(&era, &validator).commission;
let validator_exposure = <ErasStakersClipped<T>>::get(&era, &validator);
if let Some(nominator_exposure) = validator_exposure.others
.get(nominator_index as usize)
{
if nominator_exposure.who != nominator_ledger.stash {
continue;
}
let nominator_exposure_part = Perbill::from_rational_approximation(
nominator_exposure.value,
validator_exposure.total,
);
let validator_point = era_reward_points.individual.get(&validator)
.map(|points| *points)
.unwrap_or_else(|| Zero::zero());
let validator_point_part = Perbill::from_rational_approximation(
validator_point,
era_reward_points.total,
);
reward = reward.saturating_add(
validator_point_part
.saturating_mul(Perbill::one().saturating_sub(commission))
.saturating_mul(nominator_exposure_part)
);
}
}
if let Some(imbalance) = Self::make_payout(&nominator_ledger.stash, reward * era_payout) {
Self::deposit_event(RawEvent::Reward(ctrl, imbalance.peek()));
fn do_payout_validator(ctrl: T::AccountId, era: EraIndex) -> DispatchResult {
// If migrate_era is not populated, then you should use `payout_stakers`
let migrate_era = MigrateEra::get().ok_or(Error::<T>::InvalidEraToReward)?;
// This payout mechanism will only work for eras before the migration.
// Subsequent payouts should use `payout_stakers`.
ensure!(era < migrate_era, Error::<T>::InvalidEraToReward);
let current_era = CurrentEra::get().ok_or(Error::<T>::InvalidEraToReward)?;
ensure!(era <= current_era, Error::<T>::InvalidEraToReward);
let history_depth = Self::history_depth();
ensure!(era >= current_era.saturating_sub(history_depth), Error::<T>::InvalidEraToReward);
// Note: if era has no reward to be claimed, era may be future. better not to update
// `ledger.last_reward` in this case.
let era_payout = <ErasValidatorReward<T>>::get(&era)
.ok_or_else(|| Error::<T>::InvalidEraToReward)?;
let mut ledger = <Ledger<T>>::get(&ctrl).ok_or_else(|| Error::<T>::NotController)?;
ensure!(
Self::era_election_status().is_closed() || Self::payee(&ledger.stash) != RewardDestination::Staked,
Error::<T>::CallNotAllowed,
);
ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth));
match ledger.claimed_rewards.binary_search(&era) {
Ok(_) => Err(Error::<T>::AlreadyClaimed)?,
Err(pos) => ledger.claimed_rewards.insert(pos, era),
<Ledger<T>>::insert(&ctrl, &ledger);
let era_reward_points = <ErasRewardPoints<T>>::get(&era);
let commission = Self::eras_validator_prefs(&era, &ledger.stash).commission;
let exposure = <ErasStakersClipped<T>>::get(&era, &ledger.stash);
let exposure_part = Perbill::from_rational_approximation(
exposure.own,
exposure.total,
);
let validator_point = era_reward_points.individual.get(&ledger.stash)
.map(|points| *points)
.unwrap_or_else(|| Zero::zero());
let validator_point_part = Perbill::from_rational_approximation(
validator_point,
era_reward_points.total,
);
let reward = validator_point_part.saturating_mul(
commission.saturating_add(
Perbill::one().saturating_sub(commission).saturating_mul(exposure_part)
)
);
if let Some(imbalance) = Self::make_payout(&ledger.stash, reward * era_payout) {
Self::deposit_event(RawEvent::Reward(ctrl, imbalance.peek()));
fn do_payout_stakers(
validator_stash: T::AccountId,
era: EraIndex,
) -> DispatchResult {
// Validate input data
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
let current_era = CurrentEra::get().ok_or(Error::<T>::InvalidEraToReward)?;
ensure!(era <= current_era, Error::<T>::InvalidEraToReward);
let history_depth = Self::history_depth();
ensure!(era >= current_era.saturating_sub(history_depth), Error::<T>::InvalidEraToReward);
// If there was no migration, then this function is always valid.
if let Some(migrate_era) = MigrateEra::get() {
// This payout mechanism will only work for eras on and after the migration.
// Payouts before then should use `payout_nominator`/`payout_validator`.
ensure!(migrate_era <= era, Error::<T>::InvalidEraToReward);
}
// Note: if era has no reward to be claimed, era may be future. better not to update
// `ledger.claimed_rewards` in this case.
let era_payout = <ErasValidatorReward<T>>::get(&era)
.ok_or_else(|| Error::<T>::InvalidEraToReward)?;
let controller = Self::bonded(&validator_stash).ok_or(Error::<T>::NotStash)?;
let mut ledger = <Ledger<T>>::get(&controller).ok_or_else(|| Error::<T>::NotController)?;
ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth));
match ledger.claimed_rewards.binary_search(&era) {
Ok(_) => Err(Error::<T>::AlreadyClaimed)?,
Err(pos) => ledger.claimed_rewards.insert(pos, era),
}
let exposure = <ErasStakersClipped<T>>::get(&era, &ledger.stash);
/* Input data seems good, no errors allowed after this point */
<Ledger<T>>::insert(&controller, &ledger);
// Get Era reward points. It has TOTAL and INDIVIDUAL
// Find the fraction of the era reward that belongs to the validator
// Take that fraction of the eras rewards to split to nominator and validator
//
// Then look at the validator, figure out the proportion of their reward
// which goes to them and each of their nominators.
let era_reward_points = <ErasRewardPoints<T>>::get(&era);
let total_reward_points = era_reward_points.total;
let validator_reward_points = era_reward_points.individual.get(&ledger.stash)
.map(|points| *points)
.unwrap_or_else(|| Zero::zero());
// Nothing to do if they have no reward points.
if validator_reward_points.is_zero() { return Ok(())}
// This is the fraction of the total reward that the validator and the
// nominators will get.
let validator_total_reward_part = Perbill::from_rational_approximation(
validator_reward_points,
total_reward_points,
);
// This is how much validator + nominators are entitled to.
let validator_total_payout = validator_total_reward_part * era_payout;
let validator_prefs = Self::eras_validator_prefs(&era, &validator_stash);
// Validator first gets a cut off the top.
let validator_commission = validator_prefs.commission;
let validator_commission_payout = validator_commission * validator_total_payout;
let validator_leftover_payout = validator_total_payout - validator_commission_payout;
// Now let's calculate how this is split to the validator.
let validator_exposure_part = Perbill::from_rational_approximation(
exposure.own,
exposure.total,
);
let validator_staking_payout = validator_exposure_part * validator_leftover_payout;
// We can now make total validator payout:
if let Some(imbalance) = Self::make_payout(
&ledger.stash,
validator_staking_payout + validator_commission_payout
) {
Self::deposit_event(RawEvent::Reward(ledger.stash, imbalance.peek()));
}
// Lets now calculate how this is split to the nominators.
// Sort nominators by highest to lowest exposure, but only keep `max_nominator_payouts` of them.
for nominator in exposure.others.iter() {
let nominator_exposure_part = Perbill::from_rational_approximation(
nominator.value,
exposure.total,
);
let nominator_reward: BalanceOf<T> = nominator_exposure_part * validator_leftover_payout;
// We can now make nominator payout:
if let Some(imbalance) = Self::make_payout(&nominator.who, nominator_reward) {
Self::deposit_event(RawEvent::Reward(nominator.who.clone(), imbalance.peek()));
}
}
Ok(())
}
/// 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,
<Ledger<T>>::insert(controller, ledger);
}
/// Chill a stash account.
fn chill_stash(stash: &T::AccountId) {
<Validators<T>>::remove(stash);
<Nominators<T>>::remove(stash);
}
/// 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);
/// Plan a new session potentially trigger a new era.
fn new_session(session_index: SessionIndex) -> Option<Vec<T::AccountId>> {
if let Some(current_era) = Self::current_era() {
// Initial era has been set.
let current_era_start_session_index = Self::eras_start_session_index(current_era)
.unwrap_or_else(|| {
frame_support::print("Error: start_session_index must be set for current_era");
0
});
let era_length = session_index.checked_sub(current_era_start_session_index)
.unwrap_or(0); // Must never happen.
match ForceEra::get() {
Forcing::ForceNew => ForceEra::kill(),
Forcing::ForceAlways => (),
Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (),
_ => {
// not forcing, not a new era either. If final, set the flag.
if era_length + 1 >= T::SessionsPerEra::get() {
IsCurrentSessionFinal::put(true);
}
return None
},
// new era.
IsCurrentSessionFinal::put(false);
Self::new_era(session_index)
} else {
// Set initial era
Self::new_era(session_index)
/// Basic and cheap checks that we perform in validate unsigned, and in the execution.
pub fn pre_dispatch_checks(score: PhragmenScore, era: EraIndex) -> Result<(), Error<T>> {
// discard solutions that are not in-time
// check window open
ensure!(
Self::era_election_status().is_open(),
Error::<T>::PhragmenEarlySubmission,
);
// check current era.
if let Some(current_era) = Self::current_era() {
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
ensure!(
current_era == era,
Error::<T>::PhragmenEarlySubmission,
)
}
// assume the given score is valid. Is it better than what we have on-chain, if we have any?
if let Some(queued_score) = Self::queued_score() {
ensure!(
is_score_better(queued_score, score),
Error::<T>::PhragmenWeakSubmission,
)
}
Ok(())
}
/// Checks a given solution and if correct and improved, writes it on chain as the queued result
/// of the next round. This may be called by both a signed and an unsigned transaction.
pub fn check_and_replace_solution(
winners: Vec<ValidatorIndex>,
compact_assignments: CompactAssignments,
compute: ElectionCompute,
claimed_score: PhragmenScore,
era: EraIndex,
) -> Result<(), Error<T>> {
// Do the basic checks. era, claimed score and window open.
Self::pre_dispatch_checks(claimed_score, era)?;
// Check that the number of presented winners is sane. Most often we have more candidates
// that we need. Then it should be Self::validator_count(). Else it should be all the
// candidates.
let snapshot_length = <SnapshotValidators<T>>::decode_len()
.map_err(|_| Error::<T>::SnapshotUnavailable)?;
let desired_winners = Self::validator_count().min(snapshot_length as u32);
ensure!(winners.len() as u32 == desired_winners, Error::<T>::PhragmenBogusWinnerCount);
// decode snapshot validators.
let snapshot_validators = Self::snapshot_validators()
.ok_or(Error::<T>::SnapshotUnavailable)?;
// check if all winners were legit; this is rather cheap. Replace with accountId.
let winners = winners.into_iter().map(|widx| {
// NOTE: at the moment, since staking is explicitly blocking any offence until election
// is closed, we don't check here if the account id at `snapshot_validators[widx]` is
// actually a validator. If this ever changes, this loop needs to also check this.
snapshot_validators.get(widx as usize).cloned().ok_or(Error::<T>::PhragmenBogusWinner)
}).collect::<Result<Vec<T::AccountId>, Error<T>>>()?;
// decode the rest of the snapshot.
let snapshot_nominators = <Module<T>>::snapshot_nominators()
.ok_or(Error::<T>::SnapshotUnavailable)?;
// helpers
let nominator_at = |i: NominatorIndex| -> Option<T::AccountId> {
snapshot_nominators.get(i as usize).cloned()
};
let validator_at = |i: ValidatorIndex| -> Option<T::AccountId> {
snapshot_validators.get(i as usize).cloned()
};
// un-compact.
let assignments = compact_assignments.into_assignment(
nominator_at,
validator_at,
).map_err(|e| {
// log the error since it is not propagated into the runtime error.
log!(warn, "💸 un-compacting solution failed due to {:?}", e);
Error::<T>::PhragmenBogusCompact
})?;
// check all nominators actually including the claimed vote. Also check correct self votes.
// Note that we assume all validators and nominators in `assignments` are properly bonded,
// because they are coming from the snapshot via a given index.
for Assignment { who, distribution } in assignments.iter() {
let is_validator = <Validators<T>>::contains_key(&who);
let maybe_nomination = Self::nominators(&who);
if !(maybe_nomination.is_some() ^ is_validator) {
// all of the indices must map to either a validator or a nominator. If this is ever
// not the case, then the locking system of staking is most likely faulty, or we
// have bigger problems.
log!(error, "💸 detected an error in the staking locking and snapshot.");
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
// abort.
return Err(Error::<T>::PhragmenBogusNominator);
}
if !is_validator {
// a normal vote
let nomination = maybe_nomination.expect(
"exactly one of `maybe_validator` and `maybe_nomination.is_some` is true. \
is_validator is false; maybe_nomination is some; qed"
);
// NOTE: we don't really have to check here if the sum of all edges are the
// nominator correct. Un-compacting assures this by definition.
for (t, _) in distribution {
// each target in the provided distribution must be actually nominated by the
// nominator after the last non-zero slash.
if nomination.targets.iter().find(|&tt| tt == t).is_none() {
return Err(Error::<T>::PhragmenBogusNomination);
}
if <Self as Store>::SlashingSpans::get(&t).map_or(
false,
|spans| nomination.submitted_in < spans.last_nonzero_slash(),
) {
return Err(Error::<T>::PhragmenSlashedNomination);
}
}
} else {
// a self vote
ensure!(distribution.len() == 1, Error::<T>::PhragmenBogusSelfVote);
ensure!(distribution[0].0 == *who, Error::<T>::PhragmenBogusSelfVote);
// defensive only. A compact assignment of length one does NOT encode the weight and
// it is always created to be 100%.
ensure!(
distribution[0].1 == OffchainAccuracy::one(),
Error::<T>::PhragmenBogusSelfVote,
);
}
}
// convert into staked assignments.
let staked_assignments = sp_phragmen::assignment_ratio_to_staked(
assignments,
Self::slashable_balance_of_vote_weight,
);
// build the support map thereof in order to evaluate.
// OPTIMIZATION: loop to create the staked assignments but it would bloat the code. Okay for
// now as it does not add to the complexity order.
let (supports, num_error) = build_support_map::<T::AccountId>(
&winners,
&staked_assignments,
);
// This technically checks that all targets in all nominators were among the winners.
ensure!(num_error == 0, Error::<T>::PhragmenBogusEdge);
// Check if the score is the same as the claimed one.
let submitted_score = evaluate_support(&supports);
ensure!(submitted_score == claimed_score, Error::<T>::PhragmenBogusScore);
// At last, alles Ok. Exposures and store the result.
let exposures = Self::collect_exposure(supports);
log!(
info,
"💸 A better solution (with compute {:?}) has been validated and stored on chain.",
compute,
);
// write new results.
<QueuedElected<T>>::put(ElectionResult {
elected_stashes: winners,
compute,
exposures,
});
QueuedScore::put(submitted_score);
Ok(())
}
/// Start a session potentially starting an era.
fn start_session(start_session: SessionIndex) {
let next_active_era = Self::active_era().map(|e| e.index + 1).unwrap_or(0);
if let Some(next_active_era_start_session_index) =
Self::eras_start_session_index(next_active_era)
{
if next_active_era_start_session_index == start_session {
Self::start_era(start_session);
} else if next_active_era_start_session_index < start_session {
// This arm should never happen, but better handle it than to stall the
// staking pallet.
frame_support::print("Warning: A session appears to have been skipped.");
Self::start_era(start_session);
}
}
/// End a session potentially ending an era.
fn end_session(session_index: SessionIndex) {
if let Some(active_era) = Self::active_era() {
if let Some(next_active_era_start_session_index) =
{
if next_active_era_start_session_index == session_index + 1 {
Self::end_era(active_era, session_index);
}
/// * Increment `active_era.index`,
/// * reset `active_era.start`,
/// * update `BondedEras` and apply slashes.
fn start_era(start_session: SessionIndex) {
let active_era = ActiveEra::mutate(|active_era| {
let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
*active_era = Some(ActiveEraInfo {
index: new_index,
// Set new active era start in next `on_finalize`. To guarantee usage of `Time`
start: None,
});
new_index
let bonding_duration = T::BondingDuration::get();
BondedEras::mutate(|bonded| {
if active_era > bonding_duration {
let first_kept = active_era - bonding_duration;
// 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();
// kill slashing metadata.
for (pruned_era, _) in bonded.drain(..n_to_prune) {
slashing::clear_era_metadata::<T>(pruned_era);
}
if let Some(&(_, first_session)) = bonded.first() {
T::SessionInterface::prune_historical_up_to(first_session);
}
Self::apply_unapplied_slashes(active_era);
}
/// Compute payout for era.
fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) {
// Note: active_era_start can be None if end era is called during genesis config.
if let Some(active_era_start) = active_era.start {
let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
let era_duration = now_as_millis_u64 - active_era_start;
let (validator_payout, max_payout) = inflation::compute_total_payout(
&T::RewardCurve::get(),
Self::eras_total_stake(&active_era.index),
T::Currency::total_issuance(),
// Duration of era; more than u64::MAX is rewarded as u64::MAX.
era_duration.saturated_into::<u64>(),
);
let rest = max_payout.saturating_sub(validator_payout);
Self::deposit_event(RawEvent::EraPayout(active_era.index, validator_payout, rest));
<ErasValidatorReward<T>>::insert(&active_era.index, validator_payout);
T::RewardRemainder::on_unbalanced(T::Currency::issue(rest));
}
}
/// Plan a new era. Return the potential new staking set.
fn new_era(start_session_index: SessionIndex) -> Option<Vec<T::AccountId>> {
// Increment or set current era.
let current_era = CurrentEra::mutate(|s| {
*s = Some(s.map(|s| s + 1).unwrap_or(0));
s.unwrap()
});
ErasStartSessionIndex::insert(¤t_era, &start_session_index);
// Clean old era information.
if let Some(old_era) = current_era.checked_sub(Self::history_depth() + 1) {
Self::clear_era_information(old_era);
}
// Set staking information for new era.
let maybe_new_validators = Self::select_and_update_validators(current_era);
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
/// Select the new validator set at the end of the era.
///
/// Runs [`try_do_phragmen`] and updates the following storage items:
/// - [`EraElectionStatus`]: with `None`.
/// - [`ErasStakers`]: with the new staker set.
/// - [`ErasStakersClipped`].
/// - [`ErasValidatorPrefs`].
/// - [`ErasTotalStake`]: with the new total stake.
/// - [`SnapshotValidators`] and [`SnapshotNominators`] are both removed.
///
/// Internally, [`QueuedElected`], snapshots and [`QueuedScore`] are also consumed.
///
/// If the election has been successful, It passes the new set upwards.
///
/// This should only be called at the end of an era.
fn select_and_update_validators(current_era: EraIndex) -> Option<Vec<T::AccountId>> {
if let Some(ElectionResult::<T::AccountId, BalanceOf<T>> {
elected_stashes,
exposures,
compute,
}) = Self::try_do_phragmen() {
// We have chosen the new validator set. Submission is no longer allowed.
<EraElectionStatus<T>>::put(ElectionStatus::Closed);
// kill the snapshots.
Self::kill_stakers_snapshot();
// Populate Stakers and write slot stake.
let mut total_stake: BalanceOf<T> = Zero::zero();
exposures.into_iter().for_each(|(stash, exposure)| {
total_stake = total_stake.saturating_add(exposure.total);
<ErasStakers<T>>::insert(current_era, &stash, &exposure);
let mut exposure_clipped = exposure;
let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize;
if exposure_clipped.others.len() > clipped_max_len {
exposure_clipped.others.sort_unstable_by(|a, b| a.value.cmp(&b.value).reverse());
exposure_clipped.others.truncate(clipped_max_len);
<ErasStakersClipped<T>>::insert(¤t_era, &stash, exposure_clipped);
});
// Insert current era staking information
<ErasTotalStake<T>>::insert(¤t_era, total_stake);
// collect the pref of all winners
for stash in &elected_stashes {
let pref = Self::validators(stash);
<ErasValidatorPrefs<T>>::insert(¤t_era, stash, pref);
// emit event
Self::deposit_event(RawEvent::StakingElection(compute));
log!(
info,
"💸 new validator set of size {:?} has been elected via {:?} for era {:?}",
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
elected_stashes.len(),
compute,
current_era,
);
Some(elected_stashes)
} else {
None
}
}
/// Select a new validator set from the assembled stakers and their role preferences. It tries
/// first to peek into [`QueuedElected`]. Otherwise, it runs a new phragmen.
///
/// If [`QueuedElected`] and [`QueuedScore`] exists, they are both removed. No further storage
/// is updated.
fn try_do_phragmen() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> {
// a phragmen result from either a stored submission or locally executed one.
let next_result = <QueuedElected<T>>::take().or_else(||
Self::do_phragmen_with_post_processing::<ChainAccuracy>(ElectionCompute::OnChain)
);
// either way, kill this. We remove it here to make sure it always has the exact same
// lifetime as `QueuedElected`.
QueuedScore::kill();
next_result
/// Execute phragmen and return the new results. The edge weights are processed into support
/// values.
///
/// This is basically a wrapper around [`do_phragmen`] which translates `PhragmenResult` into
/// `ElectionResult`.
/// No storage item is updated.
fn do_phragmen_with_post_processing<Accuracy: PerThing>(compute: ElectionCompute)
-> Option<ElectionResult<T::AccountId, BalanceOf<T>>>
where
Accuracy: sp_std::ops::Mul<ExtendedBalance, Output=ExtendedBalance>,
ExtendedBalance: From<<Accuracy as PerThing>::Inner>,
{
if let Some(phragmen_result) = Self::do_phragmen::<Accuracy>() {
let elected_stashes = phragmen_result.winners.iter()
.map(|(s, _)| s.clone())
.collect::<Vec<T::AccountId>>();
let assignments = phragmen_result.assignments;
let staked_assignments = sp_phragmen::assignment_ratio_to_staked(
assignments,
Self::slashable_balance_of_vote_weight,
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
);
let (supports, _) = build_support_map::<T::AccountId>(
&elected_stashes,
&staked_assignments,
);
// collect exposures
let exposures = Self::collect_exposure(supports);
// In order to keep the property required by `on_session_ending` that we must return the
// new validator set even if it's the same as the old, as long as any underlying
// economic conditions have changed, we don't attempt to do any optimization where we
// compare against the prior set.
Some(ElectionResult::<T::AccountId, BalanceOf<T>> {
elected_stashes,
exposures,
compute,
})
} 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. TODO: #2494
None
}
}
/// Execute phragmen and return the new results. No post-processing is applied and the raw edge
/// weights are returned.
/// Self votes are added and nominations before the most recent slashing span are reaped.
/// No storage item is updated.
fn do_phragmen<Accuracy: PerThing>() -> Option<PhragmenResult<T::AccountId, Accuracy>> {
let mut all_nominators: Vec<(T::AccountId, VoteWeight, Vec<T::AccountId>)> = Vec::new();
for (validator, _) in <Validators<T>>::iter() {
// append self vote
let self_vote = (validator.clone(), Self::slashable_balance_of_vote_weight(&validator), vec![validator.clone()]);
let nominator_votes = <Nominators<T>>::iter().map(|(nominator, nominations)| {
let Nominations { submitted_in, mut targets, suppressed: _ } = nominations;
// Filter out nomination targets which were nominated before the most recent
targets.retain(|stash| {
<Self as Store>::SlashingSpans::get(&stash).map_or(
true,
|spans| submitted_in >= spans.last_nonzero_slash(),
)
});
(nominator, targets)
});
all_nominators.extend(nominator_votes.map(|(n, ns)| {
let s = Self::slashable_balance_of_vote_weight(&n);
Self::validator_count() as usize,
Self::minimum_validator_count().max(1) as usize,
all_validators,
all_nominators,
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
/// Consume a set of [`Supports`] from [`sp_phragmen`] and collect them into a [`Exposure`]
fn collect_exposure(supports: SupportMap<T::AccountId>) -> Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)> {
let to_balance = |e: ExtendedBalance|
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(e);
supports.into_iter().map(|(validator, support)| {
// build `struct exposure` from `support`
let mut others = Vec::new();
let mut own: BalanceOf<T> = Zero::zero();
let mut total: BalanceOf<T> = Zero::zero();
support.voters
.into_iter()
.map(|(nominator, weight)| (nominator, to_balance(weight)))
.for_each(|(nominator, stake)| {
if nominator == validator {
own = own.saturating_add(stake);
} else {
others.push(IndividualExposure { who: nominator, value: stake });
}
total = total.saturating_add(stake);
});
let exposure = Exposure {
own,
others,
total,
};
(validator, exposure)
}).collect::<Vec<(T::AccountId, Exposure<_, _>)>>()
/// Remove all associated data of a stash account from the staking system.
///
/// Assumes storage is upgraded before calling.
///
/// - after a `withdraw_unbond()` call that frees all of a stash's bonded balance.
/// - through `reap_stash()` if the balance has fallen to zero (through slashing).
fn kill_stash(stash: &T::AccountId) -> DispatchResult {
let controller = Bonded::<T>::take(stash).ok_or(Error::<T>::NotStash)?;
<Ledger<T>>::remove(&controller);
<Payee<T>>::remove(stash);
<Validators<T>>::remove(stash);
<Nominators<T>>::remove(stash);
slashing::clear_stash_metadata::<T>(stash);
system::Module::<T>::dec_ref(stash);
Ok(())
}
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
/// Clear all era information for given era.
fn clear_era_information(era_index: EraIndex) {
<ErasStakers<T>>::remove_prefix(era_index);
<ErasStakersClipped<T>>::remove_prefix(era_index);
<ErasValidatorPrefs<T>>::remove_prefix(era_index);
<ErasValidatorReward<T>>::remove(era_index);
<ErasRewardPoints<T>>::remove(era_index);
<ErasTotalStake<T>>::remove(era_index);
ErasStartSessionIndex::remove(era_index);
}
/// Apply previously-unapplied slashes on the beginning of a new era, after a delay.
fn apply_unapplied_slashes(active_era: EraIndex) {
let slash_defer_duration = T::SlashDeferDuration::get();
<Self as Store>::EarliestUnappliedSlash::mutate(|earliest| if let Some(ref mut earliest) = earliest {
let keep_from = active_era.saturating_sub(slash_defer_duration);
for era in (*earliest)..keep_from {
let era_slashes = <Self as Store>::UnappliedSlashes::take(&era);
for slash in era_slashes {
slashing::apply_slash::<T>(slash);
}
}
*earliest = (*earliest).max(keep_from)
})
}
/// 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.
///
/// 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)>
) {
if let Some(active_era) = Self::active_era() {
<ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| {
for (validator, points) in validators_points.into_iter() {
*era_rewards.individual.entry(validator).or_default() += points;
era_rewards.total += points;
});
}
}
/// Ensures that at the end of the current session there will be a new era.
fn ensure_new_era() {
match ForceEra::get() {
Forcing::ForceAlways | Forcing::ForceNew => (),
_ => ForceEra::put(Forcing::ForceNew),
}
fn will_era_be_forced() -> bool {
match ForceEra::get() {
Forcing::ForceAlways | Forcing::ForceNew => true,
Forcing::ForceNone | Forcing::NotForcing => false,
}
}
#[cfg(feature = "runtime-benchmarks")]
pub fn add_era_stakers(current_era: EraIndex, controller: T::AccountId, exposure: Exposure<T::AccountId, BalanceOf<T>>) {
<ErasStakers<T>>::insert(¤t_era, &controller, &exposure);
}
#[cfg(feature = "runtime-benchmarks")]
pub fn put_election_status(status: ElectionStatus::<T::BlockNumber>) {
<EraElectionStatus<T>>::put(status);
}
/// 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, though
/// some session can lag in between the newest session planned and the latest session started.
impl<T: Trait> pallet_session::SessionManager<T::AccountId> for Module<T> {
fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
Self::new_session(new_index)
}
fn start_session(start_index: SessionIndex) {
Self::start_session(start_index)
}
fn end_session(end_index: SessionIndex) {
Self::end_session(end_index)
impl<T: Trait> historical::SessionManager<T::AccountId, Exposure<T::AccountId, BalanceOf<T>>> for Module<T> {
fn new_session(new_index: SessionIndex)
-> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>>
<Self as pallet_session::SessionManager<_>>::new_session(new_index).map(|validators| {
let current_era = Self::current_era()
// Must be some as a new era has been created.
.unwrap_or(0);
(v, exposure)
}).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)
/// 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 Module<T>
where
T: Trait + pallet_authorship::Trait + pallet_session::Trait
{
fn note_author(author: T::AccountId) {
}
fn note_uncle(author: T::AccountId, _age: T::BlockNumber) {
(<pallet_authorship::Module<T>>::author(), 2),
/// A `Convert` implementation that finds the stash of the given controller account,
/// if any.
pub struct StashOf<T>(sp_std::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 active exposure of nominators
/// on that account.
///
/// Active exposure is the exposure of the validator set currently validating, i.e. in
/// `active_era`. It can differ from the latest planned exposure in `current_era`.
pub struct ExposureOf<T>(sp_std::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>>> {
if let Some(active_era) = <Module<T>>::active_era() {
Some(<Module<T>>::eras_stakers(active_era.index, &validator))
} else {
None
}
/// This is intended to be used with `FilterHistoricalOffences`.
impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>> for Module<T> where
T: pallet_session::Trait<ValidatorId = <T as frame_system::Trait>::AccountId>,
T: pallet_session::historical::Trait<
FullIdentification = Exposure<<T as frame_system::Trait>::AccountId, BalanceOf<T>>,
FullIdentificationOf = ExposureOf<T>,
>,
T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Trait>::AccountId>,
T::SessionManager: pallet_session::SessionManager<<T as frame_system::Trait>::AccountId>,
T::ValidatorIdOf: Convert<<T as frame_system::Trait>::AccountId, Option<<T as frame_system::Trait>::AccountId>>
offenders: &[OffenceDetails<T::AccountId, pallet_session::historical::IdentificationTuple<T>>],
slash_session: SessionIndex,