diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 1dfa621033362..42114ecd3f559 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -21,9 +21,9 @@ use super::*; use crate::Module as Staking; use testing_utils::*; -use sp_runtime::traits::One; +pub use frame_benchmarking::{account, benchmarks}; use frame_system::RawOrigin; -pub use frame_benchmarking::{benchmarks, account}; +use sp_runtime::traits::One; const SEED: u32 = 0; const MAX_SPANS: u32 = 100; const MAX_VALIDATORS: u32 = 1000; @@ -32,13 +32,15 @@ const MAX_SLASHES: u32 = 1000; // Add slashing spans to a user account. Not relevant for actual use, only to benchmark // read and write operations. fn add_slashing_spans(who: &T::AccountId, spans: u32) { - if spans == 0 { return } + if spans == 0 { + return; + } // For the first slashing span, we initialize let mut slashing_spans = crate::slashing::SlashingSpans::new(0); SpanSlash::::insert((who, 0), crate::slashing::SpanRecord::default()); - for i in 1 .. spans { + for i in 1..spans { assert!(slashing_spans.end_span(i)); SpanSlash::::insert((who, i), crate::slashing::SpanRecord::default()); } @@ -47,7 +49,10 @@ fn add_slashing_spans(who: &T::AccountId, spans: u32) { // This function generates one validator being nominated by n nominators, and returns the validator // stash account. It also starts an era and creates pending payouts. -pub fn create_validator_with_nominators(n: u32, upper_bound: u32) -> Result { +pub fn create_validator_with_nominators( + n: u32, + upper_bound: u32, +) -> Result { let mut points_total = 0; let mut points_individual = Vec::new(); @@ -64,10 +69,13 @@ pub fn create_validator_with_nominators(n: u32, upper_bound: u32) -> R points_individual.push((v_stash.clone(), 10)); // Give the validator n nominators, but keep total users in the system the same. - for i in 0 .. upper_bound { + for i in 0..upper_bound { let (_n_stash, n_controller) = create_stash_controller::(u32::max_value() - i, 100)?; if i < n { - Staking::::nominate(RawOrigin::Signed(n_controller.clone()).into(), vec![stash_lookup.clone()])?; + Staking::::nominate( + RawOrigin::Signed(n_controller.clone()).into(), + vec![stash_lookup.clone()], + )?; } } @@ -602,7 +610,7 @@ benchmarks! { #[cfg(test)] mod tests { use super::*; - use crate::mock::{ExtBuilder, Test, Balances, Staking, Origin}; + use crate::mock::{Balances, ExtBuilder, Origin, Staking, Test}; use frame_support::assert_ok; #[test] @@ -611,8 +619,7 @@ mod tests { let v = 10; let n = 100; - create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None) - .unwrap(); + create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None).unwrap(); let count_validators = Validators::::iter().count(); let count_nominators = Nominators::::iter().count(); @@ -630,12 +637,17 @@ mod tests { let validator_stash = create_validator_with_nominators::( n, ::MaxNominatorRewardedPerValidator::get() as u32, - ).unwrap(); + ) + .unwrap(); let current_era = CurrentEra::get().unwrap(); let original_free_balance = Balances::free_balance(&validator_stash); - assert_ok!(Staking::payout_stakers(Origin::signed(1337), validator_stash, current_era)); + assert_ok!(Staking::payout_stakers( + Origin::signed(1337), + validator_stash, + current_era + )); let new_free_balance = Balances::free_balance(&validator_stash); assert!(original_free_balance < new_free_balance); @@ -650,7 +662,8 @@ mod tests { let validator_stash = create_validator_with_nominators::( n, ::MaxNominatorRewardedPerValidator::get() as u32, - ).unwrap(); + ) + .unwrap(); // Add 20 slashing spans let num_of_slashing_spans = 20; @@ -658,14 +671,14 @@ mod tests { let slashing_spans = SlashingSpans::::get(&validator_stash).unwrap(); assert_eq!(slashing_spans.iter().count(), num_of_slashing_spans as usize); - for i in 0 .. num_of_slashing_spans { + for i in 0..num_of_slashing_spans { assert!(SpanSlash::::contains_key((&validator_stash, i))); } // Test everything is cleaned up assert_ok!(Staking::kill_stash(&validator_stash, num_of_slashing_spans)); assert!(SlashingSpans::::get(&validator_stash).is_none()); - for i in 0 .. num_of_slashing_spans { + for i in 0..num_of_slashing_spans { assert!(!SpanSlash::::contains_key((&validator_stash, i))); } }); @@ -678,12 +691,16 @@ mod tests { let n = 100; let selected_benchmark = SelectedBenchmark::payout_all; - let c = vec![(frame_benchmarking::BenchmarkParameter::v, v), (frame_benchmarking::BenchmarkParameter::n, n)]; + let c = vec![ + (frame_benchmarking::BenchmarkParameter::v, v), + (frame_benchmarking::BenchmarkParameter::n, n), + ]; let closure_to_benchmark = >::instance( &selected_benchmark, - &c - ).unwrap(); + &c, + ) + .unwrap(); assert_ok!(closure_to_benchmark()); }); @@ -729,5 +746,4 @@ mod tests { assert_ok!(test_benchmark_submit_solution_weaker::()); }); } - } diff --git a/frame/staking/src/inflation.rs b/frame/staking/src/inflation.rs index 04bfc98357a7f..c600dca873436 100644 --- a/frame/staking/src/inflation.rs +++ b/frame/staking/src/inflation.rs @@ -20,7 +20,7 @@ //! The staking rate in NPoS is the total amount of tokens staked by nominators and validators, //! divided by the total token supply. -use sp_runtime::{Perbill, traits::AtLeast32Bit, curve::PiecewiseLinear}; +use sp_runtime::{curve::PiecewiseLinear, traits::AtLeast32Bit, Perbill}; /// The total payout to all validators (and their nominators) per era and maximum payout. /// @@ -33,16 +33,17 @@ pub fn compute_total_payout( yearly_inflation: &PiecewiseLinear<'static>, npos_token_staked: N, total_tokens: N, - era_duration: u64 -) -> (N, N) where N: AtLeast32Bit + Clone { + era_duration: u64, +) -> (N, N) +where + N: AtLeast32Bit + Clone, +{ // Milliseconds per year for the Julian year (365.25 days). const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100; let portion = Perbill::from_rational_approximation(era_duration as u64, MILLISECONDS_PER_YEAR); - let payout = portion * yearly_inflation.calculate_for_fraction_times_denominator( - npos_token_staked, - total_tokens.clone(), - ); + let payout = portion + * yearly_inflation.calculate_for_fraction_times_denominator(npos_token_staked, total_tokens.clone()); let maximum = portion * (yearly_inflation.maximum * total_tokens); (payout, maximum) } @@ -72,24 +73,63 @@ mod test { //super::I_NPOS.calculate_for_fraction_times_denominator(25, 100) assert_eq!(super::compute_total_payout(&I_NPOS, 0, 100_000u64, YEAR).0, 2_498); - assert_eq!(super::compute_total_payout(&I_NPOS, 5_000, 100_000u64, YEAR).0, 3_248); - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, YEAR).0, 6_246); - assert_eq!(super::compute_total_payout(&I_NPOS, 40_000, 100_000u64, YEAR).0, 8_494); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, YEAR).0, 9_993); - assert_eq!(super::compute_total_payout(&I_NPOS, 60_000, 100_000u64, YEAR).0, 4_379); - assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, YEAR).0, 2_733); - assert_eq!(super::compute_total_payout(&I_NPOS, 95_000, 100_000u64, YEAR).0, 2_513); - assert_eq!(super::compute_total_payout(&I_NPOS, 100_000, 100_000u64, YEAR).0, 2_505); + assert_eq!( + super::compute_total_payout(&I_NPOS, 5_000, 100_000u64, YEAR).0, + 3_248 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, YEAR).0, + 6_246 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 40_000, 100_000u64, YEAR).0, + 8_494 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, YEAR).0, + 9_993 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 60_000, 100_000u64, YEAR).0, + 4_379 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, YEAR).0, + 2_733 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 95_000, 100_000u64, YEAR).0, + 2_513 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 100_000, 100_000u64, YEAR).0, + 2_505 + ); const DAY: u64 = 24 * 60 * 60 * 1000; - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, DAY).0, 17); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, DAY).0, 27); + assert_eq!( + super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, DAY).0, + 17 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, DAY).0, + 27 + ); assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, DAY).0, 7); const SIX_HOURS: u64 = 6 * 60 * 60 * 1000; - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, SIX_HOURS).0, 4); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, SIX_HOURS).0, 7); - assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, SIX_HOURS).0, 2); + assert_eq!( + super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, SIX_HOURS).0, + 4 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, SIX_HOURS).0, + 7 + ); + assert_eq!( + super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, SIX_HOURS).0, + 2 + ); const HOUR: u64 = 60 * 60 * 1000; assert_eq!( @@ -98,7 +138,8 @@ mod test { 2_500_000_000_000_000_000_000_000_000u128, 5_000_000_000_000_000_000_000_000_000u128, HOUR - ).0, + ) + .0, 57_038_500_000_000_000_000_000 ); } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index ba787c4ba74e9..45837f2d4aae3 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -267,69 +267,69 @@ #![recursion_limit = "128"] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(any(feature = "runtime-benchmarks", test))] +pub mod benchmarking; #[cfg(test)] mod mock; -#[cfg(test)] -mod tests; #[cfg(any(feature = "runtime-benchmarks", test))] pub mod testing_utils; -#[cfg(any(feature = "runtime-benchmarks", test))] -pub mod benchmarking; +#[cfg(test)] +mod tests; -pub mod slashing; -pub mod offchain_election; pub mod inflation; +pub mod offchain_election; +pub mod slashing; pub mod migration; -use sp_std::{ - result, - prelude::*, - collections::btree_map::BTreeMap, - convert::{TryInto, From}, - mem::size_of, -}; -use codec::{HasCompact, Encode, Decode}; +use codec::{Decode, Encode, HasCompact}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, - weights::{Weight, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}}, - storage::IterableStorageMap, + decl_error, decl_event, decl_module, decl_storage, dispatch::{ - IsSubType, DispatchResult, DispatchResultWithPostInfo, DispatchErrorWithPostInfo, + DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, IsSubType, WithPostDispatchInfo, }, + ensure, + storage::IterableStorageMap, traits::{ - Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, - UnixTime, EstimateNextNewSession, EnsureOrigin, MigrateAccount, - } + Currency, EnsureOrigin, EstimateNextNewSession, Get, Imbalance, LockIdentifier, LockableCurrency, + MigrateAccount, OnUnbalanced, UnixTime, WithdrawReasons, + }, + weights::{ + constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}, + Weight, + }, }; +use frame_system::{self as system, ensure_none, ensure_root, ensure_signed, offchain::SendTransactionTypes}; use pallet_session::historical; +use sp_npos_elections::{ + build_support_map, evaluate_support, generate_compact_solution_type, is_score_better, seq_phragmen, + Assignment, ElectionResult as PrimitiveElectionResult, ElectionScore, ExtendedBalance, SupportMap, + VoteWeight, VotingLimit, +}; use sp_runtime::{ - Percent, Perbill, PerU16, PerThing, RuntimeDebug, DispatchError, curve::PiecewiseLinear, traits::{ - Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, AtLeast32Bit, - Dispatchable, + AtLeast32Bit, CheckedSub, Convert, Dispatchable, SaturatedConversion, Saturating, StaticLookup, Zero, }, transaction_validity::{ - TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, - TransactionSource, TransactionPriority, + InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, + TransactionValidityError, ValidTransaction, }, + DispatchError, PerThing, PerU16, Perbill, Percent, RuntimeDebug, }; +#[cfg(feature = "std")] +use sp_runtime::{Deserialize, Serialize}; use sp_staking::{ + offence::{Offence, OffenceDetails, OffenceError, OnOffenceHandler, ReportOffence}, SessionIndex, - offence::{OnOffenceHandler, OffenceDetails, Offence, ReportOffence, OffenceError}, -}; -#[cfg(feature = "std")] -use sp_runtime::{Serialize, Deserialize}; -use frame_system::{ - self as system, ensure_signed, ensure_root, ensure_none, - offchain::SendTransactionTypes, }; -use sp_npos_elections::{ - ExtendedBalance, Assignment, ElectionScore, ElectionResult as PrimitiveElectionResult, - build_support_map, evaluate_support, seq_phragmen, generate_compact_solution_type, - is_score_better, VotingLimit, SupportMap, VoteWeight, +use sp_std::{ + collections::btree_map::BTreeMap, + convert::{From, TryInto}, + mem::size_of, + prelude::*, + result, }; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; @@ -392,12 +392,10 @@ pub type ChainAccuracy = Perbill; pub type OffchainAccuracy = PerU16; /// The balance type of this module. -pub type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; +pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The compact type for election solutions. -pub type CompactAssignments = - GenericCompactAssignments; +pub type CompactAssignments = GenericCompactAssignments; type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; @@ -493,20 +491,21 @@ pub struct StakingLedger { pub claimed_rewards: Vec, } -impl< - AccountId, - Balance: HasCompact + Copy + Saturating + AtLeast32Bit, -> StakingLedger { +impl StakingLedger { /// Remove entries from `unlocking` that are sufficiently old and reduce the /// total by the sum of their balances. fn consolidate_unlocked(self, current_era: EraIndex) -> Self { let mut total = self.total; - let unlocking = self.unlocking.into_iter() - .filter(|chunk| if chunk.era > current_era { - true - } else { - total = total.saturating_sub(chunk.value); - false + let unlocking = self + .unlocking + .into_iter() + .filter(|chunk| { + if chunk.era > current_era { + true + } else { + total = total.saturating_sub(chunk.value); + false + } }) .collect(); @@ -515,7 +514,7 @@ impl< total, active: self.active, unlocking, - claimed_rewards: self.claimed_rewards + claimed_rewards: self.claimed_rewards, } } @@ -537,7 +536,7 @@ impl< } if unlocking_balance >= value { - break + break; } } @@ -545,7 +544,8 @@ impl< } } -impl StakingLedger where +impl StakingLedger +where Balance: AtLeast32Bit + Saturating + Copy, { /// Slash the validator for a given amount of balance. This can grow the value @@ -554,20 +554,12 @@ impl StakingLedger where /// /// Slashes from `active` funds first, and then `unlocking`, starting with the /// chunks that are closest to unlocking. - fn slash( - &mut self, - mut value: Balance, - minimum_balance: Balance, - ) -> Balance { + fn slash(&mut self, mut value: Balance, minimum_balance: Balance) -> Balance { let pre_total = self.total; let total = &mut self.total; let active = &mut self.active; - let slash_out_of = | - total_remaining: &mut Balance, - target: &mut Balance, - value: &mut Balance, - | { + let slash_out_of = |total_remaining: &mut Balance, target: &mut Balance, value: &mut Balance| { let mut slash_from_target = (*value).min(*target); if !slash_from_target.is_zero() { @@ -586,7 +578,9 @@ impl StakingLedger where slash_out_of(total, active, &mut value); - let i = self.unlocking.iter_mut() + let i = self + .unlocking + .iter_mut() .map(|chunk| { slash_out_of(total, &mut chunk.value, &mut value); chunk.value @@ -701,7 +695,6 @@ pub struct ElectionSize { pub nominators: NominatorIndex, } - impl ElectionStatus { fn is_open_at(&self, n: BlockNumber) -> bool { *self == Self::Open(n) @@ -710,7 +703,7 @@ impl ElectionStatus { fn is_closed(&self) -> bool { match self { Self::Closed => true, - _ => false + _ => false, } } @@ -741,7 +734,8 @@ pub trait SessionInterface: frame_system::Trait { fn prune_historical_up_to(up_to: SessionIndex); } -impl SessionInterface<::AccountId> for T where +impl SessionInterface<::AccountId> for T +where T: pallet_session::Trait::AccountId>, T: pallet_session::historical::Trait< FullIdentification = Exposure<::AccountId, BalanceOf>, @@ -796,7 +790,8 @@ pub mod weight { compact: &CompactAssignments, size: &ElectionSize, ) -> Weight { - (630 * WEIGHT_PER_NANOS).saturating_mul(size.validators as Weight) + (630 * WEIGHT_PER_NANOS) + .saturating_mul(size.validators as Weight) .saturating_add((360 * WEIGHT_PER_NANOS).saturating_mul(size.nominators as Weight)) .saturating_add((96 * WEIGHT_PER_MICROS).saturating_mul(compact.len() as Weight)) .saturating_add((8 * WEIGHT_PER_MICROS).saturating_mul(winners.len() as Weight)) @@ -833,7 +828,7 @@ pub mod weight { pub trait Trait: frame_system::Trait + SendTransactionTypes> { /// The staking balance. - type Currency: LockableCurrency; + type Currency: LockableCurrency; /// Time used for computing era duration. /// @@ -934,7 +929,9 @@ pub enum Forcing { } impl Default for Forcing { - fn default() -> Self { Forcing::NotForcing } + fn default() -> Self { + Forcing::NotForcing + } } // A value placed in storage that represents the current version of the Staking storage. This value @@ -1380,7 +1377,7 @@ decl_module! { } } } - + // The edgeware migration is so big we just assume it consumes the whole block. fn on_runtime_upgrade() -> Weight { migrate_hasher::(); @@ -1970,6 +1967,20 @@ decl_module! { ::UnappliedSlashes::insert(&era, &unapplied); } + #[weight = 500_000_000] + fn payout_nominator(origin, era: EraIndex, validators: Vec<(T::AccountId, u32)>) + -> DispatchResult + { + let who = ensure_signed(origin)?; + Self::do_payout_nominator(who, era, validators) + } + + #[weight = 500_000_000] + fn payout_validator(origin, era: EraIndex) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::do_payout_validator(who, era) + } + /// Pay out all the stakers behind a single validator for a single era. /// /// - `validator_stash` is the stash account of the validator. Their nominators, up to @@ -2223,7 +2234,10 @@ impl MigrateAccount for Module { frame_support::runtime_print!("🕊️ Migrating Staking Account '{:?}'", a); if let Some(controller) = Bonded::::migrate_key_from_blake(a) { frame_support::runtime_print!( - "Migrating Staking stash account '{:?}' with controller '{:?}'", a, controller); + "Migrating Staking stash account '{:?}' with controller '{:?}'", + a, + controller + ); Ledger::::migrate_key_from_blake(controller); Payee::::migrate_key_from_blake(a); Validators::::migrate_key_from_blake(a); @@ -2231,7 +2245,10 @@ impl MigrateAccount for Module { SlashingSpans::::migrate_key_from_blake(a); } else if let Some(StakingLedger { stash, .. }) = Ledger::::migrate_key_from_blake(a) { frame_support::runtime_print!( - "Migrating Staking controller account '{:?}' with stash '{:?}'", a, &stash); + "Migrating Staking controller account '{:?}' with stash '{:?}'", + a, + &stash + ); Bonded::::migrate_key_from_blake(&stash); Payee::::migrate_key_from_blake(&stash); Validators::::migrate_key_from_blake(&stash); @@ -2258,14 +2275,15 @@ impl Module { /// The total balance that can be slashed from a stash account as of right now. pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf { // Weight note: consider making the stake accessible through stash. - Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() + Self::bonded(stash) + .and_then(Self::ledger) + .map(|l| l.active) + .unwrap_or_default() } /// internal impl of [`slashable_balance_of`] that returns [`VoteWeight`]. fn slashable_balance_of_vote_weight(stash: &T::AccountId) -> VoteWeight { - , VoteWeight>>::convert( - Self::slashable_balance_of(stash) - ) + , VoteWeight>>::convert(Self::slashable_balance_of(stash)) } /// Dump the list of validators and nominators into vectors and keep them on-chain. @@ -2284,10 +2302,7 @@ impl Module { let num_nominators = nominators.len(); add_db_reads_writes((num_validators + num_nominators) as Weight, 0); - if - num_validators > MAX_VALIDATORS || - num_nominators.saturating_add(num_validators) > MAX_NOMINATORS - { + if num_validators > MAX_VALIDATORS || num_nominators.saturating_add(num_validators) > MAX_NOMINATORS { log!( warn, "💸 Snapshot size too big [{} <> {}][{} <> {}].", @@ -2314,25 +2329,147 @@ impl Module { >::kill(); } - fn do_payout_stakers( - validator_stash: T::AccountId, - era: EraIndex, - ) -> DispatchResult { - // Validate input data + fn do_payout_nominator(who: T::AccountId, era: EraIndex, validators: Vec<(T::AccountId, u32)>) + -> DispatchResult + { + // validators len must not exceed `MAX_NOMINATIONS` to avoid querying more validator + // exposure than necessary. + if validators.len() > MAX_NOMINATIONS { + return Err(Error::::InvalidNumberOfNominations.into()); + } + // This payout mechanism will only work for eras before the migration. + // Subsequent payouts should use `payout_stakers`. let current_era = CurrentEra::get().ok_or(Error::::InvalidEraToReward)?; ensure!(era <= current_era, Error::::InvalidEraToReward); let history_depth = Self::history_depth(); ensure!(era >= current_era.saturating_sub(history_depth), Error::::InvalidEraToReward); // Note: if era has no reward to be claimed, era may be future. better not to update - // `ledger.claimed_rewards` in this case. + // `nominator_ledger.last_reward` in this case. let era_payout = >::get(&era) .ok_or_else(|| Error::::InvalidEraToReward)?; + let mut nominator_ledger = >::get(&who).ok_or_else(|| Error::::NotController)?; + + nominator_ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth)); + match nominator_ledger.claimed_rewards.binary_search(&era) { + Ok(_) => Err(Error::::AlreadyClaimed)?, + Err(pos) => nominator_ledger.claimed_rewards.insert(pos, era), + } + + >::insert(&who, &nominator_ledger); + + let mut reward = Perbill::zero(); + let era_reward_points = >::get(&era); + + for (validator, nominator_index) in validators.into_iter() { + let commission = Self::eras_validator_prefs(&era, &validator).commission; + let validator_exposure = >::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(who, imbalance.peek())); + } + + Ok(()) + } + + fn do_payout_validator(who: T::AccountId, era: EraIndex) -> DispatchResult { + // This payout mechanism will only work for eras before the migration. + // Subsequent payouts should use `payout_stakers`. + let current_era = CurrentEra::get().ok_or(Error::::InvalidEraToReward)?; + ensure!(era <= current_era, Error::::InvalidEraToReward); + let history_depth = Self::history_depth(); + ensure!(era >= current_era.saturating_sub(history_depth), Error::::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 = >::get(&era) + .ok_or_else(|| Error::::InvalidEraToReward)?; + + let mut ledger = >::get(&who).ok_or_else(|| Error::::NotController)?; + + ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth)); + match ledger.claimed_rewards.binary_search(&era) { + Ok(_) => Err(Error::::AlreadyClaimed)?, + Err(pos) => ledger.claimed_rewards.insert(pos, era), + } + + >::insert(&who, &ledger); + + let era_reward_points = >::get(&era); + let commission = Self::eras_validator_prefs(&era, &ledger.stash).commission; + let exposure = >::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(who, imbalance.peek())); + } + + Ok(()) + } + + fn do_payout_stakers(validator_stash: T::AccountId, era: EraIndex) -> DispatchResult { + // Validate input data + let current_era = CurrentEra::get().ok_or(Error::::InvalidEraToReward)?; + ensure!(era <= current_era, Error::::InvalidEraToReward); + let history_depth = Self::history_depth(); + ensure!( + era >= current_era.saturating_sub(history_depth), + Error::::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 = >::get(&era).ok_or_else(|| Error::::InvalidEraToReward)?; + let controller = Self::bonded(&validator_stash).ok_or(Error::::NotStash)?; let mut ledger = >::get(&controller).ok_or_else(|| Error::::NotController)?; - ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth)); + ledger + .claimed_rewards + .retain(|&x| x >= current_era.saturating_sub(history_depth)); match ledger.claimed_rewards.binary_search(&era) { Ok(_) => Err(Error::::AlreadyClaimed)?, Err(pos) => ledger.claimed_rewards.insert(pos, era), @@ -2353,19 +2490,21 @@ impl Module { let era_reward_points = >::get(&era); let total_reward_points = era_reward_points.total; - let validator_reward_points = era_reward_points.individual.get(&ledger.stash) + 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(())} + 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, - ); + 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; @@ -2377,16 +2516,13 @@ impl Module { 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_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 + validator_staking_payout + validator_commission_payout, ) { Self::deposit_event(RawEvent::Reward(ledger.stash, imbalance.peek())); } @@ -2394,10 +2530,8 @@ impl Module { // 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_exposure_part = + Perbill::from_rational_approximation(nominator.value, exposure.total); let nominator_reward: BalanceOf = nominator_exposure_part * validator_leftover_payout; // We can now make nominator payout: @@ -2411,16 +2545,8 @@ impl Module { /// 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::Currency::set_lock( - STAKING_ID, - &ledger.stash, - ledger.total, - WithdrawReasons::all(), - ); + fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger>) { + T::Currency::set_lock(STAKING_ID, &ledger.stash, ledger.total, WithdrawReasons::all()); >::insert(controller, ledger); } @@ -2436,11 +2562,8 @@ impl Module { 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(), + .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)| { @@ -2458,13 +2581,14 @@ impl Module { 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(|| { + 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) + let era_length = session_index + .checked_sub(current_era_start_session_index) .unwrap_or(0); // Must never happen. match ForceEra::get() { @@ -2480,8 +2604,8 @@ impl Module { // otherwise previous arm would short circuit. Self::close_election_window(); } - return None - }, + return None; + } } // new era. @@ -2539,11 +2663,8 @@ impl Module { Self::pre_dispatch_checks(claimed_score, era)?; // the weight that we will refund in case of a correct submission. We compute this now // because the data needed for it will be consumed further down. - let adjusted_weight = weight::weight_for_correct_submit_solution::( - &winners, - &compact_assignments, - &election_size, - ); + let adjusted_weight = + weight::weight_for_correct_submit_solution::(&winners, &compact_assignments, &election_size); // Check that the number of presented winners is sane. Most often we have more candidates // than we need. Then it should be `Self::validator_count()`. Else it should be all the @@ -2561,7 +2682,10 @@ impl Module { // check the winner length only here and when we know the length of the snapshot validators // length. let desired_winners = Self::validator_count().min(snapshot_validators_length); - ensure!(winners.len() as u32 == desired_winners, Error::::PhragmenBogusWinnerCount); + ensure!( + winners.len() as u32 == desired_winners, + Error::::PhragmenBogusWinnerCount + ); let snapshot_nominators_len = >::decode_len() .map(|l| l as u32) @@ -2574,38 +2698,39 @@ impl Module { ); // decode snapshot validators. - let snapshot_validators = Self::snapshot_validators() - .ok_or(Error::::SnapshotUnavailable)?; + let snapshot_validators = Self::snapshot_validators().ok_or(Error::::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::::PhragmenBogusWinner) - }).collect::, Error>>()?; + 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::::PhragmenBogusWinner) + }) + .collect::, Error>>()?; // decode the rest of the snapshot. - let snapshot_nominators = Self::snapshot_nominators() - .ok_or(Error::::SnapshotUnavailable)?; + let snapshot_nominators = Self::snapshot_nominators().ok_or(Error::::SnapshotUnavailable)?; // helpers - let nominator_at = |i: NominatorIndex| -> Option { - snapshot_nominators.get(i as usize).cloned() - }; - let validator_at = |i: ValidatorIndex| -> Option { - snapshot_validators.get(i as usize).cloned() - }; + let nominator_at = + |i: NominatorIndex| -> Option { snapshot_nominators.get(i as usize).cloned() }; + let validator_at = + |i: ValidatorIndex| -> Option { 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::::PhragmenBogusCompact - })?; + 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::::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, @@ -2627,7 +2752,7 @@ impl Module { // 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" + 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 @@ -2640,10 +2765,9 @@ impl Module { return Err(Error::::PhragmenBogusNomination.into()); } - if ::SlashingSpans::get(&t).map_or( - false, - |spans| nomination.submitted_in < spans.last_nonzero_slash(), - ) { + if ::SlashingSpans::get(&t).map_or(false, |spans| { + nomination.submitted_in < spans.last_nonzero_slash() + }) { return Err(Error::::PhragmenSlashedNomination.into()); } } @@ -2669,10 +2793,7 @@ impl Module { // 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::( - &winners, - &staked_assignments, - ); + let (supports, num_error) = build_support_map::(&winners, &staked_assignments); // This technically checks that all targets in all nominators were among the winners. ensure!(num_error == 0, Error::::PhragmenBogusEdge); @@ -2706,9 +2827,7 @@ impl Module { /// 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 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 { @@ -2756,7 +2875,8 @@ impl Module { let first_kept = active_era - bonding_duration; // prune out everything that's from before the first-kept index. - let n_to_prune = bonded.iter() + let n_to_prune = bonded + .iter() .take_while(|&&(era_idx, _)| era_idx < first_kept) .count(); @@ -2818,7 +2938,6 @@ impl Module { maybe_new_validators } - /// Remove all the storage items associated with the election. fn close_election_window() { // Close window. @@ -2849,7 +2968,8 @@ impl Module { elected_stashes, exposures, compute, - }) = Self::try_do_election() { + }) = Self::try_do_election() + { // Totally close the election round and data. Self::close_election_window(); @@ -2862,7 +2982,9 @@ impl Module { 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 + .sort_unstable_by(|a, b| a.value.cmp(&b.value).reverse()); exposure_clipped.others.truncate(clipped_max_len); } >::insert(¤t_era, &stash, exposure_clipped); @@ -2901,9 +3023,8 @@ impl Module { /// is updated. fn try_do_election() -> Option>> { // an election result from either a stored submission or locally executed one. - let next_result = >::take().or_else(|| - Self::do_phragmen_with_post_processing::(ElectionCompute::OnChain) - ); + let next_result = >::take() + .or_else(|| Self::do_phragmen_with_post_processing::(ElectionCompute::OnChain)); // either way, kill this. We remove it here to make sure it always has the exact same // lifetime as `QueuedElected`. @@ -2919,14 +3040,17 @@ impl Module { /// `PrimitiveElectionResult` into `ElectionResult`. /// /// No storage item is updated. - fn do_phragmen_with_post_processing(compute: ElectionCompute) - -> Option>> + fn do_phragmen_with_post_processing( + compute: ElectionCompute, + ) -> Option>> where - Accuracy: sp_std::ops::Mul, + Accuracy: sp_std::ops::Mul, ExtendedBalance: From<::Inner>, { if let Some(phragmen_result) = Self::do_phragmen::() { - let elected_stashes = phragmen_result.winners.iter() + let elected_stashes = phragmen_result + .winners + .iter() .map(|(s, _)| s.clone()) .collect::>(); let assignments = phragmen_result.assignments; @@ -2936,10 +3060,7 @@ impl Module { Self::slashable_balance_of_vote_weight, ); - let (supports, _) = build_support_map::( - &elected_stashes, - &staked_assignments, - ); + let (supports, _) = build_support_map::(&elected_stashes, &staked_assignments); // collect exposures let exposures = Self::collect_exposure(supports); @@ -2973,21 +3094,27 @@ impl Module { let mut all_validators = Vec::new(); for (validator, _) in >::iter() { // append self vote - let self_vote = (validator.clone(), Self::slashable_balance_of_vote_weight(&validator), vec![validator.clone()]); + let self_vote = ( + validator.clone(), + Self::slashable_balance_of_vote_weight(&validator), + vec![validator.clone()], + ); all_nominators.push(self_vote); all_validators.push(validator); } let nominator_votes = >::iter().map(|(nominator, nominations)| { - let Nominations { submitted_in, mut targets, suppressed: _ } = nominations; + let Nominations { + submitted_in, + mut targets, + suppressed: _, + } = nominations; // Filter out nomination targets which were nominated before the most recent // slashing span. targets.retain(|stash| { - ::SlashingSpans::get(&stash).map_or( - true, - |spans| submitted_in >= spans.last_nonzero_slash(), - ) + ::SlashingSpans::get(&stash) + .map_or(true, |spans| submitted_in >= spans.last_nonzero_slash()) }); (nominator, targets) @@ -3006,35 +3133,40 @@ impl Module { } /// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a [`Exposure`] - fn collect_exposure(supports: SupportMap) -> Vec<(T::AccountId, Exposure>)> { - let to_balance = |e: ExtendedBalance| - >>::convert(e); - - supports.into_iter().map(|(validator, support)| { - // build `struct exposure` from `support` - let mut others = Vec::with_capacity(support.voters.len()); - let mut own: BalanceOf = Zero::zero(); - let mut total: BalanceOf = 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); - }); + fn collect_exposure( + supports: SupportMap, + ) -> Vec<(T::AccountId, Exposure>)> { + let to_balance = + |e: ExtendedBalance| >>::convert(e); + + supports + .into_iter() + .map(|(validator, support)| { + // build `struct exposure` from `support` + let mut others = Vec::with_capacity(support.voters.len()); + let mut own: BalanceOf = Zero::zero(); + let mut total: BalanceOf = 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, - }; + let exposure = Exposure { own, others, total }; - (validator, exposure) - }).collect::)>>() + (validator, exposure) + }) + .collect::)>>() } /// Remove all associated data of a stash account from the staking system. @@ -3075,16 +3207,18 @@ impl Module { /// 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(); - ::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 = ::UnappliedSlashes::take(&era); - for slash in era_slashes { - slashing::apply_slash::(slash); + ::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 = ::UnappliedSlashes::take(&era); + for slash in era_slashes { + slashing::apply_slash::(slash); + } } - } - *earliest = (*earliest).max(keep_from) + *earliest = (*earliest).max(keep_from) + } }) } @@ -3100,9 +3234,7 @@ impl Module { /// /// 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 - ) { + pub fn reward_by_ids(validators_points: impl IntoIterator) { if let Some(active_era) = Self::active_era() { >::mutate(active_era.index, |era_rewards| { for (validator, points) in validators_points.into_iter() { @@ -3129,12 +3261,16 @@ impl Module { } #[cfg(feature = "runtime-benchmarks")] - pub fn add_era_stakers(current_era: EraIndex, controller: T::AccountId, exposure: Exposure>) { + pub fn add_era_stakers( + current_era: EraIndex, + controller: T::AccountId, + exposure: Exposure>, + ) { >::insert(¤t_era, &controller, &exposure); } #[cfg(feature = "runtime-benchmarks")] - pub fn put_election_status(status: ElectionStatus::) { + pub fn put_election_status(status: ElectionStatus) { >::put(status); } @@ -3142,7 +3278,6 @@ impl Module { 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)` @@ -3163,18 +3298,21 @@ impl pallet_session::SessionManager for Module { } impl historical::SessionManager>> for Module { - fn new_session(new_index: SessionIndex) - -> Option>)>> - { + fn new_session( + new_index: SessionIndex, + ) -> Option>)>> { >::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); - validators.into_iter().map(|v| { - let exposure = Self::eras_stakers(current_era, &v); - (v, exposure) - }).collect() + validators + .into_iter() + .map(|v| { + let exposure = Self::eras_stakers(current_era, &v); + (v, exposure) + }) + .collect() }) } fn start_session(start_index: SessionIndex) { @@ -3190,17 +3328,14 @@ impl historical::SessionManager pallet_authorship::EventHandler for Module - where - T: Trait + pallet_authorship::Trait + pallet_session::Trait +where + T: Trait + pallet_authorship::Trait + pallet_session::Trait, { fn note_author(author: T::AccountId) { Self::reward_by_ids(vec![(author, 20)]) } fn note_uncle(author: T::AccountId, _age: T::BlockNumber) { - Self::reward_by_ids(vec![ - (>::author(), 2), - (author, 1) - ]) + Self::reward_by_ids(vec![(>::author(), 2), (author, 1)]) } } @@ -3221,9 +3356,7 @@ impl Convert> for StashOf { /// `active_era`. It can differ from the latest planned exposure in `current_era`. pub struct ExposureOf(sp_std::marker::PhantomData); -impl Convert>>> - for ExposureOf -{ +impl Convert>>> for ExposureOf { fn convert(validator: T::AccountId) -> Option>> { if let Some(active_era) = >::active_era() { Some(>::eras_stakers(active_era.index, &validator)) @@ -3234,9 +3367,9 @@ impl Convert> } /// This is intended to be used with `FilterHistoricalOffences`. -impl - OnOffenceHandler, Weight> -for Module where +impl OnOffenceHandler, Weight> + for Module +where T: pallet_session::Trait::AccountId>, T: pallet_session::historical::Trait< FullIdentification = Exposure<::AccountId, BalanceOf>, @@ -3244,10 +3377,8 @@ for Module where >, T::SessionHandler: pallet_session::SessionHandler<::AccountId>, T::SessionManager: pallet_session::SessionManager<::AccountId>, - T::ValidatorIdOf: Convert< - ::AccountId, - Option<::AccountId>, - >, + T::ValidatorIdOf: + Convert<::AccountId, Option<::AccountId>>, { fn on_offence( offenders: &[OffenceDetails>], @@ -3255,7 +3386,7 @@ for Module where slash_session: SessionIndex, ) -> Result { if !Self::can_report() { - return Err(()) + return Err(()); } let reward_proportion = SlashRewardFraction::get(); @@ -3269,12 +3400,12 @@ for Module where add_db_reads_writes(1, 0); if active_era.is_none() { // this offence need not be re-submitted. - return Ok(consumed_weight) + return Ok(consumed_weight); } 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(|| { + 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 }); @@ -3291,7 +3422,12 @@ for Module where add_db_reads_writes(1, 0); // reverse because it's more likely to find reports from recent eras. - match eras.iter().rev().filter(|&&(_, ref sesh)| sesh <= &slash_session).next() { + match eras + .iter() + .rev() + .filter(|&&(_, ref sesh)| sesh <= &slash_session) + .next() + { Some(&(ref slash_era, _)) => *slash_era, // before bonding period. defensive - should be filtered out. None => return Ok(consumed_weight), @@ -3315,7 +3451,7 @@ for Module where // Skip if the validator is invulnerable. if invulnerables.contains(stash) { - continue + continue; } let unapplied = slashing::compute_slash::(slashing::SlashParams { @@ -3346,15 +3482,14 @@ for Module where 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 + (1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len, ); } } else { // defer to end of some `slash_defer_duration` from now. - ::UnappliedSlashes::mutate( - active_era, - move |for_later| for_later.push(unapplied), - ); + ::UnappliedSlashes::mutate(active_era, move |for_later| { + for_later.push(unapplied) + }); add_db_reads_writes(1, 1); } } else { @@ -3376,7 +3511,8 @@ pub struct FilterHistoricalOffences { } impl ReportOffence - for FilterHistoricalOffences, R> where + for FilterHistoricalOffences, R> +where T: Trait, R: ReportOffence, O: Offence, @@ -3386,12 +3522,14 @@ impl ReportOffence let offence_session = offence.session_index(); let bonded_eras = BondedEras::get(); - if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { + if bonded_eras + .first() + .filter(|(_, start)| offence_session >= *start) + .is_some() + { R::report_offence(reporters, offence) } else { - >::deposit_event( - RawEvent::OldSlashingReportDiscarded(offence_session) - ); + >::deposit_event(RawEvent::OldSlashingReportDiscarded(offence_session)); Ok(()) } } @@ -3401,20 +3539,17 @@ impl ReportOffence impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { - if let Call::submit_election_solution_unsigned( - _, - _, - score, - era, - _, - ) = call { + 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."); + log!( + debug, + "rejecting unsigned transaction because it is not local/in-block." + ); return InvalidTransaction::Call.into(); } } @@ -3426,7 +3561,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { "validate unsigned pre dispatch checks failed due to error #{:?}.", invalid, ); - return invalid .into(); + return invalid.into(); } log!(debug, "validateUnsigned succeeded for a solution at era {}.", era); @@ -3443,9 +3578,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // 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::::try_into( - T::ElectionLookahead::get()).unwrap_or(DEFAULT_LONGEVITY) - ) + .longevity(TryInto::::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() @@ -3455,13 +3588,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { - if let Call::submit_election_solution_unsigned( - _, - _, - score, - era, - _, - ) = call { + if let Call::submit_election_solution_unsigned(_, _, score, era, _) = call { // IMPORTANT NOTE: These checks are performed in the dispatch call itself, yet we need // to duplicate them here to prevent a block producer from putting a previously // validated, yet no longer valid solution on chain. @@ -3489,7 +3616,7 @@ fn is_sorted_and_unique(list: &[u32]) -> bool { fn to_invalid(error_with_post_info: DispatchErrorWithPostInfo) -> InvalidTransaction { let error = error_with_post_info.error; let error_number = match error { - DispatchError::Module { error, ..} => error, + DispatchError::Module { error, .. } => error, _ => 0, }; InvalidTransaction::Custom(error_number) diff --git a/frame/staking/src/migration.rs b/frame/staking/src/migration.rs index 97c4ff4c025b1..9425712783be2 100644 --- a/frame/staking/src/migration.rs +++ b/frame/staking/src/migration.rs @@ -4,61 +4,63 @@ use frame_support::weights::Weight; /// Deprecated storages used for migration only. mod deprecated { - use crate::{Trait, BalanceOf, SessionIndex, Exposure}; - use codec::{Encode, Decode}; - use frame_support::{decl_module, decl_storage}; - use sp_std::prelude::*; - - // edgeware uses `u64` for `Moment` - type Moment = u64; - - /// Reward points of an era. Used to split era total payout between validators. - #[derive(Encode, Decode, Default)] - pub struct EraPoints { - /// Total number of points. Equals the sum of reward points for each validator. - pub total: u32, - /// The reward points earned by a given validator. The index of this vec corresponds to the - /// index into the current validator set. - pub individual: Vec, - } - - decl_module! { - pub struct Module for enum Call where origin: T::Origin { } - } - - decl_storage! { - pub trait Store for Module as Staking { - pub SlotStake: BalanceOf; - - /// The currently elected validator set keyed by stash account ID. - pub CurrentElected: Vec; - - /// The start of the current era. - pub CurrentEraStart: Moment; - - /// The session index at which the current era started. - pub CurrentEraStartSessionIndex: SessionIndex; - - /// Rewards for the current era. Using indices of current elected set. - pub CurrentEraPointsEarned: EraPoints; - - /// Nominators for a particular account that is in action right now. You can't iterate - /// through validators here, but you can find them in the Session module. - /// - /// This is keyed by the stash account. - pub Stakers: map hasher(opaque_blake2_256) T::AccountId => Exposure>; - } - } + use crate::{BalanceOf, Exposure, SessionIndex, Trait}; + use codec::{Decode, Encode}; + use frame_support::{decl_module, decl_storage}; + use sp_std::prelude::*; + + // edgeware uses `u64` for `Moment` + pub type Moment = u64; + + /// Reward points of an era. Used to split era total payout between validators. + #[derive(Encode, Decode, Default)] + pub struct EraPoints { + /// Total number of points. Equals the sum of reward points for each validator. + pub total: u32, + /// The reward points earned by a given validator. The index of this vec corresponds to the + /// index into the current validator set. + pub individual: Vec, + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin { } + } + + decl_storage! { + pub trait Store for Module as Staking { + pub SlotStake: BalanceOf; + + /// The currently elected validator set keyed by stash account ID. + pub CurrentElected: Vec; + + /// The start of the current era. + pub CurrentEraStart: Moment; + + /// The session index at which the current era started. + pub CurrentEraStartSessionIndex: SessionIndex; + + /// Rewards for the current era. Using indices of current elected set. + pub CurrentEraPointsEarned: EraPoints; + + /// Nominators for a particular account that is in action right now. You can't iterate + /// through validators here, but you can find them in the Session module. + /// + /// This is keyed by the stash account. + pub Stakers: map hasher(opaque_blake2_256) T::AccountId => Exposure>; + + pub StorageVersion: u32; + } + } } #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] struct OldStakingLedger { - pub stash: AccountId, - #[codec(compact)] - pub total: Balance, - #[codec(compact)] - pub active: Balance, - pub unlocking: Vec>, + pub stash: AccountId, + #[codec(compact)] + pub total: Balance, + #[codec(compact)] + pub active: Balance, + pub unlocking: Vec>, } /// Update storages to current version @@ -88,65 +90,560 @@ struct OldStakingLedger { // // The edgeware migration is so big we just assume it consumes the whole block. pub fn migrate_to_simple_payouts() -> Weight { - sp_runtime::print("🕊️ Migrating Staking..."); - let current_era_start_index = deprecated::CurrentEraStartSessionIndex::get(); - let current_era = as Store>::CurrentEra::get().unwrap_or(0); - let current_era_start = deprecated::CurrentEraStart::get(); - as Store>::ErasStartSessionIndex::insert(current_era, current_era_start_index); - as Store>::ActiveEra::put(ActiveEraInfo { - index: current_era, - start: Some(current_era_start), - }); - - let current_elected = deprecated::CurrentElected::::get(); - let mut current_total_stake = >::zero(); - for validator in ¤t_elected { - let exposure = deprecated::Stakers::::get(validator); - current_total_stake += exposure.total; - as Store>::ErasStakers::insert(current_era, validator, &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); - } - as Store>::ErasStakersClipped::insert(current_era, validator, exposure_clipped); - - let pref = as Store>::Validators::get(validator); - as Store>::ErasValidatorPrefs::insert(current_era, validator, pref); - } - as Store>::ErasTotalStake::insert(current_era, current_total_stake); - - let points = deprecated::CurrentEraPointsEarned::get(); - as Store>::ErasRewardPoints::insert(current_era, EraRewardPoints { - total: points.total, - individual: current_elected.iter().cloned().zip(points.individual.iter().cloned()).collect(), - }); - - let res = as Store>::Ledger::translate_values( - |old: OldStakingLedger>| StakingLedger { - stash: old.stash, - total: old.total, - active: old.active, - unlocking: old.unlocking, - claimed_rewards: vec![], - } - ); - if let Err(e) = res { - frame_support::print("Encountered error in migration of Staking::Ledger map."); - frame_support::print("The number of removed key/value is:"); - frame_support::print(e); - } - - // Kill old storages - deprecated::Stakers::::remove_all(); - deprecated::SlotStake::::kill(); - deprecated::CurrentElected::::kill(); - deprecated::CurrentEraStart::kill(); - deprecated::CurrentEraStartSessionIndex::kill(); - deprecated::CurrentEraPointsEarned::kill(); - - sp_runtime::print("🕊️ Done Staking."); - T::MaximumBlockWeight::get() -} \ No newline at end of file + let version = deprecated::StorageVersion::take(); + if version != 2 { + frame_support::runtime_print!("🕊️ Unexpected Staking StorageVersion: {}", version); + return 0; + } + sp_runtime::print("🕊️ Migrating Staking..."); + let current_era_start_index = deprecated::CurrentEraStartSessionIndex::get(); + let current_era = as Store>::CurrentEra::get().unwrap_or(0); + let current_era_start = deprecated::CurrentEraStart::get(); + as Store>::ErasStartSessionIndex::insert(current_era, current_era_start_index); + as Store>::ActiveEra::put(ActiveEraInfo { + index: current_era, + start: Some(current_era_start), + }); + + let current_elected = deprecated::CurrentElected::::get(); + let mut current_total_stake = >::zero(); + for validator in ¤t_elected { + let exposure = deprecated::Stakers::::get(validator); + current_total_stake += exposure.total; + as Store>::ErasStakers::insert(current_era, validator, &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); + } + as Store>::ErasStakersClipped::insert(current_era, validator, exposure_clipped); + + let pref = as Store>::Validators::get(validator); + as Store>::ErasValidatorPrefs::insert(current_era, validator, pref); + } + as Store>::ErasTotalStake::insert(current_era, current_total_stake); + + let points = deprecated::CurrentEraPointsEarned::get(); + as Store>::ErasRewardPoints::insert( + current_era, + EraRewardPoints { + total: points.total, + individual: current_elected + .iter() + .cloned() + .zip(points.individual.iter().cloned()) + .collect(), + }, + ); + + let res = as Store>::Ledger::translate_values( + |old: OldStakingLedger>| StakingLedger { + stash: old.stash, + total: old.total, + active: old.active, + unlocking: old.unlocking, + claimed_rewards: vec![], + }, + ); + if let Err(e) = res { + frame_support::print("Encountered error in migration of Staking::Ledger map."); + frame_support::print("The number of removed key/value is:"); + frame_support::print(e); + } + + // Kill old storages + deprecated::Stakers::::remove_all(); + deprecated::SlotStake::::kill(); + deprecated::CurrentElected::::kill(); + deprecated::CurrentEraStart::kill(); + deprecated::CurrentEraStartSessionIndex::kill(); + deprecated::CurrentEraPointsEarned::kill(); + + StorageVersion::put(Releases::V4_0_0); + + sp_runtime::print("🕊️ Done Staking."); + T::MaximumBlockWeight::get() +} + +#[cfg(test)] +mod tests { + use super::*; + use mock::*; + + use codec::Encode; + use frame_support::migration::put_storage_value; + use sp_core::blake2_256; + + use substrate_test_utils::assert_eq_uvec; + use sp_runtime::assert_eq_error_rate; + + #[test] + fn lazy_payouts_upgrade_works() { + ExtBuilder::default().build().execute_with(|| { + start_era(3); + + assert_eq!(Session::validators(), vec![21, 11]); + + // Insert fake data to check the migration + put_storage_value::>(b"Staking", b"CurrentElected", b"", vec![21, 31]); + put_storage_value::(b"Staking", b"CurrentEraStartSessionIndex", b"", 5); + put_storage_value::(b"Staking", b"CurrentEraStart", b"", 777); + put_storage_value( + b"Staking", + b"Stakers", + &blake2_256(&11u64.encode()), + Exposure:: { + total: 10, + own: 10, + others: vec![], + }, + ); + put_storage_value( + b"Staking", + b"Stakers", + &blake2_256(&21u64.encode()), + Exposure:: { + total: 20, + own: 20, + others: vec![], + }, + ); + put_storage_value( + b"Staking", + b"Stakers", + &blake2_256(&31u64.encode()), + Exposure:: { + total: 30, + own: 30, + others: vec![], + }, + ); + put_storage_value::<(u32, Vec)>( + b"Staking", + b"CurrentEraPointsEarned", + b"", + (12, vec![2, 10]), + ); + ::ErasStakers::remove_all(); + ::ErasStakersClipped::remove_all(); + deprecated::StorageVersion::put(2); + + // Perform upgrade + migrate_to_simple_payouts::(); + + assert_eq!(::StorageVersion::get(), Releases::V4_0_0); + + // Check migration + assert_eq!(::ErasStartSessionIndex::get(3).unwrap(), 5); + assert_eq!( + ::ErasRewardPoints::get(3), + EraRewardPoints { + total: 12, + individual: vec![(21, 2), (31, 10)].into_iter().collect(), + } + ); + assert_eq!(::ActiveEra::get().unwrap().index, 3); + assert_eq!(::ActiveEra::get().unwrap().start, Some(777)); + assert_eq!(::CurrentEra::get().unwrap(), 3); + assert_eq!( + ::ErasStakers::get(3, 11), + Exposure { + total: 0, + own: 0, + others: vec![], + } + ); + assert_eq!( + ::ErasStakers::get(3, 21), + Exposure { + total: 20, + own: 20, + others: vec![], + } + ); + assert_eq!( + ::ErasStakers::get(3, 31), + Exposure { + total: 30, + own: 30, + others: vec![], + } + ); + assert_eq!( + ::ErasStakersClipped::get(3, 11), + Exposure { + total: 0, + own: 0, + others: vec![], + } + ); + assert_eq!( + ::ErasStakersClipped::get(3, 21), + Exposure { + total: 20, + own: 20, + others: vec![], + } + ); + assert_eq!( + ::ErasStakersClipped::get(3, 31), + Exposure { + total: 30, + own: 30, + others: vec![], + } + ); + assert_eq!( + ::ErasValidatorPrefs::get(3, 21), + Staking::validators(21) + ); + assert_eq!( + ::ErasValidatorPrefs::get(3, 31), + Staking::validators(31) + ); + assert_eq!(::ErasTotalStake::get(3), 50); + }) + } + + #[test] + fn test_last_reward_migration() { + use sp_storage::Storage; + + let mut s = Storage::default(); + + let old_staking10 = OldStakingLedger:: { + stash: 0, + total: 10, + active: 10, + unlocking: vec![UnlockChunk { value: 1234, era: 56 }], + }; + + let old_staking11 = OldStakingLedger:: { + stash: 1, + total: 0, + active: 0, + unlocking: vec![], + }; + + let old_staking12 = OldStakingLedger:: { + stash: 2, + total: 100, + active: 100, + unlocking: vec![ + UnlockChunk { value: 9876, era: 54 }, + UnlockChunk { value: 98, era: 76 }, + ], + }; + + let old_staking13 = OldStakingLedger:: { + stash: 3, + total: 100, + active: 100, + unlocking: vec![], + }; + + let data = vec![ + ( + Ledger::::hashed_key_for(10), + old_staking10.encode().to_vec(), + ), + ( + Ledger::::hashed_key_for(11), + old_staking11.encode().to_vec(), + ), + ( + Ledger::::hashed_key_for(12), + old_staking12.encode().to_vec(), + ), + ( + Ledger::::hashed_key_for(13), + old_staking13.encode().to_vec(), + ), + ]; + + s.top = data.into_iter().collect(); + sp_io::TestExternalities::new(s).execute_with(|| { + HistoryDepth::put(84); + CurrentEra::put(99); + let nominations = Nominations:: { + targets: vec![], + submitted_in: 0, + suppressed: false, + }; + Nominators::::insert(3, nominations); + Bonded::::insert(3, 13); + deprecated::StorageVersion::put(2); + // migrate + migrate_to_simple_payouts::(); + // Test staker out of range + assert_eq!( + Ledger::::get(10), + Some(StakingLedger { + stash: 0, + total: 10, + active: 10, + unlocking: vec![UnlockChunk { value: 1234, era: 56 }], + claimed_rewards: vec![], + }) + ); + // Test staker none + assert_eq!( + Ledger::::get(11), + Some(StakingLedger { + stash: 1, + total: 0, + active: 0, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); + // Test staker migration + assert_eq!( + Ledger::::get(12), + Some(StakingLedger { + stash: 2, + total: 100, + active: 100, + unlocking: vec![ + UnlockChunk { value: 9876, era: 54 }, + UnlockChunk { value: 98, era: 76 } + ], + claimed_rewards: vec![], + }) + ); + // Test nominator migration + assert_eq!( + Ledger::::get(13), + Some(StakingLedger { + stash: 3, + total: 100, + active: 100, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); + }); + } + + #[test] + fn rewards_should_work_before_migration() { + // should check that before migration: + // * rewards get recorded per session + // * rewards get paid per Era + // * Check that nominators are also rewarded + ExtBuilder::default().nominate(true).build().execute_with(|| { + let init_balance_10 = Balances::total_balance(&10); + let init_balance_11 = Balances::total_balance(&11); + let init_balance_20 = Balances::total_balance(&20); + let init_balance_21 = Balances::total_balance(&21); + let init_balance_100 = Balances::total_balance(&100); + let init_balance_101 = Balances::total_balance(&101); + + // Check state + Payee::::insert(11, RewardDestination::Controller); + Payee::::insert(21, RewardDestination::Controller); + Payee::::insert(101, RewardDestination::Controller); + + >::reward_by_ids(vec![(11, 50)]); + >::reward_by_ids(vec![(11, 50)]); + // This is the second validator of the current elected set. + >::reward_by_ids(vec![(21, 50)]); + + // Compute total payout now for whole duration as other parameter won't change + let total_payout_0 = current_total_payout_for_duration(3 * 1000); + assert!(total_payout_0 > 10); // Test is meaningful if reward something + + start_session(1); + + assert_eq!(Balances::total_balance(&10), init_balance_10); + assert_eq!(Balances::total_balance(&11), init_balance_11); + assert_eq!(Balances::total_balance(&20), init_balance_20); + assert_eq!(Balances::total_balance(&21), init_balance_21); + assert_eq!(Balances::total_balance(&100), init_balance_100); + assert_eq!(Balances::total_balance(&101), init_balance_101); + assert_eq_uvec!(Session::validators(), vec![11, 21]); + assert_eq!( + Staking::eras_reward_points(Staking::active_era().unwrap().index), + EraRewardPoints { + total: 50 * 3, + individual: vec![(11, 100), (21, 50)].into_iter().collect(), + } + ); + let part_for_10 = Perbill::from_rational_approximation::(1000, 1125); + let part_for_20 = Perbill::from_rational_approximation::(1000, 1375); + let part_for_100_from_10 = Perbill::from_rational_approximation::(125, 1125); + let part_for_100_from_20 = Perbill::from_rational_approximation::(375, 1375); + + start_session(2); + start_session(3); + + assert_eq!(Staking::active_era().unwrap().index, 1); + mock::make_all_reward_payment_before_migration(0); + + assert_eq_error_rate!( + Balances::total_balance(&10), + init_balance_10 + part_for_10 * total_payout_0 * 2 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); + assert_eq_error_rate!( + Balances::total_balance(&20), + init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); + assert_eq_error_rate!( + Balances::total_balance(&100), + init_balance_100 + + part_for_100_from_10 * total_payout_0 * 2 / 3 + + part_for_100_from_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); + + assert_eq_uvec!(Session::validators(), vec![11, 21]); + >::reward_by_ids(vec![(11, 1)]); + + // Compute total payout now for whole duration as other parameter won't change + let total_payout_1 = current_total_payout_for_duration(3 * 1000); + assert!(total_payout_1 > 10); // Test is meaningful if reward something + + mock::start_era(2); + mock::make_all_reward_payment_before_migration(1); + + assert_eq_error_rate!( + Balances::total_balance(&10), + init_balance_10 + part_for_10 * (total_payout_0 * 2 / 3 + total_payout_1), + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); + assert_eq_error_rate!( + Balances::total_balance(&20), + init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); + assert_eq_error_rate!( + Balances::total_balance(&100), + init_balance_100 + + part_for_100_from_10 * (total_payout_0 * 2 / 3 + total_payout_1) + + part_for_100_from_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); + }); + } + + #[test] + fn migrate_era_should_work() { + // should check that before and after migration: + // * rewards get recorded per session + // * rewards get paid per Era + // * Check that nominators are also rewarded + ExtBuilder::default().nominate(true).build().execute_with(|| { + let init_balance_10 = Balances::total_balance(&10); + let init_balance_11 = Balances::total_balance(&11); + let init_balance_20 = Balances::total_balance(&20); + let init_balance_21 = Balances::total_balance(&21); + let init_balance_100 = Balances::total_balance(&100); + let init_balance_101 = Balances::total_balance(&101); + + // Check state + Payee::::insert(11, RewardDestination::Controller); + Payee::::insert(21, RewardDestination::Controller); + Payee::::insert(101, RewardDestination::Controller); + + >::reward_by_ids(vec![(11, 50)]); + >::reward_by_ids(vec![(11, 50)]); + // This is the second validator of the current elected set. + >::reward_by_ids(vec![(21, 50)]); + + // Compute total payout now for whole duration as other parameter won't change + let total_payout_0 = current_total_payout_for_duration(3 * 1000); + assert!(total_payout_0 > 10); // Test is meaningful if reward something + + start_session(1); + + assert_eq!(Balances::total_balance(&10), init_balance_10); + assert_eq!(Balances::total_balance(&11), init_balance_11); + assert_eq!(Balances::total_balance(&20), init_balance_20); + assert_eq!(Balances::total_balance(&21), init_balance_21); + assert_eq!(Balances::total_balance(&100), init_balance_100); + assert_eq!(Balances::total_balance(&101), init_balance_101); + assert_eq_uvec!(Session::validators(), vec![11, 21]); + assert_eq!( + Staking::eras_reward_points(Staking::active_era().unwrap().index), + EraRewardPoints { + total: 50 * 3, + individual: vec![(11, 100), (21, 50)].into_iter().collect(), + } + ); + let part_for_10 = Perbill::from_rational_approximation::(1000, 1125); + let part_for_20 = Perbill::from_rational_approximation::(1000, 1375); + let part_for_100_from_10 = Perbill::from_rational_approximation::(125, 1125); + let part_for_100_from_20 = Perbill::from_rational_approximation::(375, 1375); + + start_session(2); + start_session(3); + + assert_eq!(Staking::active_era().unwrap().index, 1); + mock::make_all_reward_payment_before_migration(0); + + assert_eq_error_rate!( + Balances::total_balance(&10), + init_balance_10 + part_for_10 * total_payout_0 * 2 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); + assert_eq_error_rate!( + Balances::total_balance(&20), + init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); + assert_eq_error_rate!( + Balances::total_balance(&100), + init_balance_100 + + part_for_100_from_10 * total_payout_0 * 2 / 3 + + part_for_100_from_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); + + assert_eq_uvec!(Session::validators(), vec![11, 21]); + >::reward_by_ids(vec![(11, 1)]); + + // Compute total payout now for whole duration as other parameter won't change + let total_payout_1 = current_total_payout_for_duration(3 * 1000); + assert!(total_payout_1 > 10); // Test is meaningful if reward something + + mock::start_era(2); + mock::make_all_reward_payment(1); + + assert_eq_error_rate!( + Balances::total_balance(&10), + init_balance_10 + part_for_10 * (total_payout_0 * 2 / 3 + total_payout_1), + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); + assert_eq_error_rate!( + Balances::total_balance(&20), + init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); + assert_eq_error_rate!( + Balances::total_balance(&100), + init_balance_100 + + part_for_100_from_10 * (total_payout_0 * 2 / 3 + total_payout_1) + + part_for_100_from_20 * total_payout_0 * 1 / 3, + 2 + ); + assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); + }); + } +} diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 048e187bb6ade..6db83c53971b9 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -17,25 +17,27 @@ //! Test utilities -use std::{collections::HashSet, cell::RefCell}; -use sp_runtime::Perbill; -use sp_runtime::curve::PiecewiseLinear; -use sp_runtime::traits::{IdentityLookup, Convert, SaturatedConversion, Zero}; -use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; -use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; -use sp_core::H256; +use crate::*; use frame_support::{ - assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event, - StorageValue, StorageMap, StorageDoubleMap, IterableStorageMap, - traits::{Currency, Get, FindAuthor, OnFinalize, OnInitialize}, - weights::{Weight, constants::RocksDbWeight}, + assert_ok, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, + traits::{Currency, FindAuthor, Get, OnFinalize, OnInitialize}, + weights::{constants::RocksDbWeight, Weight}, + IterableStorageMap, StorageDoubleMap, StorageMap, StorageValue, }; +use sp_core::H256; use sp_io; use sp_npos_elections::{ - build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, ElectionScore, - VoteWeight, + build_support_map, evaluate_support, reduce, ElectionScore, ExtendedBalance, StakedAssignment, VoteWeight, }; -use crate::*; +use sp_runtime::curve::PiecewiseLinear; +use sp_runtime::testing::{Header, TestXt, UintAuthorityId}; +use sp_runtime::traits::{Convert, IdentityLookup, SaturatedConversion, Zero}; +use sp_runtime::Perbill; +use sp_staking::{ + offence::{OffenceDetails, OnOffenceHandler}, + SessionIndex, +}; +use std::{cell::RefCell, collections::HashSet}; pub const INIT_TIMESTAMP: u64 = 30_000; @@ -74,17 +76,18 @@ impl pallet_session::OneSessionHandler for OtherSessionHandler { type Key = UintAuthorityId; fn on_genesis_session<'a, I: 'a>(_: I) - where I: Iterator, AccountId: 'a {} + where + I: Iterator, + AccountId: 'a, + { + } - fn on_new_session<'a, I: 'a>(_: bool, validators: I, _: I,) - where I: Iterator, AccountId: 'a + fn on_new_session<'a, I: 'a>(_: bool, validators: I, _: I) + where + I: Iterator, + AccountId: 'a, { - SESSION.with(|x| { - *x.borrow_mut() = ( - validators.map(|x| x.0.clone()).collect(), - HashSet::new(), - ) - }); + SESSION.with(|x| *x.borrow_mut() = (validators.map(|x| x.0.clone()).collect(), HashSet::new())); } fn on_disabled(validator_index: usize) { @@ -183,7 +186,8 @@ impl_outer_event! { pub struct Author11; impl FindAuthor for Author11 { fn find_author<'a, I>(_digests: I) -> Option - where I: 'a + IntoIterator, + where + I: 'a + IntoIterator, { Some(11) } @@ -329,7 +333,8 @@ impl Trait for Test { type UnsignedPriority = UnsignedPriority; } -impl frame_system::offchain::SendTransactionTypes for Test where +impl frame_system::offchain::SendTransactionTypes for Test +where Call: From, { type OverarchingCall = Call; @@ -434,9 +439,7 @@ impl ExtBuilder { self } pub fn offchain_phragmen_ext(self) -> Self { - self.session_per_era(4) - .session_length(5) - .election_lookahead(3) + self.session_per_era(4).session_length(5).election_lookahead(3) } pub fn set_associated_constants(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); @@ -452,11 +455,7 @@ impl ExtBuilder { let mut storage = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); - let balance_factor = if self.existential_deposit > 1 { - 256 - } else { - 1 - }; + let balance_factor = if self.existential_deposit > 1 { 256 } else { 1 }; let num_validators = self.num_validators.unwrap_or(self.validator_count); let validators = (0..num_validators) @@ -482,12 +481,17 @@ impl ExtBuilder { // This allows us to have a total_payout different from 0. (999, 1_000_000_000_000), ], - }.assimilate_storage(&mut storage); + } + .assimilate_storage(&mut storage); let mut stakers = vec![]; if self.has_stakers { let stake_21 = if self.fair { 1000 } else { 2000 }; - let stake_31 = if self.validator_pool { balance_factor * 1000 } else { 1 }; + let stake_31 = if self.validator_pool { + balance_factor * 1000 + } else { + 1 + }; let status_41 = if self.validator_pool { StakerStatus::::Validator } else { @@ -496,15 +500,25 @@ impl ExtBuilder { let nominated = if self.nominate { vec![11, 21] } else { vec![] }; stakers = vec![ // (stash, controller, staked_amount, status) - (11, 10, balance_factor * 1000, StakerStatus::::Validator), + ( + 11, + 10, + balance_factor * 1000, + StakerStatus::::Validator, + ), (21, 20, stake_21, StakerStatus::::Validator), (31, 30, stake_31, StakerStatus::::Validator), (41, 40, balance_factor * 1000, status_41), // nominator - (101, 100, balance_factor * 500, StakerStatus::::Nominator(nominated)) + ( + 101, + 100, + balance_factor * 500, + StakerStatus::::Nominator(nominated), + ), ]; } - let _ = GenesisConfig::{ + let _ = GenesisConfig:: { stakers: stakers, validator_count: self.validator_count, minimum_validator_count: self.minimum_validator_count, @@ -515,12 +529,20 @@ impl ExtBuilder { .assimilate_storage(&mut storage); let _ = pallet_session::GenesisConfig:: { - keys: validators.iter().map(|x| ( - *x, - *x, - SessionKeys { other: UintAuthorityId(*x as u64) } - )).collect(), - }.assimilate_storage(&mut storage); + keys: validators + .iter() + .map(|x| { + ( + *x, + *x, + SessionKeys { + other: UintAuthorityId(*x as u64), + }, + ) + }) + .collect(), + } + .assimilate_storage(&mut storage); let mut ext = sp_io::TestExternalities::from(storage); ext.execute_with(|| { @@ -587,42 +609,43 @@ fn check_nominators() { // in if the nomination was submitted before the current era. let era = active_era(); >::iter() - .filter_map(|(nominator, nomination)| + .filter_map(|(nominator, nomination)| { if nomination.submitted_in > era { Some(nominator) } else { None + } }) .for_each(|nominator| { - // must be bonded. - assert_is_stash(nominator); - let mut sum = 0; - Session::validators() - .iter() - .map(|v| Staking::eras_stakers(era, v)) - .for_each(|e| { - let individual = e.others.iter().filter(|e| e.who == nominator).collect::>(); - let len = individual.len(); - match len { - 0 => { /* not supporting this validator at all. */ }, - 1 => sum += individual[0].value, - _ => panic!("nominator cannot back a validator more than once."), - }; - }); - - let nominator_stake = Staking::slashable_balance_of(&nominator); - // a nominator cannot over-spend. - assert!( - nominator_stake >= sum, - "failed: Nominator({}) stake({}) >= sum divided({})", - nominator, - nominator_stake, - sum, - ); - - let diff = nominator_stake - sum; - assert!(diff < 100); - }); + // must be bonded. + assert_is_stash(nominator); + let mut sum = 0; + Session::validators() + .iter() + .map(|v| Staking::eras_stakers(era, v)) + .for_each(|e| { + let individual = e.others.iter().filter(|e| e.who == nominator).collect::>(); + let len = individual.len(); + match len { + 0 => { /* not supporting this validator at all. */ } + 1 => sum += individual[0].value, + _ => panic!("nominator cannot back a validator more than once."), + }; + }); + + let nominator_stake = Staking::slashable_balance_of(&nominator); + // a nominator cannot over-spend. + assert!( + nominator_stake >= sum, + "failed: Nominator({}) stake({}) >= sum divided({})", + nominator, + nominator_stake, + sum, + ); + + let diff = nominator_stake - sum; + assert!(diff < 100); + }); } fn assert_is_stash(acc: AccountId) { @@ -645,18 +668,10 @@ pub(crate) fn bond_validator(stash: AccountId, ctrl: AccountId, val: Balance) { val, RewardDestination::Controller, )); - assert_ok!(Staking::validate( - Origin::signed(ctrl), - ValidatorPrefs::default() - )); + assert_ok!(Staking::validate(Origin::signed(ctrl), ValidatorPrefs::default())); } -pub(crate) fn bond_nominator( - stash: AccountId, - ctrl: AccountId, - val: Balance, - target: Vec, -) { +pub(crate) fn bond_nominator(stash: AccountId, ctrl: AccountId, val: Balance, target: Vec) { let _ = Balances::make_free_balance_be(&stash, val); let _ = Balances::make_free_balance_be(&ctrl, val); assert_ok!(Staking::bond( @@ -686,7 +701,11 @@ pub(crate) fn advance_session() { } pub(crate) fn start_session(session_index: SessionIndex) { - assert_eq!(>::get(), 1, "start_session can only be used with session length 1."); + assert_eq!( + >::get(), + 1, + "start_session can only be used with session length 1." + ); for i in Session::current_index()..session_index { Staking::on_finalize(System::block_number()); System::set_block_number((i + 1).into()); @@ -713,7 +732,8 @@ pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance { Staking::eras_total_stake(Staking::active_era().unwrap().index), Balances::total_issuance(), duration, - ).0 + ) + .0 } pub(crate) fn reward_all_elected() { @@ -732,10 +752,7 @@ pub(crate) fn validator_controllers() -> Vec { } pub(crate) fn on_offence_in_era( - offenders: &[OffenceDetails< - AccountId, - pallet_session::historical::IdentificationTuple, - >], + offenders: &[OffenceDetails>], slash_fraction: &[Perbill], era: EraIndex, ) { @@ -750,12 +767,12 @@ pub(crate) fn on_offence_in_era( } if Staking::active_era().unwrap().index == era { - let _ = - Staking::on_offence( - offenders, - slash_fraction, - Staking::eras_start_session_index(era).unwrap() - ).unwrap(); + let _ = Staking::on_offence( + offenders, + slash_fraction, + Staking::eras_start_session_index(era).unwrap(), + ) + .unwrap(); } else { panic!("cannot slash in era {}", era); } @@ -771,12 +788,13 @@ pub(crate) fn on_offence_now( pub(crate) fn add_slash(who: &AccountId) { on_offence_now( - &[ - OffenceDetails { - offender: (who.clone(), Staking::eras_stakers(Staking::active_era().unwrap().index, who.clone())), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + who.clone(), + Staking::eras_stakers(Staking::active_era().unwrap().index, who.clone()), + ), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); } @@ -796,8 +814,7 @@ pub(crate) fn horrible_phragmen_with_post_processing( // add nominator stuff >::iter().for_each(|(who, nomination)| { nomination.targets.iter().for_each(|v| { - *backing_stake_of.entry(*v).or_insert(Zero::zero()) += - Staking::slashable_balance_of(&who) + *backing_stake_of.entry(*v).or_insert(Zero::zero()) += Staking::slashable_balance_of(&who) }) }); @@ -873,10 +890,16 @@ pub(crate) fn horrible_phragmen_with_post_processing( let snapshot_validators = Staking::snapshot_validators().unwrap(); let snapshot_nominators = Staking::snapshot_nominators().unwrap(); let nominator_index = |a: &AccountId| -> Option { - snapshot_nominators.iter().position(|x| x == a).map(|i| i as NominatorIndex) + snapshot_nominators + .iter() + .position(|x| x == a) + .map(|i| i as NominatorIndex) }; let validator_index = |a: &AccountId| -> Option { - snapshot_validators.iter().position(|x| x == a).map(|i| i as ValidatorIndex) + snapshot_validators + .iter() + .position(|x| x == a) + .map(|i| i as ValidatorIndex) }; // convert back to ratio assignment. This takes less space. @@ -884,11 +907,13 @@ pub(crate) fn horrible_phragmen_with_post_processing( sp_npos_elections::assignment_staked_to_ratio::(staked_assignment); let compact = - CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) - .unwrap(); + CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index).unwrap(); // winner ids to index - let winners = winners.into_iter().map(|w| validator_index(&w).unwrap()).collect::>(); + let winners = winners + .into_iter() + .map(|w| validator_index(&w).unwrap()) + .collect::>(); (compact, winners, score) } @@ -901,28 +926,19 @@ pub(crate) fn prepare_submission_with( tweak: impl FnOnce(&mut Vec>), ) -> (CompactAssignments, Vec, ElectionScore) { // run election on the default stuff. - let sp_npos_elections::ElectionResult { - winners, - assignments, - } = Staking::do_phragmen::().unwrap(); + let sp_npos_elections::ElectionResult { winners, assignments } = + Staking::do_phragmen::().unwrap(); let winners = sp_npos_elections::to_without_backing(winners); let stake_of = |who: &AccountId| -> VoteWeight { - >::convert( - Staking::slashable_balance_of(&who) - ) + >::convert(Staking::slashable_balance_of(&who)) }; let mut staked = sp_npos_elections::assignment_ratio_to_staked(assignments, stake_of); let (mut support_map, _) = build_support_map::(&winners, &staked); if iterations > 0 { - sp_npos_elections::balance_solution( - &mut staked, - &mut support_map, - Zero::zero(), - iterations, - ); + sp_npos_elections::balance_solution(&mut staked, &mut support_map, Zero::zero(), iterations); } // apply custom tweaks. awesome for testing. @@ -936,22 +952,22 @@ pub(crate) fn prepare_submission_with( let snapshot_validators = Staking::snapshot_validators().expect("snapshot not created."); let snapshot_nominators = Staking::snapshot_nominators().expect("snapshot not created."); let nominator_index = |a: &AccountId| -> Option { - snapshot_nominators - .iter() - .position(|x| x == a) - .map_or_else( - || { println!("unable to find nominator index for {:?}", a); None }, - |i| Some(i as NominatorIndex), - ) + snapshot_nominators.iter().position(|x| x == a).map_or_else( + || { + println!("unable to find nominator index for {:?}", a); + None + }, + |i| Some(i as NominatorIndex), + ) }; let validator_index = |a: &AccountId| -> Option { - snapshot_validators - .iter() - .position(|x| x == a) - .map_or_else( - || { println!("unable to find validator index for {:?}", a); None }, - |i| Some(i as ValidatorIndex), - ) + snapshot_validators.iter().position(|x| x == a).map_or_else( + || { + println!("unable to find validator index for {:?}", a); + None + }, + |i| Some(i as ValidatorIndex), + ) }; let assignments_reduced = sp_npos_elections::assignment_staked_to_ratio(staked); @@ -963,28 +979,62 @@ pub(crate) fn prepare_submission_with( Staking::slashable_balance_of_vote_weight, ); - let (support_map, _) = build_support_map::( - winners.as_slice(), - staked.as_slice(), - ); + let (support_map, _) = build_support_map::(winners.as_slice(), staked.as_slice()); evaluate_support::(&support_map) }; - let compact = - CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) - .map_err(|e| { println!("error in compact: {:?}", e); e }) - .expect("Failed to create compact"); - + let compact = CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) + .map_err(|e| { + println!("error in compact: {:?}", e); + e + }) + .expect("Failed to create compact"); // winner ids to index - let winners = winners.into_iter().map(|w| validator_index(&w).unwrap()).collect::>(); + let winners = winners + .into_iter() + .map(|w| validator_index(&w).unwrap()) + .collect::>(); (compact, winners, score) } +pub fn make_all_reward_payment_before_migration(era: EraIndex) { + use std::collections::HashMap; + + let validators_with_reward = ErasRewardPoints::::get(era).individual.keys() + .cloned() + .collect::>(); + // reward nominators + let mut nominator_controllers = HashMap::new(); + for validator in Staking::eras_reward_points(era).individual.keys() { + let validator_exposure = Staking::eras_stakers_clipped(era, validator); + for (nom_index, nom) in validator_exposure.others.iter().enumerate() { + if let Some(nom_ctrl) = Staking::bonded(nom.who) { + nominator_controllers.entry(nom_ctrl) + .or_insert(vec![]) + .push((validator.clone(), nom_index as u32)); + } + } + } + for (nominator_controller, validators_with_nom_index) in nominator_controllers { + assert_ok!(Staking::payout_nominator( + Origin::signed(nominator_controller), + era, + validators_with_nom_index, + )); + } + // reward validators + for validator_controller in validators_with_reward.iter().filter_map(Staking::bonded) { + assert_ok!(Staking::payout_validator(Origin::signed(validator_controller), era)); + } +} + /// Make all validator and nominator request their payment pub(crate) fn make_all_reward_payment(era: EraIndex) { - let validators_with_reward = ErasRewardPoints::::get(era).individual.keys() + let validators_with_reward = ErasRewardPoints::::get(era) + .individual + .keys() .cloned() .collect::>(); @@ -1017,13 +1067,17 @@ macro_rules! assert_session_era { } pub(crate) fn staking_events() -> Vec> { - System::events().into_iter().map(|r| r.event).filter_map(|e| { - if let MetaEvent::staking(inner) = e { - Some(inner) - } else { - None - } - }).collect() + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }) + .collect() } pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 23453e0524aa4..096c609f3ccf5 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -17,19 +17,21 @@ //! Helpers for offchain worker election. -use codec::Decode; use crate::{ - Call, CompactAssignments, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex, - ElectionSize, + Call, CompactAssignments, ElectionSize, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex, }; +use codec::Decode; +use frame_support::traits::Get; use frame_system::offchain::SubmitTransaction; use sp_npos_elections::{ - build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, ElectionResult, - ElectionScore, balance_solution, + balance_solution, build_support_map, evaluate_support, reduce, Assignment, ElectionResult, ElectionScore, + ExtendedBalance, }; use sp_runtime::offchain::storage::StorageValueRef; -use sp_runtime::{PerThing, RuntimeDebug, traits::{TrailingZeroInput, Zero}}; -use frame_support::traits::Get; +use sp_runtime::{ + traits::{TrailingZeroInput, Zero}, + PerThing, RuntimeDebug, +}; use sp_std::{convert::TryInto, prelude::*}; /// Error types related to the offchain election machinery. @@ -68,29 +70,24 @@ pub(crate) const DEFAULT_LONGEVITY: u64 = 25; /// don't run twice within a window of length [`OFFCHAIN_REPEAT`]. /// /// Returns `Ok(())` if offchain worker should happen, `Err(reason)` otherwise. -pub(crate) fn set_check_offchain_execution_status( - now: T::BlockNumber, -) -> Result<(), &'static str> { +pub(crate) fn set_check_offchain_execution_status(now: T::BlockNumber) -> Result<(), &'static str> { let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); let threshold = T::BlockNumber::from(OFFCHAIN_REPEAT); - let mutate_stat = - storage.mutate::<_, &'static str, _>(|maybe_head: Option>| { - match maybe_head { - Some(Some(head)) if now < head => Err("fork."), - Some(Some(head)) if now >= head && now <= head + threshold => { - Err("recently executed.") - } - Some(Some(head)) if now > head + threshold => { - // we can run again now. Write the new head. - Ok(now) - } - _ => { - // value doesn't exists. Probably this node just booted up. Write, and run - Ok(now) - } + let mutate_stat = storage.mutate::<_, &'static str, _>(|maybe_head: Option>| { + match maybe_head { + Some(Some(head)) if now < head => Err("fork."), + Some(Some(head)) if now >= head && now <= head + threshold => Err("recently executed."), + Some(Some(head)) if now > head + threshold => { + // we can run again now. Write the new head. + Ok(now) } - }); + _ => { + // value doesn't exists. Probably this node just booted up. Write, and run + Ok(now) + } + } + }); match mutate_stat { // all good @@ -107,11 +104,8 @@ pub(crate) fn set_check_offchain_execution_status( /// unsigned transaction, without any signature. pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { // compute raw solution. Note that we use `OffchainAccuracy`. - let ElectionResult { - winners, - assignments, - } = >::do_phragmen::() - .ok_or(OffchainElectionError::ElectionFailed)?; + let ElectionResult { winners, assignments } = + >::do_phragmen::().ok_or(OffchainElectionError::ElectionFailed)?; // process and prepare it for submission. let (winners, compact, score, size) = prepare_submission::(assignments, winners, true)?; @@ -120,19 +114,12 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti let current_era = >::current_era().unwrap_or_default(); // send it. - let call = Call::submit_election_solution_unsigned( - winners, - compact, - score, - current_era, - size, - ).into(); + let call = Call::submit_election_solution_unsigned(winners, compact, score, current_era, size).into(); SubmitTransaction::>::submit_unsigned_transaction(call) .map_err(|_| OffchainElectionError::PoolSubmissionFailed) } - /// Takes an election result and spits out some data that can be submitted to the chain. /// /// This does a lot of stuff; read the inline comments. @@ -140,12 +127,16 @@ pub fn prepare_submission( assignments: Vec>, winners: Vec<(T::AccountId, ExtendedBalance)>, do_reduce: bool, -) -> Result<( - Vec, - CompactAssignments, - ElectionScore, - ElectionSize, -), OffchainElectionError> where +) -> Result< + ( + Vec, + CompactAssignments, + ElectionScore, + ElectionSize, + ), + OffchainElectionError, +> +where ExtendedBalance: From<::Inner>, { // make sure that the snapshot is available. @@ -187,13 +178,9 @@ pub fn prepare_submission( iterations @ _ => { let seed = sp_io::offchain::random_seed(); let iterations = ::decode(&mut TrailingZeroInput::new(seed.as_ref())) - .expect("input is padded with zeroes; qed") % iterations.saturating_add(1); - balance_solution( - &mut staked, - &mut support_map, - Zero::zero(), - iterations as usize, - ) + .expect("input is padded with zeroes; qed") + % iterations.saturating_add(1); + balance_solution(&mut staked, &mut support_map, Zero::zero(), iterations as usize) } }; @@ -224,19 +211,16 @@ pub fn prepare_submission( }; // compact encode the assignment. - let compact = CompactAssignments::from_assignment( - low_accuracy_assignment, - nominator_index, - validator_index, - ).map_err(|e| OffchainElectionError::from(e))?; + let compact = + CompactAssignments::from_assignment(low_accuracy_assignment, nominator_index, validator_index) + .map_err(|e| OffchainElectionError::from(e))?; // winners to index. Use a simple for loop for a more expressive early exit in case of error. let mut winners_indexed: Vec = Vec::with_capacity(winners.len()); for w in winners { if let Some(idx) = snapshot_validators.iter().position(|v| *v == w) { - let compact_index: ValidatorIndex = idx - .try_into() - .map_err(|_| OffchainElectionError::InvalidWinner)?; + let compact_index: ValidatorIndex = + idx.try_into().map_err(|_| OffchainElectionError::InvalidWinner)?; winners_indexed.push(compact_index); } else { return Err(OffchainElectionError::InvalidWinner); diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index 4e43b754b8e37..31f78d34e41b2 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -50,16 +50,20 @@ //! Based on research at https://research.web3.foundation/en/latest/polkadot/slashing/npos/ use super::{ - EraIndex, Trait, Module, Store, BalanceOf, Exposure, Perbill, SessionInterface, - NegativeImbalanceOf, UnappliedSlash, Error, + BalanceOf, EraIndex, Error, Exposure, Module, NegativeImbalanceOf, Perbill, SessionInterface, Store, + Trait, UnappliedSlash, }; -use sp_runtime::{traits::{Zero, Saturating}, RuntimeDebug, DispatchResult}; +use codec::{Decode, Encode}; use frame_support::{ - StorageMap, StorageDoubleMap, ensure, - traits::{Currency, OnUnbalanced, Imbalance}, + ensure, + traits::{Currency, Imbalance, OnUnbalanced}, + StorageDoubleMap, StorageMap, +}; +use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchResult, RuntimeDebug, }; use sp_std::vec::Vec; -use codec::{Encode, Decode}; /// The proportion of the slashing reward to be paid out on the first slashing detection. /// This is f_1 in the paper. @@ -118,7 +122,9 @@ impl SlashingSpans { // that internal state is unchanged. pub(crate) fn end_span(&mut self, now: EraIndex) -> bool { let next_start = now + 1; - if next_start <= self.last_start { return false } + if next_start <= self.last_start { + return false; + } let last_length = next_start - self.last_start; self.prior.insert(0, last_length); @@ -131,13 +137,21 @@ impl SlashingSpans { pub(crate) fn iter(&'_ self) -> impl Iterator + '_ { let mut last_start = self.last_start; let mut index = self.span_index; - let last = SlashingSpan { index, start: last_start, length: None }; + let last = SlashingSpan { + index, + start: last_start, + length: None, + }; let prior = self.prior.iter().cloned().map(move |length| { let start = last_start - length; last_start = start; index -= 1; - SlashingSpan { index, start, length: Some(length) } + SlashingSpan { + index, + start, + length: Some(length), + } }); sp_std::iter::once(last).chain(prior) @@ -153,7 +167,8 @@ impl SlashingSpans { // If this returns `Some`, then it includes a range start..end of all the span // indices which were pruned. fn prune(&mut self, window_start: EraIndex) -> Option<(SpanIndex, SpanIndex)> { - let old_idx = self.iter() + let old_idx = self + .iter() .skip(1) // skip ongoing span. .position(|span| span.length.map_or(false, |len| span.start + len <= window_start)); @@ -214,9 +229,9 @@ pub(crate) struct SlashParams<'a, T: 'a + Trait> { /// /// The pending slash record returned does not have initialized reporters. Those have /// to be set at a higher level, if any. -pub(crate) fn compute_slash(params: SlashParams) - -> Option>> -{ +pub(crate) fn compute_slash( + params: SlashParams, +) -> Option>> { let SlashParams { stash, slash, @@ -239,19 +254,13 @@ pub(crate) fn compute_slash(params: SlashParams) return None; } - let (prior_slash_p, _era_slash) = as Store>::ValidatorSlashInEra::get( - &slash_era, - stash, - ).unwrap_or((Perbill::zero(), Zero::zero())); + let (prior_slash_p, _era_slash) = as Store>::ValidatorSlashInEra::get(&slash_era, stash) + .unwrap_or((Perbill::zero(), Zero::zero())); // compare slash proportions rather than slash values to avoid issues due to rounding // error. if slash.deconstruct() > prior_slash_p.deconstruct() { - as Store>::ValidatorSlashInEra::insert( - &slash_era, - stash, - &(slash, own_slash), - ); + as Store>::ValidatorSlashInEra::insert(&slash_era, stash, &(slash, own_slash)); } else { // we slash based on the max in era - this new event is not the max, // so neither the validator or any nominators will need an update. @@ -273,10 +282,7 @@ pub(crate) fn compute_slash(params: SlashParams) reward_proportion, ); - let target_span = spans.compare_and_update_span_slash( - slash_era, - own_slash, - ); + let target_span = spans.compare_and_update_span_slash(slash_era, own_slash); if target_span == Some(spans.span_index()) { // misbehavior occurred within the current slashing span - take appropriate @@ -309,9 +315,7 @@ pub(crate) fn compute_slash(params: SlashParams) // doesn't apply any slash, but kicks out the validator if the misbehavior is from // the most recent slashing span. -fn kick_out_if_recent( - params: SlashParams, -) { +fn kick_out_if_recent(params: SlashParams) { // these are not updated by era-span or end-span. let mut reward_payout = Zero::zero(); let mut val_slashed = Zero::zero(); @@ -367,18 +371,12 @@ fn slash_nominators( let own_slash_by_validator = slash * nominator.value; let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior); - let mut era_slash = as Store>::NominatorSlashInEra::get( - &slash_era, - stash, - ).unwrap_or(Zero::zero()); + let mut era_slash = + as Store>::NominatorSlashInEra::get(&slash_era, stash).unwrap_or(Zero::zero()); era_slash += own_slash_difference; - as Store>::NominatorSlashInEra::insert( - &slash_era, - stash, - &era_slash, - ); + as Store>::NominatorSlashInEra::insert(&slash_era, stash, &era_slash); era_slash }; @@ -393,10 +391,7 @@ fn slash_nominators( reward_proportion, ); - let target_span = spans.compare_and_update_span_slash( - slash_era, - era_slash, - ); + let target_span = spans.compare_and_update_span_slash(slash_era, era_slash); if target_span == Some(spans.span_index()) { // End the span, but don't chill the nominator. its nomination @@ -497,8 +492,7 @@ impl<'a, T: 'a + Trait> InspectingSpans<'a, T> { span_record.slashed = slash; // compute reward. - let reward = REWARD_F1 - * (self.reward_proportion * slash).saturating_sub(span_record.paid_out); + let reward = REWARD_F1 * (self.reward_proportion * slash).saturating_sub(span_record.paid_out); self.add_slash(difference, slash_era); changed = true; @@ -529,7 +523,9 @@ impl<'a, T: 'a + Trait> InspectingSpans<'a, T> { impl<'a, T: 'a + Trait> Drop for InspectingSpans<'a, T> { fn drop(&mut self) { // only update on disk if we slashed this account. - if !self.dirty { return } + if !self.dirty { + return; + } if let Some((start, end)) = self.spans.prune(self.window_start) { for span_index in start..end { @@ -557,7 +553,10 @@ pub(crate) fn clear_stash_metadata( Some(s) => s, }; - ensure!(num_slashing_spans as usize >= spans.iter().count(), Error::::IncorrectSlashingSpans); + ensure!( + num_slashing_spans as usize >= spans.iter().count(), + Error::::IncorrectSlashingSpans + ); as Store>::SlashingSpans::remove(stash); @@ -606,9 +605,7 @@ pub fn do_slash( >::update_ledger(&controller, &ledger); // trigger the event - >::deposit_event( - super::RawEvent::Slash(stash.clone(), value) - ); + >::deposit_event(super::RawEvent::Slash(stash.clone(), value)); } } @@ -636,7 +633,6 @@ pub(crate) fn apply_slash(unapplied_slash: UnappliedSlash(reward_payout, slashed_imbalance, &unapplied_slash.reporters); } - /// Apply a reward payout to some reporters, paying the rewards out of the slashed imbalance. fn pay_reporters( reward_payout: BalanceOf, @@ -647,7 +643,7 @@ fn pay_reporters( // nobody to pay out to or nothing to pay; // just treat the whole value as slashed. T::Slash::on_unbalanced(slashed_imbalance); - return + return; } // take rewards out of the slashed imbalance. @@ -676,7 +672,11 @@ mod tests { #[test] fn span_contains_era() { // unbounded end - let span = SlashingSpan { index: 0, start: 1000, length: None }; + let span = SlashingSpan { + index: 0, + start: 1000, + length: None, + }; assert!(!span.contains_era(0)); assert!(!span.contains_era(999)); @@ -685,7 +685,11 @@ mod tests { assert!(span.contains_era(10000)); // bounded end - non-inclusive range. - let span = SlashingSpan { index: 0, start: 1000, length: Some(10) }; + let span = SlashingSpan { + index: 0, + start: 1000, + length: Some(10), + }; assert!(!span.contains_era(0)); assert!(!span.contains_era(999)); @@ -707,7 +711,11 @@ mod tests { assert_eq!( spans.iter().collect::>(), - vec![SlashingSpan { index: 0, start: 1000, length: None }], + vec![SlashingSpan { + index: 0, + start: 1000, + length: None + }], ); } @@ -723,11 +731,31 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, - SlashingSpan { index: 7, start: 973, length: Some(8) }, - SlashingSpan { index: 6, start: 963, length: Some(10) }, + SlashingSpan { + index: 10, + start: 1000, + length: None + }, + SlashingSpan { + index: 9, + start: 990, + length: Some(10) + }, + SlashingSpan { + index: 8, + start: 981, + length: Some(9) + }, + SlashingSpan { + index: 7, + start: 973, + length: Some(8) + }, + SlashingSpan { + index: 6, + start: 963, + length: Some(10) + }, ], ) } @@ -745,9 +773,21 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, + SlashingSpan { + index: 10, + start: 1000, + length: None + }, + SlashingSpan { + index: 9, + start: 990, + length: Some(10) + }, + SlashingSpan { + index: 8, + start: 981, + length: Some(9) + }, ], ); @@ -755,9 +795,21 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, + SlashingSpan { + index: 10, + start: 1000, + length: None + }, + SlashingSpan { + index: 9, + start: 990, + length: Some(10) + }, + SlashingSpan { + index: 8, + start: 981, + length: Some(9) + }, ], ); @@ -765,26 +817,42 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - SlashingSpan { index: 9, start: 990, length: Some(10) }, - SlashingSpan { index: 8, start: 981, length: Some(9) }, + SlashingSpan { + index: 10, + start: 1000, + length: None + }, + SlashingSpan { + index: 9, + start: 990, + length: Some(10) + }, + SlashingSpan { + index: 8, + start: 981, + length: Some(9) + }, ], ); assert_eq!(spans.prune(1000), Some((8, 10))); assert_eq!( spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 1000, length: None }, - ], + vec![SlashingSpan { + index: 10, + start: 1000, + length: None + },], ); assert_eq!(spans.prune(2000), None); assert_eq!( spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 2000, length: None }, - ], + vec![SlashingSpan { + index: 10, + start: 2000, + length: None + },], ); // now all in one shot. @@ -797,9 +865,11 @@ mod tests { assert_eq!(spans.prune(2000), Some((6, 10))); assert_eq!( spans.iter().collect::>(), - vec![ - SlashingSpan { index: 10, start: 2000, length: None }, - ], + vec![SlashingSpan { + index: 10, + start: 2000, + length: None + },], ); } @@ -817,8 +887,16 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 2, start: 11, length: None }, - SlashingSpan { index: 1, start: 10, length: Some(1) }, + SlashingSpan { + index: 2, + start: 11, + length: None + }, + SlashingSpan { + index: 1, + start: 10, + length: Some(1) + }, ], ); @@ -826,9 +904,21 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 3, start: 16, length: None }, - SlashingSpan { index: 2, start: 11, length: Some(5) }, - SlashingSpan { index: 1, start: 10, length: Some(1) }, + SlashingSpan { + index: 3, + start: 16, + length: None + }, + SlashingSpan { + index: 2, + start: 11, + length: Some(5) + }, + SlashingSpan { + index: 1, + start: 10, + length: Some(1) + }, ], ); @@ -837,9 +927,21 @@ mod tests { assert_eq!( spans.iter().collect::>(), vec![ - SlashingSpan { index: 3, start: 16, length: None }, - SlashingSpan { index: 2, start: 11, length: Some(5) }, - SlashingSpan { index: 1, start: 10, length: Some(1) }, + SlashingSpan { + index: 3, + start: 16, + length: None + }, + SlashingSpan { + index: 2, + start: 11, + length: Some(5) + }, + SlashingSpan { + index: 1, + start: 10, + length: Some(1) + }, ], ); } diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 86d137ac30aba..619f0ab450f58 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -18,12 +18,15 @@ //! Testing utils for staking. Provides some common functions to setup staking state, such as //! bonding validators, nominators, and generating different types of solutions. -use crate::*; use crate::Module as Staking; -use frame_benchmarking::{account}; +use crate::*; +use frame_benchmarking::account; use frame_system::RawOrigin; +use rand_chacha::{ + rand_core::{RngCore, SeedableRng}, + ChaChaRng, +}; use sp_io::hashing::blake2_256; -use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaChaRng}; use sp_npos_elections::*; const SEED: u32 = 0; @@ -39,16 +42,22 @@ pub fn create_funded_user(string: &'static str, n: u32, balance_factor } /// Create a stash and controller pair. -pub fn create_stash_controller(n: u32, balance_factor: u32) - -> Result<(T::AccountId, T::AccountId), &'static str> -{ +pub fn create_stash_controller( + n: u32, + balance_factor: u32, +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance_factor); let controller = create_funded_user::("controller", n, balance_factor); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); let reward_destination = RewardDestination::Staked; let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, reward_destination)?; - return Ok((stash, controller)) + Staking::::bond( + RawOrigin::Signed(stash.clone()).into(), + controller_lookup, + amount, + reward_destination, + )?; + return Ok((stash, controller)); } /// create `max` validators. @@ -57,7 +66,7 @@ pub fn create_validators( balance_factor: u32, ) -> Result::Source>, &'static str> { let mut validators: Vec<::Source> = Vec::with_capacity(max as usize); - for i in 0 .. max { + for i in 0..max { let (stash, controller) = create_stash_controller::(i, balance_factor)?; let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), @@ -88,13 +97,17 @@ pub fn create_validators_with_nominators_for_era( randomize_stake: bool, to_nominate: Option, ) -> Result::Source>, &'static str> { - let mut validators_stash: Vec<::Source> - = Vec::with_capacity(validators as usize); + let mut validators_stash: Vec<::Source> = + Vec::with_capacity(validators as usize); let mut rng = ChaChaRng::from_seed(SEED.using_encoded(blake2_256)); // Create validators - for i in 0 .. validators { - let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 }; + for i in 0..validators { + let balance_factor = if randomize_stake { + rng.next_u32() % 255 + 10 + } else { + 100u32 + }; let (v_stash, v_controller) = create_stash_controller::(i, balance_factor)?; let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), @@ -108,24 +121,28 @@ pub fn create_validators_with_nominators_for_era( let validator_choosen = validators_stash[0..to_nominate].to_vec(); // Create nominators - for j in 0 .. nominators { - let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 }; - let (_n_stash, n_controller) = create_stash_controller::( - u32::max_value() - j, - balance_factor, - )?; + for j in 0..nominators { + let balance_factor = if randomize_stake { + rng.next_u32() % 255 + 10 + } else { + 100u32 + }; + let (_n_stash, n_controller) = create_stash_controller::(u32::max_value() - j, balance_factor)?; // Have them randomly validate let mut available_validators = validator_choosen.clone(); let mut selected_validators: Vec<::Source> = Vec::with_capacity(edge_per_nominator); - for _ in 0 .. validators.min(edge_per_nominator as u32) { + for _ in 0..validators.min(edge_per_nominator as u32) { let selected = rng.next_u32() as usize % available_validators.len(); let validator = available_validators.remove(selected); selected_validators.push(validator); } - Staking::::nominate(RawOrigin::Signed(n_controller.clone()).into(), selected_validators)?; + Staking::::nominate( + RawOrigin::Signed(n_controller.clone()).into(), + selected_validators, + )?; } ValidatorCount::put(validators); @@ -133,12 +150,16 @@ pub fn create_validators_with_nominators_for_era( Ok(validator_choosen) } - /// Build a _really bad_ but acceptable solution for election. This should always yield a solution /// which has a less score than the seq-phragmen. pub fn get_weak_solution( do_reduce: bool, -) -> (Vec, CompactAssignments, ElectionScore, ElectionSize) { +) -> ( + Vec, + CompactAssignments, + ElectionScore, + ElectionSize, +) { let mut backing_stake_of: BTreeMap> = BTreeMap::new(); // self stake @@ -167,9 +188,9 @@ pub fn get_weak_solution( who: w.clone(), distribution: vec![( w.clone(), - , u64>>::convert( - >::slashable_balance_of(&w), - ) as ExtendedBalance, + , u64>>::convert(>::slashable_balance_of( + &w, + )) as ExtendedBalance, )], }) }); @@ -195,37 +216,28 @@ pub fn get_weak_solution( .and_then(|i| >::try_into(i).ok()) }; let stake_of = |who: &T::AccountId| -> VoteWeight { - , u64>>::convert( - >::slashable_balance_of(who), - ) + , u64>>::convert(>::slashable_balance_of(who)) }; // convert back to ratio assignment. This takes less space. - let low_accuracy_assignment: Vec> = - staked_assignments - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect(); + let low_accuracy_assignment: Vec> = staked_assignments + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); // re-calculate score based on what the chain will decode. let score = { - let staked = assignment_ratio_to_staked::<_, OffchainAccuracy, _>( - low_accuracy_assignment.clone(), - stake_of - ); + let staked = + assignment_ratio_to_staked::<_, OffchainAccuracy, _>(low_accuracy_assignment.clone(), stake_of); - let (support_map, _) = - build_support_map::(winners.as_slice(), staked.as_slice()); + let (support_map, _) = build_support_map::(winners.as_slice(), staked.as_slice()); evaluate_support::(&support_map) }; // compact encode the assignment. - let compact = CompactAssignments::from_assignment( - low_accuracy_assignment, - nominator_index, - validator_index, - ) - .unwrap(); + let compact = + CompactAssignments::from_assignment(low_accuracy_assignment, nominator_index, validator_index) + .unwrap(); // winners to index. let winners = winners @@ -252,28 +264,44 @@ pub fn get_weak_solution( /// worker code. pub fn get_seq_phragmen_solution( do_reduce: bool, -) -> (Vec, CompactAssignments, ElectionScore, ElectionSize) { - let sp_npos_elections::ElectionResult { - winners, - assignments, - } = >::do_phragmen::().unwrap(); +) -> ( + Vec, + CompactAssignments, + ElectionScore, + ElectionSize, +) { + let sp_npos_elections::ElectionResult { winners, assignments } = + >::do_phragmen::().unwrap(); offchain_election::prepare_submission::(assignments, winners, do_reduce).unwrap() } /// Returns a solution in which only one winner is elected with just a self vote. pub fn get_single_winner_solution( - winner: T::AccountId -) -> Result<(Vec, CompactAssignments, ElectionScore, ElectionSize), &'static str> { + winner: T::AccountId, +) -> Result< + ( + Vec, + CompactAssignments, + ElectionScore, + ElectionSize, + ), + &'static str, +> { let snapshot_validators = >::snapshot_validators().unwrap(); let snapshot_nominators = >::snapshot_nominators().unwrap(); - let val_index = snapshot_validators.iter().position(|x| *x == winner).ok_or("not a validator")?; - let nom_index = snapshot_nominators.iter().position(|x| *x == winner).ok_or("not a nominator")?; + let val_index = snapshot_validators + .iter() + .position(|x| *x == winner) + .ok_or("not a validator")?; + let nom_index = snapshot_nominators + .iter() + .position(|x| *x == winner) + .ok_or("not a nominator")?; let stake = >::slashable_balance_of(&winner); - let stake = , VoteWeight>>::convert(stake) - as ExtendedBalance; + let stake = , VoteWeight>>::convert(stake) as ExtendedBalance; let val_index = val_index as ValidatorIndex; let nom_index = nom_index as NominatorIndex; @@ -315,7 +343,7 @@ pub fn create_assignments_for_offchain( Vec<(T::AccountId, ExtendedBalance)>, Vec>, ), - &'static str + &'static str, > { let ratio = OffchainAccuracy::from_rational_approximation(1, MAX_NOMINATIONS); let assignments: Vec> = >::iter() @@ -326,11 +354,15 @@ pub fn create_assignments_for_offchain( }) .collect(); - ensure!(assignments.len() == num_assignments as usize, "must bench for `a` assignments"); + ensure!( + assignments.len() == num_assignments as usize, + "must bench for `a` assignments" + ); - let winners = winners.into_iter().map(|v| { - (::lookup(v).unwrap(), 0) - }).collect(); + let winners = winners + .into_iter() + .map(|v| (::lookup(v).unwrap(), 0)) + .collect(); Ok((winners, assignments)) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index eeac2c5c90e38..f4a681e13f4b4 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -18,16 +18,15 @@ //! Tests for the module. use super::*; -use mock::*; -use sp_runtime::{ - assert_eq_error_rate, traits::BadOrigin, -}; -use sp_staking::offence::OffenceDetails; use frame_support::{ - assert_ok, assert_noop, StorageMap, - traits::{Currency, ReservableCurrency, OnInitialize, OnFinalize}, + assert_noop, assert_ok, + traits::{Currency, OnFinalize, OnInitialize, ReservableCurrency}, + StorageMap, }; +use mock::*; use pallet_balances::Error as BalancesError; +use sp_runtime::{assert_eq_error_rate, traits::BadOrigin}; +use sp_staking::offence::OffenceDetails; use substrate_test_utils::assert_eq_uvec; #[test] @@ -87,26 +86,47 @@ fn basic_setup_works() { // Account 10 controls the stash from account 11, which is 100 * balance_factor units assert_eq!( Staking::ledger(&10), - Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![], claimed_rewards: vec![] }) + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![] + }) ); // Account 20 controls the stash from account 21, which is 200 * balance_factor units assert_eq!( Staking::ledger(&20), - Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![], claimed_rewards: vec![] }) + Some(StakingLedger { + stash: 21, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![] + }) ); // Account 1 does not control any stash assert_eq!(Staking::ledger(&1), None); // ValidatorPrefs are default - assert_eq_uvec!(>::iter().collect::>(), vec![ - (31, ValidatorPrefs::default()), - (21, ValidatorPrefs::default()), - (11, ValidatorPrefs::default()) - ]); + assert_eq_uvec!( + >::iter().collect::>(), + vec![ + (31, ValidatorPrefs::default()), + (21, ValidatorPrefs::default()), + (11, ValidatorPrefs::default()) + ] + ); assert_eq!( Staking::ledger(100), - Some(StakingLedger { stash: 101, total: 500, active: 500, unlocking: vec![], claimed_rewards: vec![] }) + Some(StakingLedger { + stash: 101, + total: 500, + active: 500, + unlocking: vec![], + claimed_rewards: vec![] + }) ); assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); @@ -115,7 +135,7 @@ fn basic_setup_works() { Exposure { total: 1125, own: 1000, - others: vec![ IndividualExposure { who: 101, value: 125 }] + others: vec![IndividualExposure { who: 101, value: 125 }] }, ); assert_eq!( @@ -123,13 +143,15 @@ fn basic_setup_works() { Exposure { total: 1375, own: 1000, - others: vec![ IndividualExposure { who: 101, value: 375 }] + others: vec![IndividualExposure { who: 101, value: 375 }] }, ); // initial total stake = 1125 + 1375 - assert_eq!(Staking::eras_total_stake(Staking::active_era().unwrap().index), 2500); - + assert_eq!( + Staking::eras_total_stake(Staking::active_era().unwrap().index), + 2500 + ); // The number of validators required. assert_eq!(Staking::validator_count(), 2); @@ -207,10 +229,13 @@ fn rewards_should_work() { assert_eq!(Balances::total_balance(&100), init_balance_100); assert_eq!(Balances::total_balance(&101), init_balance_101); assert_eq_uvec!(Session::validators(), vec![11, 21]); - assert_eq!(Staking::eras_reward_points(Staking::active_era().unwrap().index), EraRewardPoints { - total: 50*3, - individual: vec![(11, 100), (21, 50)].into_iter().collect(), - }); + assert_eq!( + Staking::eras_reward_points(Staking::active_era().unwrap().index), + EraRewardPoints { + total: 50 * 3, + individual: vec![(11, 100), (21, 50)].into_iter().collect(), + } + ); let part_for_10 = Perbill::from_rational_approximation::(1000, 1125); let part_for_20 = Perbill::from_rational_approximation::(1000, 1375); let part_for_100_from_10 = Perbill::from_rational_approximation::(125, 1125); @@ -221,18 +246,29 @@ fn rewards_should_work() { assert_eq!(Staking::active_era().unwrap().index, 1); assert_eq!(mock::REWARD_REMAINDER_UNBALANCED.with(|v| *v.borrow()), 7050); - assert_eq!(*mock::staking_events().last().unwrap(), RawEvent::EraPayout(0, 2350, 7050)); + assert_eq!( + *mock::staking_events().last().unwrap(), + RawEvent::EraPayout(0, 2350, 7050) + ); mock::make_all_reward_payment(0); - assert_eq_error_rate!(Balances::total_balance(&10), init_balance_10 + part_for_10 * total_payout_0*2/3, 2); + assert_eq_error_rate!( + Balances::total_balance(&10), + init_balance_10 + part_for_10 * total_payout_0 * 2 / 3, + 2 + ); assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); - assert_eq_error_rate!(Balances::total_balance(&20), init_balance_20 + part_for_20 * total_payout_0*1/3, 2); + assert_eq_error_rate!( + Balances::total_balance(&20), + init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + 2 + ); assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); assert_eq_error_rate!( Balances::total_balance(&100), init_balance_100 - + part_for_100_from_10 * total_payout_0 * 2/3 - + part_for_100_from_20 * total_payout_0 * 1/3, + + part_for_100_from_10 * total_payout_0 * 2 / 3 + + part_for_100_from_20 * total_payout_0 * 1 / 3, 2 ); assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); @@ -245,19 +281,30 @@ fn rewards_should_work() { assert!(total_payout_1 > 10); // Test is meaningful if reward something mock::start_era(2); - assert_eq!(mock::REWARD_REMAINDER_UNBALANCED.with(|v| *v.borrow()), 7050*2); - assert_eq!(*mock::staking_events().last().unwrap(), RawEvent::EraPayout(1, 2350, 7050)); + assert_eq!(mock::REWARD_REMAINDER_UNBALANCED.with(|v| *v.borrow()), 7050 * 2); + assert_eq!( + *mock::staking_events().last().unwrap(), + RawEvent::EraPayout(1, 2350, 7050) + ); mock::make_all_reward_payment(1); - assert_eq_error_rate!(Balances::total_balance(&10), init_balance_10 + part_for_10 * (total_payout_0 * 2/3 + total_payout_1), 2); + assert_eq_error_rate!( + Balances::total_balance(&10), + init_balance_10 + part_for_10 * (total_payout_0 * 2 / 3 + total_payout_1), + 2 + ); assert_eq_error_rate!(Balances::total_balance(&11), init_balance_11, 2); - assert_eq_error_rate!(Balances::total_balance(&20), init_balance_20 + part_for_20 * total_payout_0 * 1/3, 2); + assert_eq_error_rate!( + Balances::total_balance(&20), + init_balance_20 + part_for_20 * total_payout_0 * 1 / 3, + 2 + ); assert_eq_error_rate!(Balances::total_balance(&21), init_balance_21, 2); assert_eq_error_rate!( Balances::total_balance(&100), init_balance_100 - + part_for_100_from_10 * (total_payout_0 * 2/3 + total_payout_1) - + part_for_100_from_20 * total_payout_0 * 1/3, + + part_for_100_from_10 * (total_payout_0 * 2 / 3 + total_payout_1) + + part_for_100_from_20 * total_payout_0 * 1 / 3, 2 ); assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); @@ -282,12 +329,19 @@ fn staking_should_work() { assert_eq_uvec!(validator_controllers(), vec![20, 10]); // put some money in account that we'll use. - for i in 1..5 { let _ = Balances::make_free_balance_be(&i, 2000); } + for i in 1..5 { + let _ = Balances::make_free_balance_be(&i, 2000); + } // --- Block 2: start_session(2); // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(3), + 4, + 1500, + RewardDestination::Controller + )); assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); // No effects will be seen so far. @@ -299,7 +353,6 @@ fn staking_should_work() { // No effects will be seen so far. Era has not been yet triggered. assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // --- Block 4: the validators will now be queued. start_session(4); assert_eq!(Staking::active_era().unwrap().index, 1); @@ -385,7 +438,9 @@ fn no_candidate_emergency_condition() { .execute_with(|| { // initial validators assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); - let prefs = ValidatorPrefs { commission: Perbill::one() }; + let prefs = ValidatorPrefs { + commission: Perbill::one(), + }; ::Validators::insert(11, prefs.clone()); // set the minimum validator count. @@ -415,10 +470,22 @@ fn nominating_and_rewards_should_work() { assert_eq_uvec!(validator_controllers(), vec![40, 30]); // Set payee to controller - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller)); + assert_ok!(Staking::set_payee( + Origin::signed(10), + RewardDestination::Controller + )); + assert_ok!(Staking::set_payee( + Origin::signed(20), + RewardDestination::Controller + )); + assert_ok!(Staking::set_payee( + Origin::signed(30), + RewardDestination::Controller + )); + assert_ok!(Staking::set_payee( + Origin::signed(40), + RewardDestination::Controller + )); // give the man some money let initial_balance = 1000; @@ -428,10 +495,20 @@ fn nominating_and_rewards_should_work() { // bond two account pairs and state interest in nomination. // 2 will nominate for 10, 20, 30 - assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + 1000, + RewardDestination::Controller + )); assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21, 31])); // 4 will nominate for 10, 20, 40 - assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(3), + 4, + 1000, + RewardDestination::Controller + )); assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41])); // the total reward for era 0 @@ -453,7 +530,10 @@ fn nominating_and_rewards_should_work() { // ------ check the staked value of all parties. // 30 and 40 are not chosen anymore - assert_eq!(ErasStakers::::iter_prefix_values(Staking::active_era().unwrap().index).count(), 2); + assert_eq!( + ErasStakers::::iter_prefix_values(Staking::active_era().unwrap().index).count(), + 2 + ); assert_eq!( Staking::eras_stakers(Staking::active_era().unwrap().index, 11), Exposure { @@ -526,10 +606,7 @@ fn nominators_also_get_slashed_pro_rata() { let slash_percent = Perbill::from_percent(5); let initial_exposure = Staking::eras_stakers(active_era(), 11); // 101 is a nominator for 11 - assert_eq!( - initial_exposure.others.first().unwrap().who, - 101, - ); + assert_eq!(initial_exposure.others.first().unwrap().who, 101,); // staked values; let nominator_stake = Staking::ledger(100).unwrap().active; @@ -543,10 +620,7 @@ fn nominators_also_get_slashed_pro_rata() { // 11 goes offline on_offence_now( &[OffenceDetails { - offender: ( - 11, - initial_exposure.clone(), - ), + offender: (11, initial_exposure.clone()), reporters: vec![], }], &[slash_percent], @@ -559,10 +633,8 @@ fn nominators_also_get_slashed_pro_rata() { let slash_amount = slash_percent * exposed_stake; let validator_share = Perbill::from_rational_approximation(exposed_validator, exposed_stake) * slash_amount; - let nominator_share = Perbill::from_rational_approximation( - exposed_nominator, - exposed_stake, - ) * slash_amount; + let nominator_share = + Perbill::from_rational_approximation(exposed_nominator, exposed_stake) * slash_amount; // both slash amounts need to be positive for the test to make sense. assert!(validator_share > 0); @@ -599,17 +671,27 @@ fn double_staking_should_fail() { ExtBuilder::default().build_and_execute(|| { let arbitrary_value = 5; // 2 = controller, 1 stashed => ok - assert_ok!( - Staking::bond(Origin::signed(1), 2, arbitrary_value, - RewardDestination::default()) - ); + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + arbitrary_value, + RewardDestination::default() + )); // 4 = not used so far, 1 stashed => not allowed. assert_noop!( - Staking::bond(Origin::signed(1), 4, arbitrary_value, - RewardDestination::default()), Error::::AlreadyBonded, + Staking::bond( + Origin::signed(1), + 4, + arbitrary_value, + RewardDestination::default() + ), + Error::::AlreadyBonded, ); // 1 = stashed => attempting to nominate should fail. - assert_noop!(Staking::nominate(Origin::signed(1), vec![1]), Error::::NotController); + assert_noop!( + Staking::nominate(Origin::signed(1), vec![1]), + Error::::NotController + ); // 2 = controller => nominating should work. assert_ok!(Staking::nominate(Origin::signed(2), vec![1])); }); @@ -630,7 +712,12 @@ fn double_controlling_should_fail() { )); // 2 = controller, 3 stashed (Note that 2 is reused.) => no-op assert_noop!( - Staking::bond(Origin::signed(3), 2, arbitrary_value, RewardDestination::default()), + Staking::bond( + Origin::signed(3), + 2, + arbitrary_value, + RewardDestination::default() + ), Error::::AlreadyPaired, ); }); @@ -725,7 +812,6 @@ fn forcing_new_era_works() { assert_eq!(Staking::active_era().unwrap().index, 6); start_session(15); assert_eq!(Staking::active_era().unwrap().index, 6); - }); } @@ -738,7 +824,10 @@ fn cannot_transfer_staked_balance() { // Confirm account 11 has some free balance assert_eq!(Balances::free_balance(11), 1000); // Confirm account 11 (via controller 10) is totally staked - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, 1000); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, + 1000 + ); // Confirm account 11 cannot transfer as a result assert_noop!( Balances::transfer(Origin::signed(11), 20, 1), @@ -757,20 +846,26 @@ fn cannot_transfer_staked_balance_2() { // Tests that a stash account cannot transfer funds // Same test as above but with 20, and more accurate. // 21 has 2000 free balance but 1000 at stake - ExtBuilder::default().nominate(false).fair(true).build_and_execute(|| { - // Confirm account 21 is stashed - assert_eq!(Staking::bonded(&21), Some(20)); - // Confirm account 21 has some free balance - assert_eq!(Balances::free_balance(21), 2000); - // Confirm account 21 (via controller 20) is totally staked - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, 1000); - // Confirm account 21 can transfer at most 1000 - assert_noop!( - Balances::transfer(Origin::signed(21), 20, 1001), - BalancesError::::LiquidityRestrictions - ); - assert_ok!(Balances::transfer(Origin::signed(21), 20, 1000)); - }); + ExtBuilder::default() + .nominate(false) + .fair(true) + .build_and_execute(|| { + // Confirm account 21 is stashed + assert_eq!(Staking::bonded(&21), Some(20)); + // Confirm account 21 has some free balance + assert_eq!(Balances::free_balance(21), 2000); + // Confirm account 21 (via controller 20) is totally staked + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, + 1000 + ); + // Confirm account 21 can transfer at most 1000 + assert_noop!( + Balances::transfer(Origin::signed(21), 20, 1001), + BalancesError::::LiquidityRestrictions + ); + assert_ok!(Balances::transfer(Origin::signed(21), 20, 1000)); + }); } #[test] @@ -782,7 +877,10 @@ fn cannot_reserve_staked_balance() { // Confirm account 11 has some free balance assert_eq!(Balances::free_balance(11), 1000); // Confirm account 11 (via controller 10) is totally staked - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).own, 1000); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).own, + 1000 + ); // Confirm account 11 cannot reserve as a result assert_noop!( Balances::reserve(&11, 1), @@ -807,13 +905,16 @@ fn reward_destination_works() { // Check the balance of the stash account assert_eq!(Balances::free_balance(11), 1000); // Check how much is at stake - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(3000); @@ -828,13 +929,16 @@ fn reward_destination_works() { // Check that reward went to the stash account of validator assert_eq!(Balances::free_balance(11), 1000 + total_payout_0); // Check that amount at stake increased accordingly - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000 + total_payout_0, - active: 1000 + total_payout_0, - unlocking: vec![], - claimed_rewards: vec![0], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000 + total_payout_0, + active: 1000 + total_payout_0, + unlocking: vec![], + claimed_rewards: vec![0], + }) + ); //Change RewardDestination to Stash >::insert(&11, RewardDestination::Stash); @@ -854,13 +958,16 @@ fn reward_destination_works() { // Record this value let recorded_stash_balance = 1000 + total_payout_0 + total_payout_1; // Check that amount at stake is NOT increased - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000 + total_payout_0, - active: 1000 + total_payout_0, - unlocking: vec![], - claimed_rewards: vec![0,1], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000 + total_payout_0, + active: 1000 + total_payout_0, + unlocking: vec![], + claimed_rewards: vec![0, 1], + }) + ); // Change RewardDestination to Controller >::insert(&11, RewardDestination::Controller); @@ -881,13 +988,16 @@ fn reward_destination_works() { // Check that reward went to the controller account assert_eq!(Balances::free_balance(10), 1 + total_payout_2); // Check that amount at stake is NOT increased - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000 + total_payout_0, - active: 1000 + total_payout_0, - unlocking: vec![], - claimed_rewards: vec![0,1,2], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000 + total_payout_0, + active: 1000 + total_payout_0, + unlocking: vec![], + claimed_rewards: vec![0, 1, 2], + }) + ); // Check that amount in staked account is NOT increased. assert_eq!(Balances::free_balance(11), recorded_stash_balance); }); @@ -900,9 +1010,12 @@ fn validator_payment_prefs_work() { // This test will focus on validator payment. ExtBuilder::default().build_and_execute(|| { let commission = Perbill::from_percent(40); - >::insert(&11, ValidatorPrefs { - commission: commission.clone(), - }); + >::insert( + &11, + ValidatorPrefs { + commission: commission.clone(), + }, + ); // Reward controller so staked ratio doesn't change. >::insert(&11, RewardDestination::Controller); @@ -928,9 +1041,12 @@ fn validator_payment_prefs_work() { let reward_of_10 = shared_cut * exposure_1.own / exposure_1.total + taken_cut; let reward_of_100 = shared_cut * exposure_1.others[0].value / exposure_1.total; assert_eq_error_rate!(Balances::total_balance(&10), balance_era_1_10 + reward_of_10, 2); - assert_eq_error_rate!(Balances::total_balance(&100), balance_era_1_100 + reward_of_100, 2); + assert_eq_error_rate!( + Balances::total_balance(&100), + balance_era_1_100 + reward_of_100, + 2 + ); }); - } #[test] @@ -944,13 +1060,16 @@ fn bond_extra_works() { // Check that account 10 is bonded to account 11 assert_eq!(Staking::bonded(&11), Some(10)); // Check how much is at stake - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); // Give account 11 some large free balance greater than total let _ = Balances::make_free_balance_be(&11, 1000000); @@ -958,24 +1077,30 @@ fn bond_extra_works() { // Call the bond_extra function from controller, add only 100 assert_ok!(Staking::bond_extra(Origin::signed(11), 100)); // There should be 100 more `total` and `active` in the ledger - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 1000 + 100, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000 + 100, + active: 1000 + 100, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); // Call the bond_extra function with a large number, should handle it assert_ok!(Staking::bond_extra(Origin::signed(11), Balance::max_value())); // The full amount of the funds should now be in the total and active - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000000, - active: 1000000, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000000, + active: 1000000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); }); } @@ -988,7 +1113,10 @@ fn bond_extra_and_withdraw_unbonded_works() { // * Once the unbonding period is done, it can actually take the funds out of the stash. ExtBuilder::default().nominate(false).build_and_execute(|| { // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee( + Origin::signed(10), + RewardDestination::Controller + )); // Give account 11 some large free balance greater than total let _ = Balances::make_free_balance_be(&11, 1000000); @@ -1004,32 +1132,46 @@ fn bond_extra_and_withdraw_unbonded_works() { mock::start_era(1); // Initial state of 10 - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); assert_eq!( Staking::eras_stakers(Staking::active_era().unwrap().index, 11), - Exposure { total: 1000, own: 1000, others: vec![] } + Exposure { + total: 1000, + own: 1000, + others: vec![] + } ); // deposit the extra 100 units Staking::bond_extra(Origin::signed(11), 100).unwrap(); - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 1000 + 100, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000 + 100, + active: 1000 + 100, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); // Exposure is a snapshot! only updated after the next era update. assert_ne!( Staking::eras_stakers(Staking::active_era().unwrap().index, 11), - Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] } + Exposure { + total: 1000 + 100, + own: 1000 + 100, + others: vec![] + } ); // trigger next era. @@ -1037,17 +1179,24 @@ fn bond_extra_and_withdraw_unbonded_works() { assert_eq!(Staking::active_era().unwrap().index, 2); // ledger should be the same. - assert_eq!(Staking::ledger(&10), Some(StakingLedger { - stash: 11, - total: 1000 + 100, - active: 1000 + 100, - unlocking: vec![], - claimed_rewards: vec![], - })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000 + 100, + active: 1000 + 100, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); // Exposure is now updated. assert_eq!( Staking::eras_stakers(Staking::active_era().unwrap().index, 11), - Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] } + Exposure { + total: 1000 + 100, + own: 1000 + 100, + others: vec![] + } ); // Unbond almost all of the funds in stash. @@ -1058,7 +1207,10 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 100, - unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}], + unlocking: vec![UnlockChunk { + value: 1000, + era: 2 + 3 + }], claimed_rewards: vec![] }), ); @@ -1071,7 +1223,10 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 100, - unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}], + unlocking: vec![UnlockChunk { + value: 1000, + era: 2 + 3 + }], claimed_rewards: vec![] }), ); @@ -1087,7 +1242,10 @@ fn bond_extra_and_withdraw_unbonded_works() { stash: 11, total: 1000 + 100, active: 100, - unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}], + unlocking: vec![UnlockChunk { + value: 1000, + era: 2 + 3 + }], claimed_rewards: vec![] }), ); @@ -1114,7 +1272,7 @@ fn bond_extra_and_withdraw_unbonded_works() { fn too_many_unbond_calls_should_not_work() { ExtBuilder::default().build_and_execute(|| { // locked at era 0 until 3 - for _ in 0..MAX_UNLOCKING_CHUNKS-1 { + for _ in 0..MAX_UNLOCKING_CHUNKS - 1 { assert_ok!(Staking::unbond(Origin::signed(10), 1)); } @@ -1123,11 +1281,17 @@ fn too_many_unbond_calls_should_not_work() { // locked at era 1 until 4 assert_ok!(Staking::unbond(Origin::signed(10), 1)); // can't do more. - assert_noop!(Staking::unbond(Origin::signed(10), 1), Error::::NoMoreChunks); + assert_noop!( + Staking::unbond(Origin::signed(10), 1), + Error::::NoMoreChunks + ); mock::start_era(3); - assert_noop!(Staking::unbond(Origin::signed(10), 1), Error::::NoMoreChunks); + assert_noop!( + Staking::unbond(Origin::signed(10), 1), + Error::::NoMoreChunks + ); // free up. assert_ok!(Staking::withdraw_unbonded(Origin::signed(10), 0)); @@ -1143,429 +1307,511 @@ fn rebond_works() { // * Given an account being bonded [and chosen as a validator](not mandatory) // * it can unbond a portion of its funds from the stash account. // * it can re-bond a portion of the funds scheduled to unlock. - ExtBuilder::default() - .nominate(false) - .build() - .execute_with(|| { - // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee( - Origin::signed(10), - RewardDestination::Controller - )); + ExtBuilder::default().nominate(false).build().execute_with(|| { + // Set payee to controller. avoids confusion + assert_ok!(Staking::set_payee( + Origin::signed(10), + RewardDestination::Controller + )); - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + // Give account 11 some large free balance greater than total + let _ = Balances::make_free_balance_be(&11, 1000000); - // confirm that 10 is a normal validator and gets paid at the end of the era. - mock::start_era(1); + // confirm that 10 is a normal validator and gets paid at the end of the era. + mock::start_era(1); - // Initial state of 10 - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - }) - ); + // Initial state of 10 + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); - mock::start_era(2); - assert_eq!(Staking::active_era().unwrap().index, 2); + mock::start_era(2); + assert_eq!(Staking::active_era().unwrap().index, 2); - // Try to rebond some funds. We get an error since no fund is unbonded. - assert_noop!( - Staking::rebond(Origin::signed(10), 500), - Error::::NoUnlockChunk, - ); + // Try to rebond some funds. We get an error since no fund is unbonded. + assert_noop!( + Staking::rebond(Origin::signed(10), 500), + Error::::NoUnlockChunk, + ); - // Unbond almost all of the funds in stash. - Staking::unbond(Origin::signed(10), 900).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: vec![UnlockChunk { - value: 900, - era: 2 + 3, - }], - claimed_rewards: vec![], - }) - ); + // Unbond almost all of the funds in stash. + Staking::unbond(Origin::signed(10), 900).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 100, + unlocking: vec![UnlockChunk { + value: 900, + era: 2 + 3, + }], + claimed_rewards: vec![], + }) + ); - // Re-bond all the funds unbonded. - Staking::rebond(Origin::signed(10), 900).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - }) - ); + // Re-bond all the funds unbonded. + Staking::rebond(Origin::signed(10), 900).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); - // Unbond almost all of the funds in stash. - Staking::unbond(Origin::signed(10), 900).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: vec![UnlockChunk { value: 900, era: 5 }], - claimed_rewards: vec![], - }) - ); + // Unbond almost all of the funds in stash. + Staking::unbond(Origin::signed(10), 900).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 100, + unlocking: vec![UnlockChunk { value: 900, era: 5 }], + claimed_rewards: vec![], + }) + ); - // Re-bond part of the funds unbonded. - Staking::rebond(Origin::signed(10), 500).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 600, - unlocking: vec![UnlockChunk { value: 400, era: 5 }], - claimed_rewards: vec![], - }) - ); + // Re-bond part of the funds unbonded. + Staking::rebond(Origin::signed(10), 500).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 600, + unlocking: vec![UnlockChunk { value: 400, era: 5 }], + claimed_rewards: vec![], + }) + ); - // Re-bond the remainder of the funds unbonded. - Staking::rebond(Origin::signed(10), 500).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - }) - ); + // Re-bond the remainder of the funds unbonded. + Staking::rebond(Origin::signed(10), 500).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); - // Unbond parts of the funds in stash. - Staking::unbond(Origin::signed(10), 300).unwrap(); - Staking::unbond(Origin::signed(10), 300).unwrap(); - Staking::unbond(Origin::signed(10), 300).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: vec![ - UnlockChunk { value: 300, era: 5 }, - UnlockChunk { value: 300, era: 5 }, - UnlockChunk { value: 300, era: 5 }, - ], - claimed_rewards: vec![], - }) - ); + // Unbond parts of the funds in stash. + Staking::unbond(Origin::signed(10), 300).unwrap(); + Staking::unbond(Origin::signed(10), 300).unwrap(); + Staking::unbond(Origin::signed(10), 300).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 100, + unlocking: vec![ + UnlockChunk { value: 300, era: 5 }, + UnlockChunk { value: 300, era: 5 }, + UnlockChunk { value: 300, era: 5 }, + ], + claimed_rewards: vec![], + }) + ); - // Re-bond part of the funds unbonded. - Staking::rebond(Origin::signed(10), 500).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 600, - unlocking: vec![ - UnlockChunk { value: 300, era: 5 }, - UnlockChunk { value: 100, era: 5 }, - ], - claimed_rewards: vec![], - }) - ); - }) + // Re-bond part of the funds unbonded. + Staking::rebond(Origin::signed(10), 500).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 600, + unlocking: vec![ + UnlockChunk { value: 300, era: 5 }, + UnlockChunk { value: 100, era: 5 }, + ], + claimed_rewards: vec![], + }) + ); + }) } #[test] fn rebond_is_fifo() { // Rebond should proceed by reversing the most recent bond operations. - ExtBuilder::default() - .nominate(false) - .build() - .execute_with(|| { - // Set payee to controller. avoids confusion - assert_ok!(Staking::set_payee( - Origin::signed(10), - RewardDestination::Controller - )); + ExtBuilder::default().nominate(false).build().execute_with(|| { + // Set payee to controller. avoids confusion + assert_ok!(Staking::set_payee( + Origin::signed(10), + RewardDestination::Controller + )); - // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + // Give account 11 some large free balance greater than total + let _ = Balances::make_free_balance_be(&11, 1000000); - // confirm that 10 is a normal validator and gets paid at the end of the era. - mock::start_era(1); + // confirm that 10 is a normal validator and gets paid at the end of the era. + mock::start_era(1); - // Initial state of 10 - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: vec![], - claimed_rewards: vec![], - }) - ); + // Initial state of 10 + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); - mock::start_era(2); + mock::start_era(2); - // Unbond some of the funds in stash. - Staking::unbond(Origin::signed(10), 400).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 600, - unlocking: vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - ], - claimed_rewards: vec![], - }) - ); + // Unbond some of the funds in stash. + Staking::unbond(Origin::signed(10), 400).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 600, + unlocking: vec![UnlockChunk { + value: 400, + era: 2 + 3 + },], + claimed_rewards: vec![], + }) + ); - mock::start_era(3); + mock::start_era(3); - // Unbond more of the funds in stash. - Staking::unbond(Origin::signed(10), 300).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 300, - unlocking: vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 300, era: 3 + 3 }, - ], - claimed_rewards: vec![], - }) - ); + // Unbond more of the funds in stash. + Staking::unbond(Origin::signed(10), 300).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 300, + unlocking: vec![ + UnlockChunk { + value: 400, + era: 2 + 3 + }, + UnlockChunk { + value: 300, + era: 3 + 3 + }, + ], + claimed_rewards: vec![], + }) + ); - mock::start_era(4); + mock::start_era(4); - // Unbond yet more of the funds in stash. - Staking::unbond(Origin::signed(10), 200).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 100, - unlocking: vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 300, era: 3 + 3 }, - UnlockChunk { value: 200, era: 4 + 3 }, - ], - claimed_rewards: vec![], - }) - ); + // Unbond yet more of the funds in stash. + Staking::unbond(Origin::signed(10), 200).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 100, + unlocking: vec![ + UnlockChunk { + value: 400, + era: 2 + 3 + }, + UnlockChunk { + value: 300, + era: 3 + 3 + }, + UnlockChunk { + value: 200, + era: 4 + 3 + }, + ], + claimed_rewards: vec![], + }) + ); - // Re-bond half of the unbonding funds. - Staking::rebond(Origin::signed(10), 400).unwrap(); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 500, - unlocking: vec![ - UnlockChunk { value: 400, era: 2 + 3 }, - UnlockChunk { value: 100, era: 3 + 3 }, - ], - claimed_rewards: vec![], - }) - ); - }) + // Re-bond half of the unbonding funds. + Staking::rebond(Origin::signed(10), 400).unwrap(); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { + stash: 11, + total: 1000, + active: 500, + unlocking: vec![ + UnlockChunk { + value: 400, + era: 2 + 3 + }, + UnlockChunk { + value: 100, + era: 3 + 3 + }, + ], + claimed_rewards: vec![], + }) + ); + }) } #[test] fn reward_to_stake_works() { - ExtBuilder::default().nominate(false).fair(false).build_and_execute(|| { - // Confirm validator count is 2 - assert_eq!(Staking::validator_count(), 2); - // Confirm account 10 and 20 are validators - assert!(>::contains_key(&11) && >::contains_key(&21)); + ExtBuilder::default() + .nominate(false) + .fair(false) + .build_and_execute(|| { + // Confirm validator count is 2 + assert_eq!(Staking::validator_count(), 2); + // Confirm account 10 and 20 are validators + assert!(>::contains_key(&11) && >::contains_key(&21)); - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, 1000); - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, 2000); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, + 1000 + ); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, + 2000 + ); - // Give the man some money. - let _ = Balances::make_free_balance_be(&10, 1000); - let _ = Balances::make_free_balance_be(&20, 1000); + // Give the man some money. + let _ = Balances::make_free_balance_be(&10, 1000); + let _ = Balances::make_free_balance_be(&20, 1000); - // Bypass logic and change current exposure - ErasStakers::::insert(0, 21, Exposure { total: 69, own: 69, others: vec![] }); + // Bypass logic and change current exposure + ErasStakers::::insert( + 0, + 21, + Exposure { + total: 69, + own: 69, + others: vec![], + }, + ); - // Now lets lower account 20 stake - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, 69); - >::insert(&20, StakingLedger { stash: 21, total: 69, active: 69, unlocking: vec![], claimed_rewards: vec![] }); + // Now lets lower account 20 stake + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, + 69 + ); + >::insert( + &20, + StakingLedger { + stash: 21, + total: 69, + active: 69, + unlocking: vec![], + claimed_rewards: vec![], + }, + ); - // Compute total payout now for whole duration as other parameter won't change - let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 100); // Test is meaningful if reward something - >::reward_by_ids(vec![(11, 1)]); - >::reward_by_ids(vec![(21, 1)]); + // Compute total payout now for whole duration as other parameter won't change + let total_payout_0 = current_total_payout_for_duration(3000); + assert!(total_payout_0 > 100); // Test is meaningful if reward something + >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(21, 1)]); - // New era --> rewards are paid --> stakes are changed - mock::start_era(1); - mock::make_all_reward_payment(0); + // New era --> rewards are paid --> stakes are changed + mock::start_era(1); + mock::make_all_reward_payment(0); - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, 1000); - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, 69); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, + 1000 + ); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, + 69 + ); - let _11_balance = Balances::free_balance(&11); - assert_eq!(_11_balance, 1000 + total_payout_0 / 2); + let _11_balance = Balances::free_balance(&11); + assert_eq!(_11_balance, 1000 + total_payout_0 / 2); - // Trigger another new era as the info are frozen before the era start. - mock::start_era(2); + // Trigger another new era as the info are frozen before the era start. + mock::start_era(2); - // -- new infos - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, 1000 + total_payout_0 / 2); - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, 69 + total_payout_0 / 2); - }); + // -- new infos + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, + 1000 + total_payout_0 / 2 + ); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 21).total, + 69 + total_payout_0 / 2 + ); + }); } #[test] fn on_free_balance_zero_stash_removes_validator() { // Tests that validator storage items are cleaned up when stash is empty // Tests that storage items are untouched when controller is empty - ExtBuilder::default().existential_deposit(10).build_and_execute(|| { - // Check the balance of the validator account - assert_eq!(Balances::free_balance(10), 256); - // Check the balance of the stash account - assert_eq!(Balances::free_balance(11), 256000); - // Check these two accounts are bonded - assert_eq!(Staking::bonded(&11), Some(10)); - - // Set some storage items which we expect to be cleaned up - // Set payee information - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); - - // Check storage items that should be cleaned up - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of controller to 0 - let _ = Balances::slash(&10, Balance::max_value()); - - // Check the balance of the stash account has not been touched - assert_eq!(Balances::free_balance(11), 256000); - // Check these two accounts are still bonded - assert_eq!(Staking::bonded(&11), Some(10)); - - // Check storage items have not changed - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of stash to 0 - let _ = Balances::slash(&11, Balance::max_value()); - // Check total balance of stash - assert_eq!(Balances::total_balance(&11), 0); - - // Reap the stash - assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); - - // Check storage items do not exist - assert!(!>::contains_key(&10)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - }); + ExtBuilder::default() + .existential_deposit(10) + .build_and_execute(|| { + // Check the balance of the validator account + assert_eq!(Balances::free_balance(10), 256); + // Check the balance of the stash account + assert_eq!(Balances::free_balance(11), 256000); + // Check these two accounts are bonded + assert_eq!(Staking::bonded(&11), Some(10)); + + // Set some storage items which we expect to be cleaned up + // Set payee information + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); + + // Check storage items that should be cleaned up + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of controller to 0 + let _ = Balances::slash(&10, Balance::max_value()); + + // Check the balance of the stash account has not been touched + assert_eq!(Balances::free_balance(11), 256000); + // Check these two accounts are still bonded + assert_eq!(Staking::bonded(&11), Some(10)); + + // Check storage items have not changed + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of stash to 0 + let _ = Balances::slash(&11, Balance::max_value()); + // Check total balance of stash + assert_eq!(Balances::total_balance(&11), 0); + + // Reap the stash + assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); + + // Check storage items do not exist + assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + }); } #[test] fn on_free_balance_zero_stash_removes_nominator() { // Tests that nominator storage items are cleaned up when stash is empty // Tests that storage items are untouched when controller is empty - ExtBuilder::default().existential_deposit(10).build_and_execute(|| { - // Make 10 a nominator - assert_ok!(Staking::nominate(Origin::signed(10), vec![20])); - // Check that account 10 is a nominator - assert!(>::contains_key(11)); - // Check the balance of the nominator account - assert_eq!(Balances::free_balance(10), 256); - // Check the balance of the stash account - assert_eq!(Balances::free_balance(11), 256000); - - // Set payee information - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); - - // Check storage items that should be cleaned up - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of controller to 0 - let _ = Balances::slash(&10, Balance::max_value()); - // Check total balance of account 10 - assert_eq!(Balances::total_balance(&10), 0); - - // Check the balance of the stash account has not been touched - assert_eq!(Balances::free_balance(11), 256000); - // Check these two accounts are still bonded - assert_eq!(Staking::bonded(&11), Some(10)); - - // Check storage items have not changed - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of stash to 0 - let _ = Balances::slash(&11, Balance::max_value()); - // Check total balance of stash - assert_eq!(Balances::total_balance(&11), 0); - - // Reap the stash - assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); - - // Check storage items do not exist - assert!(!>::contains_key(&10)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - }); + ExtBuilder::default() + .existential_deposit(10) + .build_and_execute(|| { + // Make 10 a nominator + assert_ok!(Staking::nominate(Origin::signed(10), vec![20])); + // Check that account 10 is a nominator + assert!(>::contains_key(11)); + // Check the balance of the nominator account + assert_eq!(Balances::free_balance(10), 256); + // Check the balance of the stash account + assert_eq!(Balances::free_balance(11), 256000); + + // Set payee information + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); + + // Check storage items that should be cleaned up + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of controller to 0 + let _ = Balances::slash(&10, Balance::max_value()); + // Check total balance of account 10 + assert_eq!(Balances::total_balance(&10), 0); + + // Check the balance of the stash account has not been touched + assert_eq!(Balances::free_balance(11), 256000); + // Check these two accounts are still bonded + assert_eq!(Staking::bonded(&11), Some(10)); + + // Check storage items have not changed + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of stash to 0 + let _ = Balances::slash(&11, Balance::max_value()); + // Check total balance of stash + assert_eq!(Balances::total_balance(&11), 0); + + // Reap the stash + assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); + + // Check storage items do not exist + assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + }); } - #[test] fn switching_roles() { // Test that it should be possible to switch between roles (nominator, validator, idle) with minimal overhead. ExtBuilder::default().nominate(false).build_and_execute(|| { // Reset reward destination - for i in &[10, 20] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); } + for i in &[10, 20] { + assert_ok!(Staking::set_payee( + Origin::signed(*i), + RewardDestination::Controller + )); + } assert_eq_uvec!(validator_controllers(), vec![20, 10]); // put some money in account that we'll use. - for i in 1..7 { let _ = Balances::deposit_creating(&i, 5000); } + for i in 1..7 { + let _ = Balances::deposit_creating(&i, 5000); + } // add 2 nominators - assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + 2000, + RewardDestination::Controller + )); assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 5])); - assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(3), + 4, + 500, + RewardDestination::Controller + )); assert_ok!(Staking::nominate(Origin::signed(4), vec![21, 1])); // add a new validator candidate - assert_ok!(Staking::bond(Origin::signed(5), 6, 1000, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(5), + 6, + 1000, + RewardDestination::Controller + )); assert_ok!(Staking::validate(Origin::signed(6), ValidatorPrefs::default())); mock::start_era(1); @@ -1590,24 +1836,37 @@ fn switching_roles() { #[test] fn wrong_vote_is_null() { - ExtBuilder::default().nominate(false).validator_pool(true).build_and_execute(|| { - assert_eq_uvec!(validator_controllers(), vec![40, 30]); + ExtBuilder::default() + .nominate(false) + .validator_pool(true) + .build_and_execute(|| { + assert_eq_uvec!(validator_controllers(), vec![40, 30]); - // put some money in account that we'll use. - for i in 1..3 { let _ = Balances::deposit_creating(&i, 5000); } + // put some money in account that we'll use. + for i in 1..3 { + let _ = Balances::deposit_creating(&i, 5000); + } - // add 1 nominators - assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(2), vec![ - 11, 21, // good votes - 1, 2, 15, 1000, 25 // crap votes. No effect. - ])); + // add 1 nominators + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + 2000, + RewardDestination::default() + )); + assert_ok!(Staking::nominate( + Origin::signed(2), + vec![ + 11, 21, // good votes + 1, 2, 15, 1000, 25 // crap votes. No effect. + ] + )); - // new block - mock::start_era(1); + // new block + mock::start_era(1); - assert_eq_uvec!(validator_controllers(), vec![20, 10]); - }); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); + }); } #[test] @@ -1627,7 +1886,12 @@ fn bond_with_no_staked_value() { Error::::InsufficientValue, ); // bonded with absolute minimum value possible. - assert_ok!(Staking::bond(Origin::signed(1), 2, 5, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + 5, + RewardDestination::Controller + )); assert_eq!(Balances::locks(&1)[0].amount, 5); // unbonding even 1 will cause all to be unbonded. @@ -1638,7 +1902,7 @@ fn bond_with_no_staked_value() { stash: 1, active: 0, total: 5, - unlocking: vec![UnlockChunk {value: 5, era: 3}], + unlocking: vec![UnlockChunk { value: 5, era: 3 }], claimed_rewards: vec![], }) ); @@ -1672,12 +1936,20 @@ fn bond_with_little_staked_value_bounded() { .execute_with(|| { // setup assert_ok!(Staking::chill(Origin::signed(30))); - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee( + Origin::signed(10), + RewardDestination::Controller + )); let init_balance_2 = Balances::free_balance(&2); let init_balance_10 = Balances::free_balance(&10); // Stingy validator. - assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + 1, + RewardDestination::Controller + )); assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default())); // reward era 0 @@ -1690,7 +1962,10 @@ fn bond_with_little_staked_value_bounded() { // 2 is elected. assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); // And has minimal stake - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 2).total, 0); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 2).total, + 0 + ); // Old ones are rewarded. assert_eq!(Balances::free_balance(10), init_balance_10 + total_payout_0 / 3); @@ -1705,7 +1980,10 @@ fn bond_with_little_staked_value_bounded() { mock::make_all_reward_payment(1); assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 2).total, 0); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 2).total, + 0 + ); assert_eq!(Balances::free_balance(2), init_balance_2 + total_payout_1 / 3); assert_eq!( @@ -1770,7 +2048,11 @@ fn reward_validator_slashing_validator_does_not_overflow() { // Set staker let _ = Balances::make_free_balance_be(&11, stake); - let exposure = Exposure:: { total: stake, own: stake, others: vec![] }; + let exposure = Exposure:: { + total: stake, + own: stake, + others: vec![], + }; let reward = EraRewardPoints:: { total: 1, individual: vec![(11, 1)].into_iter().collect(), @@ -1792,20 +2074,28 @@ fn reward_validator_slashing_validator_does_not_overflow() { // it is 0. Staking::bond(Origin::signed(2), 20000, stake - 1, RewardDestination::default()).unwrap(); // Override exposure of 11 - ErasStakers::::insert(0, 11, Exposure { - total: stake, - own: 1, - others: vec![ IndividualExposure { who: 2, value: stake - 1 }] - }); + ErasStakers::::insert( + 0, + 11, + Exposure { + total: stake, + own: 1, + others: vec![IndividualExposure { + who: 2, + value: stake - 1, + }], + }, + ); // Check slashing on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }], &[Perbill::from_percent(100)], ); @@ -1847,17 +2137,9 @@ fn add_reward_points_fns_works() { // Not mandatory but must be coherent with rewards assert_eq!(Session::validators(), vec![21, 11]); - >::reward_by_ids(vec![ - (21, 1), - (11, 1), - (11, 1), - ]); + >::reward_by_ids(vec![(21, 1), (11, 1), (11, 1)]); - >::reward_by_ids(vec![ - (21, 1), - (11, 1), - (11, 1), - ]); + >::reward_by_ids(vec![(21, 1), (11, 1), (11, 1)]); assert_eq!( ErasRewardPoints::::get(Staking::active_era().unwrap().index), @@ -1875,7 +2157,7 @@ fn unbonded_balance_is_not_slashable() { // total amount staked is slashable. assert_eq!(Staking::slashable_balance_of(&11), 1000); - assert_ok!(Staking::unbond(Origin::signed(10), 800)); + assert_ok!(Staking::unbond(Origin::signed(10), 800)); // only the active portion. assert_eq!(Staking::slashable_balance_of(&11), 200); @@ -1890,20 +2172,32 @@ fn era_is_always_same_length() { let session_per_era = >::get(); mock::start_era(1); - assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session_per_era); + assert_eq!( + Staking::eras_start_session_index(current_era()).unwrap(), + session_per_era + ); mock::start_era(2); - assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session_per_era * 2u32); + assert_eq!( + Staking::eras_start_session_index(current_era()).unwrap(), + session_per_era * 2u32 + ); let session = Session::current_index(); ForceEra::put(Forcing::ForceNew); advance_session(); advance_session(); assert_eq!(current_era(), 3); - assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session + 2); + assert_eq!( + Staking::eras_start_session_index(current_era()).unwrap(), + session + 2 + ); mock::start_era(4); - assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session + 2u32 + session_per_era); + assert_eq!( + Staking::eras_start_session_index(current_era()).unwrap(), + session + 2u32 + session_per_era + ); }); } @@ -1978,7 +2272,10 @@ fn slashing_performed_according_exposure() { // This test checks that slashing is performed according the exposure (or more precisely, // historical exposure), not the current balance. ExtBuilder::default().build_and_execute(|| { - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).own, 1000); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).own, + 1000 + ); // Handle an offence with a historical exposure. on_offence_now( @@ -2080,7 +2377,10 @@ fn reporters_receive_their_slice() { // The reporters' reward is calculated from the total exposure. let initial_balance = 1125; - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, initial_balance); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, + initial_balance + ); on_offence_now( &[OffenceDetails { @@ -2110,7 +2410,10 @@ fn subsequent_reports_in_same_span_pay_out_less() { // The reporters' reward is calculated from the total exposure. let initial_balance = 1125; - assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, initial_balance); + assert_eq!( + Staking::eras_stakers(Staking::active_era().unwrap().index, 11).total, + initial_balance + ); on_offence_now( &[OffenceDetails { @@ -2151,43 +2454,54 @@ fn subsequent_reports_in_same_span_pay_out_less() { #[test] fn invulnerables_are_not_slashed() { // For invulnerable validators no slashing is performed. - ExtBuilder::default().invulnerables(vec![11]).build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(21), 2000); - - let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 21); - let initial_balance = Staking::slashable_balance_of(&21); - - let nominator_balances: Vec<_> = exposure.others - .iter().map(|o| Balances::free_balance(&o.who)).collect(); - - on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - OffenceDetails { - offender: (21, Staking::eras_stakers(Staking::active_era().unwrap().index, 21)), - reporters: vec![], - }, - ], - &[Perbill::from_percent(50), Perbill::from_percent(20)], - ); + ExtBuilder::default() + .invulnerables(vec![11]) + .build_and_execute(|| { + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(21), 2000); + + let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 21); + let initial_balance = Staking::slashable_balance_of(&21); + + let nominator_balances: Vec<_> = exposure + .others + .iter() + .map(|o| Balances::free_balance(&o.who)) + .collect(); + + on_offence_now( + &[ + OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }, + OffenceDetails { + offender: ( + 21, + Staking::eras_stakers(Staking::active_era().unwrap().index, 21), + ), + reporters: vec![], + }, + ], + &[Perbill::from_percent(50), Perbill::from_percent(20)], + ); - // The validator 11 hasn't been slashed, but 21 has been. - assert_eq!(Balances::free_balance(11), 1000); - // 2000 - (0.2 * initial_balance) - assert_eq!(Balances::free_balance(21), 2000 - (2 * initial_balance / 10)); + // The validator 11 hasn't been slashed, but 21 has been. + assert_eq!(Balances::free_balance(11), 1000); + // 2000 - (0.2 * initial_balance) + assert_eq!(Balances::free_balance(21), 2000 - (2 * initial_balance / 10)); - // ensure that nominators were slashed as well. - for (initial_balance, other) in nominator_balances.into_iter().zip(exposure.others) { - assert_eq!( - Balances::free_balance(&other.who), - initial_balance - (2 * other.value / 10), - ); - } - }); + // ensure that nominators were slashed as well. + for (initial_balance, other) in nominator_balances.into_iter().zip(exposure.others) { + assert_eq!( + Balances::free_balance(&other.who), + initial_balance - (2 * other.value / 10), + ); + } + }); } #[test] @@ -2221,12 +2535,13 @@ fn only_slash_for_max_in_era() { assert_eq!(Balances::free_balance(11), 1000); on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }], &[Perbill::from_percent(50)], ); @@ -2235,12 +2550,13 @@ fn only_slash_for_max_in_era() { assert_eq!(Staking::force_era(), Forcing::ForceNew); on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }], &[Perbill::from_percent(25)], ); @@ -2248,12 +2564,13 @@ fn only_slash_for_max_in_era() { assert_eq!(Balances::free_balance(11), 500); on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }], &[Perbill::from_percent(60)], ); @@ -2265,49 +2582,62 @@ fn only_slash_for_max_in_era() { #[test] fn garbage_collection_after_slashing() { // ensures that `SlashingSpans` and `SpanSlash` of an account is removed after reaping. - ExtBuilder::default().existential_deposit(2).build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 256_000); - - on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), + ExtBuilder::default() + .existential_deposit(2) + .build_and_execute(|| { + assert_eq!(Balances::free_balance(11), 256_000); + + on_offence_now( + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), reporters: vec![], - }, - ], - &[Perbill::from_percent(10)], - ); + }], + &[Perbill::from_percent(10)], + ); - assert_eq!(Balances::free_balance(11), 256_000 - 25_600); - assert!(::SlashingSpans::get(&11).is_some()); - assert_eq!(::SpanSlash::get(&(11, 0)).amount_slashed(), &25_600); + assert_eq!(Balances::free_balance(11), 256_000 - 25_600); + assert!(::SlashingSpans::get(&11).is_some()); + assert_eq!( + ::SpanSlash::get(&(11, 0)).amount_slashed(), + &25_600 + ); - on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), + on_offence_now( + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), reporters: vec![], - }, - ], - &[Perbill::from_percent(100)], - ); + }], + &[Perbill::from_percent(100)], + ); - // validator and nominator slash in era are garbage-collected by era change, - // so we don't test those here. + // validator and nominator slash in era are garbage-collected by era change, + // so we don't test those here. - assert_eq!(Balances::free_balance(11), 0); - assert_eq!(Balances::total_balance(&11), 0); + assert_eq!(Balances::free_balance(11), 0); + assert_eq!(Balances::total_balance(&11), 0); - let slashing_spans = ::SlashingSpans::get(&11).unwrap(); - assert_eq!(slashing_spans.iter().count(), 2); + let slashing_spans = ::SlashingSpans::get(&11).unwrap(); + assert_eq!(slashing_spans.iter().count(), 2); - // reap_stash respects num_slashing_spans so that weight is accurate - assert_noop!(Staking::reap_stash(Origin::none(), 11, 0), Error::::IncorrectSlashingSpans); - assert_ok!(Staking::reap_stash(Origin::none(), 11, 2)); + // reap_stash respects num_slashing_spans so that weight is accurate + assert_noop!( + Staking::reap_stash(Origin::none(), 11, 0), + Error::::IncorrectSlashingSpans + ); + assert_ok!(Staking::reap_stash(Origin::none(), 11, 2)); - assert!(::SlashingSpans::get(&11).is_none()); - assert_eq!(::SpanSlash::get(&(11, 0)).amount_slashed(), &0); - }) + assert!(::SlashingSpans::get(&11).is_none()); + assert_eq!( + ::SpanSlash::get(&(11, 0)).amount_slashed(), + &0 + ); + }) } #[test] @@ -2325,12 +2655,10 @@ fn garbage_collection_on_window_pruning() { let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(now, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: (11, Staking::eras_stakers(now, 11)), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); @@ -2371,12 +2699,13 @@ fn slashing_nominators_by_span_max() { let nominated_value_21 = exposure_21.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_in_era( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }], &[Perbill::from_percent(10)], 2, ); @@ -2387,30 +2716,33 @@ fn slashing_nominators_by_span_max() { assert_eq!(Balances::free_balance(101), 2000 - slash_1_amount); let expected_spans = vec![ - slashing::SlashingSpan { index: 1, start: 4, length: None }, - slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, + slashing::SlashingSpan { + index: 1, + start: 4, + length: None, + }, + slashing::SlashingSpan { + index: 0, + start: 0, + length: Some(4), + }, ]; let get_span = |account| ::SlashingSpans::get(&account).unwrap(); - assert_eq!( - get_span(11).iter().collect::>(), - expected_spans, - ); + assert_eq!(get_span(11).iter().collect::>(), expected_spans,); - assert_eq!( - get_span(101).iter().collect::>(), - expected_spans, - ); + assert_eq!(get_span(101).iter().collect::>(), expected_spans,); // second slash: higher era, higher value, same span. on_offence_in_era( - &[ - OffenceDetails { - offender: (21, Staking::eras_stakers(Staking::active_era().unwrap().index, 21)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 21, + Staking::eras_stakers(Staking::active_era().unwrap().index, 21), + ), + reporters: vec![], + }], &[Perbill::from_percent(30)], 3, ); @@ -2428,12 +2760,13 @@ fn slashing_nominators_by_span_max() { // third slash: in same era and on same validator as first, higher // in-era value, but lower slash value than slash 2. on_offence_in_era( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![], + }], &[Perbill::from_percent(20)], 2, ); @@ -2464,18 +2797,27 @@ fn slashes_are_summed_across_spans() { let get_span = |account| ::SlashingSpans::get(&account).unwrap(); on_offence_now( - &[ - OffenceDetails { - offender: (21, Staking::eras_stakers(Staking::active_era().unwrap().index, 21)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 21, + Staking::eras_stakers(Staking::active_era().unwrap().index, 21), + ), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); let expected_spans = vec![ - slashing::SlashingSpan { index: 1, start: 4, length: None }, - slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, + slashing::SlashingSpan { + index: 1, + start: 4, + length: None, + }, + slashing::SlashingSpan { + index: 0, + start: 0, + length: Some(4), + }, ]; assert_eq!(get_span(21).iter().collect::>(), expected_spans); @@ -2489,19 +2831,32 @@ fn slashes_are_summed_across_spans() { assert_eq!(Staking::slashable_balance_of(&21), 900); on_offence_now( - &[ - OffenceDetails { - offender: (21, Staking::eras_stakers(Staking::active_era().unwrap().index, 21)), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: ( + 21, + Staking::eras_stakers(Staking::active_era().unwrap().index, 21), + ), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); let expected_spans = vec![ - slashing::SlashingSpan { index: 2, start: 5, length: None }, - slashing::SlashingSpan { index: 1, start: 4, length: Some(1) }, - slashing::SlashingSpan { index: 0, start: 0, length: Some(4) }, + slashing::SlashingSpan { + index: 2, + start: 5, + length: None, + }, + slashing::SlashingSpan { + index: 1, + start: 4, + length: Some(1), + }, + slashing::SlashingSpan { + index: 0, + start: 0, + length: Some(4), + }, ]; assert_eq!(get_span(21).iter().collect::>(), expected_spans); @@ -2511,218 +2866,213 @@ fn slashes_are_summed_across_spans() { #[test] fn deferred_slashes_are_deferred() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_era(1); + ExtBuilder::default() + .slash_defer_duration(2) + .build_and_execute(|| { + mock::start_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); - assert_eq!(Balances::free_balance(101), 2000); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; + let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); + assert_eq!(Balances::free_balance(101), 2000); + let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; - on_offence_now( - &[ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), + on_offence_now( + &[OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), reporters: vec![], - }, - ], - &[Perbill::from_percent(10)], - ); + }], + &[Perbill::from_percent(10)], + ); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - mock::start_era(2); + mock::start_era(2); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - mock::start_era(3); + mock::start_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - // at the start of era 4, slashes from era 1 are processed, - // after being deferred for at least 2 full eras. - mock::start_era(4); + // at the start of era 4, slashes from era 1 are processed, + // after being deferred for at least 2 full eras. + mock::start_era(4); - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); - }) + assert_eq!(Balances::free_balance(11), 900); + assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); + }) } #[test] fn remove_deferred() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_era(1); + ExtBuilder::default() + .slash_defer_duration(2) + .build_and_execute(|| { + mock::start_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); - assert_eq!(Balances::free_balance(101), 2000); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; + let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); + assert_eq!(Balances::free_balance(101), 2000); + let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; - on_offence_now( - &[ - OffenceDetails { + on_offence_now( + &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![], - }, - ], - &[Perbill::from_percent(10)], - ); + }], + &[Perbill::from_percent(10)], + ); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - mock::start_era(2); + mock::start_era(2); - on_offence_in_era( - &[ - OffenceDetails { + on_offence_in_era( + &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![], - }, - ], - &[Perbill::from_percent(15)], - 1, - ); + }], + &[Perbill::from_percent(15)], + 1, + ); - // fails if empty - assert_noop!( - Staking::cancel_deferred_slash(Origin::root(), 1, vec![]), - Error::::EmptyTargets - ); + // fails if empty + assert_noop!( + Staking::cancel_deferred_slash(Origin::root(), 1, vec![]), + Error::::EmptyTargets + ); - assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0])); + assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0])); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - mock::start_era(3); + mock::start_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - // at the start of era 4, slashes from era 1 are processed, - // after being deferred for at least 2 full eras. - mock::start_era(4); + // at the start of era 4, slashes from era 1 are processed, + // after being deferred for at least 2 full eras. + mock::start_era(4); - // the first slash for 10% was cancelled, so no effect. - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + // the first slash for 10% was cancelled, so no effect. + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); - mock::start_era(5); + mock::start_era(5); - let slash_10 = Perbill::from_percent(10); - let slash_15 = Perbill::from_percent(15); - let initial_slash = slash_10 * nominated_value; + let slash_10 = Perbill::from_percent(10); + let slash_15 = Perbill::from_percent(15); + let initial_slash = slash_10 * nominated_value; - let total_slash = slash_15 * nominated_value; - let actual_slash = total_slash - initial_slash; + let total_slash = slash_15 * nominated_value; + let actual_slash = total_slash - initial_slash; - // 5% slash (15 - 10) processed now. - assert_eq!(Balances::free_balance(11), 950); - assert_eq!(Balances::free_balance(101), 2000 - actual_slash); - }) + // 5% slash (15 - 10) processed now. + assert_eq!(Balances::free_balance(11), 950); + assert_eq!(Balances::free_balance(101), 2000 - actual_slash); + }) } #[test] fn remove_multi_deferred() { - ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { - mock::start_era(1); + ExtBuilder::default() + .slash_defer_duration(2) + .build_and_execute(|| { + mock::start_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); - assert_eq!(Balances::free_balance(101), 2000); + let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); + assert_eq!(Balances::free_balance(101), 2000); - on_offence_now( - &[ - OffenceDetails { + on_offence_now( + &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![], - }, - ], - &[Perbill::from_percent(10)], - ); + }], + &[Perbill::from_percent(10)], + ); - on_offence_now( - &[ - OffenceDetails { - offender: (21, Staking::eras_stakers(Staking::active_era().unwrap().index, 21)), + on_offence_now( + &[OffenceDetails { + offender: ( + 21, + Staking::eras_stakers(Staking::active_era().unwrap().index, 21), + ), reporters: vec![], - } - ], - &[Perbill::from_percent(10)], - ); + }], + &[Perbill::from_percent(10)], + ); - on_offence_now( - &[ - OffenceDetails { + on_offence_now( + &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![], - }, - ], - &[Perbill::from_percent(25)], - ); + }], + &[Perbill::from_percent(25)], + ); - on_offence_now( - &[ - OffenceDetails { + on_offence_now( + &[OffenceDetails { offender: (42, exposure.clone()), reporters: vec![], - }, - ], - &[Perbill::from_percent(25)], - ); + }], + &[Perbill::from_percent(25)], + ); - on_offence_now( - &[ - OffenceDetails { + on_offence_now( + &[OffenceDetails { offender: (69, exposure.clone()), reporters: vec![], - }, - ], - &[Perbill::from_percent(25)], - ); + }], + &[Perbill::from_percent(25)], + ); - assert_eq!(::UnappliedSlashes::get(&1).len(), 5); + assert_eq!(::UnappliedSlashes::get(&1).len(), 5); - // fails if list is not sorted - assert_noop!( - Staking::cancel_deferred_slash(Origin::root(), 1, vec![2, 0, 4]), - Error::::NotSortedAndUnique - ); - // fails if list is not unique - assert_noop!( - Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 2]), - Error::::NotSortedAndUnique - ); - // fails if bad index - assert_noop!( - Staking::cancel_deferred_slash(Origin::root(), 1, vec![1, 2, 3, 4, 5]), - Error::::InvalidSlashIndex - ); + // fails if list is not sorted + assert_noop!( + Staking::cancel_deferred_slash(Origin::root(), 1, vec![2, 0, 4]), + Error::::NotSortedAndUnique + ); + // fails if list is not unique + assert_noop!( + Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 2]), + Error::::NotSortedAndUnique + ); + // fails if bad index + assert_noop!( + Staking::cancel_deferred_slash(Origin::root(), 1, vec![1, 2, 3, 4, 5]), + Error::::InvalidSlashIndex + ); - assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 4])); + assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 4])); - let slashes = ::UnappliedSlashes::get(&1); - assert_eq!(slashes.len(), 2); - assert_eq!(slashes[0].validator, 21); - assert_eq!(slashes[1].validator, 42); - }) + let slashes = ::UnappliedSlashes::get(&1); + assert_eq!(slashes.len(), 2); + assert_eq!(slashes[0].validator, 21); + assert_eq!(slashes[1].validator, 42); + }) } mod offchain_phragmen { use crate::*; use codec::Encode; + use frame_support::traits::OffchainWorker; use frame_support::{ - assert_noop, assert_ok, assert_err_with_weight, - dispatch::DispatchResultWithPostInfo, + assert_err_with_weight, assert_noop, assert_ok, dispatch::DispatchResultWithPostInfo, }; - use sp_runtime::transaction_validity::TransactionSource; use mock::*; use parking_lot::RwLock; use sp_core::offchain::{ @@ -2731,7 +3081,7 @@ mod offchain_phragmen { }; use sp_io::TestExternalities; use sp_npos_elections::StakedAssignment; - use frame_support::traits::OffchainWorker; + use sp_runtime::transaction_validity::TransactionSource; use std::sync::Arc; use substrate_test_utils::assert_eq_uvec; @@ -2766,9 +3116,9 @@ mod offchain_phragmen { let (offchain, offchain_state) = TestOffchainExt::new(); let (pool, pool_state) = TestTransactionPoolExt::new(); - let mut seed = [0_u8; 32]; - seed[0..4].copy_from_slice(&iterations.to_le_bytes()); - offchain_state.write().seed = seed; + let mut seed = [0_u8; 32]; + seed[0..4].copy_from_slice(&iterations.to_le_bytes()); + offchain_state.write().seed = seed; ext.register_extension(OffchainExt::new(offchain)); ext.register_extension(TransactionPoolExt::new(pool)); @@ -2789,38 +3139,28 @@ mod offchain_phragmen { compact: CompactAssignments, score: ElectionScore, ) -> DispatchResultWithPostInfo { - Staking::submit_election_solution( - origin, - winners, - compact, - score, - current_era(), - election_size(), - ) + Staking::submit_election_solution(origin, winners, compact, score, current_era(), election_size()) } #[test] fn is_current_session_final_works() { - ExtBuilder::default() - .session_per_era(3) - .build() - .execute_with(|| { - mock::start_era(1); - assert_eq!(Session::current_index(), 3); - assert_eq!(Staking::current_era(), Some(1)); - assert_eq!(Staking::is_current_session_final(), false); - - start_session(4); - assert_eq!(Session::current_index(), 4); - assert_eq!(Staking::current_era(), Some(1)); - assert_eq!(Staking::is_current_session_final(), true); - - start_session(5); - assert_eq!(Session::current_index(), 5); - // era changed. - assert_eq!(Staking::current_era(), Some(2)); - assert_eq!(Staking::is_current_session_final(), false); - }) + ExtBuilder::default().session_per_era(3).build().execute_with(|| { + mock::start_era(1); + assert_eq!(Session::current_index(), 3); + assert_eq!(Staking::current_era(), Some(1)); + assert_eq!(Staking::is_current_session_final(), false); + + start_session(4); + assert_eq!(Session::current_index(), 4); + assert_eq!(Staking::current_era(), Some(1)); + assert_eq!(Staking::is_current_session_final(), true); + + start_session(5); + assert_eq!(Session::current_index(), 5); + // era changed. + assert_eq!(Staking::current_era(), Some(2)); + assert_eq!(Staking::is_current_session_final(), false); + }) } #[test] @@ -2912,7 +3252,6 @@ mod offchain_phragmen { .election_lookahead(3) .build() .execute_with(|| { - ForceEra::put(Forcing::ForceAlways); run_to_block(16); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); @@ -3043,10 +3382,7 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); // chill et. al. are now not allowed. - assert_noop!( - Staking::chill(Origin::signed(10)), - Error::::CallNotAllowed, - ); + assert_noop!(Staking::chill(Origin::signed(10)), Error::::CallNotAllowed,); }) } @@ -3063,12 +3399,7 @@ mod offchain_phragmen { assert!(Staking::snapshot_validators().is_some()); let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - )); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,)); let queued_result = Staking::queued_elected().unwrap(); assert_eq!(queued_result.compute, ElectionCompute::Signed); @@ -3192,22 +3523,12 @@ mod offchain_phragmen { // a good solution let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - )); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,)); // a bad solution let (compact, winners, score) = horrible_phragmen_with_post_processing(false); assert_err_with_weight!( - submit_solution( - Origin::signed(10), - winners.clone(), - compact.clone(), - score, - ), + submit_solution(Origin::signed(10), winners.clone(), compact.clone(), score,), Error::::PhragmenWeakSubmission, Some(::DbWeight::get().reads(3)) ); @@ -3228,21 +3549,11 @@ mod offchain_phragmen { // a meeeeh solution let (compact, winners, score) = horrible_phragmen_with_post_processing(false); - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - )); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,)); // a better solution let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - )); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,)); }) } @@ -3342,12 +3653,7 @@ mod offchain_phragmen { run_to_block(12); // put a good solution on-chain let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - ),); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,),); // now run the offchain worker in the same chain state. Staking::offchain_worker(12); @@ -3392,12 +3698,7 @@ mod offchain_phragmen { assert_eq!(winners.len(), 3); assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusWinnerCount, ); }) @@ -3446,12 +3747,7 @@ mod offchain_phragmen { assert_eq!(winners.len(), 3); assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusWinnerCount, ); }) @@ -3474,12 +3770,7 @@ mod offchain_phragmen { assert_eq!(winners.len(), 4); // all good. We chose 4 and it works. - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - ),); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,),); }) } @@ -3504,12 +3795,7 @@ mod offchain_phragmen { // The error type sadly cannot be more specific now. assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusCompact, ); }) @@ -3536,12 +3822,7 @@ mod offchain_phragmen { // The error type sadly cannot be more specific now. assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusCompact, ); }) @@ -3567,12 +3848,7 @@ mod offchain_phragmen { let winners = vec![0, 1, 2, 4]; assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusWinner, ); }) @@ -3602,12 +3878,7 @@ mod offchain_phragmen { }); assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusEdge, ); }) @@ -3628,21 +3899,13 @@ mod offchain_phragmen { let (compact, winners, score) = prepare_submission_with(true, 2, |a| { // mutate a self vote to target someone else. That someone else is still among the // winners - a.iter_mut().find(|x| x.who == 11).map(|x| { - x.distribution - .iter_mut() - .find(|y| y.0 == 11) - .map(|y| y.0 = 21) - }); + a.iter_mut() + .find(|x| x.who == 11) + .map(|x| x.distribution.iter_mut().find(|y| y.0 == 11).map(|y| y.0 = 21)); }); assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusSelfVote, ); }) @@ -3672,12 +3935,7 @@ mod offchain_phragmen { // This raises score issue. assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusSelfVote, ); }) @@ -3706,12 +3964,7 @@ mod offchain_phragmen { } assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusCompact, ); }) @@ -3747,12 +4000,7 @@ mod offchain_phragmen { }); assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusNomination, ); }) @@ -3790,10 +4038,7 @@ mod offchain_phragmen { // validate 10 again for the next round. But this guy will not have the votes that // it should have had from 1 and 2. - assert_ok!(Staking::validate( - Origin::signed(10), - Default::default() - )); + assert_ok!(Staking::validate(Origin::signed(10), Default::default())); // open the election window and create snapshots. run_to_block(32); @@ -3803,36 +4048,24 @@ mod offchain_phragmen { // no one is allowed to vote for 10, except for itself. a.into_iter() .filter(|s| s.who != 11) - .for_each(|s| - assert!(s.distribution.iter().find(|(t, _)| *t == 11).is_none()) - ); + .for_each(|s| assert!(s.distribution.iter().find(|(t, _)| *t == 11).is_none())); }); // can be submitted. - assert_ok!(submit_solution( - Origin::signed(10), - winners, - compact, - score, - )); + assert_ok!(submit_solution(Origin::signed(10), winners, compact, score,)); // a wrong solution. let (compact, winners, score) = prepare_submission_with(false, 0, |a| { // add back the vote that has been filtered out. a.push(StakedAssignment { who: 1, - distribution: vec![(11, 100)] + distribution: vec![(11, 100)], }); }); // is rejected. assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenSlashedNomination, ); }) @@ -3854,12 +4087,7 @@ mod offchain_phragmen { score[0] += 1; assert_noop!( - submit_solution( - Origin::signed(10), - winners, - compact, - score, - ), + submit_solution(Origin::signed(10), winners, compact, score,), Error::::PhragmenBogusScore, ); }) @@ -3987,10 +4215,7 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid // post-slash balance let nominator_slash_amount_11 = 125 / 10; assert_eq!(Balances::free_balance(11), 900); - assert_eq!( - Balances::free_balance(101), - 2000 - nominator_slash_amount_11 - ); + assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); // This is the best way to check that the validator was chilled; `get` will // return default value. @@ -4117,12 +4342,10 @@ fn zero_slash_keeps_nominators() { assert_eq!(Balances::free_balance(101), 2000); on_offence_now( - &[ - OffenceDetails { - offender: (11, exposure.clone()), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: (11, exposure.clone()), + reporters: vec![], + }], &[Perbill::from_percent(0)], ); @@ -4139,7 +4362,9 @@ fn zero_slash_keeps_nominators() { // and make sure that the vote will not be ignored, because the slash was // zero. - let last_slash = ::SlashingSpans::get(&11).unwrap().last_nonzero_slash(); + let last_slash = ::SlashingSpans::get(&11) + .unwrap() + .last_nonzero_slash(); assert!(nominations.submitted_in >= last_slash); }); } @@ -4153,11 +4378,26 @@ fn six_session_delay() { let init_session = Session::current_index(); let init_active_era = Staking::active_era().unwrap().index; // pallet-session is delaying session by one, thus the next session to plan is +2. - assert_eq!(>::new_session(init_session + 2), None); - assert_eq!(>::new_session(init_session + 3), Some(val_set.clone())); - assert_eq!(>::new_session(init_session + 4), None); - assert_eq!(>::new_session(init_session + 5), None); - assert_eq!(>::new_session(init_session + 6), Some(val_set.clone())); + assert_eq!( + >::new_session(init_session + 2), + None + ); + assert_eq!( + >::new_session(init_session + 3), + Some(val_set.clone()) + ); + assert_eq!( + >::new_session(init_session + 4), + None + ); + assert_eq!( + >::new_session(init_session + 5), + None + ); + assert_eq!( + >::new_session(init_session + 6), + Some(val_set.clone()) + ); >::end_session(init_session); >::start_session(init_session + 1); @@ -4206,14 +4446,12 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward( let controller = 20_000 + i as AccountId; let balance = 10_000 + i as Balance; Balances::make_free_balance_be(&stash, balance); - assert_ok!( - Staking::bond( - Origin::signed(stash), - controller, - balance, - RewardDestination::Stash - ) - ); + assert_ok!(Staking::bond( + Origin::signed(stash), + controller, + balance, + RewardDestination::Stash + )); assert_ok!(Staking::nominate(Origin::signed(controller), vec![11])); } mock::start_era(1); @@ -4294,7 +4532,13 @@ fn test_payout_stakers() { // We track rewards in `claimed_rewards` vec assert_eq!( Staking::ledger(&10), - Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![], claimed_rewards: vec![1] }) + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![1] + }) ); for i in 3..16 { @@ -4309,7 +4553,13 @@ fn test_payout_stakers() { // We track rewards in `claimed_rewards` vec assert_eq!( Staking::ledger(&10), - Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![], claimed_rewards: (1..=14).collect() }) + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: (1..=14).collect() + }) ); for i in 16..100 { @@ -4325,7 +4575,13 @@ fn test_payout_stakers() { assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98)); assert_eq!( Staking::ledger(&10), - Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![], claimed_rewards: vec![15, 98] }) + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![15, 98] + }) ); // Out of order claims works. @@ -4334,7 +4590,13 @@ fn test_payout_stakers() { assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 42)); assert_eq!( Staking::ledger(&10), - Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![], claimed_rewards: vec![15, 23, 42, 69, 98] }) + Some(StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: vec![], + claimed_rewards: vec![15, 23, 42, 69, 98] + }) ); }); } @@ -4360,9 +4622,15 @@ fn payout_stakers_handles_basic_errors() { mock::start_era(2); // Wrong Era, too big - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 2), Error::::InvalidEraToReward); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 2), + Error::::InvalidEraToReward + ); // Wrong Staker - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 10, 1), Error::::NotStash); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 10, 1), + Error::::NotStash + ); for i in 3..100 { Staking::reward_by_ids(vec![(11, 1)]); @@ -4373,14 +4641,26 @@ fn payout_stakers_handles_basic_errors() { } // We are at era 99, with history depth of 84 // We should be able to payout era 15 through 98 (84 total eras), but not 14 or 99. - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 14), Error::::InvalidEraToReward); - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 99), Error::::InvalidEraToReward); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 14), + Error::::InvalidEraToReward + ); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 99), + Error::::InvalidEraToReward + ); assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 15)); assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98)); // Can't claim again - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 15), Error::::AlreadyClaimed); - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 98), Error::::AlreadyClaimed); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 15), + Error::::AlreadyClaimed + ); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 98), + Error::::AlreadyClaimed + ); }); } @@ -4431,28 +4711,39 @@ fn offences_weight_calculated_correctly() { ExtBuilder::default().nominate(true).build_and_execute(|| { // On offence with zero offenders: 4 Reads, 1 Write let zero_offence_weight = ::DbWeight::get().reads_writes(4, 1); - assert_eq!(Staking::on_offence(&[], &[Perbill::from_percent(50)], 0), Ok(zero_offence_weight)); + assert_eq!( + Staking::on_offence(&[], &[Perbill::from_percent(50)], 0), + Ok(zero_offence_weight) + ); // On Offence with N offenders, Unapplied: 4 Reads, 1 Write + 4 Reads, 5 Writes let n_offence_unapplied_weight = ::DbWeight::get().reads_writes(4, 1) + ::DbWeight::get().reads_writes(4, 5); - let offenders: Vec::AccountId, pallet_session::historical::IdentificationTuple>> - = (1..10).map(|i| - OffenceDetails { - offender: (i, Staking::eras_stakers(Staking::active_era().unwrap().index, i)), - reporters: vec![], - } - ).collect(); - assert_eq!(Staking::on_offence(&offenders, &[Perbill::from_percent(50)], 0), Ok(n_offence_unapplied_weight)); + let offenders: Vec< + OffenceDetails< + ::AccountId, + pallet_session::historical::IdentificationTuple, + >, + > = (1..10) + .map(|i| OffenceDetails { + offender: (i, Staking::eras_stakers(Staking::active_era().unwrap().index, i)), + reporters: vec![], + }) + .collect(); + assert_eq!( + Staking::on_offence(&offenders, &[Perbill::from_percent(50)], 0), + Ok(n_offence_unapplied_weight) + ); // On Offence with one offenders, Applied - let one_offender = [ - OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), - reporters: vec![1], - }, - ]; + let one_offender = [OffenceDetails { + offender: ( + 11, + Staking::eras_stakers(Staking::active_era().unwrap().index, 11), + ), + reporters: vec![1], + }]; let n = 1; // Number of offenders let rw = 3 + 3 * n; // rw reads and writes @@ -4465,7 +4756,10 @@ fn offences_weight_calculated_correctly() { // `reward_cost` * reporters (1) + ::DbWeight::get().reads_writes(2, 2); - assert_eq!(Staking::on_offence(&one_offender, &[Perbill::from_percent(50)], 0), Ok(one_offence_unapplied_weight)); + assert_eq!( + Staking::on_offence(&one_offender, &[Perbill::from_percent(50)], 0), + Ok(one_offence_unapplied_weight) + ); }); } @@ -4480,24 +4774,24 @@ fn on_initialize_weight_is_correct() { }); ExtBuilder::default() - .offchain_phragmen_ext() - .validator_count(4) - .has_stakers(false) - .build() - .execute_with(|| { - crate::tests::offchain_phragmen::build_offchain_phragmen_test_ext(); - run_to_block(11); - Staking::on_finalize(System::block_number()); - System::set_block_number((System::block_number() + 1).into()); - Timestamp::set_timestamp(System::block_number() * 1000 + INIT_TIMESTAMP); - Session::on_initialize(System::block_number()); - - assert_eq!(Validators::::iter().count(), 4); - assert_eq!(Nominators::::iter().count(), 5); - // With 4 validators and 5 nominator, we should increase weight by: - // - (4 + 5) reads - // - 3 Writes - let final_weight = ::DbWeight::get().reads_writes(4 + 9, 3); - assert_eq!(final_weight, Staking::on_initialize(System::block_number())); - }); + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with(|| { + crate::tests::offchain_phragmen::build_offchain_phragmen_test_ext(); + run_to_block(11); + Staking::on_finalize(System::block_number()); + System::set_block_number((System::block_number() + 1).into()); + Timestamp::set_timestamp(System::block_number() * 1000 + INIT_TIMESTAMP); + Session::on_initialize(System::block_number()); + + assert_eq!(Validators::::iter().count(), 4); + assert_eq!(Nominators::::iter().count(), 5); + // With 4 validators and 5 nominator, we should increase weight by: + // - (4 + 5) reads + // - 3 Writes + let final_weight = ::DbWeight::get().reads_writes(4 + 9, 3); + assert_eq!(final_weight, Staking::on_initialize(System::block_number())); + }); }