diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index d3affda05277a..eb42a3e9745f2 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -17,797 +17,805 @@ //! Test utilities -use crate::{self as pallet_staking, *}; -use frame_election_provider_support::{onchain, SequentialPhragmen, VoteWeight}; -use frame_support::{ - assert_ok, parameter_types, - traits::{ - ConstU32, ConstU64, Currency, FindAuthor, GenesisBuild, Get, Hooks, Imbalance, - OnUnbalanced, OneSessionHandler, - }, - weights::constants::RocksDbWeight, -}; -use sp_core::H256; -use sp_io; -use sp_runtime::{ - curve::PiecewiseLinear, - testing::{Header, UintAuthorityId}, - traits::{IdentityLookup, Zero}, -}; -use sp_staking::offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}; - -pub const INIT_TIMESTAMP: u64 = 30_000; -pub const BLOCK_TIME: u64 = 1000; - -/// The AccountId alias in this test module. -pub(crate) type AccountId = u64; -pub(crate) type AccountIndex = u64; -pub(crate) type BlockNumber = u64; -pub(crate) type Balance = u128; - -/// Another session handler struct to test on_disabled. -pub struct OtherSessionHandler; -impl OneSessionHandler for OtherSessionHandler { - type Key = UintAuthorityId; - - fn on_genesis_session<'a, I: 'a>(_: I) - where - I: Iterator, - AccountId: 'a, - { - } - - fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I) - where - I: Iterator, - AccountId: 'a, - { - } - - fn on_disabled(_validator_index: u32) {} -} - -impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { - type Public = UintAuthorityId; -} - -pub fn is_disabled(controller: AccountId) -> bool { - let stash = Staking::ledger(&controller).unwrap().stash; - let validator_index = match Session::validators().iter().position(|v| *v == stash) { - Some(index) => index as u32, - None => return false, +#[cfg(test)] +mod test_mod { + use crate::{self as pallet_staking, *}; + use frame_election_provider_support::{onchain, SequentialPhragmen, VoteWeight}; + use frame_support::{ + assert_ok, parameter_types, + traits::{ + ConstU32, ConstU64, Currency, FindAuthor, GenesisBuild, Get, Hooks, Imbalance, + OnUnbalanced, OneSessionHandler, + }, + weights::constants::RocksDbWeight, + }; + use sp_core::H256; + use sp_io; + use sp_runtime::{ + curve::PiecewiseLinear, + testing::{Header, UintAuthorityId}, + traits::{IdentityLookup, Zero}, }; + use sp_staking::offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}; + + pub const INIT_TIMESTAMP: u64 = 30_000; + pub const BLOCK_TIME: u64 = 1000; + + /// The AccountId alias in this test module. + pub(crate) type AccountId = u64; + pub(crate) type AccountIndex = u64; + pub(crate) type BlockNumber = u64; + pub(crate) type Balance = u128; + + /// Another session handler struct to test on_disabled. + pub struct OtherSessionHandler; + impl OneSessionHandler for OtherSessionHandler { + type Key = UintAuthorityId; + + fn on_genesis_session<'a, I: 'a>(_: I) + where + I: Iterator, + AccountId: 'a, + { + } - Session::disabled_validators().contains(&validator_index) -} + fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I) + where + I: Iterator, + AccountId: 'a, + { + } -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Staking: pallet_staking::{Pallet, Call, Config, Storage, Event}, - Session: pallet_session::{Pallet, Call, Storage, Event, Config}, - Historical: pallet_session::historical::{Pallet, Storage}, - VoterBagsList: pallet_bags_list::::{Pallet, Call, Storage, Event}, - } -); - -/// Author of block is always 11 -pub struct Author11; -impl FindAuthor for Author11 { - fn find_author<'a, I>(_digests: I) -> Option - where - I: 'a + IntoIterator, - { - Some(11) + fn on_disabled(_validator_index: u32) {} } -} -parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - Weight::from_parts(frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, u64::MAX), - ); - pub static SessionsPerEra: SessionIndex = 3; - pub static ExistentialDeposit: Balance = 1; - pub static SlashDeferDuration: EraIndex = 0; - pub static Period: BlockNumber = 5; - pub static Offset: BlockNumber = 0; -} + impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { + type Public = UintAuthorityId; + } -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type Index = AccountIndex; - type BlockNumber = BlockNumber; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = frame_support::traits::ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} -impl pallet_balances::Config for Test { - type MaxLocks = frame_support::traits::ConstU32<1024>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); -} + pub fn is_disabled(controller: AccountId) -> bool { + let stash = Staking::ledger(&controller).unwrap().stash; + let validator_index = match Session::validators().iter().position(|v| *v == stash) { + Some(index) => index as u32, + None => return false, + }; -sp_runtime::impl_opaque_keys! { - pub struct SessionKeys { - pub other: OtherSessionHandler, + Session::disabled_validators().contains(&validator_index) } -} -impl pallet_session::Config for Test { - type SessionManager = pallet_session::historical::NoteHistoricalRoot; - type Keys = SessionKeys; - type ShouldEndSession = pallet_session::PeriodicSessions; - type SessionHandler = (OtherSessionHandler,); - type RuntimeEvent = RuntimeEvent; - type ValidatorId = AccountId; - type ValidatorIdOf = crate::StashOf; - type NextSessionRotation = pallet_session::PeriodicSessions; - type WeightInfo = (); -} -impl pallet_session::historical::Config for Test { - type FullIdentification = crate::Exposure; - type FullIdentificationOf = crate::ExposureOf; -} -impl pallet_authorship::Config for Test { - type FindAuthor = Author11; - type UncleGenerations = ConstU64<0>; - type FilterUncle = (); - type EventHandler = Pallet; -} + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + type Block = frame_system::mocking::MockBlock; -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<5>; - type WeightInfo = (); -} - -pallet_staking_reward_curve::build! { - const I_NPOS: PiecewiseLinear<'static> = curve!( - min_inflation: 0_025_000, - max_inflation: 0_100_000, - ideal_stake: 0_500_000, - falloff: 0_050_000, - max_piece_count: 40, - test_precision: 0_005_000, + frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Staking: pallet_staking::{Pallet, Call, Config, Storage, Event}, + Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Historical: pallet_session::historical::{Pallet, Storage}, + VoterBagsList: pallet_bags_list::::{Pallet, Call, Storage, Event}, + } ); -} -parameter_types! { - pub const BondingDuration: EraIndex = 3; - pub const RewardCurve: &'static PiecewiseLinear<'static> = &I_NPOS; - pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(75); -} -parameter_types! { - pub static RewardRemainderUnbalanced: u128 = 0; -} + /// Author of block is always 11 + pub struct Author11; + impl FindAuthor for Author11 { + fn find_author<'a, I>(_digests: I) -> Option + where + I: 'a + IntoIterator, + { + Some(11) + } + } -pub struct RewardRemainderMock; + parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + Weight::from_parts(frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, u64::MAX), + ); + pub static SessionsPerEra: SessionIndex = 3; + pub static ExistentialDeposit: Balance = 1; + pub static SlashDeferDuration: EraIndex = 0; + pub static Period: BlockNumber = 5; + pub static Offset: BlockNumber = 0; + } + + impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type Index = AccountIndex; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = frame_support::traits::ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + } + impl pallet_balances::Config for Test { + type MaxLocks = frame_support::traits::ConstU32<1024>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + } + + sp_runtime::impl_opaque_keys! { + pub struct SessionKeys { + pub other: OtherSessionHandler, + } + } + impl pallet_session::Config for Test { + type SessionManager = pallet_session::historical::NoteHistoricalRoot; + type Keys = SessionKeys; + type ShouldEndSession = pallet_session::PeriodicSessions; + type SessionHandler = (OtherSessionHandler,); + type RuntimeEvent = RuntimeEvent; + type ValidatorId = AccountId; + type ValidatorIdOf = crate::StashOf; + type NextSessionRotation = pallet_session::PeriodicSessions; + type WeightInfo = (); + } + + impl pallet_session::historical::Config for Test { + type FullIdentification = crate::Exposure; + type FullIdentificationOf = crate::ExposureOf; + } + impl pallet_authorship::Config for Test { + type FindAuthor = Author11; + type UncleGenerations = ConstU64<0>; + type FilterUncle = (); + type EventHandler = Pallet; + } + + impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<5>; + type WeightInfo = (); + } + + pallet_staking_reward_curve::build! { + const I_NPOS: PiecewiseLinear<'static> = curve!( + min_inflation: 0_025_000, + max_inflation: 0_100_000, + ideal_stake: 0_500_000, + falloff: 0_050_000, + max_piece_count: 40, + test_precision: 0_005_000, + ); + } + parameter_types! { + pub const BondingDuration: EraIndex = 3; + pub const RewardCurve: &'static PiecewiseLinear<'static> = &I_NPOS; + pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(75); + } -impl OnUnbalanced> for RewardRemainderMock { - fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { - RewardRemainderUnbalanced::mutate(|v| { - *v += amount.peek(); - }); - drop(amount); + parameter_types! { + pub static RewardRemainderUnbalanced: u128 = 0; } -} -const THRESHOLDS: [sp_npos_elections::VoteWeight; 9] = - [10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000]; - -parameter_types! { - pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS; - pub static MaxNominations: u32 = 16; - pub static HistoryDepth: u32 = 80; - pub static MaxUnlockingChunks: u32 = 32; - pub static RewardOnUnbalanceWasCalled: bool = false; - pub static LedgerSlashPerEra: (BalanceOf, BTreeMap>) = (Zero::zero(), BTreeMap::new()); - pub static MaxWinners: u32 = 100; -} + pub struct RewardRemainderMock; -type VoterBagsListInstance = pallet_bags_list::Instance1; -impl pallet_bags_list::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - // Staking is the source of truth for voter bags list, since they are not kept up to date. - type ScoreProvider = Staking; - type BagThresholds = BagThresholds; - type Score = VoteWeight; -} + impl OnUnbalanced> for RewardRemainderMock { + fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { + RewardRemainderUnbalanced::mutate(|v| { + *v += amount.peek(); + }); + drop(amount); + } + } -pub struct OnChainSeqPhragmen; -impl onchain::Config for OnChainSeqPhragmen { - type System = Test; - type Solver = SequentialPhragmen; - type DataProvider = Staking; - type WeightInfo = (); - type MaxWinners = MaxWinners; - type VotersBound = ConstU32<{ u32::MAX }>; - type TargetsBound = ConstU32<{ u32::MAX }>; -} + const THRESHOLDS: [sp_npos_elections::VoteWeight; 9] = + [10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000]; + + parameter_types! { + pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS; + pub static MaxNominations: u32 = 16; + pub static HistoryDepth: u32 = 80; + pub static MaxUnlockingChunks: u32 = 32; + pub static RewardOnUnbalanceWasCalled: bool = false; + pub static LedgerSlashPerEra: (BalanceOf, BTreeMap>) = (Zero::zero(), BTreeMap::new()); + pub static MaxWinners: u32 = 100; + } + + type VoterBagsListInstance = pallet_bags_list::Instance1; + impl pallet_bags_list::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + // Staking is the source of truth for voter bags list, since they are not kept up to date. + type ScoreProvider = Staking; + type BagThresholds = BagThresholds; + type Score = VoteWeight; + } + + pub struct OnChainSeqPhragmen; + impl onchain::Config for OnChainSeqPhragmen { + type System = Test; + type Solver = SequentialPhragmen; + type DataProvider = Staking; + type WeightInfo = (); + type MaxWinners = MaxWinners; + type VotersBound = ConstU32<{ u32::MAX }>; + type TargetsBound = ConstU32<{ u32::MAX }>; + } + + pub struct MockReward {} + impl OnUnbalanced> for MockReward { + fn on_unbalanced(_: PositiveImbalanceOf) { + RewardOnUnbalanceWasCalled::set(true); + } + } -pub struct MockReward {} -impl OnUnbalanced> for MockReward { - fn on_unbalanced(_: PositiveImbalanceOf) { - RewardOnUnbalanceWasCalled::set(true); + pub struct OnStakerSlashMock(core::marker::PhantomData); + impl sp_staking::OnStakerSlash for OnStakerSlashMock { + fn on_slash( + _pool_account: &AccountId, + slashed_bonded: Balance, + slashed_chunks: &BTreeMap, + ) { + LedgerSlashPerEra::set((slashed_bonded, slashed_chunks.clone())); + } } -} -pub struct OnStakerSlashMock(core::marker::PhantomData); -impl sp_staking::OnStakerSlash for OnStakerSlashMock { - fn on_slash( - _pool_account: &AccountId, - slashed_bonded: Balance, - slashed_chunks: &BTreeMap, - ) { - LedgerSlashPerEra::set((slashed_bonded, slashed_chunks.clone())); + impl crate::pallet::pallet::Config for Test { + type MaxNominations = MaxNominations; + type Currency = Balances; + type CurrencyBalance = ::Balance; + type UnixTime = Timestamp; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type RewardRemainder = RewardRemainderMock; + type RuntimeEvent = RuntimeEvent; + type Slash = (); + type Reward = MockReward; + type SessionsPerEra = SessionsPerEra; + type SlashDeferDuration = SlashDeferDuration; + type SlashCancelOrigin = frame_system::EnsureRoot; + type BondingDuration = BondingDuration; + type SessionInterface = Self; + type EraPayout = ConvertCurve; + type NextNewSession = Session; + type MaxNominatorRewardedPerValidator = ConstU32<64>; + type OffendingValidatorsThreshold = OffendingValidatorsThreshold; + type ElectionProvider = onchain::OnChainExecution; + type GenesisElectionProvider = Self::ElectionProvider; + // NOTE: consider a macro and use `UseNominatorsAndValidatorsMap` as well. + type VoterList = VoterBagsList; + type TargetList = UseValidatorsMap; + type MaxUnlockingChunks = MaxUnlockingChunks; + type HistoryDepth = HistoryDepth; + type OnStakerSlash = OnStakerSlashMock; + type BenchmarkingConfig = TestBenchmarkingConfig; + type WeightInfo = (); + } + + pub(crate) type StakingCall = crate::Call; + pub(crate) type TestCall = ::RuntimeCall; + + pub struct ExtBuilder { + nominate: bool, + validator_count: u32, + minimum_validator_count: u32, + invulnerables: Vec, + has_stakers: bool, + initialize_first_session: bool, + pub min_nominator_bond: Balance, + min_validator_bond: Balance, + balance_factor: Balance, + status: BTreeMap>, + stakes: BTreeMap, + stakers: Vec<(AccountId, AccountId, Balance, StakerStatus)>, + } + + impl Default for ExtBuilder { + fn default() -> Self { + Self { + nominate: true, + validator_count: 2, + minimum_validator_count: 0, + balance_factor: 1, + invulnerables: vec![], + has_stakers: true, + initialize_first_session: true, + min_nominator_bond: ExistentialDeposit::get(), + min_validator_bond: ExistentialDeposit::get(), + status: Default::default(), + stakes: Default::default(), + stakers: Default::default(), + } + } } -} -impl crate::pallet::pallet::Config for Test { - type MaxNominations = MaxNominations; - type Currency = Balances; - type CurrencyBalance = ::Balance; - type UnixTime = Timestamp; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; - type RewardRemainder = RewardRemainderMock; - type RuntimeEvent = RuntimeEvent; - type Slash = (); - type Reward = MockReward; - type SessionsPerEra = SessionsPerEra; - type SlashDeferDuration = SlashDeferDuration; - type SlashCancelOrigin = frame_system::EnsureRoot; - type BondingDuration = BondingDuration; - type SessionInterface = Self; - type EraPayout = ConvertCurve; - type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = ConstU32<64>; - type OffendingValidatorsThreshold = OffendingValidatorsThreshold; - type ElectionProvider = onchain::OnChainExecution; - type GenesisElectionProvider = Self::ElectionProvider; - // NOTE: consider a macro and use `UseNominatorsAndValidatorsMap` as well. - type VoterList = VoterBagsList; - type TargetList = UseValidatorsMap; - type MaxUnlockingChunks = MaxUnlockingChunks; - type HistoryDepth = HistoryDepth; - type OnStakerSlash = OnStakerSlashMock; - type BenchmarkingConfig = TestBenchmarkingConfig; - type WeightInfo = (); -} + impl ExtBuilder { + pub fn existential_deposit(self, existential_deposit: Balance) -> Self { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = existential_deposit); + self + } + pub fn nominate(mut self, nominate: bool) -> Self { + self.nominate = nominate; + self + } + pub fn validator_count(mut self, count: u32) -> Self { + self.validator_count = count; + self + } + pub fn minimum_validator_count(mut self, count: u32) -> Self { + self.minimum_validator_count = count; + self + } + pub fn slash_defer_duration(self, eras: EraIndex) -> Self { + SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = eras); + self + } + pub fn invulnerables(mut self, invulnerables: Vec) -> Self { + self.invulnerables = invulnerables; + self + } + pub fn session_per_era(self, length: SessionIndex) -> Self { + SESSIONS_PER_ERA.with(|v| *v.borrow_mut() = length); + self + } + pub fn period(self, length: BlockNumber) -> Self { + PERIOD.with(|v| *v.borrow_mut() = length); + self + } + pub fn has_stakers(mut self, has: bool) -> Self { + self.has_stakers = has; + self + } + pub fn initialize_first_session(mut self, init: bool) -> Self { + self.initialize_first_session = init; + self + } + pub fn offset(self, offset: BlockNumber) -> Self { + OFFSET.with(|v| *v.borrow_mut() = offset); + self + } + pub fn min_nominator_bond(mut self, amount: Balance) -> Self { + self.min_nominator_bond = amount; + self + } + pub fn min_validator_bond(mut self, amount: Balance) -> Self { + self.min_validator_bond = amount; + self + } + pub fn set_status(mut self, who: AccountId, status: StakerStatus) -> Self { + self.status.insert(who, status); + self + } + pub fn set_stake(mut self, who: AccountId, stake: Balance) -> Self { + self.stakes.insert(who, stake); + self + } + pub fn add_staker( + mut self, + stash: AccountId, + ctrl: AccountId, + stake: Balance, + status: StakerStatus, + ) -> Self { + self.stakers.push((stash, ctrl, stake, status)); + self + } + pub fn balance_factor(mut self, factor: Balance) -> Self { + self.balance_factor = factor; + self + } + fn build(self) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let _ = pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 300 * self.balance_factor), + (4, 400 * self.balance_factor), + // controllers + (10, self.balance_factor), + (20, self.balance_factor), + (30, self.balance_factor), + (40, self.balance_factor), + (50, self.balance_factor), + // stashes + (11, self.balance_factor * 1000), + (21, self.balance_factor * 2000), + (31, self.balance_factor * 2000), + (41, self.balance_factor * 2000), + (51, self.balance_factor * 2000), + // optional nominator + (100, self.balance_factor * 2000), + (101, self.balance_factor * 2000), + // aux accounts + (60, self.balance_factor), + (61, self.balance_factor * 2000), + (70, self.balance_factor), + (71, self.balance_factor * 2000), + (80, self.balance_factor), + (81, self.balance_factor * 2000), + // This allows us to have a total_payout different from 0. + (999, 1_000_000_000_000), + ], + } + .assimilate_storage(&mut storage); + + let mut stakers = vec![]; + if self.has_stakers { + stakers = vec![ + // (stash, ctrl, stake, status) + // these two will be elected in the default test where we elect 2. + (11, 10, self.balance_factor * 1000, StakerStatus::::Validator), + (21, 20, self.balance_factor * 1000, StakerStatus::::Validator), + // a loser validator + (31, 30, self.balance_factor * 500, StakerStatus::::Validator), + // an idle validator + (41, 40, self.balance_factor * 1000, StakerStatus::::Idle), + ]; + // optionally add a nominator + if self.nominate { + stakers.push(( + 101, + 100, + self.balance_factor * 500, + StakerStatus::::Nominator(vec![11, 21]), + )) + } + // replace any of the status if needed. + self.status.into_iter().for_each(|(stash, status)| { + let (_, _, _, ref mut prev_status) = stakers + .iter_mut() + .find(|s| s.0 == stash) + .expect("set_status staker should exist; qed"); + *prev_status = status; + }); + // replaced any of the stakes if needed. + self.stakes.into_iter().for_each(|(stash, stake)| { + let (_, _, ref mut prev_stake, _) = stakers + .iter_mut() + .find(|s| s.0 == stash) + .expect("set_stake staker should exits; qed."); + *prev_stake = stake; + }); + // extend stakers if needed. + stakers.extend(self.stakers) + } -pub(crate) type StakingCall = crate::Call; -pub(crate) type TestCall = ::RuntimeCall; - -pub struct ExtBuilder { - nominate: bool, - validator_count: u32, - minimum_validator_count: u32, - invulnerables: Vec, - has_stakers: bool, - initialize_first_session: bool, - pub min_nominator_bond: Balance, - min_validator_bond: Balance, - balance_factor: Balance, - status: BTreeMap>, - stakes: BTreeMap, - stakers: Vec<(AccountId, AccountId, Balance, StakerStatus)>, -} + let _ = pallet_staking::GenesisConfig:: { + stakers: stakers.clone(), + validator_count: self.validator_count, + minimum_validator_count: self.minimum_validator_count, + invulnerables: self.invulnerables, + slash_reward_fraction: Perbill::from_percent(10), + min_nominator_bond: self.min_nominator_bond, + min_validator_bond: self.min_validator_bond, + ..Default::default() + } + .assimilate_storage(&mut storage); + + let _ = pallet_session::GenesisConfig:: { + keys: if self.has_stakers { + // set the keys for the first session. + stakers + .into_iter() + .map(|(id, ..)| (id, id, SessionKeys { other: id.into() })) + .collect() + } else { + // set some dummy validators in genesis. + (0..self.validator_count as u64) + .map(|id| (id, id, SessionKeys { other: id.into() })) + .collect() + }, + } + .assimilate_storage(&mut storage); + + let mut ext = sp_io::TestExternalities::from(storage); + + if self.initialize_first_session { + // We consider all test to start after timestamp is initialized This must be ensured by + // having `timestamp::on_initialize` called before `staking::on_initialize`. Also, if + // session length is 1, then it is already triggered. + ext.execute_with(|| { + System::set_block_number(1); + Session::on_initialize(1); + >::on_initialize(1); + Timestamp::set_timestamp(INIT_TIMESTAMP); + }); + } -impl Default for ExtBuilder { - fn default() -> Self { - Self { - nominate: true, - validator_count: 2, - minimum_validator_count: 0, - balance_factor: 1, - invulnerables: vec![], - has_stakers: true, - initialize_first_session: true, - min_nominator_bond: ExistentialDeposit::get(), - min_validator_bond: ExistentialDeposit::get(), - status: Default::default(), - stakes: Default::default(), - stakers: Default::default(), + ext + } + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + sp_tracing::try_init_simple(); + let mut ext = self.build(); + ext.execute_with(test); + ext.execute_with(|| { + Staking::do_try_state(System::block_number()).unwrap(); + }); } } -} -impl ExtBuilder { - pub fn existential_deposit(self, existential_deposit: Balance) -> Self { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = existential_deposit); - self - } - pub fn nominate(mut self, nominate: bool) -> Self { - self.nominate = nominate; - self - } - pub fn validator_count(mut self, count: u32) -> Self { - self.validator_count = count; - self - } - pub fn minimum_validator_count(mut self, count: u32) -> Self { - self.minimum_validator_count = count; - self - } - pub fn slash_defer_duration(self, eras: EraIndex) -> Self { - SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = eras); - self - } - pub fn invulnerables(mut self, invulnerables: Vec) -> Self { - self.invulnerables = invulnerables; - self - } - pub fn session_per_era(self, length: SessionIndex) -> Self { - SESSIONS_PER_ERA.with(|v| *v.borrow_mut() = length); - self - } - pub fn period(self, length: BlockNumber) -> Self { - PERIOD.with(|v| *v.borrow_mut() = length); - self - } - pub fn has_stakers(mut self, has: bool) -> Self { - self.has_stakers = has; - self - } - pub fn initialize_first_session(mut self, init: bool) -> Self { - self.initialize_first_session = init; - self + pub(crate) fn active_era() -> EraIndex { + Staking::active_era().unwrap().index } - pub fn offset(self, offset: BlockNumber) -> Self { - OFFSET.with(|v| *v.borrow_mut() = offset); - self - } - pub fn min_nominator_bond(mut self, amount: Balance) -> Self { - self.min_nominator_bond = amount; - self - } - pub fn min_validator_bond(mut self, amount: Balance) -> Self { - self.min_validator_bond = amount; - self + + pub(crate) fn current_era() -> EraIndex { + Staking::current_era().unwrap() } - pub fn set_status(mut self, who: AccountId, status: StakerStatus) -> Self { - self.status.insert(who, status); - self + + pub(crate) fn bond(stash: AccountId, ctrl: AccountId, val: Balance) { + let _ = Balances::make_free_balance_be(&stash, val); + let _ = Balances::make_free_balance_be(&ctrl, val); + assert_ok!(Staking::bond( + RuntimeOrigin::signed(stash), + ctrl, + val, + RewardDestination::Controller + )); } - pub fn set_stake(mut self, who: AccountId, stake: Balance) -> Self { - self.stakes.insert(who, stake); - self + + pub(crate) fn bond_validator(stash: AccountId, ctrl: AccountId, val: Balance) { + bond(stash, ctrl, val); + assert_ok!(Staking::validate(RuntimeOrigin::signed(ctrl), ValidatorPrefs::default())); + assert_ok!(Session::set_keys( + RuntimeOrigin::signed(ctrl), + SessionKeys { other: ctrl.into() }, + vec![] + )); } - pub fn add_staker( - mut self, + + pub(crate) fn bond_nominator( stash: AccountId, ctrl: AccountId, - stake: Balance, - status: StakerStatus, - ) -> Self { - self.stakers.push((stash, ctrl, stake, status)); - self - } - pub fn balance_factor(mut self, factor: Balance) -> Self { - self.balance_factor = factor; - self - } - fn build(self) -> sp_io::TestExternalities { - sp_tracing::try_init_simple(); - let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - let _ = pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 10 * self.balance_factor), - (2, 20 * self.balance_factor), - (3, 300 * self.balance_factor), - (4, 400 * self.balance_factor), - // controllers - (10, self.balance_factor), - (20, self.balance_factor), - (30, self.balance_factor), - (40, self.balance_factor), - (50, self.balance_factor), - // stashes - (11, self.balance_factor * 1000), - (21, self.balance_factor * 2000), - (31, self.balance_factor * 2000), - (41, self.balance_factor * 2000), - (51, self.balance_factor * 2000), - // optional nominator - (100, self.balance_factor * 2000), - (101, self.balance_factor * 2000), - // aux accounts - (60, self.balance_factor), - (61, self.balance_factor * 2000), - (70, self.balance_factor), - (71, self.balance_factor * 2000), - (80, self.balance_factor), - (81, self.balance_factor * 2000), - // This allows us to have a total_payout different from 0. - (999, 1_000_000_000_000), - ], - } - .assimilate_storage(&mut storage); - - let mut stakers = vec![]; - if self.has_stakers { - stakers = vec![ - // (stash, ctrl, stake, status) - // these two will be elected in the default test where we elect 2. - (11, 10, self.balance_factor * 1000, StakerStatus::::Validator), - (21, 20, self.balance_factor * 1000, StakerStatus::::Validator), - // a loser validator - (31, 30, self.balance_factor * 500, StakerStatus::::Validator), - // an idle validator - (41, 40, self.balance_factor * 1000, StakerStatus::::Idle), - ]; - // optionally add a nominator - if self.nominate { - stakers.push(( - 101, - 100, - self.balance_factor * 500, - StakerStatus::::Nominator(vec![11, 21]), - )) + val: Balance, + target: Vec, + ) { + bond(stash, ctrl, val); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(ctrl), target)); + } + + /// Progress to the given block, triggering session and era changes as we progress. + /// + /// This will finalize the previous block, initialize up to the given block, essentially simulating + /// a block import/propose process where we first initialize the block, then execute some stuff (not + /// in the function), and then finalize the block. + pub(crate) fn run_to_block(n: BlockNumber) { + Staking::on_finalize(System::block_number()); + for b in (System::block_number() + 1)..=n { + System::set_block_number(b); + Session::on_initialize(b); + >::on_initialize(b); + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + if b != n { + Staking::on_finalize(System::block_number()); } - // replace any of the status if needed. - self.status.into_iter().for_each(|(stash, status)| { - let (_, _, _, ref mut prev_status) = stakers - .iter_mut() - .find(|s| s.0 == stash) - .expect("set_status staker should exist; qed"); - *prev_status = status; - }); - // replaced any of the stakes if needed. - self.stakes.into_iter().for_each(|(stash, stake)| { - let (_, _, ref mut prev_stake, _) = stakers - .iter_mut() - .find(|s| s.0 == stash) - .expect("set_stake staker should exits; qed."); - *prev_stake = stake; - }); - // extend stakers if needed. - stakers.extend(self.stakers) - } - - let _ = pallet_staking::GenesisConfig:: { - stakers: stakers.clone(), - validator_count: self.validator_count, - minimum_validator_count: self.minimum_validator_count, - invulnerables: self.invulnerables, - slash_reward_fraction: Perbill::from_percent(10), - min_nominator_bond: self.min_nominator_bond, - min_validator_bond: self.min_validator_bond, - ..Default::default() - } - .assimilate_storage(&mut storage); - - let _ = pallet_session::GenesisConfig:: { - keys: if self.has_stakers { - // set the keys for the first session. - stakers - .into_iter() - .map(|(id, ..)| (id, id, SessionKeys { other: id.into() })) - .collect() - } else { - // set some dummy validators in genesis. - (0..self.validator_count as u64) - .map(|id| (id, id, SessionKeys { other: id.into() })) - .collect() - }, - } - .assimilate_storage(&mut storage); - - let mut ext = sp_io::TestExternalities::from(storage); - - if self.initialize_first_session { - // We consider all test to start after timestamp is initialized This must be ensured by - // having `timestamp::on_initialize` called before `staking::on_initialize`. Also, if - // session length is 1, then it is already triggered. - ext.execute_with(|| { - System::set_block_number(1); - Session::on_initialize(1); - >::on_initialize(1); - Timestamp::set_timestamp(INIT_TIMESTAMP); - }); } - - ext - } - pub fn build_and_execute(self, test: impl FnOnce() -> ()) { - sp_tracing::try_init_simple(); - let mut ext = self.build(); - ext.execute_with(test); - ext.execute_with(|| { - Staking::do_try_state(System::block_number()).unwrap(); - }); } -} - -pub(crate) fn active_era() -> EraIndex { - Staking::active_era().unwrap().index -} -pub(crate) fn current_era() -> EraIndex { - Staking::current_era().unwrap() -} - -pub(crate) fn bond(stash: AccountId, ctrl: AccountId, val: Balance) { - let _ = Balances::make_free_balance_be(&stash, val); - let _ = Balances::make_free_balance_be(&ctrl, val); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(stash), - ctrl, - val, - RewardDestination::Controller - )); -} - -pub(crate) fn bond_validator(stash: AccountId, ctrl: AccountId, val: Balance) { - bond(stash, ctrl, val); - assert_ok!(Staking::validate(RuntimeOrigin::signed(ctrl), ValidatorPrefs::default())); - assert_ok!(Session::set_keys( - RuntimeOrigin::signed(ctrl), - SessionKeys { other: ctrl.into() }, - vec![] - )); -} + /// Progresses from the current block number (whatever that may be) to the `P * session_index + 1`. + pub(crate) fn start_session(session_index: SessionIndex) { + let end: u64 = if Offset::get().is_zero() { + (session_index as u64) * Period::get() + } else { + Offset::get() + (session_index.saturating_sub(1) as u64) * Period::get() + }; + run_to_block(end); + // session must have progressed properly. + assert_eq!( + Session::current_index(), + session_index, + "current session index = {}, expected = {}", + Session::current_index(), + session_index, + ); + } -pub(crate) fn bond_nominator( - stash: AccountId, - ctrl: AccountId, - val: Balance, - target: Vec, -) { - bond(stash, ctrl, val); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(ctrl), target)); -} + /// Go one session forward. + pub(crate) fn advance_session() { + let current_index = Session::current_index(); + start_session(current_index + 1); + } -/// Progress to the given block, triggering session and era changes as we progress. -/// -/// This will finalize the previous block, initialize up to the given block, essentially simulating -/// a block import/propose process where we first initialize the block, then execute some stuff (not -/// in the function), and then finalize the block. -pub(crate) fn run_to_block(n: BlockNumber) { - Staking::on_finalize(System::block_number()); - for b in (System::block_number() + 1)..=n { - System::set_block_number(b); - Session::on_initialize(b); - >::on_initialize(b); - Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); - if b != n { - Staking::on_finalize(System::block_number()); - } + /// Progress until the given era. + pub(crate) fn start_active_era(era_index: EraIndex) { + start_session((era_index * >::get()).into()); + assert_eq!(active_era(), era_index); + // One way or another, current_era must have changed before the active era, so they must match + // at this point. + assert_eq!(current_era(), active_era()); } -} -/// Progresses from the current block number (whatever that may be) to the `P * session_index + 1`. -pub(crate) fn start_session(session_index: SessionIndex) { - let end: u64 = if Offset::get().is_zero() { - (session_index as u64) * Period::get() - } else { - Offset::get() + (session_index.saturating_sub(1) as u64) * Period::get() - }; - run_to_block(end); - // session must have progressed properly. - assert_eq!( - Session::current_index(), - session_index, - "current session index = {}, expected = {}", - Session::current_index(), - session_index, - ); -} + pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance { + let (payout, _rest) = ::EraPayout::era_payout( + Staking::eras_total_stake(active_era()), + Balances::total_issuance(), + duration, + ); + assert!(payout > 0); + payout + } -/// Go one session forward. -pub(crate) fn advance_session() { - let current_index = Session::current_index(); - start_session(current_index + 1); -} + pub(crate) fn maximum_payout_for_duration(duration: u64) -> Balance { + let (payout, rest) = ::EraPayout::era_payout( + Staking::eras_total_stake(active_era()), + Balances::total_issuance(), + duration, + ); + payout + rest + } -/// Progress until the given era. -pub(crate) fn start_active_era(era_index: EraIndex) { - start_session((era_index * >::get()).into()); - assert_eq!(active_era(), era_index); - // One way or another, current_era must have changed before the active era, so they must match - // at this point. - assert_eq!(current_era(), active_era()); -} + /// Time it takes to finish a session. + /// + /// Note, if you see `time_per_session() - BLOCK_TIME`, it is fine. This is because we set the + /// timestamp after on_initialize, so the timestamp is always one block old. + pub(crate) fn time_per_session() -> u64 { + Period::get() * BLOCK_TIME + } -pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance { - let (payout, _rest) = ::EraPayout::era_payout( - Staking::eras_total_stake(active_era()), - Balances::total_issuance(), - duration, - ); - assert!(payout > 0); - payout -} + /// Time it takes to finish an era. + /// + /// Note, if you see `time_per_era() - BLOCK_TIME`, it is fine. This is because we set the + /// timestamp after on_initialize, so the timestamp is always one block old. + pub(crate) fn time_per_era() -> u64 { + time_per_session() * SessionsPerEra::get() as u64 + } -pub(crate) fn maximum_payout_for_duration(duration: u64) -> Balance { - let (payout, rest) = ::EraPayout::era_payout( - Staking::eras_total_stake(active_era()), - Balances::total_issuance(), - duration, - ); - payout + rest -} + /// Time that will be calculated for the reward per era. + pub(crate) fn reward_time_per_era() -> u64 { + time_per_era() - BLOCK_TIME + } -/// Time it takes to finish a session. -/// -/// Note, if you see `time_per_session() - BLOCK_TIME`, it is fine. This is because we set the -/// timestamp after on_initialize, so the timestamp is always one block old. -pub(crate) fn time_per_session() -> u64 { - Period::get() * BLOCK_TIME -} + pub(crate) fn reward_all_elected() { + let rewards = ::SessionInterface::validators().into_iter().map(|v| (v, 1)); -/// Time it takes to finish an era. -/// -/// Note, if you see `time_per_era() - BLOCK_TIME`, it is fine. This is because we set the -/// timestamp after on_initialize, so the timestamp is always one block old. -pub(crate) fn time_per_era() -> u64 { - time_per_session() * SessionsPerEra::get() as u64 -} + >::reward_by_ids(rewards) + } -/// Time that will be calculated for the reward per era. -pub(crate) fn reward_time_per_era() -> u64 { - time_per_era() - BLOCK_TIME -} + pub(crate) fn validator_controllers() -> Vec { + Session::validators() + .into_iter() + .map(|s| Staking::bonded(&s).expect("no controller for validator")) + .collect() + } -pub(crate) fn reward_all_elected() { - let rewards = ::SessionInterface::validators().into_iter().map(|v| (v, 1)); + pub(crate) fn on_offence_in_era( + offenders: &[OffenceDetails< + AccountId, + pallet_session::historical::IdentificationTuple, + >], + slash_fraction: &[Perbill], + era: EraIndex, + disable_strategy: DisableStrategy, + ) { + let bonded_eras = crate::BondedEras::::get(); + for &(bonded_era, start_session) in bonded_eras.iter() { + if bonded_era == era { + let _ = Staking::on_offence(offenders, slash_fraction, start_session, disable_strategy); + return + } else if bonded_era > era { + break + } + } - >::reward_by_ids(rewards) -} + if Staking::active_era().unwrap().index == era { + let _ = Staking::on_offence( + offenders, + slash_fraction, + Staking::eras_start_session_index(era).unwrap(), + disable_strategy, + ); + } else { + panic!("cannot slash in era {}", era); + } + } -pub(crate) fn validator_controllers() -> Vec { - Session::validators() - .into_iter() - .map(|s| Staking::bonded(&s).expect("no controller for validator")) - .collect() -} + pub(crate) fn on_offence_now( + offenders: &[OffenceDetails< + AccountId, + pallet_session::historical::IdentificationTuple, + >], + slash_fraction: &[Perbill], + ) { + let now = Staking::active_era().unwrap().index; + on_offence_in_era(offenders, slash_fraction, now, DisableStrategy::WhenSlashed) + } -pub(crate) fn on_offence_in_era( - offenders: &[OffenceDetails< - AccountId, - pallet_session::historical::IdentificationTuple, - >], - slash_fraction: &[Perbill], - era: EraIndex, - disable_strategy: DisableStrategy, -) { - let bonded_eras = crate::BondedEras::::get(); - for &(bonded_era, start_session) in bonded_eras.iter() { - if bonded_era == era { - let _ = Staking::on_offence(offenders, slash_fraction, start_session, disable_strategy); - return - } else if bonded_era > era { - break - } - } - - if Staking::active_era().unwrap().index == era { - let _ = Staking::on_offence( - offenders, - slash_fraction, - Staking::eras_start_session_index(era).unwrap(), - disable_strategy, + pub(crate) fn add_slash(who: &AccountId) { + on_offence_now( + &[OffenceDetails { + offender: (*who, Staking::eras_stakers(active_era(), *who)), + reporters: vec![], + }], + &[Perbill::from_percent(10)], ); - } else { - panic!("cannot slash in era {}", era); } -} -pub(crate) fn on_offence_now( - offenders: &[OffenceDetails< - AccountId, - pallet_session::historical::IdentificationTuple, - >], - slash_fraction: &[Perbill], -) { - let now = Staking::active_era().unwrap().index; - on_offence_in_era(offenders, slash_fraction, now, DisableStrategy::WhenSlashed) -} + /// 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() + .cloned() + .collect::>(); -pub(crate) fn add_slash(who: &AccountId) { - on_offence_now( - &[OffenceDetails { - offender: (*who, Staking::eras_stakers(active_era(), *who)), - reporters: vec![], - }], - &[Perbill::from_percent(10)], - ); -} + // reward validators + for validator_controller in validators_with_reward.iter().filter_map(Staking::bonded) { + let ledger = >::get(&validator_controller).unwrap(); + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), ledger.stash, 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() - .cloned() - .collect::>(); + #[macro_export] + macro_rules! assert_session_era { + ($session:expr, $era:expr) => { + assert_eq!( + Session::current_index(), + $session, + "wrong session {} != {}", + Session::current_index(), + $session, + ); + assert_eq!( + Staking::current_era().unwrap(), + $era, + "wrong current era {} != {}", + Staking::current_era().unwrap(), + $era, + ); + }; + } - // reward validators - for validator_controller in validators_with_reward.iter().filter_map(Staking::bonded) { - let ledger = >::get(&validator_controller).unwrap(); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), ledger.stash, era)); + pub(crate) fn staking_events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None }) + .collect() } -} -#[macro_export] -macro_rules! assert_session_era { - ($session:expr, $era:expr) => { - assert_eq!( - Session::current_index(), - $session, - "wrong session {} != {}", - Session::current_index(), - $session, - ); - assert_eq!( - Staking::current_era().unwrap(), - $era, - "wrong current era {} != {}", - Staking::current_era().unwrap(), - $era, - ); - }; -} + parameter_types! { + static StakingEventsIndex: usize = 0; + } -pub(crate) fn staking_events() -> Vec> { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None }) - .collect() -} + pub(crate) fn staking_events_since_last_call() -> Vec> { + let all: Vec<_> = System::events() + .into_iter() + .filter_map(|r| if let RuntimeEvent::Staking(inner) = r.event { Some(inner) } else { None }) + .collect(); + let seen = StakingEventsIndex::get(); + StakingEventsIndex::set(all.len()); + all.into_iter().skip(seen).collect() + } -parameter_types! { - static StakingEventsIndex: usize = 0; + pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { + (Balances::free_balance(who), Balances::reserved_balance(who)) + } } -pub(crate) fn staking_events_since_last_call() -> Vec> { - let all: Vec<_> = System::events() - .into_iter() - .filter_map(|r| if let RuntimeEvent::Staking(inner) = r.event { Some(inner) } else { None }) - .collect(); - let seen = StakingEventsIndex::get(); - StakingEventsIndex::set(all.len()); - all.into_iter().skip(seen).collect() -} +#[cfg(not(test))] +mod test_mod {} -pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { - (Balances::free_balance(who), Balances::reserved_balance(who)) -} +pub use test_mod::*;