Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

[NPoS] Unlimited number of nominators eligible for Rewards payout #13498

Open
wants to merge 118 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
15805ce
rewards payout in multi block fashion
Ank4n Mar 1, 2023
bb6a79b
use MaxExposurePageSize from staking
Ank4n Mar 1, 2023
1e40f4d
add max exposure page count to staking interface
Ank4n Mar 1, 2023
ac43c26
fmt
Ank4n Mar 1, 2023
09e9147
fix unimplemented function
Ank4n Mar 1, 2023
1eab166
feature gate
Ank4n Mar 1, 2023
bb60f7b
rewards payout in multi block fashion
Ank4n Mar 1, 2023
1c88aa2
use MaxExposurePageSize from staking
Ank4n Mar 1, 2023
98550eb
add max exposure page count to staking interface
Ank4n Mar 1, 2023
fb80ae2
fmt
Ank4n Mar 1, 2023
494bbde
fix unimplemented function
Ank4n Mar 1, 2023
f2d06ea
feature gate
Ank4n Mar 1, 2023
5141b67
add staking api
Ank4n Mar 2, 2023
f1b90a8
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n Mar 13, 2023
7d0c66b
Merge branch 'ankan/paged-rewards-staking-api' into ankan/paged-rewar…
Ank4n Mar 13, 2023
6308d43
fmt
Ank4n Mar 13, 2023
69737f0
update weights
Ank4n Mar 13, 2023
26a2e5b
fix imports
Ank4n Mar 13, 2023
cd01ee0
fix docs
Ank4n Mar 13, 2023
4e30f22
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n Mar 18, 2023
3e2fed1
fix merge
Ank4n Mar 18, 2023
06d6bfe
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
Mar 20, 2023
10b8b34
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
Mar 26, 2023
6ea4f57
resolve pr comments
Ank4n Mar 26, 2023
d824e92
keep the order of staking interface impl
Ank4n Mar 26, 2023
6b6a319
move Exposure, ExposurePage, ExposureOverview and IndividualExposure …
Ank4n Mar 26, 2023
910dfe3
remove dependency to stakign for staking runtime api
Ank4n Mar 26, 2023
b0c7c36
initialise size of others while prepping pages
Ank4n Mar 26, 2023
832353f
imrpovements
Ank4n Mar 26, 2023
7dfadbc
fix code
Ank4n Mar 26, 2023
2ba18e5
link migration changelog
Ank4n Mar 26, 2023
9c5d405
remove unused import
Ank4n Mar 26, 2023
8abc250
fix staking tests
Ank4n Mar 26, 2023
f497d19
fmt
Ank4n Mar 26, 2023
70425f8
Apply suggestions from code review
Ank4n Mar 30, 2023
a0f64c4
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
Mar 30, 2023
c9b5c4c
move EraInfo to lib
Ank4n Mar 30, 2023
2354bd7
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
Apr 10, 2023
f12a75a
fix docs
Ank4n Apr 10, 2023
ae28d84
expect instead of unwrap
Ank4n Apr 10, 2023
f76dbcf
remove useless code
Ank4n Apr 10, 2023
f4dd55d
remove deprecated weight doc
Ank4n Apr 10, 2023
d99769d
clip required page count
Ank4n Apr 10, 2023
d939789
defensively assert
Ank4n Apr 10, 2023
b2eed7b
doc wrapping fix
Ank4n Apr 10, 2023
b776386
explain why we return one page when no nominator pages exist
Ank4n Apr 10, 2023
5f82274
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
Apr 16, 2023
3ccb0af
eras stakers might be an unsafe runtime api, should evaluate separate…
Ank4n Apr 16, 2023
2865046
remove eras stakers runtime api from node runtime
Ank4n Apr 16, 2023
8c013e7
rename Era info functions
Ank4n Apr 16, 2023
809b47e
fmt
Ank4n Apr 16, 2023
2a14298
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n May 18, 2023
fe09ecd
fix tests
Ank4n May 18, 2023
2023135
fixes
Ank4n May 18, 2023
32a507e
".git/.scripts/commands/fmt/fmt.sh"
May 18, 2023
6b5fca1
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
May 20, 2023
945f64d
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n May 31, 2023
fa02b72
add back missed config while resolving conflict
Ank4n May 31, 2023
a4884b2
not needed anymore
Ank4n May 31, 2023
84368c5
fix migration
Ank4n May 31, 2023
77fece6
Merge remote-tracking branch 'origin/master' into ankan/paged-rewards…
Jun 19, 2023
2fa9227
Update frame/staking/runtime-api/src/lib.rs
Ank4n Jun 20, 2023
98ffc9f
Update frame/staking/src/pallet/mod.rs
Ank4n Jun 20, 2023
9275f3e
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n Jun 22, 2023
559e1e2
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n Jul 9, 2023
d73c7a1
cargo fmt
Ank4n Jul 9, 2023
dd70800
rename page count api
Ank4n Jul 9, 2023
746049e
make exposure page count and page size u16
Ank4n Jul 9, 2023
1a4963b
Apply suggestions from code review
Ank4n Jul 9, 2023
916dde2
wrap to 100 letters
Ank4n Jul 9, 2023
53ef105
Update frame/staking/src/lib.rs
Ank4n Jul 9, 2023
62835ef
rustdoc for Individual exposure
Ank4n Jul 9, 2023
0d7b4a8
rename ExposureOverview to more explicit PagedExposureMetadata
Ank4n Jul 9, 2023
1efa770
don't repeat docs
Ank4n Jul 9, 2023
ef07f43
rename the exposure_overfiew field
Ank4n Jul 9, 2023
60785b7
rename ExposureExt
Ank4n Jul 9, 2023
99d595f
fix benchmark
Ank4n Jul 9, 2023
3bd8768
".git/.scripts/commands/fmt/fmt.sh"
Jul 9, 2023
12a7566
Trigger Build
Ank4n Jul 9, 2023
fcd336f
rename test fn to set max page count
Ank4n Jul 9, 2023
12af748
revert changing types of exposure page size and count back to u32
Ank4n Jul 9, 2023
338f69a
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n Aug 10, 2023
ac63a5f
unused import
Ank4n Aug 11, 2023
954a3a6
".git/.scripts/commands/fmt/fmt.sh"
Aug 11, 2023
38827d0
Pallets: Treasury deprecate `propose_spend` dispatchable (#14538)
muharem Aug 10, 2023
a1a06b5
Sync NFT metadata limits with AssetHub values (#14748)
jsidorenko Aug 11, 2023
307cec8
sc-cli: add no-beefy flag to cli config (#14754)
acatangiu Aug 11, 2023
0875d0b
sc-consensus-beefy: fix initialization when BEEFY genesis state unava…
acatangiu Aug 11, 2023
776adab
make `BagsList::put_in_front_of` be permissionless (#14714)
kianenigma Aug 12, 2023
ef8962a
[contracts] Derive useful traits for public types (#14723)
pmikolajczyk41 Aug 14, 2023
8b6ae95
add `frame_system::DefaultConfig` to individual pallet `DefaultConfig…
kianenigma Aug 14, 2023
3451985
Fixes CI (#14763)
gupnik Aug 14, 2023
e47ecde
fix: node cli docs (#14718)
muse254 Aug 14, 2023
df1f1ae
[fix lint warnings: Uniques pallet] fix clippy::missing_errors_doc li…
Aug 14, 2023
829f0d7
Add `try_state` check to Pallet `MessageQueue` (#13502)
gitofdeepanshu Aug 14, 2023
02c8757
Remove missing sentence (#14769)
naterarmstrong Aug 15, 2023
bd55f14
[fix lint warnings: NFTs pallet] fix clippy::missing_errors_doc lint …
Aug 15, 2023
981de06
chainHead: Limit ongoing operations (#14699)
lexnv Aug 15, 2023
de0528b
Update Scheduler Pallet Documentation (#14740)
snowmead Aug 15, 2023
28afcb8
Revert "sc-cli: add no-beefy flag to cli config (#14754)" (#14766)
acatangiu Aug 15, 2023
f6b8cbd
Add `--workspace` to clippy (#14772)
alvicsam Aug 15, 2023
4051fde
Make peer evictions less aggressive (#14619)
altonen Aug 15, 2023
cbf6e1c
`cargo clippy +nightly --fix` run on downstream node template (#14693)
nuke-web3 Aug 16, 2023
c40cf54
Improve `storage_alias` and make `UnlockAndUnreserveAllFunds` indepen…
bkchr Aug 16, 2023
d6f9693
[fix lint warnings: NFTs pallet] fix clippy::missing_docs_in_private_…
Aug 16, 2023
7674d26
Update wasm-opt to 0.114 (#14695)
brson Aug 16, 2023
7071c60
Revert "chore: update libp2p to 0.52.1 (#14429)" (#14722)
altonen Aug 16, 2023
3ddf325
Free standing `elections-phragmen` and `tips` Gov V1 unlock/unreserve…
liamaharon Aug 16, 2023
81c7234
Disarm `OnRuntimeUpgrade::pre/post_upgrade` `Tuple` footgun (#14759)
liamaharon Aug 16, 2023
5f07634
deprecate `try-runtime` subcommand and direct users to standalone cli…
liamaharon Aug 17, 2023
858140e
Runtime: avoid duplication and test all signature (#14663)
ashWhiteHat Aug 17, 2023
29f800b
Set `StateBackend::Transaction` to `PrefixedMemoryDB` (#14612)
bkchr Aug 17, 2023
e1bbd95
remove page count requirement
Ank4n Aug 20, 2023
4e0c8b6
Merge branch 'master' into ankan/paged-rewards-rebased2
Ank4n Aug 20, 2023
79e3fb0
paging is always enabled
Ank4n Aug 20, 2023
4d51ae0
".git/.scripts/commands/fmt/fmt.sh"
Aug 20, 2023
989b391
no clipping or sorting of nominators
Ank4n Aug 20, 2023
ec0694b
small edits to the changelog
Ank4n Aug 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ impl pallet_babe::Config for Runtime {
type DisabledValidators = Session;
type WeightInfo = ();
type MaxAuthorities = MaxAuthorities;
type MaxNominators = MaxNominatorRewardedPerValidator;
type MaxNominators = MaxExposurePageSize;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for weight calculation for reporting equivocation. The older configuration (changed here) was incorrect since for slashing, the max nominators exposed could be larger than MaxNominatorRewardedPerValidator . MaxNominatorRewardedPerValidator is only used to limit nominators that can be rewarded but nominators eligible for slashing (per validator) is unbounded (though there is a hard count for all nominators => 22500).

Not sure the correct way to solve this and is most probably out of scope of this PR. Setting this to MaxExposurePageSize is strictly same as before (though now its more apparent that this configuration is wrong).

type KeyOwnerProof =
<Historical as KeyOwnerProofSystem<(KeyTypeId, pallet_babe::AuthorityId)>>::Proof;
type EquivocationReportSystem =
Expand Down Expand Up @@ -558,8 +558,6 @@ parameter_types! {
pub const SlashDeferDuration: sp_staking::EraIndex = 24 * 7; // 1/4 the bonding duration.
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxExposurePageSize: u32 = 256;
pub const MaxExposurePageCount: u32 = 1;
pub const MaxNominatorRewardedPerValidator: u32 = MaxExposurePageSize::get() * MaxExposurePageCount::get();
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub OffchainRepeat: BlockNumber = 5;
pub HistoryDepth: u32 = 84;
Expand Down Expand Up @@ -595,7 +593,6 @@ impl pallet_staking::Config for Runtime {
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxExposurePageSize = ConstU32<256>;
type MaxExposurePageCount = ConstU32<1>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down Expand Up @@ -1368,7 +1365,7 @@ impl pallet_grandpa::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type MaxAuthorities = MaxAuthorities;
type MaxNominators = MaxNominatorRewardedPerValidator;
type MaxNominators = MaxExposurePageSize;
type MaxSetIdSessionEntries = MaxSetIdSessionEntries;
type KeyOwnerProof = <Historical as KeyOwnerProofSystem<(KeyTypeId, GrandpaId)>>::Proof;
type EquivocationReportSystem =
Expand Down
1 change: 0 additions & 1 deletion frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ impl pallet_staking::Config for Test {
type UnixTime = pallet_timestamp::Pallet<Test>;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<64>;
type MaxExposurePageCount = ConstU32<1>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
1 change: 0 additions & 1 deletion frame/beefy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ impl pallet_staking::Config for Test {
type UnixTime = pallet_timestamp::Pallet<Test>;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<64>;
type MaxExposurePageCount = ConstU32<1>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,6 @@ impl pallet_staking::Config for Runtime {
type EraPayout = ();
type NextNewSession = Session;
type MaxExposurePageSize = ConstU32<256>;
type MaxExposurePageCount = ConstU32<1>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
1 change: 0 additions & 1 deletion frame/fast-unstake/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ impl pallet_staking::Config for Runtime {
type NextNewSession = ();
type HistoryDepth = ConstU32<84>;
type MaxExposurePageSize = ConstU32<64>;
type MaxExposurePageCount = ConstU32<1>;
type OffendingValidatorsThreshold = ();
type ElectionProvider = MockElection;
type GenesisElectionProvider = Self::ElectionProvider;
Expand Down
1 change: 0 additions & 1 deletion frame/grandpa/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ impl pallet_staking::Config for Test {
type UnixTime = pallet_timestamp::Pallet<Test>;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<64>;
type MaxExposurePageCount = ConstU32<1>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
1 change: 0 additions & 1 deletion frame/nomination-pools/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = ();
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = ();
type MaxExposurePageCount = ConstU32<1>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = ();
type ElectionProvider =
Expand Down
1 change: 0 additions & 1 deletion frame/nomination-pools/test-staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = ();
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = ();
type MaxExposurePageCount = ConstU32<1>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = ();
type ElectionProvider =
Expand Down
1 change: 0 additions & 1 deletion frame/offences/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ impl pallet_staking::Config for Test {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxExposurePageCount = ConstU32<1>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = ();
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
1 change: 0 additions & 1 deletion frame/root-offences/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ impl pallet_staking::Config for Test {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxExposurePageCount = ConstU32<1>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
1 change: 0 additions & 1 deletion frame/session/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ impl pallet_staking::Config for Test {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxExposurePageCount = ConstU32<1>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = ();
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand Down
8 changes: 4 additions & 4 deletions frame/staking/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

All notable changes to this project will be documented in this file.
All notable changes and migrations to pallet-staking 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
Expand All @@ -19,9 +19,9 @@ migrations.

### Deprecated

- `ErasStakersClipped` is deprecated and may be removed after 84 eras.
- `ErasStakers` is deprecated and may be removed after 84 eras.
- `ErasStakers` and `ErasStakersClipped` is deprecated, will not be used any longer for the exposures of the new era
post v14 and can be removed after 84 eras once all the exposures are stale.
- Field `claimed_rewards` in item `Ledger` is renamed
to `legacy_claimed_rewards` and may be removed after 84 eras.
to `legacy_claimed_rewards` and can be removed after 84 eras.

[v14]: https://github.com/paritytech/substrate/pull/13498
32 changes: 8 additions & 24 deletions frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,11 @@
//! [`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
//! [`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 up to a maximum of
//! [`Config::MaxExposurePageCount`] pages. To pay out all nominators, `payout_stakers` must be
//! called once for each available page. In a scenario where the number of nominators N exceed M,
//! where M = [`Config::MaxExposurePageSize`] * [`Config::MaxExposurePageCount`], only the 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.
//! number of nominators exceeds [`Config::MaxExposurePageSize`], then the exposed nominators are
//! stored in multiple pages, with each page containing up to
//! [`Config::MaxExposurePageSize`] nominators. To pay out all nominators, `payout_stakers` must be
//! called once for each available page. 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
Expand Down Expand Up @@ -1169,28 +1167,14 @@ impl<T: Config> EraInfo<T> {
exposure: Exposure<T::AccountId, BalanceOf<T>>,
) {
let page_size = T::MaxExposurePageSize::get().defensive_max(1);
let max_page_count = T::MaxExposurePageCount::get();

let nominator_count = exposure.others.len();
let mut required_page_count =
// expected page count is the number of nominators divided by the page size, rounded up.
let expected_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 {
required_page_count = max_page_count as usize;
// 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_metadata, exposure_pages) = exposure.into_pages(page_size);
defensive_assert!(exposure_pages.len() == required_page_count, "unexpected page count");
defensive_assert!(exposure_pages.len() == expected_page_count, "unexpected page count");

<ErasStakersOverview<T>>::insert(era, &validator, &exposure_metadata);
exposure_pages.iter().enumerate().for_each(|(page, paged_exposure)| {
Ank4n marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
6 changes: 0 additions & 6 deletions frame/staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ parameter_types! {
pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS;
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 MaxWinners: u32 = 100;
Expand Down Expand Up @@ -302,7 +301,6 @@ impl crate::pallet::pallet::Config for Test {
type EraPayout = ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxExposurePageSize = MaxExposurePageSize;
type MaxExposurePageCount = MaxExposurePageCount;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = Self::ElectionProvider;
Expand Down Expand Up @@ -837,7 +835,3 @@ pub(crate) fn staking_events_since_last_call() -> Vec<crate::Event<Test>> {
pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) {
(Balances::free_balance(who), Balances::reserved_balance(who))
}

pub(crate) fn set_max_exposure_page_count(max_pages: PageIndex) {
MaxExposurePageCount::set(max_pages.max(1));
}
9 changes: 0 additions & 9 deletions frame/staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,6 @@ pub mod pallet {
#[pallet::constant]
type MaxExposurePageSize: Get<u32>;

/// 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 MaxExposurePageCount: Get<u32>;

/// The fraction of the validator set that is safe to be offending.
/// After the threshold is reached a new era will be forced.
type OffendingValidatorsThreshold: Get<Perbill>;
Expand Down
94 changes: 25 additions & 69 deletions frame/staking/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3692,9 +3692,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.
fn test_nominators_over_max_exposure_page_size_are_rewarded() {
ExtBuilder::default().build_and_execute(|| {
// bond one nominator more than the max exposure page size to validator 11.
for i in 0..=MaxExposurePageSize::get() {
let stash = 10_000 + i as AccountId;
let balance = 10_000 + i as Balance;
Expand All @@ -3715,25 +3715,24 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward(
mock::start_active_era(2);
mock::make_all_reward_payment(1);

// Assert only nominators from 1 to Max are rewarded
for i in 0..=MaxExposurePageSize::get() {
// Assert nominators from 1 to Max are rewarded
let mut i: u32 = 0;
while i < MaxExposurePageSize::get() {
let stash = 10_000 + i as AccountId;
let balance = 10_000 + i as Balance;
if stash == 10_000 {
assert!(Balances::free_balance(&stash) == balance);
} else {
assert!(Balances::free_balance(&stash) > balance);
}
assert!(Balances::free_balance(&stash) > balance);
i += 1;
}

// Assert overflowing nominators from page 1 are also rewarded
let stash = 10_000 + i as AccountId;
assert!(Balances::free_balance(&stash) > (10_000 + i) as Balance);
});
}

#[test]
fn test_nominators_are_rewarded_for_all_exposure_page() {
ExtBuilder::default().build_and_execute(|| {
// enable multi paged rewards payout
set_max_exposure_page_count(3);

// 3 pages of exposure
let nominator_count = 2 * MaxExposurePageSize::get() + 1;

Expand Down Expand Up @@ -3780,9 +3779,6 @@ fn test_nominators_are_rewarded_for_all_exposure_page() {
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
set_max_exposure_page_count(10);

let balance = 1000;
// Track the exposure of the validator and all nominators.
let mut total_exposure = balance;
Expand Down Expand Up @@ -3977,9 +3973,6 @@ fn test_multi_page_payout_stakers_by_page() {
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
set_max_exposure_page_count(10);

let balance = 1000;
// Track the exposure of the validator and all nominators.
let mut total_exposure = balance;
Expand Down Expand Up @@ -4226,69 +4219,38 @@ fn test_page_count_and_size() {

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::<Test>::get_page_count(1, &11), 1);
let exposure = EraInfo::<Test>::get_paged_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
set_max_exposure_page_count(10);
mock::start_active_era(2);
// Since max exposure page size is 64, 2 pages of nominators are created.
assert_eq!(EraInfo::<Test>::get_page_count(1, &11), 2);

assert_eq!(EraInfo::<Test>::get_page_count(2, &11), 2);
// first page has 64 nominators
assert_eq!(EraInfo::<Test>::get_paged_exposure(2, &11, 0).unwrap().others().len(), 64);
assert_eq!(EraInfo::<Test>::get_paged_exposure(1, &11, 0).unwrap().others().len(), 64);
// second page has 36 nominators
assert_eq!(EraInfo::<Test>::get_paged_exposure(2, &11, 1).unwrap().others().len(), 36);
assert_eq!(EraInfo::<Test>::get_paged_exposure(1, &11, 1).unwrap().others().len(), 36);

// now lets decrease page size
MaxExposurePageSize::set(32);
mock::start_active_era(3);
mock::start_active_era(2);
// now we expect 4 pages.
assert_eq!(EraInfo::<Test>::get_page_count(3, &11), 4);
assert_eq!(EraInfo::<Test>::get_page_count(2, &11), 4);
// first 3 pages have 32 nominators each
assert_eq!(EraInfo::<Test>::get_paged_exposure(3, &11, 0).unwrap().others().len(), 32);
assert_eq!(EraInfo::<Test>::get_paged_exposure(3, &11, 1).unwrap().others().len(), 32);
assert_eq!(EraInfo::<Test>::get_paged_exposure(3, &11, 2).unwrap().others().len(), 32);
assert_eq!(EraInfo::<Test>::get_paged_exposure(3, &11, 3).unwrap().others().len(), 4);
assert_eq!(EraInfo::<Test>::get_paged_exposure(2, &11, 0).unwrap().others().len(), 32);
assert_eq!(EraInfo::<Test>::get_paged_exposure(2, &11, 1).unwrap().others().len(), 32);
assert_eq!(EraInfo::<Test>::get_paged_exposure(2, &11, 2).unwrap().others().len(), 32);
assert_eq!(EraInfo::<Test>::get_paged_exposure(2, &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::<Test>::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::<Test>::get_paged_exposure(4, &11, page).unwrap();
exposure.others().iter().for_each(|e| {
assert!(e.value < previous_nominator_balance);
previous_nominator_balance = e.value;
});
}
MaxExposurePageSize::set(5);
mock::start_active_era(3);

// the last nominator balance is higher than the last 10 clipped nominators.
assert_eq!(previous_nominator_balance, 1000 + 10);
// now we expect the max 20 pages (100/5).
assert_eq!(EraInfo::<Test>::get_page_count(3, &11), 20);
});
}

#[test]
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
set_max_exposure_page_count(2);

// Consumed weight for all payout_stakers dispatches that fail
let err_weight = <Test as Config>::WeightInfo::payout_stakers_alive_staked(0);

Expand Down Expand Up @@ -4421,9 +4383,6 @@ fn payout_stakers_handles_basic_errors() {
#[test]
fn test_commission_paid_across_pages() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
// enable multi paged rewards payout
set_max_exposure_page_count(4);

let balance = 1;
let commission = 50;
// Create a validator:
Expand Down Expand Up @@ -6617,9 +6576,6 @@ fn test_legacy_claimed_rewards_is_checked_at_reward_payout() {
#[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
set_max_exposure_page_count(2);

// case 1: exposure exist in clipped.
// set page cap to 10
MaxExposurePageSize::set(10);
Expand Down