diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e02792b95275a..f84f5d32c9ffa 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -540,7 +540,7 @@ parameter_types! { pub const BondingDuration: sp_staking::EraIndex = 24 * 28; pub const SlashDeferDuration: sp_staking::EraIndex = 24 * 7; // 1/4 the bonding duration. pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; - pub const MaxNominatorRewardedPerValidator: u32 = 256; + pub const MaxExposurePageSize: u32 = 256; pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17); pub OffchainRepeat: BlockNumber = 5; pub HistoryDepth: u32 = 84; @@ -573,7 +573,8 @@ impl pallet_staking::Config for Runtime { type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; + type MaxExposurePageSize = MaxExposurePageSize; + type MaxExposurePageCount = ConstU32<1>; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type ElectionProvider = ElectionProviderMultiPhase; type GenesisElectionProvider = onchain::OnChainExecution; @@ -596,7 +597,7 @@ impl pallet_fast_unstake::Config for Runtime { type Staking = Staking; type MaxErasToCheckPerBlock = ConstU32<1>; #[cfg(feature = "runtime-benchmarks")] - type MaxBackersPerValidator = MaxNominatorRewardedPerValidator; + type MaxExposurePageSize = MaxExposurePageSize; type WeightInfo = (); } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index b130677897883..d906106b2fd22 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -192,7 +192,8 @@ impl pallet_staking::Config for Test { type SessionInterface = Self; type UnixTime = pallet_timestamp::Pallet; type EraPayout = pallet_staking::ConvertCurve; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageSize = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type NextNewSession = Session; type ElectionProvider = onchain::OnChainExecution; diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 68426d0a8636e..0c9a3f7db7c5c 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -437,7 +437,7 @@ fn report_equivocation_current_session_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(1, validator), + Staking::eras_stakers(1, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -478,7 +478,7 @@ fn report_equivocation_current_session_works() { assert_eq!(Balances::total_balance(&offending_validator_id), 10_000_000 - 10_000); assert_eq!(Staking::slashable_balance_of(&offending_validator_id), 0); assert_eq!( - Staking::eras_stakers(2, offending_validator_id), + Staking::eras_stakers(2, &offending_validator_id), pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, ); @@ -491,7 +491,7 @@ fn report_equivocation_current_session_works() { assert_eq!(Balances::total_balance(validator), 10_000_000); assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(2, validator), + Staking::eras_stakers(2, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -550,7 +550,7 @@ fn report_equivocation_old_session_works() { assert_eq!(Balances::total_balance(&offending_validator_id), 10_000_000 - 10_000); assert_eq!(Staking::slashable_balance_of(&offending_validator_id), 0); assert_eq!( - Staking::eras_stakers(3, offending_validator_id), + Staking::eras_stakers(3, &offending_validator_id), pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, ); }) diff --git a/frame/beefy/src/mock.rs b/frame/beefy/src/mock.rs index 74dba2a01b81d..50a2aba9a7bb1 100644 --- a/frame/beefy/src/mock.rs +++ b/frame/beefy/src/mock.rs @@ -221,7 +221,8 @@ impl pallet_staking::Config for Test { type SessionInterface = Self; type UnixTime = pallet_timestamp::Pallet; type EraPayout = pallet_staking::ConvertCurve; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageSize = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type NextNewSession = Session; type ElectionProvider = onchain::OnChainExecution; diff --git a/frame/beefy/src/tests.rs b/frame/beefy/src/tests.rs index 767b1e5657502..2f7c65548dcae 100644 --- a/frame/beefy/src/tests.rs +++ b/frame/beefy/src/tests.rs @@ -277,7 +277,7 @@ fn report_equivocation_current_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(1, validator), + Staking::eras_stakers(1, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -315,7 +315,7 @@ fn report_equivocation_current_set_works() { assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000); assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0); assert_eq!( - Staking::eras_stakers(2, equivocation_validator_id), + Staking::eras_stakers(2, &equivocation_validator_id), pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, ); @@ -329,7 +329,7 @@ fn report_equivocation_current_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(2, validator), + Staking::eras_stakers(2, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -365,7 +365,7 @@ fn report_equivocation_old_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(2, validator), + Staking::eras_stakers(2, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -399,7 +399,7 @@ fn report_equivocation_old_set_works() { assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000); assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0); assert_eq!( - Staking::eras_stakers(3, equivocation_validator_id), + Staking::eras_stakers(3, &equivocation_validator_id), pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, ); @@ -413,7 +413,7 @@ fn report_equivocation_old_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(3, validator), + Staking::eras_stakers(3, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } diff --git a/frame/fast-unstake/src/benchmarking.rs b/frame/fast-unstake/src/benchmarking.rs index 5ec997e8eaa2a..5d31c1f9ded41 100644 --- a/frame/fast-unstake/src/benchmarking.rs +++ b/frame/fast-unstake/src/benchmarking.rs @@ -74,7 +74,7 @@ fn setup_staking(v: u32, until: EraIndex) { .collect::>(); for era in 0..=until { - let others = (0..T::MaxBackersPerValidator::get()) + let others = (0..T::MaxExposurePageSize::get()) .map(|s| { let who = frame_benchmarking::account::("nominator", era, s); let value = ed; diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index 81eb3087b998e..8b05e9b553b0a 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -142,7 +142,7 @@ pub mod pallet { /// Use only for benchmarking. #[cfg(feature = "runtime-benchmarks")] - type MaxBackersPerValidator: Get; + type MaxExposurePageSize: Get; } /// The current "head of the queue" being unstaked. diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index b20ba43ab3758..49b29c448e27b 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -146,7 +146,8 @@ impl pallet_staking::Config for Runtime { type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = (); type HistoryDepth = ConstU32<84>; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageSize = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; type OffendingValidatorsThreshold = (); type ElectionProvider = MockElection; type GenesisElectionProvider = Self::ElectionProvider; @@ -187,7 +188,7 @@ impl fast_unstake::Config for Runtime { type WeightInfo = (); type MaxErasToCheckPerBlock = ConstU32<16>; #[cfg(feature = "runtime-benchmarks")] - type MaxBackersPerValidator = ConstU32<128>; + type MaxExposurePageSize = ConstU32<128>; } type Block = frame_system::mocking::MockBlock; diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 0948be0080889..dbf92a5c72be8 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -196,7 +196,8 @@ impl pallet_staking::Config for Test { type SessionInterface = Self; type UnixTime = pallet_timestamp::Pallet; type EraPayout = pallet_staking::ConvertCurve; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageSize = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type NextNewSession = Session; type ElectionProvider = onchain::OnChainExecution; diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index 4fd5727d54ab2..bc71671ac8766 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -334,7 +334,7 @@ fn report_equivocation_current_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(1, validator), + Staking::eras_stakers(1, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -372,7 +372,7 @@ fn report_equivocation_current_set_works() { assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000); assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0); assert_eq!( - Staking::eras_stakers(2, equivocation_validator_id), + Staking::eras_stakers(2, &equivocation_validator_id), pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, ); @@ -386,7 +386,7 @@ fn report_equivocation_current_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(2, validator), + Staking::eras_stakers(2, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -418,7 +418,7 @@ fn report_equivocation_old_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(2, validator), + Staking::eras_stakers(2, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } @@ -451,7 +451,7 @@ fn report_equivocation_old_set_works() { assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0); assert_eq!( - Staking::eras_stakers(3, equivocation_validator_id), + Staking::eras_stakers(3, &equivocation_validator_id), pallet_staking::Exposure { total: 0, own: 0, others: vec![] }, ); @@ -465,7 +465,7 @@ fn report_equivocation_old_set_works() { assert_eq!(Staking::slashable_balance_of(validator), 10_000); assert_eq!( - Staking::eras_stakers(3, validator), + Staking::eras_stakers(3, &validator), pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] }, ); } diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 4e188ea7ef189..177f3311e8e2f 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -107,7 +107,8 @@ impl pallet_staking::Config for Runtime { type SessionInterface = (); type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = (); - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; + type MaxExposurePageSize = ConstU32<64>; type OffendingValidatorsThreshold = (); type ElectionProvider = frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking, ())>; diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 3d0ab2c6f35f3..027865ab5e356 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -121,7 +121,8 @@ impl pallet_staking::Config for Runtime { type SessionInterface = (); type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = (); - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; + type MaxExposurePageSize = ConstU32<64>; type OffendingValidatorsThreshold = (); type ElectionProvider = frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking, ())>; diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 233aa449d391c..b008f379ca5f8 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -170,7 +170,8 @@ impl pallet_staking::Config for Test { type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; + type MaxExposurePageSize = ConstU32<64>; type OffendingValidatorsThreshold = (); type ElectionProvider = onchain::OnChainExecution; type GenesisElectionProvider = Self::ElectionProvider; diff --git a/frame/root-offences/src/lib.rs b/frame/root-offences/src/lib.rs index 92f3e17b5e17e..692feef6c3a52 100644 --- a/frame/root-offences/src/lib.rs +++ b/frame/root-offences/src/lib.rs @@ -112,7 +112,7 @@ pub mod pallet { .clone() .into_iter() .map(|(o, _)| OffenceDetails:: { - offender: (o.clone(), Staking::::eras_stakers(now, o)), + offender: (o.clone(), Staking::::eras_stakers(now, &o)), reporters: vec![], }) .collect()) diff --git a/frame/root-offences/src/mock.rs b/frame/root-offences/src/mock.rs index 0937c43d6e519..d8bf065baec5f 100644 --- a/frame/root-offences/src/mock.rs +++ b/frame/root-offences/src/mock.rs @@ -184,7 +184,8 @@ impl pallet_staking::Config for Test { type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; + type MaxExposurePageSize = ConstU32<64>; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type ElectionProvider = onchain::OnChainExecution; type GenesisElectionProvider = Self::ElectionProvider; diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 4c4accbbfac8f..249cb2d3fd789 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -172,7 +172,8 @@ impl pallet_staking::Config for Test { type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageCount = ConstU32<1>; + type MaxExposurePageSize = ConstU32<64>; type OffendingValidatorsThreshold = (); type ElectionProvider = onchain::OnChainExecution; type GenesisElectionProvider = Self::ElectionProvider; diff --git a/frame/staking/CHANGELOG.md b/frame/staking/CHANGELOG.md new file mode 100644 index 0000000000000..411f63b397c7e --- /dev/null +++ b/frame/staking/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is loosely based +on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). We maintain a +single integer version number for staking pallet to keep track of all storage +migrations. + +## [14] + +### Added + +- New item `ErasStakersPaged` that keeps up to `MaxExposurePageSize` + individual nominator exposures by era, validator and page. +- New item `ErasStakersOverview` complementary to `ErasStakersPaged` which keeps + state of own and total stake of the validator across pages. +- New item `ClaimedRewards` to support paged rewards payout. + +### Deprecated + +- `ErasStakersClipped` is deprecated and may be removed after 84 eras. +- `ErasStakers` is deprecated and may be removed after 84 eras. +- Field `claimed_rewards` in item `Ledger` is renamed + to `legacy_claimed_rewards` and may be removed after 84 eras. + +[14]: https://github.com/paritytech/substrate/pull/13059 diff --git a/frame/staking/README.md b/frame/staking/README.md index bbd5bd18f6e81..d09f777ac8bbf 100644 --- a/frame/staking/README.md +++ b/frame/staking/README.md @@ -88,11 +88,11 @@ An account can become a nominator via the [`nominate`](https://docs.rs/pallet-st The **reward and slashing** procedure is the core of the Staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. -Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the +Rewards must be claimed for each era before it gets too old by [`HistoryDeth`] using the `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the -validator as well as its nominators. Only the [`Config::MaxNominatorRewardedPerValidator`] -biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each -nominator's account. +validator as well as its nominators. Rewards are paged to maximum of [`Config::MaxExposurePageSize`] +nominators per call. Each page of staker payout needs to be called separately to ensure all nominators are +paid. This is to limit the i/o cost to mutate storage for each nominator's account. Slashing can occur at any point in time, once misbehavior is reported. Once slashing is determined, a value is deducted from the balance of the validator and all the nominators who diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index ad7aab984bdca..62d386e1ee959 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -546,10 +546,10 @@ benchmarks! { } payout_stakers_dead_controller { - let n in 0 .. T::MaxNominatorRewardedPerValidator::get() as u32; + let n in 0 .. T::MaxExposurePageSize::get() as u32; let (validator, nominators) = create_validator_with_nominators::( n, - T::MaxNominatorRewardedPerValidator::get() as u32, + T::MaxExposurePageSize::get() as u32, true, RewardDestination::Controller, )?; @@ -565,7 +565,7 @@ benchmarks! { let balance = T::Currency::free_balance(controller); ensure!(balance.is_zero(), "Controller has balance, but should be dead."); } - }: payout_stakers(RawOrigin::Signed(caller), validator, current_era) + }: payout_stakers_by_page(RawOrigin::Signed(caller), validator, current_era, 0) verify { let balance_after = T::Currency::free_balance(&validator_controller); ensure!( @@ -579,10 +579,10 @@ benchmarks! { } payout_stakers_alive_staked { - let n in 0 .. T::MaxNominatorRewardedPerValidator::get() as u32; + let n in 0 .. T::MaxExposurePageSize::get() as u32; let (validator, nominators) = create_validator_with_nominators::( n, - T::MaxNominatorRewardedPerValidator::get() as u32, + T::MaxExposurePageSize::get() as u32, false, RewardDestination::Staked, )?; @@ -681,7 +681,7 @@ benchmarks! { active: T::Currency::minimum_balance() - One::one(), total: T::Currency::minimum_balance() - One::one(), unlocking: Default::default(), - claimed_rewards: Default::default(), + legacy_claimed_rewards: Default::default(), }; Ledger::::insert(&controller, l); @@ -754,7 +754,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller); let calls: Vec<_> = payout_calls_arg.iter().map(|arg| - Call::::payout_stakers { validator_stash: arg.0.clone(), era: arg.1 }.encode() + Call::::payout_stakers_by_page { validator_stash: arg.0.clone(), era: arg.1, page: 0 }.encode() ).collect(); }: { for call in calls { @@ -976,7 +976,7 @@ mod tests { let (validator_stash, nominators) = create_validator_with_nominators::( n, - <::MaxNominatorRewardedPerValidator as Get<_>>::get(), + <::MaxExposurePageSize as Get<_>>::get(), false, RewardDestination::Staked, ) @@ -987,10 +987,11 @@ mod tests { let current_era = CurrentEra::::get().unwrap(); let original_free_balance = Balances::free_balance(&validator_stash); - assert_ok!(Staking::payout_stakers( + assert_ok!(Staking::payout_stakers_by_page( RuntimeOrigin::signed(1337), validator_stash, - current_era + current_era, + 0 )); let new_free_balance = Balances::free_balance(&validator_stash); @@ -1005,7 +1006,7 @@ mod tests { let (validator_stash, _nominators) = create_validator_with_nominators::( n, - <::MaxNominatorRewardedPerValidator as Get<_>>::get(), + <::MaxExposurePageSize as Get<_>>::get(), false, RewardDestination::Staked, ) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index d6345c2161f73..b761687184ebb 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -112,11 +112,17 @@ //! The **reward and slashing** procedure is the core of the Staking pallet, attempting to _embrace //! valid behavior_ while _punishing any misbehavior or lack of availability_. //! -//! Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the -//! `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the -//! validator as well as its nominators. Only the [`Config::MaxNominatorRewardedPerValidator`] -//! biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each -//! nominator's account. +//! Rewards must be claimed for each era before it gets too old by +//! [`HistoryDepth`](`Config::HistoryDepth`) using the `payout_stakers` call. Any account can call +//! `payout_stakers`, which pays the reward to the validator as well as its nominators. Only the +//! [`Config::MaxExposurePageSize`] nominator rewards can be claimed in a single call. When the +//! number of nominators exceeds [`Config::MaxExposurePageSize`], then the nominators are stored in +//! multiple pages of [`Config::MaxExposurePageSize`] each with upto maximum of +//! [`Config::MaxExposurePageCount`] pages. To pay out all nominators, `payout_stakers` must be +//! called once for each available page. In a scenario where number of nominators N exceed M where M +//! = [`Config::MaxExposurePageSize`] * [`Config::MaxExposurePageCount`], only top M nominators by +//! stake balance are paid out. The rest of the nominators are not paid out. Paging exists to limit +//! the i/o cost to mutate storage for each nominator's account. //! //! Slashing can occur at any point in time, once misbehavior is reported. Once slashing is //! determined, a value is deducted from the balance of the validator and all the nominators who @@ -224,13 +230,13 @@ //! The validator can declare an amount, named [`commission`](ValidatorPrefs::commission), that does //! not get shared with the nominators at each reward payout through its [`ValidatorPrefs`]. This //! value gets deducted from the total reward that is paid to the validator and its nominators. The -//! remaining portion is split pro rata among the validator and the top -//! [`Config::MaxNominatorRewardedPerValidator`] nominators that nominated the validator, -//! proportional to the value staked behind the validator (_i.e._ dividing the +//! remaining portion is split pro rata among the validator and the nominators that nominated the +//! validator, proportional to the value staked behind the validator (_i.e._ dividing the //! [`own`](Exposure::own) or [`others`](Exposure::others) by [`total`](Exposure::total) in -//! [`Exposure`]). Note that the pro rata division of rewards uses the total exposure behind the -//! validator, *not* just the exposure of the validator and the top -//! [`Config::MaxNominatorRewardedPerValidator`] nominators. +//! [`Exposure`]). Note that payouts are made in pages with each page capped at +//! [`Config::MaxExposurePageSize`] nominators. The distribution of nominators across +//! pages may be unsorted. The total commission is paid out proportionally across pages based on the +//! total stake of the page. //! //! All entities who receive a reward have the option to choose their reward destination through the //! [`Payee`] storage item (see @@ -313,7 +319,7 @@ use sp_runtime::{ }; use sp_staking::{ offence::{Offence, OffenceError, ReportOffence}, - EraIndex, SessionIndex, + EraIndex, PageIndex, SessionIndex, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; pub use weights::WeightInfo; @@ -467,7 +473,10 @@ pub struct StakingLedger { pub unlocking: BoundedVec>, T::MaxUnlockingChunks>, /// List of eras for which the stakers behind a validator have claimed rewards. Only updated /// for validators. - pub claimed_rewards: BoundedVec, + /// + /// This is deprecated as of V14 in favor of `T::ClaimedRewards` and will be removed in future. + /// Refer issue: #13034 + pub legacy_claimed_rewards: BoundedVec, } impl StakingLedger { @@ -478,7 +487,7 @@ impl StakingLedger { total: Zero::zero(), active: Zero::zero(), unlocking: Default::default(), - claimed_rewards: Default::default(), + legacy_claimed_rewards: Default::default(), } } @@ -508,7 +517,7 @@ impl StakingLedger { total, active: self.active, unlocking, - claimed_rewards: self.claimed_rewards, + legacy_claimed_rewards: self.legacy_claimed_rewards, } } @@ -732,6 +741,140 @@ impl Default for Exposure Exposure +{ + /// Splits an `Exposure` into `ExposureOverview` and multiple chunks of `IndividualExposure` + /// with each chunk having maximum of `page_size` elements. + fn into_pages( + self, + page_size: u32, + ) -> (ExposureOverview, Vec>) { + let individual_chunks = self.others.chunks(page_size as usize); + let mut exposure_pages: Vec> = + Vec::with_capacity(individual_chunks.len()); + + for chunk in individual_chunks { + let mut page_total: Balance = Zero::zero(); + let mut others: Vec> = vec![]; + for individual in chunk.iter() { + page_total = page_total.saturating_add(individual.value); + others.push(IndividualExposure { + who: individual.who.clone(), + value: individual.value, + }) + } + + exposure_pages.push(ExposurePage { page_total, others }); + } + + ( + ExposureOverview { + total: self.total, + own: self.own, + nominator_count: self.others.len() as u32, + page_count: exposure_pages.len() as PageIndex, + }, + exposure_pages, + ) + } +} + +/// A snapshot of the stake backing a single validator in the system. +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ExposurePage { + /// The total balance of this chunk/page. + #[codec(compact)] + pub page_total: Balance, + /// The portions of nominators stashes that are exposed. + pub others: Vec>, +} + +impl Default for ExposurePage { + fn default() -> Self { + ExposurePage { page_total: Default::default(), others: vec![] } + } +} + +/// An overview of stake backing a single validator. +/// +/// It, in combination with a list of `ExposurePage`s, can be used to reconstruct a full `Exposure` +/// struct. This is useful for cases where we want to query a single page of `Exposure`s. +#[derive( + PartialEq, + Eq, + PartialOrd, + Ord, + Clone, + Encode, + Decode, + RuntimeDebug, + TypeInfo, + Default, + MaxEncodedLen, +)] +pub struct ExposureOverview { + /// The total balance backing this validator. + #[codec(compact)] + pub total: Balance, + /// The validator's own stash that is exposed. + #[codec(compact)] + pub own: Balance, + /// Number of nominators backing this validator. + pub nominator_count: u32, + /// Number of pages of nominators. + pub page_count: PageIndex, +} + +/// Extended view of Exposure comprising of `ExposureOverview` and a single page of `ExposurePage`. +/// +/// This is useful where we need to take into account the validator's own stake and total exposure +/// in consideration, in addition to the individual nominators backing them. +#[derive(Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Eq)] +struct ExposureExt { + exposure_overview: ExposureOverview, + exposure_page: ExposurePage, +} + +impl + ExposureExt +{ + /// Create a new instance of `ExposureExt` from legacy clipped exposures. + pub fn from_clipped(exposure: Exposure) -> Self { + Self { + exposure_overview: ExposureOverview { + total: exposure.total, + own: exposure.own, + nominator_count: exposure.others.len() as u32, + page_count: 1, + }, + exposure_page: ExposurePage { page_total: exposure.total, others: exposure.others }, + } + } + + /// Returns total exposure of this validator across pages + pub fn total(&self) -> Balance { + self.exposure_overview.total + } + + /// Returns total exposure of this validator for the current page + pub fn page_total(&self) -> Balance { + self.exposure_page.page_total + self.exposure_overview.own + } + + /// Returns validator's own stake that is exposed + pub fn own(&self) -> Balance { + self.exposure_overview.own + } + + /// Returns the portions of nominators stashes that are exposed in this page. + pub fn others(&self) -> &Vec> { + &self.exposure_page.others + } +} + /// A pending slash record. The value of the slash has been computed but not applied yet, /// rather deferred for several eras. #[derive(Encode, Decode, RuntimeDebug, TypeInfo)] diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index 3c33ae2a35d62..b1c84d6c034fa 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -52,6 +52,47 @@ impl Default for ObsoleteReleases { #[storage_alias] type StorageVersion = StorageValue, ObsoleteReleases, ValueQuery>; +pub mod v14 { + use super::*; + + pub struct MigrateToV14(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV14 { + fn on_runtime_upgrade() -> Weight { + let current = Pallet::::current_storage_version(); + let on_chain = Pallet::::on_chain_storage_version(); + + if current == 14 && on_chain == 13 { + current.put::>(); + + log!(info, "v14 applied successfully."); + T::DbWeight::get().reads_writes(1, 1) + } else { + log!(warn, "v14 not applied."); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + frame_support::ensure!( + Pallet::::on_chain_storage_version() == 13, + "Required v13 before upgrading to v14." + ); + + Ok(Default::default()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + frame_support::ensure!( + Pallet::::on_chain_storage_version() == 14, + "v14 not applied" + ); + Ok(()) + } + } +} + pub mod v13 { use super::*; @@ -107,9 +148,9 @@ pub mod v12 { #[storage_alias] type HistoryDepth = StorageValue, u32, ValueQuery>; - /// Clean up `HistoryDepth` from storage. + /// Clean up `T::HistoryDepth` from storage. /// - /// We will be depending on the configurable value of `HistoryDepth` post + /// We will be depending on the configurable value of `T::HistoryDepth` post /// this release. pub struct MigrateToV12(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV12 { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index e876940cb92d3..76c8532b9d8cd 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -230,6 +230,8 @@ parameter_types! { pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS; pub static MaxNominations: u32 = 16; pub static HistoryDepth: u32 = 80; + pub static MaxExposurePageSize: u32 = 64; + pub static MaxExposurePageCount: u32 = 1; pub static MaxUnlockingChunks: u32 = 32; pub static RewardOnUnbalanceWasCalled: bool = false; pub static LedgerSlashPerEra: (BalanceOf, BTreeMap>) = (Zero::zero(), BTreeMap::new()); @@ -292,7 +294,8 @@ impl crate::pallet::pallet::Config for Test { type SessionInterface = Self; type EraPayout = ConvertCurve; type NextNewSession = Session; - type MaxNominatorRewardedPerValidator = ConstU32<64>; + type MaxExposurePageSize = MaxExposurePageSize; + type MaxExposurePageCount = MaxExposurePageCount; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type ElectionProvider = onchain::OnChainExecution; type GenesisElectionProvider = Self::ElectionProvider; @@ -739,7 +742,7 @@ pub(crate) fn on_offence_now( pub(crate) fn add_slash(who: &AccountId) { on_offence_now( &[OffenceDetails { - offender: (*who, Staking::eras_stakers(active_era(), *who)), + offender: (*who, Staking::eras_stakers(active_era(), who)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -757,7 +760,14 @@ pub(crate) fn make_all_reward_payment(era: EraIndex) { // 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)); + for page in 0..EraInfo::::get_page_count(era, &ledger.stash) { + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + ledger.stash, + era, + page + )); + } } } @@ -811,3 +821,7 @@ pub(crate) fn staking_events_since_last_call() -> Vec> { pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { (Balances::free_balance(who), Balances::reserved_balance(who)) } + +pub(crate) fn allow_paged_rewards(max_pages: u32) { + MaxExposurePageCount::set(max_pages.max(1)); +} diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 99c2f6043ee2c..f8222f37bf390 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -25,8 +25,8 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Currency, CurrencyToVote, Defensive, DefensiveResult, EstimateNextNewSession, Get, - Imbalance, LockableCurrency, OnUnbalanced, TryCollect, UnixTime, WithdrawReasons, + Currency, CurrencyToVote, Defensive, EstimateNextNewSession, Get, Imbalance, Len, + LockableCurrency, OnUnbalanced, TryCollect, UnixTime, WithdrawReasons, }, weights::Weight, }; @@ -38,7 +38,7 @@ use sp_runtime::{ }; use sp_staking::{ offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, SessionIndex, Stake, StakingInterface, + EraIndex, PageIndex, SessionIndex, Stake, StakingInterface, }; use sp_std::prelude::*; @@ -134,12 +134,31 @@ impl Pallet { pub(super) fn do_payout_stakers( validator_stash: T::AccountId, era: EraIndex, + ) -> DispatchResultWithPostInfo { + let controller = Self::bonded(&validator_stash).ok_or_else(|| { + Error::::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + })?; + let ledger = >::get(&controller).ok_or(Error::::NotController)?; + let page = EraInfo::::get_next_claimable_page(era, &validator_stash, &ledger) + .ok_or_else(|| { + Error::::AlreadyClaimed + .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + })?; + + Self::do_payout_stakers_by_page(validator_stash, era, page) + } + + pub(super) fn do_payout_stakers_by_page( + validator_stash: T::AccountId, + era: EraIndex, + page: PageIndex, ) -> DispatchResultWithPostInfo { // Validate input data let current_era = CurrentEra::::get().ok_or_else(|| { Error::::InvalidEraToReward .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) })?; + let history_depth = T::HistoryDepth::get(); ensure!( era <= current_era && era >= current_era.saturating_sub(history_depth), @@ -147,8 +166,13 @@ impl Pallet { .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) ); + ensure!( + page < EraInfo::::get_page_count(era, &validator_stash), + Error::::InvalidPage.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + ); + // Note: if era has no reward to be claimed, era may be future. better not to update - // `ledger.claimed_rewards` in this case. + // `ledger.legacy_claimed_rewards` in this case. let era_payout = >::get(&era).ok_or_else(|| { Error::::InvalidEraToReward .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) @@ -159,29 +183,28 @@ impl Pallet { })?; let mut ledger = >::get(&controller).ok_or(Error::::NotController)?; + // clean up older claimed rewards ledger - .claimed_rewards + .legacy_claimed_rewards .retain(|&x| x >= current_era.saturating_sub(history_depth)); + >::insert(&controller, &ledger); - match ledger.claimed_rewards.binary_search(&era) { - Ok(_) => - return Err(Error::::AlreadyClaimed - .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))), - Err(pos) => ledger - .claimed_rewards - .try_insert(pos, era) - // Since we retain era entries in `claimed_rewards` only upto - // `HistoryDepth`, following bound is always expected to be - // satisfied. - .defensive_map_err(|_| Error::::BoundNotMet)?, + if EraInfo::::is_rewards_claimed_with_legacy_fallback(era, &ledger, &ledger.stash, page) + { + return Err(Error::::AlreadyClaimed + .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))) + } else { + EraInfo::::set_rewards_as_claimed(era, &ledger.stash, page); } - let exposure = >::get(&era, &ledger.stash); + let exposure = + EraInfo::::get_validator_exposure(era, &ledger.stash, page).ok_or_else(|| { + Error::::InvalidEraToReward + .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + })?; // Input data seems good, no errors allowed after this point - >::insert(&controller, &ledger); - // Get Era reward points. It has TOTAL and INDIVIDUAL // Find the fraction of the era reward that belongs to the validator // Take that fraction of the eras rewards to split to nominator and validator @@ -210,15 +233,17 @@ impl Pallet { // This is how much validator + nominators are entitled to. let validator_total_payout = validator_total_reward_part * era_payout; - let validator_prefs = Self::eras_validator_prefs(&era, &validator_stash); - // Validator first gets a cut off the top. - let validator_commission = validator_prefs.commission; - let validator_commission_payout = validator_commission * validator_total_payout; + let validator_commission = EraInfo::::get_validator_commission(era, &ledger.stash); + // total commission validator takes across all nominator pages + let validator_total_commission_payout = validator_commission * validator_total_payout; - let validator_leftover_payout = validator_total_payout - validator_commission_payout; + let validator_leftover_payout = validator_total_payout - validator_total_commission_payout; // Now let's calculate how this is split to the validator. - let validator_exposure_part = Perbill::from_rational(exposure.own, exposure.total); + let validator_exposure_part = Perbill::from_rational(exposure.own(), exposure.total()); let validator_staking_payout = validator_exposure_part * validator_leftover_payout; + let page_stake_part = Perbill::from_rational(exposure.page_total(), exposure.total()); + // validator commission is paid out in fraction across pages proportional to the page stake. + let validator_commission_payout = page_stake_part * validator_total_commission_payout; Self::deposit_event(Event::::PayoutStarted { era_index: era, @@ -244,8 +269,8 @@ impl Pallet { // Lets now calculate how this is split to the nominators. // Reward only the clipped exposures. Note this is not necessarily sorted. - for nominator in exposure.others.iter() { - let nominator_exposure_part = Perbill::from_rational(nominator.value, exposure.total); + for nominator in exposure.others().iter() { + let nominator_exposure_part = Perbill::from_rational(nominator.value, exposure.total()); let nominator_reward: BalanceOf = nominator_exposure_part * validator_leftover_payout; @@ -261,7 +286,8 @@ impl Pallet { } T::Reward::on_unbalanced(total_imbalance); - debug_assert!(nominator_payout_count <= T::MaxNominatorRewardedPerValidator::get()); + debug_assert!(nominator_payout_count <= T::MaxExposurePageSize::get()); + Ok(Some(T::WeightInfo::payout_stakers_alive_staked(nominator_payout_count)).into()) } @@ -561,31 +587,24 @@ impl Pallet { >, new_planned_era: EraIndex, ) -> BoundedVec> { - let elected_stashes: BoundedVec<_, MaxWinnersOf> = exposures - .iter() - .cloned() - .map(|(x, _)| x) - .collect::>() - .try_into() - .expect("since we only map through exposures, size of elected_stashes is always same as exposures; qed"); - - // Populate stakers, exposures, and the snapshot of validator prefs. + // Populate elected stash, stakers, exposures, and the snapshot of validator prefs. let mut total_stake: BalanceOf = Zero::zero(); + let mut elected_stashes = Vec::with_capacity(exposures.len()); + exposures.into_iter().for_each(|(stash, exposure)| { + // build elected stash + elected_stashes.push(stash.clone()); + // accumulate total stake total_stake = total_stake.saturating_add(exposure.total); - >::insert(new_planned_era, &stash, &exposure); - - let mut exposure_clipped = exposure; - let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize; - if exposure_clipped.others.len() > clipped_max_len { - exposure_clipped.others.sort_by(|a, b| a.value.cmp(&b.value).reverse()); - exposure_clipped.others.truncate(clipped_max_len); - } - >::insert(&new_planned_era, &stash, exposure_clipped); + // store staker exposure for this era + EraInfo::::set_validator_exposure(new_planned_era, &stash, exposure); }); - // Insert current era staking information - >::insert(&new_planned_era, total_stake); + let elected_stashes: BoundedVec<_, MaxWinnersOf> = elected_stashes + .try_into() + .expect("elected_stashes.len() always equal to exposures.len(); qed"); + + EraInfo::::set_total_stake(new_planned_era, total_stake); // Collect the pref of all winners. for stash in &elected_stashes { @@ -674,6 +693,13 @@ impl Pallet { >::remove_prefix(era_index, None); #[allow(deprecated)] >::remove_prefix(era_index, None); + #[allow(deprecated)] + >::remove_prefix(era_index, None); + #[allow(deprecated)] + >::remove_prefix((era_index,), None); + #[allow(deprecated)] + >::remove_prefix(era_index, None); + >::remove(era_index); >::remove(era_index); >::remove(era_index); @@ -970,6 +996,18 @@ impl Pallet { DispatchClass::Mandatory, ); } + + /// Returns full exposure of a validator for a given era. + /// + /// History note: This used to be a getter for old storage item `ErasStakers` deprecated in v14. + /// Since this function is used in the codebase at various places, we kept it as a custom getter + /// that takes care of getting the full exposure of the validator in a backward compatible way. + pub fn eras_stakers( + era: EraIndex, + account: &T::AccountId, + ) -> Exposure> { + EraInfo::::get_non_paged_validator_exposure(era, &account) + } } impl Pallet { @@ -1065,7 +1103,7 @@ impl ElectionDataProvider for Pallet { active: stake, total: stake, unlocking: Default::default(), - claimed_rewards: Default::default(), + legacy_claimed_rewards: Default::default(), }, ); @@ -1083,7 +1121,7 @@ impl ElectionDataProvider for Pallet { active: stake, total: stake, unlocking: Default::default(), - claimed_rewards: Default::default(), + legacy_claimed_rewards: Default::default(), }, ); Self::do_add_validator( @@ -1124,7 +1162,7 @@ impl ElectionDataProvider for Pallet { active: stake, total: stake, unlocking: Default::default(), - claimed_rewards: Default::default(), + legacy_claimed_rewards: Default::default(), }, ); Self::do_add_validator( @@ -1145,7 +1183,7 @@ impl ElectionDataProvider for Pallet { active: stake, total: stake, unlocking: Default::default(), - claimed_rewards: Default::default(), + legacy_claimed_rewards: Default::default(), }, ); Self::do_add_nominator( diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index a030538878241..56447b8e510a1 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -24,9 +24,8 @@ use frame_support::{ dispatch::Codec, pallet_prelude::*, traits::{ - Currency, CurrencyToVote, Defensive, DefensiveResult, DefensiveSaturating, EnsureOrigin, - EstimateNextNewSession, Get, LockIdentifier, LockableCurrency, OnUnbalanced, TryCollect, - UnixTime, + Currency, CurrencyToVote, Defensive, DefensiveSaturating, EnsureOrigin, + EstimateNextNewSession, Get, LockIdentifier, LockableCurrency, OnUnbalanced, UnixTime, }, weights::Weight, BoundedVec, @@ -36,7 +35,7 @@ use sp_runtime::{ traits::{CheckedSub, SaturatedConversion, StaticLookup, Zero}, ArithmeticError, Perbill, Percent, }; -use sp_staking::{EraIndex, SessionIndex}; +use sp_staking::{EraIndex, PageIndex, SessionIndex}; use sp_std::prelude::*; mod impls; @@ -45,9 +44,9 @@ pub use impls::*; use crate::{ slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout, - EraRewardPoints, Exposure, Forcing, NegativeImbalanceOf, Nominations, PositiveImbalanceOf, - RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk, - ValidatorPrefs, + EraRewardPoints, Exposure, ExposurePage, Forcing, NegativeImbalanceOf, Nominations, + PositiveImbalanceOf, RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, + UnlockChunk, ValidatorPrefs, }; const STAKING_ID: LockIdentifier = *b"staking "; @@ -59,13 +58,14 @@ pub(crate) const SPECULATIVE_NUM_SPANS: u32 = 32; #[frame_support::pallet] pub mod pallet { use frame_election_provider_support::ElectionDataProvider; + use frame_support::{defensive, traits::DefensiveMax}; - use crate::BenchmarkingConfig; + use crate::{BenchmarkingConfig, ExposureExt, ExposureOverview}; use super::*; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(13); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(14); #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] @@ -139,8 +139,8 @@ pub mod pallet { /// Following information is kept for eras in `[current_era - /// HistoryDepth, current_era]`: `ErasStakers`, `ErasStakersClipped`, /// `ErasValidatorPrefs`, `ErasValidatorReward`, `ErasRewardPoints`, - /// `ErasTotalStake`, `ErasStartSessionIndex`, - /// `StakingLedger.claimed_rewards`. + /// `ErasTotalStake`, `ErasStartSessionIndex`, `ClaimedRewards`, `ErasStakersPaged`, + /// `ErasStakersOverview`. /// /// Must be more than the number of eras delayed by session. /// I.e. active era must always be in history. I.e. `active_era > @@ -150,7 +150,7 @@ pub mod pallet { /// this should be set to same value or greater as in storage. /// /// Note: `HistoryDepth` is used as the upper bound for the `BoundedVec` - /// item `StakingLedger.claimed_rewards`. Setting this value lower than + /// item `StakingLedger.legacy_claimed_rewards`. Setting this value lower than /// the existing value can lead to inconsistencies in the /// `StakingLedger` and will need to be handled properly in a migration. /// The test `reducing_history_depth_abrupt` shows this effect. @@ -203,12 +203,28 @@ pub mod pallet { /// guess. type NextNewSession: EstimateNextNewSession; - /// The maximum number of nominators rewarded for each validator. + /// The maximum size of each `T::ExposurePage`. /// - /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can - /// claim their reward. This used to limit the i/o cost for the nominator payout. + /// An `ExposurePage` is weakly bounded to a maximum of `MaxExposurePageSize` + /// nominators. + /// + /// For older non-paged exposure, a reward payout was restricted to the top + /// `MaxExposurePageSize` nominators. This is to limit the i/o cost for the + /// nominator payout. + /// + /// Note: `MaxExposurePageSize` is used to bound `ClaimedRewards` and is unsafe to reduce + /// without handling it in a migration. + #[pallet::constant] + type MaxExposurePageSize: Get; + + /// Maximum number of exposure pages that can be stored for a single validator in an era. + /// + /// Must be greater than 0. + /// + /// When this is set to 1, the reward payout behaviour is similar to how it used to work + /// before we had paged exposures. #[pallet::constant] - type MaxNominatorRewardedPerValidator: Get; + type MaxExposurePageCount: Get; /// The fraction of the validator set that is safe to be offending. /// After the threshold is reached a new era will be forced. @@ -388,7 +404,7 @@ pub mod pallet { #[pallet::getter(fn active_era)] pub type ActiveEra = StorageValue<_, ActiveEraInfo>; - /// The session index at which the era start for the last `HISTORY_DEPTH` eras. + /// The session index at which the era start for the last [`Config::HistoryDepth`] eras. /// /// Note: This tracks the starting session (i.e. session index when era start being active) /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`. @@ -400,10 +416,11 @@ pub mod pallet { /// /// This is keyed first by the era index to allow bulk deletion and then the stash account. /// - /// Is it removed after `HISTORY_DEPTH` eras. + /// Is it removed after [`Config::HistoryDepth`] eras. /// If stakers hasn't been set or has been removed then empty exposure is returned. + /// + /// Note: Deprecated since v14. Use `EraInfo` instead to work with exposures. #[pallet::storage] - #[pallet::getter(fn eras_stakers)] #[pallet::unbounded] pub type ErasStakers = StorageDoubleMap< _, @@ -415,17 +432,45 @@ pub mod pallet { ValueQuery, >; + /// Summary of validator exposure at a given era. + /// + /// This contains the total stake in support of the validator and their own stake. In addition, + /// it can also be used to get the number of nominators backing this validator and the number of + /// exposure pages they are divided into. The page count is useful to determine the number of + /// pages of rewards that needs to be claimed. + /// + /// This is keyed first by the era index to allow bulk deletion and then the stash account. + /// Should only be accessed through `EraInfo`. + /// + /// Is it removed after [`Config::HistoryDepth`] eras. + /// If stakers hasn't been set or has been removed then empty overview is returned. + #[pallet::storage] + pub type ErasStakersOverview = StorageDoubleMap< + _, + Twox64Concat, + EraIndex, + Twox64Concat, + T::AccountId, + ExposureOverview>, + OptionQuery, + >; + /// Clipped Exposure of validator at era. /// + /// Note: This is deprecated, should be used as read-only and will be removed in the future. + /// New `Exposure`s are stored in a paged manner in `ErasStakersPaged` instead. + /// /// This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the - /// `T::MaxNominatorRewardedPerValidator` biggest stakers. + /// `T::MaxExposurePageSize` biggest stakers. /// (Note: the field `total` and `own` of the exposure remains unchanged). /// This is used to limit the i/o cost for the nominator payout. /// /// This is keyed fist by the era index to allow bulk deletion and then the stash account. /// - /// Is it removed after `HISTORY_DEPTH` eras. + /// It is removed after [`Config::HistoryDepth`] eras. /// If stakers hasn't been set or has been removed then empty exposure is returned. + /// + /// Note: Deprecated since v14. Use `EraInfo` instead to work with exposures. #[pallet::storage] #[pallet::unbounded] #[pallet::getter(fn eras_stakers_clipped)] @@ -439,11 +484,49 @@ pub mod pallet { ValueQuery, >; + /// Paginated exposure of a validator at given era. + /// + /// This is keyed first by the era index to allow bulk deletion, then stash account and finally + /// the page. Should only be accessed through `EraInfo`. + /// + /// This is cleared after [`Config::HistoryDepth`] eras. + #[pallet::storage] + #[pallet::unbounded] + pub type ErasStakersPaged = StorageNMap< + _, + ( + NMapKey, + NMapKey, + NMapKey, + ), + ExposurePage>, + OptionQuery, + >; + + /// History of claimed paged rewards by era and validator. + /// + /// This is keyed by era and validator stash which maps to the set of page indexes which have + /// been claimed. + /// + /// It is removed after [`Config::HistoryDepth`] eras. + #[pallet::storage] + #[pallet::getter(fn claimed_rewards)] + #[pallet::unbounded] + pub type ClaimedRewards = StorageDoubleMap< + _, + Twox64Concat, + EraIndex, + Twox64Concat, + T::AccountId, + Vec, + ValueQuery, + >; + /// Similar to `ErasStakers`, this holds the preferences of validators. /// /// This is keyed first by the era index to allow bulk deletion and then the stash account. /// - /// Is it removed after `HISTORY_DEPTH` eras. + /// Is it removed after [`Config::HistoryDepth`] eras. // If prefs hasn't been set or has been removed then 0 commission is returned. #[pallet::storage] #[pallet::getter(fn eras_validator_prefs)] @@ -457,14 +540,14 @@ pub mod pallet { ValueQuery, >; - /// The total validator era payout for the last `HISTORY_DEPTH` eras. + /// The total validator era payout for the last [`Config::HistoryDepth`] eras. /// /// Eras that haven't finished yet or has been removed doesn't have reward. #[pallet::storage] #[pallet::getter(fn eras_validator_reward)] pub type ErasValidatorReward = StorageMap<_, Twox64Concat, EraIndex, BalanceOf>; - /// Rewards for the last `HISTORY_DEPTH` eras. + /// Rewards for the last [`Config::HistoryDepth`] eras. /// If reward hasn't been set or has been removed then 0 reward is returned. #[pallet::storage] #[pallet::unbounded] @@ -472,7 +555,7 @@ pub mod pallet { pub type ErasRewardPoints = StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints, ValueQuery>; - /// The total amount staked for the last `HISTORY_DEPTH` eras. + /// The total amount staked for the last [`Config::HistoryDepth`] eras. /// If total hasn't been set or has been removed then 0 stake is returned. #[pallet::storage] #[pallet::getter(fn eras_total_stake)] @@ -559,6 +642,217 @@ pub mod pallet { #[pallet::getter(fn current_planned_session)] pub type CurrentPlannedSession = StorageValue<_, SessionIndex, ValueQuery>; + /// Wrapper struct for Era related information. It is not a pure encapsulation as these storage + /// items can be accessed directly but nevertheless, its recommended to use `EraInfo` where we + /// can and add more functions to it as needed. + pub(crate) struct EraInfo(sp_std::marker::PhantomData); + impl EraInfo { + /// Temporary function which looks at both (1) passed param `T::StakingLedger` for legacy + /// non-paged rewards, and (2) `T::ClaimedRewards` for paged rewards. This function can be + /// removed once `T::HistoryDepth` eras have passed and none of the older non-paged rewards + /// are relevant/claimable. + // Refer tracker issue for cleanup: #13034 + pub(crate) fn is_rewards_claimed_with_legacy_fallback( + era: EraIndex, + ledger: &StakingLedger, + validator: &T::AccountId, + page: PageIndex, + ) -> bool { + ledger.legacy_claimed_rewards.binary_search(&era).is_ok() || + Self::is_rewards_claimed(era, validator, page) + } + + /// Check if the rewards for the given era and page index have been claimed. + /// + /// This is only used for paged rewards. Once older non-paged rewards are no longer + /// relevant, `is_rewards_claimed_with_legacy_fallback` can be removed and this function can + /// be made public. + fn is_rewards_claimed(era: EraIndex, validator: &T::AccountId, page: PageIndex) -> bool { + ClaimedRewards::::get(era, validator).contains(&page) + } + + /// Get exposure info for a validator at a given era and page. + /// + /// This builds a paged exposure from `ExposureOverview` and `ExposurePage` of the + /// validator. For older non-paged exposure, it returns the clipped exposure directly. + pub(crate) fn get_validator_exposure( + era: EraIndex, + validator: &T::AccountId, + page: PageIndex, + ) -> Option>> { + let overview = >::get(&era, validator); + + // return clipped exposure if page zero and paged exposure does not exist + // exists for backward compatibility and can be removed as part of #13034 + if overview.is_none() && page == 0 { + return Some(ExposureExt::from_clipped(>::get(era, validator))) + } + + // no exposure for this validator + if overview.is_none() { + return None + } + + let overview = overview.unwrap(); + + // validator stake is added only in page zero + let validator_stake = if page == 0 { overview.own } else { Zero::zero() }; + + // since overview is present, paged exposure will always be present except when a + // validator has only own stake and no nominator stake. + let exposure_page = + >::get((era, validator, page)).unwrap_or_default(); + + // build the exposure + Some(ExposureExt { + exposure_overview: ExposureOverview { own: validator_stake, ..overview }, + exposure_page, + }) + } + + /// Get complete exposure of the validator at a given era. + pub(crate) fn get_non_paged_validator_exposure( + era: EraIndex, + validator: &T::AccountId, + ) -> Exposure> { + let overview = >::get(&era, validator); + + if overview.is_none() { + return ErasStakers::::get(era, validator) + } + + let overview = overview.unwrap(); + + if overview.page_count == 0 { + return Exposure { total: overview.total, own: overview.own, others: vec![] } + } + + let mut others = Vec::with_capacity(overview.nominator_count as usize); + for page in 0..overview.page_count { + let nominators = >::get((era, validator, page)); + others.append(&mut nominators.map(|n| n.others).defensive_unwrap_or_default()); + } + + Exposure { total: overview.total, own: overview.own, others } + } + + /// Returns the number of pages of exposure a validator has for the given era. + /// + /// This will always return at minimum one count of exposure to be backward compatible to + /// non-paged reward payouts. + pub(crate) fn get_page_count(era: EraIndex, validator: &T::AccountId) -> PageIndex { + >::get(&era, validator) + .map(|overview| { + if overview.page_count == 0 && overview.own > Zero::zero() { + // this means the validator has their own stake but no nominators + 1 + } else { + overview.page_count + } + }) + // Returns minimum of one page for backward compatibility with non paged exposure. + // FIXME: Can be cleaned up with issue #13034. + .unwrap_or(1) + } + + /// Returns the next page that can be claimed or `None` if nothing to claim. + pub(crate) fn get_next_claimable_page( + era: EraIndex, + validator: &T::AccountId, + ledger: &StakingLedger, + ) -> Option { + if Self::is_non_paged_exposure(era, validator) { + return match ledger.legacy_claimed_rewards.binary_search(&era) { + // already claimed + Ok(_) => None, + // Non-paged exposure is considered as a single page + Err(_) => Some(0), + } + } + + // Find next claimable page of paged exposure. + let page_count = Self::get_page_count(era, validator); + let all_claimable_pages: Vec = (0..page_count).collect(); + let claimed_pages = ClaimedRewards::::get(era, validator); + + all_claimable_pages.into_iter().find(|p| !claimed_pages.contains(p)) + } + + /// Checks if exposure is paged or not. + fn is_non_paged_exposure(era: EraIndex, validator: &T::AccountId) -> bool { + >::contains_key(&era, validator) + } + + /// Returns validator commission for this era and page. + pub(crate) fn get_validator_commission( + era: EraIndex, + validator_stash: &T::AccountId, + ) -> Perbill { + >::get(&era, validator_stash).commission + } + + /// Creates an entry to track validator reward has been claimed for a given era and page. + /// Noop if already claimed. + pub(crate) fn set_rewards_as_claimed( + era: EraIndex, + validator: &T::AccountId, + page: PageIndex, + ) { + let mut claimed_pages = ClaimedRewards::::get(era, validator); + + // this should never be called if the reward has already been claimed + if claimed_pages.contains(&page) { + defensive!("Trying to set an already claimed reward"); + // nevertheless don't do anything since the page already exist in claimed rewards. + return + } + + // add page to claimed entries + claimed_pages.push(page); + ClaimedRewards::::insert(era, validator, claimed_pages); + } + + /// Store exposure for elected validators at start of an era. + pub(crate) fn set_validator_exposure( + era: EraIndex, + validator: &T::AccountId, + exposure: Exposure>, + ) { + let page_size = T::MaxExposurePageSize::get().defensive_max(1); + let max_page_count = T::MaxExposurePageCount::get(); + + let nominator_count = exposure.others.len(); + let required_page_count = nominator_count + .defensive_saturating_add(page_size as usize - 1) / + page_size as usize; + + // clip nominators if it exceeds the maximum page count. + let exposure = if required_page_count as PageIndex > max_page_count { + // sort before clipping. + let mut exposure_clipped = exposure; + let clipped_max_len = max_page_count.saturating_mul(page_size); + + exposure_clipped.others.sort_by(|a, b| b.value.cmp(&a.value)); + exposure_clipped.others.truncate(clipped_max_len as usize); + exposure_clipped + } else { + exposure + }; + + let (exposure_overview, exposure_pages) = exposure.into_pages(page_size); + + >::insert(era, &validator, &exposure_overview); + exposure_pages.iter().enumerate().for_each(|(page, paged_exposure)| { + >::insert((era, &validator, page as u32), &paged_exposure); + }); + } + + /// Store total exposure for all the elected validators in the era. + pub(crate) fn set_total_stake(era: EraIndex, total_stake: BalanceOf) { + >::insert(era, total_stake); + } + } + /// Indices of validators that have offended in the active era and whether they are currently /// disabled. /// @@ -752,6 +1046,8 @@ pub mod pallet { NotSortedAndUnique, /// Rewards for this era have already been claimed for this validator. AlreadyClaimed, + /// No nominators exist on this page. + InvalidPage, /// Incorrect previous history depth input provided. IncorrectHistoryDepth, /// Incorrect number of slashing spans provided. @@ -879,10 +1175,6 @@ pub mod pallet { >::insert(&stash, &controller); >::insert(&stash, payee); - let current_era = CurrentEra::::get().unwrap_or(0); - let history_depth = T::HistoryDepth::get(); - let last_reward_era = current_era.saturating_sub(history_depth); - let stash_balance = T::Currency::free_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); @@ -891,12 +1183,7 @@ pub mod pallet { total: value, active: value, unlocking: Default::default(), - claimed_rewards: (last_reward_era..current_era) - .try_collect() - // Since last_reward_era is calculated as `current_era - - // HistoryDepth`, following bound is always expected to be - // satisfied. - .defensive_map_err(|_| Error::::BoundNotMet)?, + legacy_claimed_rewards: Default::default(), }; Self::update_ledger(&controller, &item); Ok(()) @@ -1465,21 +1752,42 @@ pub mod pallet { Ok(()) } - /// Pay out all the stakers behind a single validator for a single era. + /// Pay out next page of the stakers behind a validator for the given era. /// - /// - `validator_stash` is the stash account of the validator. Their nominators, up to - /// `T::MaxNominatorRewardedPerValidator`, will also receive their rewards. + /// - `validator_stash` is the stash account of the validator. /// - `era` may be any era between `[current_era - history_depth; current_era]`. /// /// The origin of this call must be _Signed_. Any account can call this function, even if /// it is not one of the stakers. /// + /// This pays out the earliest exposure page not claimed for the era. If all pages are + /// claimed, it returns an error `InvalidPage`. + /// + /// If a validator has more than [`Config::MaxExposurePageSize`] nominators backing + /// them, then the list of nominators is paged, with each page being capped at + /// [`Config::MaxExposurePageSize`]. If a validator has more than one page of + /// nominators, the call needs to be made for each page separately in order for all the + /// nominators backing a validator receive the reward. The nominators are not sorted across + /// pages and so it should not be assumed the highest staker would be on the topmost page + /// and vice versa. If rewards are not claimed in [`Config::HistoryDepth`] eras, they are + /// lost. + /// + /// # + /// - Time complexity: at most O(MaxExposurePageSize). + /// - Contains a limited number of reads and writes. + /// ----------- + /// N is the Number of payouts for the validator (including the validator) + /// Weight: + /// - Reward Destination Staked: O(N) + /// - Reward Destination Controller (Creating): O(N) + /// + /// NOTE: weights are assuming that payouts are made to alive stash account (Staked). + /// Paying even a dead controller is cheaper weight-wise. We don't do any refunds here. + /// # /// ## Complexity - /// - At most O(MaxNominatorRewardedPerValidator). + /// - At most O(MaxExposurePageSize). #[pallet::call_index(18)] - #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked( - T::MaxNominatorRewardedPerValidator::get() - ))] + #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))] pub fn payout_stakers( origin: OriginFor, validator_stash: T::AccountId, @@ -1776,6 +2084,49 @@ pub mod pallet { MinCommission::::put(new); Ok(()) } + + /// Pay out a page of the stakers behind a validator for the given era and page. + /// + /// - `validator_stash` is the stash account of the validator. + /// - `era` may be any era between `[current_era - history_depth; current_era]`. + /// - `page` is the page index of nominators to pay out with value between 0 and + /// `num_nominators / T::MaxExposurePageSize`. + /// + /// The origin of this call must be _Signed_. Any account can call this function, even if + /// it is not one of the stakers. + /// + /// If a validator has more than [`Config::MaxExposurePageSize`] nominators backing + /// them, then the list of nominators is paged, with each page being capped at + /// [`Config::MaxExposurePageSize`.] If a validator has more than one page of + /// nominators, the call needs to be made for each page separately in order for all the + /// nominators backing a validator receive the reward. The nominators are not sorted across + /// pages and so it should not be assumed the highest staker would be on the topmost page + /// and vice versa. If rewards are not claimed in [`Config::HistoryDepth`] eras, they are + /// lost. + /// + /// # + /// - Time complexity: at most O(MaxExposurePageSize). + /// - Contains a limited number of reads and writes. + /// ----------- + /// N is the Number of payouts for the validator (including the validator) + /// Weight: + /// - Reward Destination Staked: O(N) + /// - Reward Destination Controller (Creating): O(N) + /// + /// NOTE: weights are assuming that payouts are made to alive stash account (Staked). + /// Paying even a dead controller is cheaper weight-wise. We don't do any refunds here. + /// # + #[pallet::call_index(26)] + #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))] + pub fn payout_stakers_by_page( + origin: OriginFor, + validator_stash: T::AccountId, + era: EraIndex, + page: PageIndex, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + Self::do_payout_stakers_by_page(validator_stash, era, page) + } } } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 16bafae01e3c3..d151aa9982fa8 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -25,6 +25,7 @@ use frame_support::{ pallet_prelude::*, traits::{Currency, Get, ReservableCurrency}, }; + use mock::*; use pallet_balances::Error as BalancesError; use sp_runtime::{ @@ -154,7 +155,7 @@ fn basic_setup_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); // Account 20 controls the stash from account 21, which is 200 * balance_factor units @@ -165,7 +166,7 @@ fn basic_setup_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); // Account 1 does not control any stash @@ -188,13 +189,13 @@ fn basic_setup_works() { total: 500, active: 500, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); assert_eq!( - Staking::eras_stakers(active_era(), 11), + Staking::eras_stakers(active_era(), &11), Exposure { total: 1125, own: 1000, @@ -202,7 +203,7 @@ fn basic_setup_works() { }, ); assert_eq!( - Staking::eras_stakers(active_era(), 21), + Staking::eras_stakers(active_era(), &21), Exposure { total: 1375, own: 1000, @@ -441,7 +442,7 @@ fn staking_should_work() { total: 1500, active: 1500, unlocking: Default::default(), - claimed_rewards: bounded_vec![0], + legacy_claimed_rewards: bounded_vec![], }) ); // e.g. it cannot reserve more than 500 that it has free from the total 2000 @@ -496,7 +497,7 @@ fn less_than_needed_candidates_works() { // But the exposure is updated in a simple way. No external votes exists. // This is purely self-vote. - assert!(ErasStakers::::iter_prefix_values(active_era()) + assert!(ErasStakersPaged::::iter_prefix_values((active_era(),)) .all(|exposure| exposure.others.is_empty())); }); } @@ -616,9 +617,9 @@ fn nominating_and_rewards_should_work() { assert_eq!(Balances::total_balance(&20), initial_balance_20 + total_payout_0 / 2); initial_balance_20 = Balances::total_balance(&20); - assert_eq!(ErasStakers::::iter_prefix_values(active_era()).count(), 2); + assert_eq!(ErasStakersPaged::::iter_prefix_values((active_era(),)).count(), 2); assert_eq!( - Staking::eras_stakers(active_era(), 11), + Staking::eras_stakers(active_era(), &11), Exposure { total: 1000 + 800, own: 1000, @@ -629,7 +630,7 @@ fn nominating_and_rewards_should_work() { }, ); assert_eq!( - Staking::eras_stakers(active_era(), 21), + Staking::eras_stakers(active_era(), &21), Exposure { total: 1000 + 1200, own: 1000, @@ -689,7 +690,7 @@ fn nominators_also_get_slashed_pro_rata() { ExtBuilder::default().build_and_execute(|| { mock::start_active_era(1); let slash_percent = Perbill::from_percent(5); - let initial_exposure = Staking::eras_stakers(active_era(), 11); + let initial_exposure = Staking::eras_stakers(active_era(), &11); // 101 is a nominator for 11 assert_eq!(initial_exposure.others.first().unwrap().who, 101); @@ -957,7 +958,7 @@ 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(active_era(), 11).total, 1000); + assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000); // Confirm account 11 cannot transfer as a result assert_noop!( Balances::transfer(RuntimeOrigin::signed(11), 20, 1), @@ -982,7 +983,7 @@ fn cannot_transfer_staked_balance_2() { // 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(active_era(), 21).total, 1000); + assert_eq!(Staking::eras_stakers(active_era(), &21).total, 1000); // Confirm account 21 can transfer at most 1000 assert_noop!( Balances::transfer(RuntimeOrigin::signed(21), 20, 1001), @@ -1001,7 +1002,7 @@ 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(active_era(), 11).own, 1000); + assert_eq!(Staking::eras_stakers(active_era(), &11).own, 1000); // Confirm account 11 cannot reserve as a result assert_noop!(Balances::reserve(&11, 1), BalancesError::::LiquidityRestrictions); @@ -1030,7 +1031,7 @@ fn reward_destination_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1053,10 +1054,13 @@ fn reward_destination_works() { total: 1000 + total_payout_0, active: 1000 + total_payout_0, unlocking: Default::default(), - claimed_rewards: bounded_vec![0], + legacy_claimed_rewards: bounded_vec![], }) ); + // (era 0, page 0) is claimed + assert_eq!(Staking::claimed_rewards(0, &11), vec![0]); + // Change RewardDestination to Stash >::insert(&11, RewardDestination::Stash); @@ -1081,10 +1085,13 @@ fn reward_destination_works() { total: 1000 + total_payout_0, active: 1000 + total_payout_0, unlocking: Default::default(), - claimed_rewards: bounded_vec![0, 1], + legacy_claimed_rewards: bounded_vec![], }) ); + // (era 1, page 0) is claimed + assert_eq!(Staking::claimed_rewards(1, &11), vec![0]); + // Change RewardDestination to Controller >::insert(&11, RewardDestination::Controller); @@ -1110,9 +1117,13 @@ fn reward_destination_works() { total: 1000 + total_payout_0, active: 1000 + total_payout_0, unlocking: Default::default(), - claimed_rewards: bounded_vec![0, 1, 2], + legacy_claimed_rewards: bounded_vec![], }) ); + + // (era 2, page 0) is claimed + assert_eq!(Staking::claimed_rewards(2, &11), vec![0]); + // Check that amount in staked account is NOT increased. assert_eq!(Balances::free_balance(11), recorded_stash_balance); }); @@ -1139,7 +1150,7 @@ fn validator_payment_prefs_work() { // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - let exposure_1 = Staking::eras_stakers(active_era(), 11); + let exposure_1 = Staking::eras_stakers(active_era(), &11); Pallet::::reward_by_ids(vec![(11, 1)]); mock::start_active_era(2); @@ -1172,7 +1183,7 @@ fn bond_extra_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1189,7 +1200,7 @@ fn bond_extra_works() { total: 1000 + 100, active: 1000 + 100, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1203,7 +1214,7 @@ fn bond_extra_works() { total: 1000000, active: 1000000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); }); @@ -1241,11 +1252,11 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); assert_eq!( - Staking::eras_stakers(active_era(), 11), + Staking::eras_stakers(active_era(), &11), Exposure { total: 1000, own: 1000, others: vec![] } ); @@ -1259,12 +1270,12 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 1000 + 100, active: 1000 + 100, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); // Exposure is a snapshot! only updated after the next era update. assert_ne!( - Staking::eras_stakers(active_era(), 11), + Staking::eras_stakers(active_era(), &11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] } ); @@ -1280,12 +1291,12 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 1000 + 100, active: 1000 + 100, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); // Exposure is now updated. assert_eq!( - Staking::eras_stakers(active_era(), 11), + Staking::eras_stakers(active_era(), &11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] } ); @@ -1298,7 +1309,7 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 1000 + 100, active: 100, unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }), ); @@ -1311,7 +1322,7 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 1000 + 100, active: 100, unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }), ); @@ -1327,7 +1338,7 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 1000 + 100, active: 100, unlocking: bounded_vec![UnlockChunk { value: 1000, era: 2 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }), ); @@ -1343,7 +1354,7 @@ fn bond_extra_and_withdraw_unbonded_works() { total: 100, active: 100, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }), ); }) @@ -1446,7 +1457,7 @@ fn rebond_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1465,7 +1476,7 @@ fn rebond_works() { total: 1000, active: 100, unlocking: bounded_vec![UnlockChunk { value: 900, era: 2 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1478,7 +1489,7 @@ fn rebond_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1491,7 +1502,7 @@ fn rebond_works() { total: 1000, active: 100, unlocking: bounded_vec![UnlockChunk { value: 900, era: 5 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1504,7 +1515,7 @@ fn rebond_works() { total: 1000, active: 600, unlocking: bounded_vec![UnlockChunk { value: 400, era: 5 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1517,7 +1528,7 @@ fn rebond_works() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1532,7 +1543,7 @@ fn rebond_works() { total: 1000, active: 100, unlocking: bounded_vec![UnlockChunk { value: 900, era: 5 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1545,7 +1556,7 @@ fn rebond_works() { total: 1000, active: 600, unlocking: bounded_vec![UnlockChunk { value: 400, era: 5 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); }) @@ -1572,7 +1583,7 @@ fn rebond_is_fifo() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1587,7 +1598,7 @@ fn rebond_is_fifo() { total: 1000, active: 600, unlocking: bounded_vec![UnlockChunk { value: 400, era: 2 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1605,7 +1616,7 @@ fn rebond_is_fifo() { UnlockChunk { value: 400, era: 2 + 3 }, UnlockChunk { value: 300, era: 3 + 3 }, ], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1624,7 +1635,7 @@ fn rebond_is_fifo() { UnlockChunk { value: 300, era: 3 + 3 }, UnlockChunk { value: 200, era: 4 + 3 }, ], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1640,7 +1651,7 @@ fn rebond_is_fifo() { UnlockChunk { value: 400, era: 2 + 3 }, UnlockChunk { value: 100, era: 3 + 3 }, ], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); }) @@ -1669,7 +1680,7 @@ fn rebond_emits_right_value_in_event() { total: 1000, active: 100, unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1682,7 +1693,7 @@ fn rebond_emits_right_value_in_event() { total: 1000, active: 200, unlocking: bounded_vec![UnlockChunk { value: 800, era: 1 + 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); // Event emitted should be correct @@ -1697,7 +1708,7 @@ fn rebond_emits_right_value_in_event() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); // Event emitted should be correct, only 800 @@ -1717,15 +1728,19 @@ fn reward_to_stake_works() { // Confirm account 10 and 20 are validators assert!(>::contains_key(&11) && >::contains_key(&21)); - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 2000); + assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000); + assert_eq!(Staking::eras_stakers(active_era(), &21).total, 2000); // 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![] }); + EraInfo::::set_validator_exposure( + 0, + &21, + Exposure { total: 69, own: 69, others: vec![] }, + ); >::insert( &20, StakingLedger { @@ -1733,7 +1748,7 @@ fn reward_to_stake_works() { total: 69, active: 69, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }, ); @@ -1746,8 +1761,8 @@ fn reward_to_stake_works() { mock::start_active_era(1); mock::make_all_reward_payment(0); - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 69); + assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000); + assert_eq!(Staking::eras_stakers(active_era(), &21).total, 69); let _11_balance = Balances::free_balance(&11); assert_eq!(_11_balance, 1000 + total_payout_0 / 2); @@ -1756,8 +1771,8 @@ fn reward_to_stake_works() { mock::start_active_era(2); // -- new infos - assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000 + total_payout_0 / 2); - assert_eq!(Staking::eras_stakers(active_era(), 21).total, 69 + total_payout_0 / 2); + assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000 + total_payout_0 / 2); + assert_eq!(Staking::eras_stakers(active_era(), &21).total, 69 + total_payout_0 / 2); }); } @@ -1797,7 +1812,7 @@ fn reap_stash_works() { total: 5, active: 5, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }, ); @@ -1893,8 +1908,8 @@ fn wrong_vote_is_moot() { assert_eq_uvec!(validator_controllers(), vec![20, 10]); // our new voter is taken into account - assert!(Staking::eras_stakers(active_era(), 11).others.iter().any(|i| i.who == 61)); - assert!(Staking::eras_stakers(active_era(), 21).others.iter().any(|i| i.who == 61)); + assert!(Staking::eras_stakers(active_era(), &11).others.iter().any(|i| i.who == 61)); + assert!(Staking::eras_stakers(active_era(), &21).others.iter().any(|i| i.who == 61)); }); } @@ -1932,7 +1947,7 @@ fn bond_with_no_staked_value() { active: 0, total: 5, unlocking: bounded_vec![UnlockChunk { value: 5, era: 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); @@ -1993,7 +2008,7 @@ fn bond_with_little_staked_value_bounded() { // 2 is elected. assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); - assert_eq!(Staking::eras_stakers(active_era(), 2).total, 0); + assert_eq!(Staking::eras_stakers(active_era(), &2).total, 0); // Old ones are rewarded. assert_eq_error_rate!( @@ -2011,7 +2026,7 @@ 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(active_era(), 2).total, 0); + assert_eq!(Staking::eras_stakers(active_era(), &2).total, 0); // 2 is now rewarded. assert_eq_error_rate!( @@ -2166,8 +2181,8 @@ fn phragmen_should_not_overflow() { assert_eq_uvec!(validator_controllers(), vec![4, 2]); // We can safely convert back to values within [u64, u128]. - assert!(Staking::eras_stakers(active_era(), 3).total > Votes::max_value() as Balance); - assert!(Staking::eras_stakers(active_era(), 5).total > Votes::max_value() as Balance); + assert!(Staking::eras_stakers(active_era(), &3).total > Votes::max_value() as Balance); + assert!(Staking::eras_stakers(active_era(), &5).total > Votes::max_value() as Balance); }) } @@ -2191,10 +2206,9 @@ fn reward_validator_slashing_validator_does_not_overflow() { // Check reward ErasRewardPoints::::insert(0, reward); - ErasStakers::::insert(0, 11, &exposure); - ErasStakersClipped::::insert(0, 11, exposure); + EraInfo::::set_validator_exposure(0, &11, exposure); ErasValidatorReward::::insert(0, stake); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 0)); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0)); assert_eq!(Balances::total_balance(&11), stake * 2); // Set staker @@ -2205,9 +2219,9 @@ fn reward_validator_slashing_validator_does_not_overflow() { Staking::bond(RuntimeOrigin::signed(2), 20000, stake - 1, RewardDestination::default()) .unwrap(); // Override exposure of 11 - ErasStakers::::insert( + EraInfo::::set_validator_exposure( 0, - 11, + &11, Exposure { total: stake, own: 1, @@ -2218,7 +2232,7 @@ fn reward_validator_slashing_validator_does_not_overflow() { // Check slashing on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(100)], @@ -2317,7 +2331,7 @@ fn offence_forces_new_era() { ExtBuilder::default().build_and_execute(|| { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(5)], @@ -2335,7 +2349,7 @@ fn offence_ensures_new_era_without_clobbering() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(5)], @@ -2353,7 +2367,7 @@ fn offence_deselects_validator_even_when_slash_is_zero() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(0)], @@ -2374,7 +2388,7 @@ 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(active_era(), 11).own, 1000); + assert_eq!(Staking::eras_stakers(active_era(), &11).own, 1000); // Handle an offence with a historical exposure. on_offence_now( @@ -2400,7 +2414,7 @@ fn slash_in_old_span_does_not_deselect() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(0)], @@ -2423,7 +2437,7 @@ fn slash_in_old_span_does_not_deselect() { on_offence_in_era( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(0)], @@ -2432,14 +2446,14 @@ fn slash_in_old_span_does_not_deselect() { ); // the validator doesn't get chilled again - assert!(::Validators::iter().any(|(stash, _)| stash == 11)); + assert!(Validators::::iter().any(|(stash, _)| stash == 11)); // but we are still forcing a new era assert_eq!(Staking::force_era(), Forcing::ForceNew); on_offence_in_era( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], // NOTE: A 100% slash here would clean up the account, causing de-registration. @@ -2449,7 +2463,7 @@ fn slash_in_old_span_does_not_deselect() { ); // the validator doesn't get chilled again - assert!(::Validators::iter().any(|(stash, _)| stash == 11)); + assert!(Validators::::iter().any(|(stash, _)| stash == 11)); // but it's disabled assert!(is_disabled(10)); @@ -2466,11 +2480,11 @@ fn reporters_receive_their_slice() { // The reporters' reward is calculated from the total exposure. let initial_balance = 1125; - assert_eq!(Staking::eras_stakers(active_era(), 11).total, initial_balance); + assert_eq!(Staking::eras_stakers(active_era(), &11).total, initial_balance); on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![1, 2], }], &[Perbill::from_percent(50)], @@ -2493,11 +2507,11 @@ 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(active_era(), 11).total, initial_balance); + assert_eq!(Staking::eras_stakers(active_era(), &11).total, initial_balance); on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![1], }], &[Perbill::from_percent(20)], @@ -2510,7 +2524,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![1], }], &[Perbill::from_percent(50)], @@ -2532,7 +2546,7 @@ fn invulnerables_are_not_slashed() { assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(21), 2000); - let exposure = Staking::eras_stakers(active_era(), 21); + let exposure = Staking::eras_stakers(active_era(), &21); let initial_balance = Staking::slashable_balance_of(&21); let nominator_balances: Vec<_> = @@ -2541,11 +2555,11 @@ fn invulnerables_are_not_slashed() { on_offence_now( &[ OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }, OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), + offender: (21, Staking::eras_stakers(active_era(), &21)), reporters: vec![], }, ], @@ -2575,7 +2589,7 @@ fn dont_slash_if_fraction_is_zero() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(0)], @@ -2596,7 +2610,7 @@ fn only_slash_for_max_in_era() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(50)], @@ -2608,7 +2622,7 @@ fn only_slash_for_max_in_era() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(25)], @@ -2619,7 +2633,7 @@ fn only_slash_for_max_in_era() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(60)], @@ -2641,7 +2655,7 @@ fn garbage_collection_after_slashing() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -2653,7 +2667,7 @@ fn garbage_collection_after_slashing() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(100)], @@ -2690,12 +2704,15 @@ fn garbage_collection_on_window_pruning() { assert_eq!(Balances::free_balance(11), 1000); let now = active_era(); - let exposure = Staking::eras_stakers(now, 11); + let exposure = Staking::eras_stakers(now, &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(now, 11)), reporters: vec![] }], + &[OffenceDetails { + offender: (11, Staking::eras_stakers(now, &11)), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); @@ -2730,14 +2747,14 @@ fn slashing_nominators_by_span_max() { assert_eq!(Balances::free_balance(101), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); - let exposure_11 = Staking::eras_stakers(active_era(), 11); - let exposure_21 = Staking::eras_stakers(active_era(), 21); + let exposure_11 = Staking::eras_stakers(active_era(), &11); + let exposure_21 = Staking::eras_stakers(active_era(), &21); let nominated_value_11 = exposure_11.others.iter().find(|o| o.who == 101).unwrap().value; 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(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -2764,7 +2781,7 @@ fn slashing_nominators_by_span_max() { // second slash: higher era, higher value, same span. on_offence_in_era( &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), + offender: (21, Staking::eras_stakers(active_era(), &21)), reporters: vec![], }], &[Perbill::from_percent(30)], @@ -2786,7 +2803,7 @@ fn slashing_nominators_by_span_max() { // in-era value, but lower slash value than slash 2. on_offence_in_era( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(20)], @@ -2821,7 +2838,7 @@ fn slashes_are_summed_across_spans() { on_offence_now( &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), + offender: (21, Staking::eras_stakers(active_era(), &21)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -2844,7 +2861,7 @@ fn slashes_are_summed_across_spans() { on_offence_now( &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), + offender: (21, Staking::eras_stakers(active_era(), &21)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -2868,7 +2885,7 @@ fn deferred_slashes_are_deferred() { assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(active_era(), 11); + let exposure = Staking::eras_stakers(active_era(), &11); assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; @@ -2876,7 +2893,7 @@ fn deferred_slashes_are_deferred() { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -2927,7 +2944,7 @@ fn retroactive_deferred_slashes_two_eras_before() { assert_eq!(BondingDuration::get(), 3); mock::start_active_era(1); - let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); + let exposure_11_at_era1 = Staking::eras_stakers(active_era(), &11); mock::start_active_era(3); @@ -2963,7 +2980,7 @@ fn retroactive_deferred_slashes_one_before() { assert_eq!(BondingDuration::get(), 3); mock::start_active_era(1); - let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); + let exposure_11_at_era1 = Staking::eras_stakers(active_era(), &11); // unbond at slash era. mock::start_active_era(2); @@ -3011,12 +3028,12 @@ fn staker_cannot_bail_deferred_slash() { assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); - let exposure = Staking::eras_stakers(active_era(), 11); + let exposure = Staking::eras_stakers(active_era(), &11); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -3035,7 +3052,7 @@ fn staker_cannot_bail_deferred_slash() { active: 0, total: 500, stash: 101, - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], unlocking: bounded_vec![UnlockChunk { era: 4u32, value: 500 }], } ); @@ -3085,7 +3102,7 @@ fn remove_deferred() { assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(active_era(), 11); + let exposure = Staking::eras_stakers(active_era(), &11); assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; @@ -3161,7 +3178,7 @@ fn remove_multi_deferred() { assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(active_era(), 11); + let exposure = Staking::eras_stakers(active_era(), &11); assert_eq!(Balances::free_balance(101), 2000); on_offence_now( @@ -3171,7 +3188,7 @@ fn remove_multi_deferred() { on_offence_now( &[OffenceDetails { - offender: (21, Staking::eras_stakers(active_era(), 21)), + offender: (21, Staking::eras_stakers(active_era(), &21)), reporters: vec![], }], &[Perbill::from_percent(10)], @@ -3192,7 +3209,7 @@ fn remove_multi_deferred() { &[Perbill::from_percent(25)], ); - assert_eq!(::UnappliedSlashes::get(&4).len(), 5); + assert_eq!(UnappliedSlashes::::get(&4).len(), 5); // fails if list is not sorted assert_noop!( @@ -3212,7 +3229,7 @@ fn remove_multi_deferred() { assert_ok!(Staking::cancel_deferred_slash(RuntimeOrigin::root(), 4, vec![0, 2, 4])); - let slashes = ::UnappliedSlashes::get(&4); + let slashes = UnappliedSlashes::::get(&4); assert_eq!(slashes.len(), 2); assert_eq!(slashes[0].validator, 21); assert_eq!(slashes[1].validator, 42); @@ -3267,7 +3284,7 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); // check that validator was chilled. - assert!(::Validators::iter().all(|(stash, _)| stash != 11)); + assert!(Validators::::iter().all(|(stash, _)| stash != 11)); // actually re-bond the slashed validator assert_ok!(Staking::validate(RuntimeOrigin::signed(10), Default::default())); @@ -3536,8 +3553,9 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { mock::start_active_era(1); Pallet::::reward_by_ids(vec![(11, 1)]); - // Change total issuance in order to modify total payout + // Increase total token issuance to affect the total payout. let _ = Balances::deposit_creating(&999, 1_000_000_000); + // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); assert!(total_payout_1 != total_payout_0); @@ -3545,7 +3563,7 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { mock::start_active_era(2); Pallet::::reward_by_ids(vec![(11, 1)]); - // Change total issuance in order to modify total payout + // Increase total token issuance to affect the total payout. let _ = Balances::deposit_creating(&999, 1_000_000_000); // Compute total payout now for whole duration as other parameter won't change let total_payout_2 = current_total_payout_for_duration(reward_time_per_era()); @@ -3562,19 +3580,19 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { // Last kept is 1: assert!(current_era - HistoryDepth::get() == 1); assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 0), + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0), // Fail: Era out of history Error::::InvalidEraToReward.with_weight(err_weight) ); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2)); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0)); assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2), + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0), // Fail: Double claim Error::::AlreadyClaimed.with_weight(err_weight) ); assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, active_era), + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, active_era, 0), // Fail: Era not finished yet Error::::InvalidEraToReward.with_weight(err_weight) ); @@ -3600,7 +3618,7 @@ fn zero_slash_keeps_nominators() { assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(active_era(), 11); + let exposure = Staking::eras_stakers(active_era(), &11); assert_eq!(Balances::free_balance(101), 2000); on_offence_now( @@ -3612,7 +3630,7 @@ fn zero_slash_keeps_nominators() { assert_eq!(Balances::free_balance(101), 2000); // 11 is still removed.. - assert!(::Validators::iter().all(|(stash, _)| stash != 11)); + assert!(Validators::::iter().all(|(stash, _)| stash != 11)); // but their nominations are kept. assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); }); @@ -3680,8 +3698,9 @@ fn six_session_delay() { #[test] fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward() { + // with max exposure page count set to 1, clipped exposure logic works exactly as before. ExtBuilder::default().build_and_execute(|| { - for i in 0..=<::MaxNominatorRewardedPerValidator as Get<_>>::get() { + for i in 0..=MaxExposurePageSize::get() { let stash = 10_000 + i as AccountId; let controller = 20_000 + i as AccountId; let balance = 10_000 + i as Balance; @@ -3704,7 +3723,7 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward( mock::make_all_reward_payment(1); // Assert only nominators from 1 to Max are rewarded - for i in 0..=<::MaxNominatorRewardedPerValidator as Get<_>>::get() { + for i in 0..=MaxExposurePageSize::get() { let stash = 10_000 + i as AccountId; let balance = 10_000 + i as Balance; if stash == 10_000 { @@ -3717,15 +3736,65 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward( } #[test] -fn test_payout_stakers() { - // Test that payout_stakers work in general, including that only the top - // `T::MaxNominatorRewardedPerValidator` nominators are rewarded. +fn test_nominators_are_rewarded_for_all_exposure_page() { + ExtBuilder::default().build_and_execute(|| { + // enable multi paged rewards payout + allow_paged_rewards(3); + + // 3 pages of exposure + let nominator_count = 2 * MaxExposurePageSize::get() + 1; + + for i in 0..nominator_count { + let stash = 10_000 + i as AccountId; + 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( + RuntimeOrigin::signed(stash), + controller, + balance, + RewardDestination::Stash + )); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![11])); + } + mock::start_active_era(1); + + Pallet::::reward_by_ids(vec![(11, 1)]); + // compute and ensure the reward amount is greater than zero. + let _ = current_total_payout_for_duration(reward_time_per_era()); + + mock::start_active_era(2); + mock::make_all_reward_payment(1); + + assert_eq!(EraInfo::::get_page_count(1, &11), 3); + + // Assert all nominators are rewarded according to their stake + for i in 0..nominator_count { + // balance of the nominator after the reward payout. + let current_balance = Balances::free_balance(&((10000 + i) as AccountId)); + // balance of the nominator in the previous iteration. + let previous_balance = Balances::free_balance(&((10000 + i - 1) as AccountId)); + // balance before the reward. + let original_balance = 10_000 + i as Balance; + + assert!(current_balance > original_balance); + // since the stake of the nominator is increasing for each iteration, the final balance + // after the reward should also be higher than the previous iteration. + assert!(current_balance > previous_balance); + } + }); +} + +#[test] +fn test_multi_page_payout_stakers_by_page() { + // Test that payout_stakers work in general and that it pays the correct amount of reward. ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // enable multi paged rewards payout + allow_paged_rewards(10); + let balance = 1000; // Track the exposure of the validator and all nominators. let mut total_exposure = balance; - // Track the exposure of the validator and the nominators that will get paid out. - let mut payout_exposure = balance; // Create a validator: bond_validator(11, 10, balance); // Default(64) assert_eq!(Validators::::count(), 1); @@ -3734,44 +3803,66 @@ fn test_payout_stakers() { for i in 0..100 { let bond_amount = balance + i as Balance; bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]); + // with multi page reward payout, payout exposure is same as total exposure. total_exposure += bond_amount; - if i >= 36 { - payout_exposure += bond_amount; - }; } - let payout_exposure_part = Perbill::from_rational(payout_exposure, total_exposure); mock::start_active_era(1); Staking::reward_by_ids(vec![(11, 1)]); + // Since `MaxExposurePageSize = 64`, there are two pages of validator exposure. + assert_eq!(EraInfo::::get_page_count(1, &11), 2); + // compute and ensure the reward amount is greater than zero. let payout = current_total_payout_for_duration(reward_time_per_era()); - let actual_paid_out = payout_exposure_part * payout; - mock::start_active_era(2); + // verify the exposures are calculated correctly. + let actual_exposure_0 = EraInfo::::get_validator_exposure(1, &11, 0).unwrap(); + assert_eq!(actual_exposure_0.total(), total_exposure); + assert_eq!(actual_exposure_0.own(), 1000); + assert_eq!(actual_exposure_0.others().len(), 64); + let actual_exposure_1 = EraInfo::::get_validator_exposure(1, &11, 1).unwrap(); + assert_eq!(actual_exposure_1.total(), total_exposure); + // own stake is only included once in the first page + assert_eq!(actual_exposure_1.own(), 0); + assert_eq!(actual_exposure_1.others().len(), 100 - 64); + let pre_payout_total_issuance = Balances::total_issuance(); RewardOnUnbalanceWasCalled::set(false); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); - assert_eq_error_rate!( - Balances::total_issuance(), - pre_payout_total_issuance + actual_paid_out, - 1 - ); + + let controller_balance_before_p0_payout = Balances::free_balance(&10); + // Payout rewards for first exposure page + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); + + let controller_balance_after_p0_payout = Balances::free_balance(&10); + + // verify rewards have been paid out but still some left + assert!(Balances::total_issuance() > pre_payout_total_issuance); + assert!(Balances::total_issuance() < pre_payout_total_issuance + payout); + + // verify the validator has been rewarded + assert!(controller_balance_after_p0_payout > controller_balance_before_p0_payout); + + // Payout the second and last page of nominators + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 1)); + + // verify the validator was not rewarded the second time + assert_eq!(Balances::free_balance(&10), controller_balance_after_p0_payout); + + // verify all rewards have been paid out + assert_eq_error_rate!(Balances::total_issuance(), pre_payout_total_issuance + payout, 2); assert!(RewardOnUnbalanceWasCalled::get()); - // Top 64 nominators of validator 11 automatically paid out, including the validator + // verify all nominators of validator 11 are paid out, including the validator // Validator payout goes to controller. assert!(Balances::free_balance(&10) > balance); - for i in 36..100 { + for i in 0..100 { assert!(Balances::free_balance(&(100 + i)) > balance + i as Balance); } - // The bottom 36 do not - for i in 0..36 { - assert_eq!(Balances::free_balance(&(100 + i)), balance + i as Balance); - } - // We track rewards in `claimed_rewards` vec + // verify we no longer track rewards in `legacy_claimed_rewards` vec + let ledger = Staking::ledger(&10); assert_eq!( Staking::ledger(&10), Some(StakingLedger { @@ -3779,30 +3870,209 @@ fn test_payout_stakers() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![1] + legacy_claimed_rewards: bounded_vec![] }) ); + // verify rewards are tracked to prevent double claims + for page in 0..EraInfo::::get_page_count(1, &11) { + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + 1, + ledger.as_ref().unwrap(), + &11, + page + ), + true + ); + } + for i in 3..16 { Staking::reward_by_ids(vec![(11, 1)]); // compute and ensure the reward amount is greater than zero. let payout = current_total_payout_for_duration(reward_time_per_era()); - let actual_paid_out = payout_exposure_part * payout; let pre_payout_total_issuance = Balances::total_issuance(); mock::start_active_era(i); RewardOnUnbalanceWasCalled::set(false); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, i - 1)); + mock::make_all_reward_payment(i - 1); assert_eq_error_rate!( Balances::total_issuance(), - pre_payout_total_issuance + actual_paid_out, - 1 + pre_payout_total_issuance + payout, + 2 ); assert!(RewardOnUnbalanceWasCalled::get()); + + // verify we track rewards for each era and page + for page in 0..EraInfo::::get_page_count(i - 1, &11) { + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + i - 1, + Staking::ledger(&10).as_ref().unwrap(), + &11, + page + ), + true + ); + } + } + + assert_eq!(Staking::claimed_rewards(14, &11), vec![0, 1]); + + let last_era = 99; + let history_depth = HistoryDepth::get(); + let last_reward_era = last_era - 1; + let first_claimable_reward_era = last_era - history_depth; + for i in 16..=last_era { + Staking::reward_by_ids(vec![(11, 1)]); + // compute and ensure the reward amount is greater than zero. + let _ = current_total_payout_for_duration(reward_time_per_era()); + mock::start_active_era(i); + } + + // verify we clean up history as we go + for era in 0..15 { + assert_eq!(Staking::claimed_rewards(era, &11), Vec::::new()); + } + + // verify only page 0 is marked as claimed + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + first_claimable_reward_era, + 0 + )); + assert_eq!(Staking::claimed_rewards(first_claimable_reward_era, &11), vec![0]); + + // verify page 0 and 1 are marked as claimed + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + first_claimable_reward_era, + 1 + )); + assert_eq!(Staking::claimed_rewards(first_claimable_reward_era, &11), vec![0, 1]); + + // verify only page 0 is marked as claimed + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + last_reward_era, + 0 + )); + assert_eq!(Staking::claimed_rewards(last_reward_era, &11), vec![0]); + + // verify page 0 and 1 are marked as claimed + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + last_reward_era, + 1 + )); + assert_eq!(Staking::claimed_rewards(last_reward_era, &11), vec![0, 1]); + + // Out of order claims works. + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 69, 0)); + assert_eq!(Staking::claimed_rewards(69, &11), vec![0]); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 23, 1)); + assert_eq!(Staking::claimed_rewards(23, &11), vec![1]); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 42, 0)); + assert_eq!(Staking::claimed_rewards(42, &11), vec![0]); + }); +} + +#[test] +fn test_multi_page_payout_stakers_backward_compatible() { + // Test that payout_stakers work in general and that it pays the correct amount of reward. + ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // enable multi paged rewards payout + allow_paged_rewards(10); + + let balance = 1000; + // Track the exposure of the validator and all nominators. + let mut total_exposure = balance; + // Create a validator: + bond_validator(11, 10, balance); // Default(64) + assert_eq!(Validators::::count(), 1); + + let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); + + // Create nominators, targeting stash of validators + for i in 0..100 { + let bond_amount = balance + i as Balance; + bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]); + // with multi page reward payout, payout exposure is same as total exposure. + total_exposure += bond_amount; + } + + mock::start_active_era(1); + Staking::reward_by_ids(vec![(11, 1)]); + + // Since `MaxExposurePageSize = 64`, there are two pages of validator exposure. + assert_eq!(EraInfo::::get_page_count(1, &11), 2); + + // compute and ensure the reward amount is greater than zero. + let payout = current_total_payout_for_duration(reward_time_per_era()); + mock::start_active_era(2); + + // verify the exposures are calculated correctly. + let actual_exposure_0 = EraInfo::::get_validator_exposure(1, &11, 0).unwrap(); + assert_eq!(actual_exposure_0.total(), total_exposure); + assert_eq!(actual_exposure_0.own(), 1000); + assert_eq!(actual_exposure_0.others().len(), 64); + let actual_exposure_1 = EraInfo::::get_validator_exposure(1, &11, 1).unwrap(); + assert_eq!(actual_exposure_1.total(), total_exposure); + // own stake is only included once in the first page + assert_eq!(actual_exposure_1.own(), 0); + assert_eq!(actual_exposure_1.others().len(), 100 - 64); + + let pre_payout_total_issuance = Balances::total_issuance(); + RewardOnUnbalanceWasCalled::set(false); + + let controller_balance_before_p0_payout = Balances::free_balance(&10); + // Payout rewards for first exposure page + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); + // page 0 is claimed + assert_noop!( + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0), + Error::::AlreadyClaimed.with_weight(err_weight) + ); + + let controller_balance_after_p0_payout = Balances::free_balance(&10); + + // verify rewards have been paid out but still some left + assert!(Balances::total_issuance() > pre_payout_total_issuance); + assert!(Balances::total_issuance() < pre_payout_total_issuance + payout); + + // verify the validator has been rewarded + assert!(controller_balance_after_p0_payout > controller_balance_before_p0_payout); + + // This should payout the second and last page of nominators + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); + + // cannot claim any more pages + assert_noop!( + Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1), + Error::::AlreadyClaimed.with_weight(err_weight) + ); + + // verify the validator was not rewarded the second time + assert_eq!(Balances::free_balance(&10), controller_balance_after_p0_payout); + + // verify all rewards have been paid out + assert_eq_error_rate!(Balances::total_issuance(), pre_payout_total_issuance + payout, 2); + assert!(RewardOnUnbalanceWasCalled::get()); + + // verify all nominators of validator 11 are paid out, including the validator + // Validator payout goes to controller. + assert!(Balances::free_balance(&10) > balance); + for i in 0..100 { + assert!(Balances::free_balance(&(100 + i)) > balance + i as Balance); } - // We track rewards in `claimed_rewards` vec + // verify we no longer track rewards in `legacy_claimed_rewards` vec + let ledger = Staking::ledger(&10); assert_eq!( Staking::ledger(&10), Some(StakingLedger { @@ -3810,14 +4080,60 @@ fn test_payout_stakers() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: (1..=14).collect::>().try_into().unwrap() + legacy_claimed_rewards: bounded_vec![] }) ); + // verify rewards are tracked to prevent double claims + for page in 0..EraInfo::::get_page_count(1, &11) { + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + 1, + ledger.as_ref().unwrap(), + &11, + page + ), + true + ); + } + + for i in 3..16 { + Staking::reward_by_ids(vec![(11, 1)]); + + // compute and ensure the reward amount is greater than zero. + let payout = current_total_payout_for_duration(reward_time_per_era()); + let pre_payout_total_issuance = Balances::total_issuance(); + + mock::start_active_era(i); + RewardOnUnbalanceWasCalled::set(false); + mock::make_all_reward_payment(i - 1); + assert_eq_error_rate!( + Balances::total_issuance(), + pre_payout_total_issuance + payout, + 2 + ); + assert!(RewardOnUnbalanceWasCalled::get()); + + // verify we track rewards for each era and page + for page in 0..EraInfo::::get_page_count(i - 1, &11) { + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + i - 1, + Staking::ledger(&10).as_ref().unwrap(), + &11, + page + ), + true + ); + } + } + + assert_eq!(Staking::claimed_rewards(14, &11), vec![0, 1]); + let last_era = 99; let history_depth = HistoryDepth::get(); - let expected_last_reward_era = last_era - 1; - let expected_start_reward_era = last_era - history_depth; + let last_reward_era = last_era - 1; + let first_claimable_reward_era = last_era - history_depth; for i in 16..=last_era { Staking::reward_by_ids(vec![(11, 1)]); // compute and ensure the reward amount is greater than zero. @@ -3825,48 +4141,153 @@ fn test_payout_stakers() { mock::start_active_era(i); } - // We clean it up as history passes + // verify we clean up history as we go + for era in 0..15 { + assert_eq!(Staking::claimed_rewards(era, &11), Vec::::new()); + } + + // verify only page 0 is marked as claimed assert_ok!(Staking::payout_stakers( RuntimeOrigin::signed(1337), 11, - expected_start_reward_era + first_claimable_reward_era )); + assert_eq!(Staking::claimed_rewards(first_claimable_reward_era, &11), vec![0]); + + // verify page 0 and 1 are marked as claimed assert_ok!(Staking::payout_stakers( RuntimeOrigin::signed(1337), 11, - expected_last_reward_era + first_claimable_reward_era, )); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![expected_start_reward_era, expected_last_reward_era] - }) + assert_eq!(Staking::claimed_rewards(first_claimable_reward_era, &11), vec![0, 1]); + + // change order and verify only page 1 is marked as claimed + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + last_reward_era, + 1 + )); + assert_eq!(Staking::claimed_rewards(last_reward_era, &11), vec![1]); + + // verify page 0 is claimed even when explicit page is not passed + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, last_reward_era,)); + + assert_eq!(Staking::claimed_rewards(last_reward_era, &11), vec![1, 0]); + + // cannot claim any more pages + assert_noop!( + Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, last_reward_era), + Error::::AlreadyClaimed.with_weight(err_weight) ); + // Create 4 nominator pages + for i in 100..200 { + let bond_amount = balance + i as Balance; + bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]); + } + + let test_era = last_era + 1; + mock::start_active_era(test_era); + + Staking::reward_by_ids(vec![(11, 1)]); + // compute and ensure the reward amount is greater than zero. + let _ = current_total_payout_for_duration(reward_time_per_era()); + mock::start_active_era(test_era + 1); + // Out of order claims works. - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 69)); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 23)); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 42)); - assert_eq!( - Staking::ledger(&10), - Some(StakingLedger { - stash: 11, - total: 1000, - active: 1000, - unlocking: Default::default(), - claimed_rewards: bounded_vec![ - expected_start_reward_era, - 23, - 42, - 69, - expected_last_reward_era - ] - }) + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, test_era, 2)); + assert_eq!(Staking::claimed_rewards(test_era, &11), vec![2]); + + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era)); + assert_eq!(Staking::claimed_rewards(test_era, &11), vec![2, 0]); + + // cannot claim page 2 again + assert_noop!( + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, test_era, 2), + Error::::AlreadyClaimed.with_weight(err_weight) ); + + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era)); + assert_eq!(Staking::claimed_rewards(test_era, &11), vec![2, 0, 1]); + + assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era)); + assert_eq!(Staking::claimed_rewards(test_era, &11), vec![2, 0, 1, 3]); + }); +} + +#[test] +fn test_page_count_and_size() { + // Test that payout_stakers work in general and that it pays the correct amount of reward. + ExtBuilder::default().has_stakers(false).build_and_execute(|| { + let balance = 1000; + // Track the exposure of the validator and all nominators. + // Create a validator: + bond_validator(11, 10, balance); // Default(64) + assert_eq!(Validators::::count(), 1); + + // Create nominators, targeting stash of validators + for i in 0..100 { + let bond_amount = balance + i as Balance; + bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]); + } + + mock::start_active_era(1); + + // Since max exposure page count is 1, we should only have 1 page with clipped and sorted + // nominators. + assert_eq!(EraInfo::::get_page_count(1, &11), 1); + let exposure = EraInfo::::get_validator_exposure(1, &11, 0).unwrap(); + let mut previous_nominator_balance: Balance = u32::MAX as Balance; + exposure.others().iter().for_each(|e| { + // Nominators are sorted by balance in descending order. + assert!(e.value < previous_nominator_balance); + previous_nominator_balance = e.value; + }); + + // the last nominator balance is higher than the last 36 clipped nominators. + assert_eq!(previous_nominator_balance, 1000 + 36); + + // increase max page size + allow_paged_rewards(10); + mock::start_active_era(2); + + assert_eq!(EraInfo::::get_page_count(2, &11), 2); + // first page has 64 nominators + assert_eq!(EraInfo::::get_validator_exposure(2, &11, 0).unwrap().others().len(), 64); + // second page has 36 nominators + assert_eq!(EraInfo::::get_validator_exposure(2, &11, 1).unwrap().others().len(), 36); + + // now lets decrease page size + MaxExposurePageSize::set(32); + mock::start_active_era(3); + // now we expect 4 pages. + assert_eq!(EraInfo::::get_page_count(3, &11), 4); + // first 3 pages have 32 nominators each + assert_eq!(EraInfo::::get_validator_exposure(3, &11, 0).unwrap().others().len(), 32); + assert_eq!(EraInfo::::get_validator_exposure(3, &11, 1).unwrap().others().len(), 32); + assert_eq!(EraInfo::::get_validator_exposure(3, &11, 2).unwrap().others().len(), 32); + assert_eq!(EraInfo::::get_validator_exposure(3, &11, 3).unwrap().others().len(), 4); + + // now lets decrease page size even more + MaxExposurePageSize::set(9); + mock::start_active_era(4); + // now we expect the max 10 pages with each page having 9 nominators. + assert_eq!(EraInfo::::get_page_count(4, &11), 10); + + // all nominators are sorted by balance in descending order. + let mut previous_nominator_balance: Balance = u32::MAX as Balance; + for page in 0..10 { + let exposure = EraInfo::::get_validator_exposure(4, &11, page).unwrap(); + exposure.others().iter().for_each(|e| { + assert!(e.value < previous_nominator_balance); + previous_nominator_balance = e.value; + }); + } + + // the last nominator balance is higher than the last 10 clipped nominators. + assert_eq!(previous_nominator_balance, 1000 + 10); }); } @@ -3874,6 +4295,9 @@ fn test_payout_stakers() { fn payout_stakers_handles_basic_errors() { // Here we will test payouts handle all errors. ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // enable multi paged rewards payout + allow_paged_rewards(2); + // Consumed weight for all payout_stakers dispatches that fail let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); @@ -3896,12 +4320,12 @@ fn payout_stakers_handles_basic_errors() { // Wrong Era, too big assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2), + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0), Error::::InvalidEraToReward.with_weight(err_weight) ); // Wrong Staker assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 10, 1), + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 10, 1, 0), Error::::NotStash.with_weight(err_weight) ); @@ -3921,33 +4345,140 @@ fn payout_stakers_handles_basic_errors() { // to payout era starting from expected_start_reward_era=19 through // expected_last_reward_era=98 (80 total eras), but not 18 or 99. assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_start_reward_era - 1), + Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_start_reward_era - 1, + 0 + ), Error::::InvalidEraToReward.with_weight(err_weight) ); assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_last_reward_era + 1), + Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_last_reward_era + 1, + 0 + ), Error::::InvalidEraToReward.with_weight(err_weight) ); - assert_ok!(Staking::payout_stakers( + assert_ok!(Staking::payout_stakers_by_page( RuntimeOrigin::signed(1337), 11, - expected_start_reward_era + expected_start_reward_era, + 0 )); - assert_ok!(Staking::payout_stakers( + assert_ok!(Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_last_reward_era, + 0 + )); + + // can call page 1 + assert_ok!(Staking::payout_stakers_by_page( RuntimeOrigin::signed(1337), 11, - expected_last_reward_era + expected_last_reward_era, + 1 )); // Can't claim again assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_start_reward_era), + Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_start_reward_era, + 0 + ), + Error::::AlreadyClaimed.with_weight(err_weight) + ); + + assert_noop!( + Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_last_reward_era, + 0 + ), Error::::AlreadyClaimed.with_weight(err_weight) ); + assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, expected_last_reward_era), + Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_last_reward_era, + 1 + ), Error::::AlreadyClaimed.with_weight(err_weight) ); + + // invalid page + assert_noop!( + Staking::payout_stakers_by_page( + RuntimeOrigin::signed(1337), + 11, + expected_last_reward_era, + 2 + ), + Error::::InvalidPage.with_weight(err_weight) + ); + }); +} + +#[test] +fn test_commission_paid_across_pages() { + ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // enable multi paged rewards payout + allow_paged_rewards(4); + + let balance = 1; + let commission = 50; + // Create a validator: + bond_validator(11, 10, balance); + assert_ok!(Staking::validate( + RuntimeOrigin::signed(10), + ValidatorPrefs { commission: Perbill::from_percent(commission), blocked: false } + )); + assert_eq!(Validators::::count(), 1); + + // Create nominators, targeting stash of validators + for i in 0..200 { + let bond_amount = balance + i as Balance; + bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]); + } + + mock::start_active_era(1); + Staking::reward_by_ids(vec![(11, 1)]); + + // Since `MaxExposurePageSize = 64`, there are four pages of validator + // exposure. + assert_eq!(EraInfo::::get_page_count(1, &11), 4); + + // compute and ensure the reward amount is greater than zero. + let payout = current_total_payout_for_duration(reward_time_per_era()); + mock::start_active_era(2); + + let initial_balance = Balances::free_balance(&10); + // Payout rewards for first exposure page + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); + + let controller_balance_after_p0_payout = Balances::free_balance(&10); + + // some commission is paid + assert!(initial_balance < controller_balance_after_p0_payout); + + // payout all pages + for i in 1..4 { + let before_balance = Balances::free_balance(&10); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, i)); + let after_balance = Balances::free_balance(&10); + // some commission is paid for every page + assert!(before_balance < after_balance); + } + + assert_eq_error_rate!(Balances::free_balance(&10), initial_balance + payout / 2, 1,); }); } @@ -3956,8 +4487,7 @@ fn payout_stakers_handles_weight_refund() { // Note: this test relies on the assumption that `payout_stakers_alive_staked` is solely used by // `payout_stakers` to calculate the weight of each payout op. ExtBuilder::default().has_stakers(false).build_and_execute(|| { - let max_nom_rewarded = - <::MaxNominatorRewardedPerValidator as Get<_>>::get(); + let max_nom_rewarded = MaxExposurePageSize::get(); // Make sure the configured value is meaningful for our use. assert!(max_nom_rewarded >= 4); let half_max_nom_rewarded = max_nom_rewarded / 2; @@ -3993,7 +4523,11 @@ fn payout_stakers_handles_weight_refund() { start_active_era(2); // Collect payouts when there are no nominators - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 1 }); + let call = TestCall::Staking(StakingCall::payout_stakers_by_page { + validator_stash: 11, + era: 1, + page: 0, + }); let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(20)); assert_ok!(result); @@ -4006,7 +4540,11 @@ fn payout_stakers_handles_weight_refund() { start_active_era(3); // Collect payouts for an era where the validator did not receive any points. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 2 }); + let call = TestCall::Staking(StakingCall::payout_stakers_by_page { + validator_stash: 11, + era: 2, + page: 0, + }); let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(20)); assert_ok!(result); @@ -4019,7 +4557,11 @@ fn payout_stakers_handles_weight_refund() { start_active_era(4); // Collect payouts when the validator has `half_max_nom_rewarded` nominators. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 3 }); + let call = TestCall::Staking(StakingCall::payout_stakers_by_page { + validator_stash: 11, + era: 3, + page: 0, + }); let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(20)); assert_ok!(result); @@ -4042,14 +4584,22 @@ fn payout_stakers_handles_weight_refund() { start_active_era(6); // Collect payouts when the validator had `half_max_nom_rewarded` nominators. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 }); + let call = TestCall::Staking(StakingCall::payout_stakers_by_page { + validator_stash: 11, + era: 5, + page: 0, + }); let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(20)); assert_ok!(result); assert_eq!(extract_actual_weight(&result, &info), max_nom_rewarded_weight); // Try and collect payouts for an era that has already been collected. - let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 }); + let call = TestCall::Staking(StakingCall::payout_stakers_by_page { + validator_stash: 11, + era: 5, + page: 0, + }); let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(20)); assert!(result.is_err()); @@ -4059,7 +4609,7 @@ fn payout_stakers_handles_weight_refund() { } #[test] -fn bond_during_era_correctly_populates_claimed_rewards() { +fn bond_during_era_does_not_populate_legacy_claimed_rewards() { ExtBuilder::default().has_stakers(false).build_and_execute(|| { // Era = None bond_validator(9, 8, 1000); @@ -4070,7 +4620,7 @@ fn bond_during_era_correctly_populates_claimed_rewards() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }) ); mock::start_active_era(5); @@ -4082,13 +4632,12 @@ fn bond_during_era_correctly_populates_claimed_rewards() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: (0..5).collect::>().try_into().unwrap(), + legacy_claimed_rewards: bounded_vec![], }) ); // make sure only era upto history depth is stored let current_era = 99; - let last_reward_era = 99 - HistoryDepth::get(); mock::start_active_era(current_era); bond_validator(13, 12, 1000); assert_eq!( @@ -4098,10 +4647,7 @@ fn bond_during_era_correctly_populates_claimed_rewards() { total: 1000, active: 1000, unlocking: Default::default(), - claimed_rewards: (last_reward_era..current_era) - .collect::>() - .try_into() - .unwrap(), + legacy_claimed_rewards: Default::default(), }) ); }); @@ -4130,7 +4676,7 @@ fn offences_weight_calculated_correctly() { >, > = (1..10) .map(|i| OffenceDetails { - offender: (i, Staking::eras_stakers(active_era(), i)), + offender: (i, Staking::eras_stakers(active_era(), &i)), reporters: vec![], }) .collect(); @@ -4146,7 +4692,7 @@ fn offences_weight_calculated_correctly() { // On Offence with one offenders, Applied let one_offender = [OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![1], }]; @@ -4195,7 +4741,7 @@ fn payout_creates_controller() { // compute and ensure the reward amount is greater than zero. let _ = current_total_payout_for_duration(reward_time_per_era()); mock::start_active_era(2); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); // Controller is created assert!(Balances::free_balance(1337) > 0); @@ -4223,7 +4769,7 @@ fn payout_to_any_account_works() { // compute and ensure the reward amount is greater than zero. let _ = current_total_payout_for_duration(reward_time_per_era()); mock::start_active_era(2); - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); // Payment is successful assert!(Balances::free_balance(42) > 0); @@ -4346,7 +4892,7 @@ fn cannot_rebond_to_lower_than_ed() { total: 10 * 1000, active: 10 * 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); @@ -4360,7 +4906,7 @@ fn cannot_rebond_to_lower_than_ed() { total: 10 * 1000, active: 0, unlocking: bounded_vec![UnlockChunk { value: 10 * 1000, era: 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); @@ -4386,7 +4932,7 @@ fn cannot_bond_extra_to_lower_than_ed() { total: 10 * 1000, active: 10 * 1000, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); @@ -4400,7 +4946,7 @@ fn cannot_bond_extra_to_lower_than_ed() { total: 10 * 1000, active: 0, unlocking: bounded_vec![UnlockChunk { value: 10 * 1000, era: 3 }], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); @@ -4427,7 +4973,7 @@ fn do_not_die_when_active_is_ed() { total: 1000 * ed, active: 1000 * ed, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); @@ -4444,7 +4990,7 @@ fn do_not_die_when_active_is_ed() { total: ed, active: ed, unlocking: Default::default(), - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], } ); }) @@ -5251,7 +5797,7 @@ fn proportional_slash_stop_slashing_if_remaining_zero() { active: 20, // we have some chunks, but they are not affected. unlocking: bounded_vec![c(1, 10), c(2, 10)], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }; assert_eq!(BondingDuration::get(), 3); @@ -5269,7 +5815,7 @@ fn proportional_ledger_slash_works() { total: 10, active: 10, unlocking: bounded_vec![], - claimed_rewards: bounded_vec![], + legacy_claimed_rewards: bounded_vec![], }; assert_eq!(BondingDuration::get(), 3); @@ -5486,149 +6032,6 @@ fn proportional_ledger_slash_works() { ); } -#[test] -fn pre_bonding_era_cannot_be_claimed() { - // Verifies initial conditions of mock - ExtBuilder::default().nominate(false).build_and_execute(|| { - let history_depth = HistoryDepth::get(); - // jump to some era above history_depth - let mut current_era = history_depth + 10; - let last_reward_era = current_era - 1; - let start_reward_era = current_era - history_depth; - - // put some money in stash=3 and controller=4. - for i in 3..5 { - let _ = Balances::make_free_balance_be(&i, 2000); - } - - mock::start_active_era(current_era); - - // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 1500, RewardDestination::Controller)); - - let claimed_rewards: BoundedVec<_, _> = - (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); - assert_eq!( - Staking::ledger(&4).unwrap(), - StakingLedger { - stash: 3, - total: 1500, - active: 1500, - unlocking: Default::default(), - claimed_rewards, - } - ); - - // start next era - current_era = current_era + 1; - mock::start_active_era(current_era); - - // claiming reward for last era in which validator was active works - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 1)); - - // consumed weight for all payout_stakers dispatches that fail - let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); - // cannot claim rewards for an era before bonding occured as it is - // already marked as claimed. - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 2), - Error::::AlreadyClaimed.with_weight(err_weight) - ); - - // decoding will fail now since Staking Ledger is in corrupt state - HistoryDepth::set(history_depth - 1); - assert_eq!(Staking::ledger(&4), None); - - // make sure stakers still cannot claim rewards that they are not meant to - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 2), - Error::::NotController - ); - - // fix the corrupted state for post conditions check - HistoryDepth::set(history_depth); - }); -} - -#[test] -fn reducing_history_depth_abrupt() { - // Verifies initial conditions of mock - ExtBuilder::default().nominate(false).build_and_execute(|| { - let original_history_depth = HistoryDepth::get(); - let mut current_era = original_history_depth + 10; - let last_reward_era = current_era - 1; - let start_reward_era = current_era - original_history_depth; - - // put some money in (stash, controller)=(3,4),(5,6). - for i in 3..7 { - let _ = Balances::make_free_balance_be(&i, 2000); - } - - // start current era - mock::start_active_era(current_era); - - // add a new candidate for being a staker. account 3 controlled by 4. - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 4, 1500, RewardDestination::Controller)); - - // all previous era before the bonding action should be marked as - // claimed. - let claimed_rewards: BoundedVec<_, _> = - (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); - assert_eq!( - Staking::ledger(&4).unwrap(), - StakingLedger { - stash: 3, - total: 1500, - active: 1500, - unlocking: Default::default(), - claimed_rewards, - } - ); - - // next era - current_era = current_era + 1; - mock::start_active_era(current_era); - - // claiming reward for last era in which validator was active works - assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 1)); - - // next era - current_era = current_era + 1; - mock::start_active_era(current_era); - - // history_depth reduced without migration - let history_depth = original_history_depth - 1; - HistoryDepth::set(history_depth); - // claiming reward does not work anymore - assert_noop!( - Staking::payout_stakers(RuntimeOrigin::signed(4), 3, current_era - 1), - Error::::NotController - ); - - // new stakers can still bond - assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 6, 1200, RewardDestination::Controller)); - - // new staking ledgers created will be bounded by the current history depth - let last_reward_era = current_era - 1; - let start_reward_era = current_era - history_depth; - let claimed_rewards: BoundedVec<_, _> = - (start_reward_era..=last_reward_era).collect::>().try_into().unwrap(); - assert_eq!( - Staking::ledger(&6).unwrap(), - StakingLedger { - stash: 5, - total: 1200, - active: 1200, - unlocking: Default::default(), - claimed_rewards, - } - ); - - // fix the corrupted state for post conditions check - HistoryDepth::set(original_history_depth); - }); -} - #[test] fn reducing_max_unlocking_chunks_abrupt() { // Concern is on validators only @@ -5775,6 +6178,285 @@ fn set_min_commission_works_with_admin_origin() { }) } +#[test] +fn can_page_exposure() { + let mut others: Vec> = vec![]; + let mut total_stake: Balance = 0; + // 19 nominators + for i in 1..20 { + let individual_stake: Balance = 100 * i as Balance; + others.push(IndividualExposure { who: i, value: individual_stake }); + total_stake += individual_stake; + } + let own_stake: Balance = 500; + total_stake += own_stake; + assert_eq!(total_stake, 19_500); + // build full exposure set + let exposure: Exposure = + Exposure { total: total_stake, own: own_stake, others }; + + // when + let (exposure_overview, exposure_page): ( + ExposureOverview, + Vec>, + ) = exposure.clone().into_pages(3); + + // then + // 7 pages of nominators. + assert_eq!(exposure_page.len(), 7); + assert_eq!(exposure_overview.page_count, 7); + // first page stake = 100 + 200 + 300 + assert!(matches!(exposure_page[0], ExposurePage { page_total: 600, .. })); + // second page stake = 0 + 400 + 500 + 600 + assert!(matches!(exposure_page[1], ExposurePage { page_total: 1500, .. })); + // verify overview has the total + assert_eq!(exposure_overview.total, 19_500); + // verify total stake is same as in the original exposure. + assert_eq!( + exposure_page.iter().map(|a| a.page_total).reduce(|a, b| a + b).unwrap(), + 19_500 - exposure_overview.own + ); + // verify own stake is correct + assert_eq!(exposure_overview.own, 500); + // verify number of nominators are same as in the original exposure. + assert_eq!(exposure_page.iter().map(|a| a.others.len()).reduce(|a, b| a + b).unwrap(), 19); + assert_eq!(exposure_overview.nominator_count, 19); +} + +#[test] +fn should_retain_era_info_only_upto_history_depth() { + ExtBuilder::default().build_and_execute(|| { + // remove existing exposure + Pallet::::clear_era_information(0); + let validator_stash = 10; + + for era in 0..4 { + ClaimedRewards::::insert(era, &validator_stash, vec![0, 1, 2]); + for page in 0..3 { + ErasStakersPaged::::insert( + (era, &validator_stash, page), + ExposurePage { page_total: 100, others: vec![] }, + ); + } + } + + for i in 0..4 { + // Count of entries remaining in ClaimedRewards = total - cleared_count + assert_eq!(ClaimedRewards::::iter().count(), (4 - i)); + // 1 claimed_rewards entry for each era + assert_eq!(ClaimedRewards::::iter_prefix(i as EraIndex).count(), 1); + // 3 entries (pages) for each era + assert_eq!(ErasStakersPaged::::iter_prefix((i as EraIndex,)).count(), 3); + + // when clear era info + Pallet::::clear_era_information(i as EraIndex); + + // then all era entries are cleared + assert_eq!(ClaimedRewards::::iter_prefix(i as EraIndex).count(), 0); + assert_eq!(ErasStakersPaged::::iter_prefix((i as EraIndex,)).count(), 0); + } + }); +} + +#[test] +fn test_legacy_claimed_rewards_is_checked_at_reward_payout() { + ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // Create a validator: + bond_validator(11, 10, 1000); + + // reward validator for next 2 eras + mock::start_active_era(1); + Pallet::::reward_by_ids(vec![(11, 1)]); + mock::start_active_era(2); + Pallet::::reward_by_ids(vec![(11, 1)]); + mock::start_active_era(3); + + //verify rewards are not claimed + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + 1, + Staking::ledger(10).as_ref().unwrap(), + &11, + 0 + ), + false + ); + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + 2, + Staking::ledger(10).as_ref().unwrap(), + &11, + 0 + ), + false + ); + + // assume reward claim for era 1 was stored in legacy storage + Ledger::::insert( + 10, + StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: Default::default(), + legacy_claimed_rewards: bounded_vec![1], + }, + ); + + // verify rewards for era 1 cannot be claimed + assert_noop!( + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0), + Error::::AlreadyClaimed + .with_weight(::WeightInfo::payout_stakers_alive_staked(0)), + ); + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + 1, + Staking::ledger(10).as_ref().unwrap(), + &11, + 0 + ), + true + ); + + // verify rewards for era 2 can be claimed + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0)); + assert_eq!( + EraInfo::::is_rewards_claimed_with_legacy_fallback( + 2, + Staking::ledger(10).as_ref().unwrap(), + &11, + 0 + ), + true + ); + // but the new claimed rewards for era 2 is not stored in legacy storage + assert_eq!( + Ledger::::get(10).unwrap(), + StakingLedger { + stash: 11, + total: 1000, + active: 1000, + unlocking: Default::default(), + legacy_claimed_rewards: bounded_vec![1], + }, + ); + // instead it is kept in `ClaimedRewards` + assert_eq!(ClaimedRewards::::get(2, 11), vec![0]); + }); +} + +#[test] +fn test_validator_exposure_is_backward_compatible_with_non_paged_rewards_payout() { + ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // enable multi paged rewards payout + allow_paged_rewards(2); + + // case 1: exposure exist in clipped. + // set page cap to 10 + MaxExposurePageSize::set(10); + bond_validator(11, 10, 1000); + let mut expected_individual_exposures: Vec> = vec![]; + let mut total_exposure: Balance = 0; + // 1st exposure page + for i in 0..10 { + let who = 1000 + i; + let value = 1000 + i as Balance; + bond_nominator(who, 100 + i, value, vec![11]); + expected_individual_exposures.push(IndividualExposure { who, value }); + total_exposure += value; + } + + for i in 10..15 { + let who = 1000 + i; + let value = 1000 + i as Balance; + bond_nominator(who, 100 + i, value, vec![11]); + expected_individual_exposures.push(IndividualExposure { who, value }); + total_exposure += value; + } + + mock::start_active_era(1); + // reward validator for current era + Pallet::::reward_by_ids(vec![(11, 1)]); + + // start new era + mock::start_active_era(2); + // verify exposure for era 1 is stored in paged storage, that each exposure is stored in + // one and only one page, and no exposure is repeated. + let actual_exposure_page_0 = ErasStakersPaged::::get((1, 11, 0)).unwrap(); + let actual_exposure_page_1 = ErasStakersPaged::::get((1, 11, 1)).unwrap(); + expected_individual_exposures.iter().for_each(|exposure| { + assert!( + actual_exposure_page_0.others.contains(exposure) || + actual_exposure_page_1.others.contains(exposure) + ); + }); + assert_eq!( + expected_individual_exposures.len(), + actual_exposure_page_0.others.len() + actual_exposure_page_1.others.len() + ); + // verify `EraInfo` returns page from paged storage + assert_eq!( + EraInfo::::get_validator_exposure(1, &11, 0).unwrap().others(), + &actual_exposure_page_0.others + ); + assert_eq!( + EraInfo::::get_validator_exposure(1, &11, 1).unwrap().others(), + &actual_exposure_page_1.others + ); + assert_eq!(EraInfo::::get_page_count(1, &11), 2); + + // case 2: exposure exist in ErasStakers and ErasStakersClipped (legacy). + // delete paged storage and add exposure to clipped storage + >::remove((1, 11, 0)); + >::remove((1, 11, 1)); + >::remove(1, 11); + + >::insert( + 1, + 11, + Exposure { + total: total_exposure, + own: 1000, + others: expected_individual_exposures.clone(), + }, + ); + let mut clipped_exposure = expected_individual_exposures.clone(); + clipped_exposure.sort_by(|a, b| b.who.cmp(&a.who)); + clipped_exposure.truncate(10); + >::insert( + 1, + 11, + Exposure { total: total_exposure, own: 1000, others: clipped_exposure.clone() }, + ); + + // verify `EraInfo` returns exposure from clipped storage + let actual_exposure_paged = EraInfo::::get_validator_exposure(1, &11, 0).unwrap(); + assert_eq!(actual_exposure_paged.others(), &clipped_exposure); + assert_eq!(actual_exposure_paged.own(), 1000); + assert_eq!(actual_exposure_paged.exposure_overview.page_count, 1); + + let actual_exposure_full = EraInfo::::get_non_paged_validator_exposure(1, &11); + assert_eq!(actual_exposure_full.others, expected_individual_exposures); + assert_eq!(actual_exposure_full.own, 1000); + assert_eq!(actual_exposure_full.total, total_exposure); + + // for pages other than 0, clipped storage returns empty exposure + assert_eq!(EraInfo::::get_validator_exposure(1, &11, 1), None); + // page size is 1 for clipped storage + assert_eq!(EraInfo::::get_page_count(1, &11), 1); + + // payout for page 0 works + assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0)); + // payout for page 1 fails + assert_noop!( + Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 1), + Error::::InvalidPage + .with_weight(::WeightInfo::payout_stakers_alive_staked(0)) + ); + }); +} + mod staking_interface { use frame_support::storage::with_storage_layer; use sp_staking::StakingInterface; @@ -5804,7 +6486,7 @@ mod staking_interface { ExtBuilder::default().build_and_execute(|| { on_offence_now( &[OffenceDetails { - offender: (11, Staking::eras_stakers(active_era(), 11)), + offender: (11, Staking::eras_stakers(active_era(), &11)), reporters: vec![], }], &[Perbill::from_percent(100)], diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 2c9e1b2669c8b..7752ce019c240 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `runner-ehxwxxsd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_staking // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/staking/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_staking +// --chain=dev // --header=./HEADER-APACHE2 +// --output=./frame/staking/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -87,19 +88,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1079` - // Estimated: `10386` - // Minimum execution time: 40_015 nanoseconds. - Weight::from_parts(40_601_000, 10386) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `1074` + // Estimated: `9887` + // Minimum execution time: 42_791 nanoseconds. + Weight::from_ref_time(43_964_000) + .saturating_add(Weight::from_proof_size(9887)) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: Staking Bonded (r:1 w:0) @@ -114,10 +114,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `2252` + // Measured: `2214` // Estimated: `22888` - // Minimum execution time: 74_781 nanoseconds. - Weight::from_parts(75_188_000, 22888) + // Minimum execution time: 87_712 nanoseconds. + Weight::from_ref_time(90_269_000) + .saturating_add(Weight::from_proof_size(22888)) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -141,10 +142,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2457` + // Measured: `2419` // Estimated: `29534` - // Minimum execution time: 81_299 nanoseconds. - Weight::from_parts(82_242_000, 29534) + // Minimum execution time: 95_748 nanoseconds. + Weight::from_ref_time(98_423_000) + .saturating_add(Weight::from_proof_size(29534)) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -159,12 +161,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1085` + // Measured: `1080` // Estimated: `10442` - // Minimum execution time: 31_479 nanoseconds. - Weight::from_parts(32_410_035, 10442) - // Standard Error: 313 - .saturating_add(Weight::from_ref_time(9_090).saturating_mul(s.into())) + // Minimum execution time: 34_556 nanoseconds. + Weight::from_ref_time(36_352_208) + .saturating_add(Weight::from_proof_size(10442)) + // Standard Error: 975 + .saturating_add(Weight::from_ref_time(42_685).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -199,12 +202,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `32303 + s * (4 ±0)` - // Minimum execution time: 71_968 nanoseconds. - Weight::from_parts(76_631_804, 32303) - // Standard Error: 1_613 - .saturating_add(Weight::from_ref_time(1_058_968).saturating_mul(s.into())) + // Measured: `2388 + s * (4 ±0)` + // Estimated: `32205 + s * (4 ±0)` + // Minimum execution time: 82_858 nanoseconds. + Weight::from_ref_time(90_123_003) + .saturating_add(Weight::from_proof_size(32205)) + // Standard Error: 3_084 + .saturating_add(Weight::from_ref_time(1_363_655).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -234,10 +238,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1446` + // Measured: `1404` // Estimated: `19359` - // Minimum execution time: 51_963 nanoseconds. - Weight::from_parts(52_418_000, 19359) + // Minimum execution time: 57_223 nanoseconds. + Weight::from_ref_time(59_188_000) + .saturating_add(Weight::from_proof_size(19359)) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -248,12 +253,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1292 + k * (601 ±0)` + // Measured: `1287 + k * (601 ±0)` // Estimated: `3566 + k * (3033 ±0)` - // Minimum execution time: 25_685 nanoseconds. - Weight::from_parts(25_290_286, 3566) - // Standard Error: 5_164 - .saturating_add(Weight::from_ref_time(6_445_608).saturating_mul(k.into())) + // Minimum execution time: 30_970 nanoseconds. + Weight::from_ref_time(32_205_531) + .saturating_add(Weight::from_proof_size(3566)) + // Standard Error: 12_307 + .saturating_add(Weight::from_ref_time(8_599_234).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -284,12 +290,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1984 + n * (105 ±0)` + // Measured: `1942 + n * (105 ±0)` // Estimated: `21988 + n * (2520 ±0)` - // Minimum execution time: 59_542 nanoseconds. - Weight::from_parts(57_558_678, 21988) - // Standard Error: 10_364 - .saturating_add(Weight::from_ref_time(2_759_713).saturating_mul(n.into())) + // Minimum execution time: 69_223 nanoseconds. + Weight::from_ref_time(68_719_145) + .saturating_add(Weight::from_proof_size(21988)) + // Standard Error: 19_229 + .saturating_add(Weight::from_ref_time(4_075_338).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -311,10 +318,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1876` + // Measured: `1778` // Estimated: `17932` - // Minimum execution time: 52_132 nanoseconds. - Weight::from_parts(52_648_000, 17932) + // Minimum execution time: 58_126 nanoseconds. + Weight::from_ref_time(59_792_000) + .saturating_add(Weight::from_proof_size(17932)) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -324,10 +332,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `840` + // Measured: `835` // Estimated: `3566` - // Minimum execution time: 13_399 nanoseconds. - Weight::from_parts(13_567_000, 3566) + // Minimum execution time: 13_555 nanoseconds. + Weight::from_ref_time(14_016_000) + .saturating_add(Weight::from_proof_size(3566)) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -337,10 +346,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `939` + // Measured: `934` // Estimated: `9679` - // Minimum execution time: 20_425 nanoseconds. - Weight::from_parts(20_713_000, 9679) + // Minimum execution time: 21_301 nanoseconds. + Weight::from_ref_time(22_586_000) + .saturating_add(Weight::from_proof_size(9679)) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -350,8 +360,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_069 nanoseconds. - Weight::from_ref_time(3_176_000) + // Minimum execution time: 2_830 nanoseconds. + Weight::from_ref_time(2_957_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -360,8 +371,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_386 nanoseconds. - Weight::from_ref_time(11_672_000) + // Minimum execution time: 10_600 nanoseconds. + Weight::from_ref_time(11_114_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -370,8 +382,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_591 nanoseconds. - Weight::from_ref_time(11_799_000) + // Minimum execution time: 10_432 nanoseconds. + Weight::from_ref_time(10_984_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -380,8 +393,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_553 nanoseconds. - Weight::from_ref_time(11_871_000) + // Minimum execution time: 10_565 nanoseconds. + Weight::from_ref_time(11_139_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -391,10 +405,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_292 nanoseconds. - Weight::from_ref_time(3_754_352) - // Standard Error: 40 - .saturating_add(Weight::from_ref_time(9_838).saturating_mul(v.into())) + // Minimum execution time: 2_988 nanoseconds. + Weight::from_ref_time(3_521_842) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 43 + .saturating_add(Weight::from_ref_time(9_580).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -426,12 +441,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2178 + s * (4 ±0)` - // Estimated: `27930 + s * (4 ±0)` - // Minimum execution time: 65_307 nanoseconds. - Weight::from_parts(70_227_980, 27930) - // Standard Error: 2_113 - .saturating_add(Weight::from_ref_time(1_059_856).saturating_mul(s.into())) + // Measured: `2118 + s * (4 ±0)` + // Estimated: `27870 + s * (4 ±0)` + // Minimum execution time: 75_086 nanoseconds. + Weight::from_ref_time(82_863_879) + .saturating_add(Weight::from_proof_size(27870)) + // Standard Error: 3_873 + .saturating_add(Weight::from_ref_time(1_339_680).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -442,25 +458,30 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66671` - // Estimated: `69146` - // Minimum execution time: 89_123 nanoseconds. - Weight::from_parts(890_989_741, 69146) - // Standard Error: 58_282 - .saturating_add(Weight::from_ref_time(4_920_413).saturating_mul(s.into())) + // Measured: `66704` + // Estimated: `69179` + // Minimum execution time: 99_616 nanoseconds. + Weight::from_ref_time(910_033_266) + .saturating_add(Weight::from_proof_size(69179)) + // Standard Error: 58_519 + .saturating_add(Weight::from_ref_time(4_878_008).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersOverview (r:1 w:0) + /// Proof: Staking ErasStakersOverview (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: Staking ErasValidatorReward (r:1 w:0) /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:257 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:1 w:0) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ClaimedRewards (r:1 w:1) + /// Proof Skipped: Staking ClaimedRewards (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersPaged (r:1 w:0) + /// Proof Skipped: Staking ErasStakersPaged (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasRewardPoints (r:1 w:0) /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasValidatorPrefs (r:1 w:0) @@ -472,28 +493,35 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `20345 + n * (143 ±0)` - // Estimated: `54756 + n * (8024 ±1)` - // Minimum execution time: 73_652 nanoseconds. - Weight::from_parts(127_839_483, 54756) - // Standard Error: 14_195 - .saturating_add(Weight::from_ref_time(21_932_079).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `20450 + n * (143 ±0)` + // Estimated: `76596 + n * (8187 ±1)` + // Minimum execution time: 120_810 nanoseconds. + Weight::from_ref_time(153_474_546) + .saturating_add(Weight::from_proof_size(76596)) + // Standard Error: 18_966 + .saturating_add(Weight::from_ref_time(28_752_414).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(8024).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(8187).saturating_mul(n.into())) } - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasValidatorReward (r:1 w:0) - /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:257 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:257 w:257) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Staking ErasStakersClipped (r:1 w:0) /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersOverview (r:1 w:0) + /// Proof: Staking ErasStakersOverview (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: Staking ClaimedRewards (r:1 w:1) + /// Proof Skipped: Staking ClaimedRewards (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasValidatorReward (r:1 w:0) + /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersPaged (r:1 w:0) + /// Proof Skipped: Staking ErasStakersPaged (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasRewardPoints (r:1 w:0) /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasValidatorPrefs (r:1 w:0) @@ -507,17 +535,18 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `35099 + n * (465 ±0)` - // Estimated: `83594 + n * (16026 ±0)` - // Minimum execution time: 94_560 nanoseconds. - Weight::from_parts(154_033_219, 83594) - // Standard Error: 26_663 - .saturating_add(Weight::from_ref_time(31_269_223).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10_u64)) + // Measured: `35204 + n * (465 ±0)` + // Estimated: `149575 + n * (17014 ±3)` + // Minimum execution time: 150_324 nanoseconds. + Weight::from_ref_time(148_039_697) + .saturating_add(Weight::from_proof_size(149575)) + // Standard Error: 39_048 + .saturating_add(Weight::from_ref_time(43_172_238).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(16026).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(17014).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -534,12 +563,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2253 + l * (7 ±0)` + // Measured: `2215 + l * (7 ±0)` // Estimated: `25491` - // Minimum execution time: 74_764 nanoseconds. - Weight::from_parts(75_814_067, 25491) - // Standard Error: 1_217 - .saturating_add(Weight::from_ref_time(64_725).saturating_mul(l.into())) + // Minimum execution time: 86_750 nanoseconds. + Weight::from_ref_time(90_496_328) + .saturating_add(Weight::from_proof_size(25491)) + // Standard Error: 5_292 + .saturating_add(Weight::from_ref_time(58_052).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -572,12 +602,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `31810 + s * (4 ±0)` - // Minimum execution time: 77_611 nanoseconds. - Weight::from_parts(79_760_034, 31810) - // Standard Error: 1_597 - .saturating_add(Weight::from_ref_time(1_039_268).saturating_mul(s.into())) + // Measured: `2388 + s * (4 ±0)` + // Estimated: `31712 + s * (4 ±0)` + // Minimum execution time: 89_155 nanoseconds. + Weight::from_ref_time(94_547_569) + .saturating_add(Weight::from_proof_size(31712)) + // Standard Error: 4_227 + .saturating_add(Weight::from_ref_time(1_330_189).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -605,12 +636,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking MinimumValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Staking CurrentEra (r:1 w:1) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:0 w:10) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasValidatorPrefs (r:0 w:10) /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking ErasStakers (r:0 w:10) - /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersPaged (r:0 w:10) + /// Proof Skipped: Staking ErasStakersPaged (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersOverview (r:0 w:10) + /// Proof: Staking ErasStakersOverview (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: Staking ErasTotalStake (r:0 w:1) /// Proof: Staking ErasTotalStake (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) /// Storage: Staking ErasStartSessionIndex (r:0 w:1) @@ -622,20 +653,21 @@ impl WeightInfo for SubstrateWeight { fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + v * (3662 ±0) + n * (816 ±0)` - // Estimated: `528203 + v * (16743 ±0) + n * (12947 ±0)` - // Minimum execution time: 489_824 nanoseconds. - Weight::from_parts(491_687_000, 528203) - // Standard Error: 1_787_577 - .saturating_add(Weight::from_ref_time(58_719_498).saturating_mul(v.into())) - // Standard Error: 178_122 - .saturating_add(Weight::from_ref_time(13_273_555).saturating_mul(n.into())) + // Estimated: `523708 + v * (15469 ±41) + n * (12348 ±4)` + // Minimum execution time: 577_839 nanoseconds. + Weight::from_ref_time(587_063_000) + .saturating_add(Weight::from_proof_size(523708)) + // Standard Error: 2_193_535 + .saturating_add(Weight::from_ref_time(72_675_973).saturating_mul(v.into())) + // Standard Error: 218_573 + .saturating_add(Weight::from_ref_time(19_272_321).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(16743).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(12947).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(15469).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(12348).saturating_mul(n.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -657,14 +689,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3167 + v * (459 ±0) + n * (1007 ±0)` + // Measured: `3125 + v * (459 ±0) + n * (1007 ±0)` // Estimated: `511899 + v * (14295 ±0) + n * (11775 ±0)` - // Minimum execution time: 23_373_467 nanoseconds. - Weight::from_parts(23_497_257_000, 511899) - // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(3_434_000).saturating_mul(v.into())) - // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(2_568_954).saturating_mul(n.into())) + // Minimum execution time: 38_014_741 nanoseconds. + Weight::from_ref_time(39_016_264_000) + .saturating_add(Weight::from_proof_size(511899)) + // Standard Error: 409_554 + .saturating_add(Weight::from_ref_time(4_585_268).saturating_mul(v.into())) + // Standard Error: 409_554 + .saturating_add(Weight::from_ref_time(5_027_951).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -679,12 +712,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `983 + v * (50 ±0)` + // Measured: `979 + v * (50 ±0)` // Estimated: `3019 + v * (2520 ±0)` - // Minimum execution time: 3_882_120 nanoseconds. - Weight::from_parts(3_951_993_000, 3019) - // Standard Error: 46_729 - .saturating_add(Weight::from_ref_time(2_856_043).saturating_mul(v.into())) + // Minimum execution time: 4_283_114 nanoseconds. + Weight::from_ref_time(4_393_678_000) + .saturating_add(Weight::from_proof_size(3019)) + // Standard Error: 49_897 + .saturating_add(Weight::from_ref_time(3_190_372).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_proof_size(2520).saturating_mul(v.into())) @@ -705,8 +739,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_427 nanoseconds. - Weight::from_ref_time(8_794_000) + // Minimum execution time: 7_398 nanoseconds. + Weight::from_ref_time(7_711_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -725,8 +760,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_620 nanoseconds. - Weight::from_ref_time(7_901_000) + // Minimum execution time: 6_598 nanoseconds. + Weight::from_ref_time(6_970_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -751,10 +787,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `2031` + // Measured: `1933` // Estimated: `19438` - // Minimum execution time: 66_188 nanoseconds. - Weight::from_parts(66_767_000, 19438) + // Minimum execution time: 72_003 nanoseconds. + Weight::from_ref_time(73_575_000) + .saturating_add(Weight::from_proof_size(19438)) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -764,10 +801,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `694` + // Measured: `691` // Estimated: `3019` - // Minimum execution time: 14_703 nanoseconds. - Weight::from_parts(15_031_000, 3019) + // Minimum execution time: 13_540 nanoseconds. + Weight::from_ref_time(14_129_000) + .saturating_add(Weight::from_proof_size(3019)) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -777,8 +815,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_518 nanoseconds. - Weight::from_ref_time(4_656_000) + // Minimum execution time: 3_511 nanoseconds. + Weight::from_ref_time(3_633_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -789,19 +828,18 @@ impl WeightInfo for () { /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Locks (r:1 w:1) /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) /// Storage: Staking Payee (r:0 w:1) /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1079` - // Estimated: `10386` - // Minimum execution time: 40_015 nanoseconds. - Weight::from_parts(40_601_000, 10386) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `1074` + // Estimated: `9887` + // Minimum execution time: 42_791 nanoseconds. + Weight::from_ref_time(43_964_000) + .saturating_add(Weight::from_proof_size(9887)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: Staking Bonded (r:1 w:0) @@ -816,10 +854,11 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `2252` + // Measured: `2214` // Estimated: `22888` - // Minimum execution time: 74_781 nanoseconds. - Weight::from_parts(75_188_000, 22888) + // Minimum execution time: 87_712 nanoseconds. + Weight::from_ref_time(90_269_000) + .saturating_add(Weight::from_proof_size(22888)) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -843,10 +882,11 @@ impl WeightInfo for () { /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2457` + // Measured: `2419` // Estimated: `29534` - // Minimum execution time: 81_299 nanoseconds. - Weight::from_parts(82_242_000, 29534) + // Minimum execution time: 95_748 nanoseconds. + Weight::from_ref_time(98_423_000) + .saturating_add(Weight::from_proof_size(29534)) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -861,12 +901,13 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1085` + // Measured: `1080` // Estimated: `10442` - // Minimum execution time: 31_479 nanoseconds. - Weight::from_parts(32_410_035, 10442) - // Standard Error: 313 - .saturating_add(Weight::from_ref_time(9_090).saturating_mul(s.into())) + // Minimum execution time: 34_556 nanoseconds. + Weight::from_ref_time(36_352_208) + .saturating_add(Weight::from_proof_size(10442)) + // Standard Error: 975 + .saturating_add(Weight::from_ref_time(42_685).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -901,12 +942,13 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `32303 + s * (4 ±0)` - // Minimum execution time: 71_968 nanoseconds. - Weight::from_parts(76_631_804, 32303) - // Standard Error: 1_613 - .saturating_add(Weight::from_ref_time(1_058_968).saturating_mul(s.into())) + // Measured: `2388 + s * (4 ±0)` + // Estimated: `32205 + s * (4 ±0)` + // Minimum execution time: 82_858 nanoseconds. + Weight::from_ref_time(90_123_003) + .saturating_add(Weight::from_proof_size(32205)) + // Standard Error: 3_084 + .saturating_add(Weight::from_ref_time(1_363_655).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -936,10 +978,11 @@ impl WeightInfo for () { /// Proof: Staking CounterForValidators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1446` + // Measured: `1404` // Estimated: `19359` - // Minimum execution time: 51_963 nanoseconds. - Weight::from_parts(52_418_000, 19359) + // Minimum execution time: 57_223 nanoseconds. + Weight::from_ref_time(59_188_000) + .saturating_add(Weight::from_proof_size(19359)) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -950,12 +993,13 @@ impl WeightInfo for () { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1292 + k * (601 ±0)` + // Measured: `1287 + k * (601 ±0)` // Estimated: `3566 + k * (3033 ±0)` - // Minimum execution time: 25_685 nanoseconds. - Weight::from_parts(25_290_286, 3566) - // Standard Error: 5_164 - .saturating_add(Weight::from_ref_time(6_445_608).saturating_mul(k.into())) + // Minimum execution time: 30_970 nanoseconds. + Weight::from_ref_time(32_205_531) + .saturating_add(Weight::from_proof_size(3566)) + // Standard Error: 12_307 + .saturating_add(Weight::from_ref_time(8_599_234).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -986,12 +1030,13 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1984 + n * (105 ±0)` + // Measured: `1942 + n * (105 ±0)` // Estimated: `21988 + n * (2520 ±0)` - // Minimum execution time: 59_542 nanoseconds. - Weight::from_parts(57_558_678, 21988) - // Standard Error: 10_364 - .saturating_add(Weight::from_ref_time(2_759_713).saturating_mul(n.into())) + // Minimum execution time: 69_223 nanoseconds. + Weight::from_ref_time(68_719_145) + .saturating_add(Weight::from_proof_size(21988)) + // Standard Error: 19_229 + .saturating_add(Weight::from_ref_time(4_075_338).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1013,10 +1058,11 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1876` + // Measured: `1778` // Estimated: `17932` - // Minimum execution time: 52_132 nanoseconds. - Weight::from_parts(52_648_000, 17932) + // Minimum execution time: 58_126 nanoseconds. + Weight::from_ref_time(59_792_000) + .saturating_add(Weight::from_proof_size(17932)) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1026,10 +1072,11 @@ impl WeightInfo for () { /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `840` + // Measured: `835` // Estimated: `3566` - // Minimum execution time: 13_399 nanoseconds. - Weight::from_parts(13_567_000, 3566) + // Minimum execution time: 13_555 nanoseconds. + Weight::from_ref_time(14_016_000) + .saturating_add(Weight::from_proof_size(3566)) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1039,10 +1086,11 @@ impl WeightInfo for () { /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `939` + // Measured: `934` // Estimated: `9679` - // Minimum execution time: 20_425 nanoseconds. - Weight::from_parts(20_713_000, 9679) + // Minimum execution time: 21_301 nanoseconds. + Weight::from_ref_time(22_586_000) + .saturating_add(Weight::from_proof_size(9679)) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1052,8 +1100,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_069 nanoseconds. - Weight::from_ref_time(3_176_000) + // Minimum execution time: 2_830 nanoseconds. + Weight::from_ref_time(2_957_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1062,8 +1111,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_386 nanoseconds. - Weight::from_ref_time(11_672_000) + // Minimum execution time: 10_600 nanoseconds. + Weight::from_ref_time(11_114_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1072,8 +1122,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_591 nanoseconds. - Weight::from_ref_time(11_799_000) + // Minimum execution time: 10_432 nanoseconds. + Weight::from_ref_time(10_984_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking ForceEra (r:0 w:1) @@ -1082,8 +1133,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_553 nanoseconds. - Weight::from_ref_time(11_871_000) + // Minimum execution time: 10_565 nanoseconds. + Weight::from_ref_time(11_139_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Invulnerables (r:0 w:1) @@ -1093,10 +1145,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_292 nanoseconds. - Weight::from_ref_time(3_754_352) - // Standard Error: 40 - .saturating_add(Weight::from_ref_time(9_838).saturating_mul(v.into())) + // Minimum execution time: 2_988 nanoseconds. + Weight::from_ref_time(3_521_842) + .saturating_add(Weight::from_proof_size(0)) + // Standard Error: 43 + .saturating_add(Weight::from_ref_time(9_580).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking Bonded (r:1 w:1) @@ -1128,12 +1181,13 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2178 + s * (4 ±0)` - // Estimated: `27930 + s * (4 ±0)` - // Minimum execution time: 65_307 nanoseconds. - Weight::from_parts(70_227_980, 27930) - // Standard Error: 2_113 - .saturating_add(Weight::from_ref_time(1_059_856).saturating_mul(s.into())) + // Measured: `2118 + s * (4 ±0)` + // Estimated: `27870 + s * (4 ±0)` + // Minimum execution time: 75_086 nanoseconds. + Weight::from_ref_time(82_863_879) + .saturating_add(Weight::from_proof_size(27870)) + // Standard Error: 3_873 + .saturating_add(Weight::from_ref_time(1_339_680).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1144,25 +1198,30 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66671` - // Estimated: `69146` - // Minimum execution time: 89_123 nanoseconds. - Weight::from_parts(890_989_741, 69146) - // Standard Error: 58_282 - .saturating_add(Weight::from_ref_time(4_920_413).saturating_mul(s.into())) + // Measured: `66704` + // Estimated: `69179` + // Minimum execution time: 99_616 nanoseconds. + Weight::from_ref_time(910_033_266) + .saturating_add(Weight::from_proof_size(69179)) + // Standard Error: 58_519 + .saturating_add(Weight::from_ref_time(4_878_008).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Staking CurrentEra (r:1 w:0) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersOverview (r:1 w:0) + /// Proof: Staking ErasStakersOverview (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: Staking ErasValidatorReward (r:1 w:0) /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:257 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:1 w:0) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ClaimedRewards (r:1 w:1) + /// Proof Skipped: Staking ClaimedRewards (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersPaged (r:1 w:0) + /// Proof Skipped: Staking ErasStakersPaged (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasRewardPoints (r:1 w:0) /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasValidatorPrefs (r:1 w:0) @@ -1174,28 +1233,35 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `20345 + n * (143 ±0)` - // Estimated: `54756 + n * (8024 ±1)` - // Minimum execution time: 73_652 nanoseconds. - Weight::from_parts(127_839_483, 54756) - // Standard Error: 14_195 - .saturating_add(Weight::from_ref_time(21_932_079).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `20450 + n * (143 ±0)` + // Estimated: `76596 + n * (8187 ±1)` + // Minimum execution time: 120_810 nanoseconds. + Weight::from_ref_time(153_474_546) + .saturating_add(Weight::from_proof_size(76596)) + // Standard Error: 18_966 + .saturating_add(Weight::from_ref_time(28_752_414).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(8024).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(8187).saturating_mul(n.into())) } - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasValidatorReward (r:1 w:0) - /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) /// Storage: Staking Bonded (r:257 w:0) /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) /// Storage: Staking Ledger (r:257 w:257) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) /// Storage: Staking ErasStakersClipped (r:1 w:0) /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersOverview (r:1 w:0) + /// Proof: Staking ErasStakersOverview (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: Staking ClaimedRewards (r:1 w:1) + /// Proof Skipped: Staking ClaimedRewards (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking CurrentEra (r:1 w:0) + /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Staking ErasValidatorReward (r:1 w:0) + /// Proof: Staking ErasValidatorReward (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: Staking ErasStakersPaged (r:1 w:0) + /// Proof Skipped: Staking ErasStakersPaged (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasRewardPoints (r:1 w:0) /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasValidatorPrefs (r:1 w:0) @@ -1209,17 +1275,18 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `35099 + n * (465 ±0)` - // Estimated: `83594 + n * (16026 ±0)` - // Minimum execution time: 94_560 nanoseconds. - Weight::from_parts(154_033_219, 83594) - // Standard Error: 26_663 - .saturating_add(Weight::from_ref_time(31_269_223).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(10_u64)) + // Measured: `35204 + n * (465 ±0)` + // Estimated: `149575 + n * (17014 ±3)` + // Minimum execution time: 150_324 nanoseconds. + Weight::from_ref_time(148_039_697) + .saturating_add(Weight::from_proof_size(149575)) + // Standard Error: 39_048 + .saturating_add(Weight::from_ref_time(43_172_238).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_proof_size(16026).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(17014).saturating_mul(n.into())) } /// Storage: Staking Ledger (r:1 w:1) /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) @@ -1236,12 +1303,13 @@ impl WeightInfo for () { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2253 + l * (7 ±0)` + // Measured: `2215 + l * (7 ±0)` // Estimated: `25491` - // Minimum execution time: 74_764 nanoseconds. - Weight::from_parts(75_814_067, 25491) - // Standard Error: 1_217 - .saturating_add(Weight::from_ref_time(64_725).saturating_mul(l.into())) + // Minimum execution time: 86_750 nanoseconds. + Weight::from_ref_time(90_496_328) + .saturating_add(Weight::from_proof_size(25491)) + // Standard Error: 5_292 + .saturating_add(Weight::from_ref_time(58_052).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -1274,12 +1342,13 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2486 + s * (4 ±0)` - // Estimated: `31810 + s * (4 ±0)` - // Minimum execution time: 77_611 nanoseconds. - Weight::from_parts(79_760_034, 31810) - // Standard Error: 1_597 - .saturating_add(Weight::from_ref_time(1_039_268).saturating_mul(s.into())) + // Measured: `2388 + s * (4 ±0)` + // Estimated: `31712 + s * (4 ±0)` + // Minimum execution time: 89_155 nanoseconds. + Weight::from_ref_time(94_547_569) + .saturating_add(Weight::from_proof_size(31712)) + // Standard Error: 4_227 + .saturating_add(Weight::from_ref_time(1_330_189).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1307,12 +1376,12 @@ impl WeightInfo for () { /// Proof: Staking MinimumValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Staking CurrentEra (r:1 w:1) /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStakersClipped (r:0 w:10) - /// Proof Skipped: Staking ErasStakersClipped (max_values: None, max_size: None, mode: Measured) /// Storage: Staking ErasValidatorPrefs (r:0 w:10) /// Proof: Staking ErasValidatorPrefs (max_values: None, max_size: Some(57), added: 2532, mode: MaxEncodedLen) - /// Storage: Staking ErasStakers (r:0 w:10) - /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersPaged (r:0 w:10) + /// Proof Skipped: Staking ErasStakersPaged (max_values: None, max_size: None, mode: Measured) + /// Storage: Staking ErasStakersOverview (r:0 w:10) + /// Proof: Staking ErasStakersOverview (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) /// Storage: Staking ErasTotalStake (r:0 w:1) /// Proof: Staking ErasTotalStake (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) /// Storage: Staking ErasStartSessionIndex (r:0 w:1) @@ -1324,20 +1393,21 @@ impl WeightInfo for () { fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + v * (3662 ±0) + n * (816 ±0)` - // Estimated: `528203 + v * (16743 ±0) + n * (12947 ±0)` - // Minimum execution time: 489_824 nanoseconds. - Weight::from_parts(491_687_000, 528203) - // Standard Error: 1_787_577 - .saturating_add(Weight::from_ref_time(58_719_498).saturating_mul(v.into())) - // Standard Error: 178_122 - .saturating_add(Weight::from_ref_time(13_273_555).saturating_mul(n.into())) + // Estimated: `523708 + v * (15469 ±41) + n * (12348 ±4)` + // Minimum execution time: 577_839 nanoseconds. + Weight::from_ref_time(587_063_000) + .saturating_add(Weight::from_proof_size(523708)) + // Standard Error: 2_193_535 + .saturating_add(Weight::from_ref_time(72_675_973).saturating_mul(v.into())) + // Standard Error: 218_573 + .saturating_add(Weight::from_ref_time(19_272_321).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_proof_size(16743).saturating_mul(v.into())) - .saturating_add(Weight::from_proof_size(12947).saturating_mul(n.into())) + .saturating_add(Weight::from_proof_size(15469).saturating_mul(v.into())) + .saturating_add(Weight::from_proof_size(12348).saturating_mul(n.into())) } /// Storage: VoterList CounterForListNodes (r:1 w:0) /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) @@ -1359,14 +1429,15 @@ impl WeightInfo for () { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3167 + v * (459 ±0) + n * (1007 ±0)` + // Measured: `3125 + v * (459 ±0) + n * (1007 ±0)` // Estimated: `511899 + v * (14295 ±0) + n * (11775 ±0)` - // Minimum execution time: 23_373_467 nanoseconds. - Weight::from_parts(23_497_257_000, 511899) - // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(3_434_000).saturating_mul(v.into())) - // Standard Error: 299_205 - .saturating_add(Weight::from_ref_time(2_568_954).saturating_mul(n.into())) + // Minimum execution time: 38_014_741 nanoseconds. + Weight::from_ref_time(39_016_264_000) + .saturating_add(Weight::from_proof_size(511899)) + // Standard Error: 409_554 + .saturating_add(Weight::from_ref_time(4_585_268).saturating_mul(v.into())) + // Standard Error: 409_554 + .saturating_add(Weight::from_ref_time(5_027_951).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1381,12 +1452,13 @@ impl WeightInfo for () { /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `983 + v * (50 ±0)` + // Measured: `979 + v * (50 ±0)` // Estimated: `3019 + v * (2520 ±0)` - // Minimum execution time: 3_882_120 nanoseconds. - Weight::from_parts(3_951_993_000, 3019) - // Standard Error: 46_729 - .saturating_add(Weight::from_ref_time(2_856_043).saturating_mul(v.into())) + // Minimum execution time: 4_283_114 nanoseconds. + Weight::from_ref_time(4_393_678_000) + .saturating_add(Weight::from_proof_size(3019)) + // Standard Error: 49_897 + .saturating_add(Weight::from_ref_time(3_190_372).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_proof_size(2520).saturating_mul(v.into())) @@ -1407,8 +1479,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_427 nanoseconds. - Weight::from_ref_time(8_794_000) + // Minimum execution time: 7_398 nanoseconds. + Weight::from_ref_time(7_711_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking MinCommission (r:0 w:1) @@ -1427,8 +1500,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_620 nanoseconds. - Weight::from_ref_time(7_901_000) + // Minimum execution time: 6_598 nanoseconds. + Weight::from_ref_time(6_970_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: Staking Ledger (r:1 w:0) @@ -1453,10 +1527,11 @@ impl WeightInfo for () { /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `2031` + // Measured: `1933` // Estimated: `19438` - // Minimum execution time: 66_188 nanoseconds. - Weight::from_parts(66_767_000, 19438) + // Minimum execution time: 72_003 nanoseconds. + Weight::from_ref_time(73_575_000) + .saturating_add(Weight::from_proof_size(19438)) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1466,10 +1541,11 @@ impl WeightInfo for () { /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `694` + // Measured: `691` // Estimated: `3019` - // Minimum execution time: 14_703 nanoseconds. - Weight::from_parts(15_031_000, 3019) + // Minimum execution time: 13_540 nanoseconds. + Weight::from_ref_time(14_129_000) + .saturating_add(Weight::from_proof_size(3019)) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1479,8 +1555,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_518 nanoseconds. - Weight::from_ref_time(4_656_000) + // Minimum execution time: 3_511 nanoseconds. + Weight::from_ref_time(3_633_000) + .saturating_add(Weight::from_proof_size(0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/primitives/staking/src/lib.rs b/primitives/staking/src/lib.rs index a8d8e6a602c94..cadddbbc8f4a0 100644 --- a/primitives/staking/src/lib.rs +++ b/primitives/staking/src/lib.rs @@ -31,6 +31,9 @@ pub type SessionIndex = u32; /// Counter for the number of eras that have passed. pub type EraIndex = u32; +/// Counter for paged storage items. +pub type PageIndex = u32; + /// Trait describing something that implements a hook for any operations to perform when a staker is /// slashed. pub trait OnStakerSlash {