Newer
Older
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, num_slashing_spans: u32) -> DispatchResult {
let controller = Bonded::<T>::get(stash).ok_or(Error::<T>::NotStash)?;
slashing::clear_stash_metadata::<T>(stash, num_slashing_spans)?;
Bonded::<T>::remove(stash);
<Payee<T>>::remove(stash);
<Validators<T>>::remove(stash);
<Nominators<T>>::remove(stash);
system::Module::<T>::dec_ref(stash);
Ok(())
}
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
/// 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);
}
#[cfg(feature = "runtime-benchmarks")]
pub fn set_slash_reward_fraction(fraction: Perbill) {
SlashRewardFraction::put(fraction);
}
/// 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,
) -> Result<(), ()> {
if !Self::can_report() {
return Err(())
}
let reward_proportion = SlashRewardFraction::get();
let active_era = {
let active_era = Self::active_era();
if active_era.is_none() {
// this offence need not be re-submitted.
return Ok(())
active_era.expect("value checked not to be `None`; qed").index
};
let active_era_start_session_index = Self::eras_start_session_index(active_era)
.unwrap_or_else(|| {
frame_support::print("Error: start_session_index must be set for current_era");
0
});
let window_start = active_era.saturating_sub(T::BondingDuration::get());
// fast path for active-era report - most likely.
// `slash_session` cannot be in a future active era. It must be in `active_era` or before.
let slash_era = if slash_session >= active_era_start_session_index {
active_era
} else {
let eras = BondedEras::get();
// reverse because it's more likely to find reports from recent eras.
match eras.iter().rev().filter(|&&(_, ref sesh)| sesh <= &slash_session).next() {
None => return Ok(()), // before bonding period. defensive - should be filtered out.
Some(&(ref slash_era, _)) => *slash_era,
}
};
<Self as Store>::EarliestUnappliedSlash::mutate(|earliest| {
if earliest.is_none() {
}
});
let slash_defer_duration = T::SlashDeferDuration::get();
for (details, slash_fraction) in offenders.iter().zip(slash_fraction) {
let (stash, exposure) = &details.offender;
// Skip if the validator is invulnerable.
if Self::invulnerables().contains(stash) {
continue
}
let unapplied = slashing::compute_slash::<T>(slashing::SlashParams {
stash,
slash: *slash_fraction,
exposure,
slash_era,
window_start,
reward_proportion,
});
if let Some(mut unapplied) = unapplied {
unapplied.reporters = details.reporters.clone();
if slash_defer_duration == 0 {
// apply right away.
slashing::apply_slash::<T>(unapplied);
} else {
// defer to end of some `slash_defer_duration` from now.
<Self as Store>::UnappliedSlashes::mutate(
move |for_later| for_later.push(unapplied),
);
Ok(())
}
fn can_report() -> bool {
Self::era_election_status().is_closed()
/// Filter historical offences out and only allow those from the bonding period.
pub struct FilterHistoricalOffences<T, R> {
_inner: sp_std::marker::PhantomData<(T, R)>,
}
impl<T, Reporter, Offender, R, O> ReportOffence<Reporter, Offender, O>
for FilterHistoricalOffences<Module<T>, R> where
T: Trait,
R: ReportOffence<Reporter, Offender, O>,
O: Offence<Offender>,
{
fn report_offence(reporters: Vec<Reporter>, offence: O) -> Result<(), OffenceError> {
// disallow any slashing from before the current bonding period.
let offence_session = offence.session_index();
let bonded_eras = BondedEras::get();
if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() {
R::report_offence(reporters, offence)
} else {
<Module<T>>::deposit_event(
RawEvent::OldSlashingReportDiscarded(offence_session)
impl<T: Trait> From<Error<T>> for InvalidTransaction {
fn from(e: Error<T>) -> Self {
InvalidTransaction::Custom(e.as_u8())
}
}
#[allow(deprecated)]
impl<T: Trait> frame_support::unsigned::ValidateUnsigned for Module<T> {
type Call = Call<T>;
fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
if let Call::submit_election_solution_unsigned(
_,
_,
score,
era,
) = call {
use offchain_election::DEFAULT_LONGEVITY;
// discard solution not coming from the local OCW.
match source {
TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ }
_ => {
log!(debug, "rejecting unsigned transaction because it is not local/in-block.");
return InvalidTransaction::Call.into();
}
}
if let Err(e) = Self::pre_dispatch_checks(*score, *era) {
log!(debug, "validate unsigned pre dispatch checks failed due to {:?}.", e);
return InvalidTransaction::from(e).into();
log!(debug, "validateUnsigned succeeded for a solution at era {}.", era);
ValidTransaction::with_tag_prefix("StakingOffchain")
// The higher the score[0], the better a solution is.
.priority(T::UnsignedPriority::get().saturating_add(score[0].saturated_into()))
// Defensive only. A single solution can exist in the pool per era. Each validator
// will run OCW at most once per era, hence there should never exist more than one
// transaction anyhow.
// Note: this can be more accurate in the future. We do something like
// `era_end_block - current_block` but that is not needed now as we eagerly run
// offchain workers now and the above should be same as `T::ElectionLookahead`
// without the need to query more storage in the validation phase. If we randomize
// offchain worker, then we might re-consider this.
.longevity(TryInto::<u64>::try_into(
T::ElectionLookahead::get()).unwrap_or(DEFAULT_LONGEVITY)
)
// We don't propagate this. This can never the validated at a remote node.
.propagate(false)
.build()
} else {
InvalidTransaction::Call.into()
}
}
fn pre_dispatch(_: &Self::Call) -> Result<(), TransactionValidityError> {
// IMPORTANT NOTE: By default, a sane `pre-dispatch` should always do the same checks as
// `validate_unsigned` and overriding this should be done with care. this module has only
// one unsigned entry point, in which we call into `<Module<T>>::pre_dispatch_checks()`
// which is all the important checks that we do in `validate_unsigned`. Hence, we can safely
// override this to save some time.
Ok(())
}
}
/// Check that list is sorted and has no duplicates.
fn is_sorted_and_unique(list: &[u32]) -> bool {
list.windows(2).all(|w| w[0] < w[1])
}