@@ -127,16 +127,19 @@ impl<T: Config> Eras<T> {
127127
128128 /// Get exposure for a validator at a given era and page.
129129 ///
130+ /// This is mainly used for rewards and slashing. Validator's self-stake is only returned in
131+ /// page 0.
132+ ///
130133 /// This builds a paged exposure from `PagedExposureMetadata` and `ExposurePage` of the
131- /// validator. For older non-paged exposure, it returns the clipped exposure directly.
134+ /// validator.
132135 pub ( crate ) fn get_paged_exposure (
133136 era : EraIndex ,
134137 validator : & T :: AccountId ,
135138 page : Page ,
136139 ) -> Option < PagedExposure < T :: AccountId , BalanceOf < T > > > {
137140 let overview = <ErasStakersOverview < T > >:: get ( & era, validator) ?;
138141
139- // validator stake is added only in page zero
142+ // validator stake is added only in page zero.
140143 let validator_stake = if page == 0 { overview. own } else { Zero :: zero ( ) } ;
141144
142145 // since overview is present, paged exposure will always be present except when a
@@ -230,57 +233,97 @@ impl<T: Config> Eras<T> {
230233 mut exposure : Exposure < T :: AccountId , BalanceOf < T > > ,
231234 ) {
232235 let page_size = T :: MaxExposurePageSize :: get ( ) . defensive_max ( 1 ) ;
236+ if cfg ! ( debug_assertions) && cfg ! ( not( feature = "runtime-benchmarks" ) ) {
237+ // sanitize the exposure in case some test data from this pallet is wrong.
238+ // ignore benchmarks as other pallets might do weird things.
239+ let expected_total = exposure
240+ . others
241+ . iter ( )
242+ . map ( |ie| ie. value )
243+ . fold :: < BalanceOf < T > , _ > ( Default :: default ( ) , |acc, x| acc + x)
244+ . saturating_add ( exposure. own ) ;
245+ debug_assert_eq ! ( expected_total, exposure. total, "exposure total must equal own + sum(others) for (era: {:?}, validator: {:?}, exposure: {:?})" , era, validator, exposure) ;
246+ }
233247
234- if let Some ( stored_overview ) = ErasStakersOverview :: < T > :: get ( era, & validator) {
235- let last_page_idx = stored_overview . page_count . saturating_sub ( 1 ) ;
236-
248+ if let Some ( overview ) = ErasStakersOverview :: < T > :: get ( era, & validator) {
249+ // collect some info from the un-touched overview for later use.
250+ let last_page_idx = overview . page_count . saturating_sub ( 1 ) ;
237251 let mut last_page =
238252 ErasStakersPaged :: < T > :: get ( ( era, validator, last_page_idx) ) . unwrap_or_default ( ) ;
239253 let last_page_empty_slots =
240254 T :: MaxExposurePageSize :: get ( ) . saturating_sub ( last_page. others . len ( ) as u32 ) ;
241255
242- // splits the exposure so that `exposures_append` will fit within the last exposure
243- // page, up to the max exposure page size. The remaining individual exposures in
244- // `exposure` will be added to new pages.
245- let exposures_append = exposure. split_others ( last_page_empty_slots) ;
246-
247- ErasStakersOverview :: < T > :: mutate ( era, & validator, |stored| {
248- // new metadata is updated based on 3 different set of exposures: the
249- // current one, the exposure split to be "fitted" into the current last page and
250- // the exposure set that will be appended from the new page onwards.
251- let new_metadata =
252- stored. defensive_unwrap_or_default ( ) . update_with :: < T :: MaxExposurePageSize > (
253- [ & exposures_append, & exposure]
254- . iter ( )
255- . fold ( Default :: default ( ) , |total, expo| {
256- total. saturating_add ( expo. total . saturating_sub ( expo. own ) )
257- } ) ,
258- [ & exposures_append, & exposure]
259- . iter ( )
260- . fold ( Default :: default ( ) , |count, expo| {
261- count. saturating_add ( expo. others . len ( ) as u32 )
262- } ) ,
256+ // update nominator-count, page-count, and total stake in overview (done in
257+ // `update_with`).
258+ let new_stake_added = exposure. total ;
259+ let new_nominators_added = exposure. others . len ( ) as u32 ;
260+ let mut updated_overview = overview
261+ . update_with :: < T :: MaxExposurePageSize > ( new_stake_added, new_nominators_added) ;
262+
263+ // update own stake, if applicable.
264+ match ( updated_overview. own . is_zero ( ) , exposure. own . is_zero ( ) ) {
265+ ( true , false ) => {
266+ // first time we see own exposure -- good.
267+ // note: `total` is already updated above.
268+ updated_overview. own = exposure. own ;
269+ } ,
270+ ( true , true ) | ( false , true ) => {
271+ // no new own exposure is added, nothing to do
272+ } ,
273+ ( false , false ) => {
274+ debug_assert ! (
275+ false ,
276+ "validator own stake already set in overview for (era: {:?}, validator: {:?}, current overview: {:?}, new exposure: {:?})" ,
277+ era,
278+ validator,
279+ updated_overview,
280+ exposure,
263281 ) ;
264- * stored = new_metadata. into ( ) ;
265- } ) ;
282+ defensive ! ( "duplicate validator self stake in election" ) ;
283+ } ,
284+ } ;
285+
286+ ErasStakersOverview :: < T > :: insert ( era, & validator, updated_overview) ;
287+ // we are done updating the overview now, `updated_overview` should not be used anymore.
288+ // We've updated:
289+ // * nominator count
290+ // * total stake
291+ // * own stake (if applicable)
292+ // * page count
293+ //
294+ // next step:
295+ // * new-keys or updates in `ErasStakersPaged`
296+ //
297+ // we don't need the information about own stake anymore -- drop it.
298+ exposure. total = exposure. total . saturating_sub ( exposure. own ) ;
299+ exposure. own = Zero :: zero ( ) ;
300+
301+ // splits the exposure so that `append_to_last_page` will fit within the last exposure
302+ // page, up to the max exposure page size. The remaining individual exposures in
303+ // `put_in_new_pages` will be added to new pages.
304+ let append_to_last_page = exposure. split_others ( last_page_empty_slots) ;
305+ let put_in_new_pages = exposure;
306+
307+ // handle last page first.
266308
267309 // fill up last page with exposures.
268- last_page. page_total = last_page
269- . page_total
270- . saturating_add ( exposures_append. total )
271- . saturating_sub ( exposures_append. own ) ;
272- last_page. others . extend ( exposures_append. others ) ;
310+ last_page. page_total = last_page. page_total . saturating_add ( append_to_last_page. total ) ;
311+ last_page. others . extend ( append_to_last_page. others ) ;
273312 ErasStakersPaged :: < T > :: insert ( ( era, & validator, last_page_idx) , last_page) ;
274313
275314 // now handle the remaining exposures and append the exposure pages. The metadata update
276315 // has been already handled above.
277- let ( _, exposure_pages) = exposure. into_pages ( page_size) ;
278-
279- exposure_pages. into_iter ( ) . enumerate ( ) . for_each ( |( idx, paged_exposure) | {
280- let append_at =
281- ( last_page_idx. saturating_add ( 1 ) . saturating_add ( idx as u32 ) ) as Page ;
282- <ErasStakersPaged < T > >:: insert ( ( era, & validator, append_at) , paged_exposure) ;
283- } ) ;
316+ let ( _unused_metadata, put_in_new_pages_chunks) =
317+ put_in_new_pages. into_pages ( page_size) ;
318+
319+ put_in_new_pages_chunks
320+ . into_iter ( )
321+ . enumerate ( )
322+ . for_each ( |( idx, paged_exposure) | {
323+ let append_at =
324+ ( last_page_idx. saturating_add ( 1 ) . saturating_add ( idx as u32 ) ) as Page ;
325+ <ErasStakersPaged < T > >:: insert ( ( era, & validator, append_at) , paged_exposure) ;
326+ } ) ;
284327 } else {
285328 // expected page count is the number of nominators divided by the page size, rounded up.
286329 let expected_page_count = exposure
@@ -1003,16 +1046,17 @@ impl<T: Config> EraElectionPlanner<T> {
10031046 exposures. into_iter ( ) . for_each ( |( stash, exposure) | {
10041047 log ! (
10051048 trace,
1006- "stored exposure for stash {:?} and {:?} backers" ,
1049+ "storing exposure for stash {:?} with {:?} own-stake and {:?} backers" ,
10071050 stash,
1051+ exposure. own,
10081052 exposure. others. len( )
10091053 ) ;
10101054 // build elected stash.
10111055 elected_stashes_page. push ( stash. clone ( ) ) ;
1012- // accumulate total stake.
1056+ // accumulate total stake and backer count for bookkeeping .
10131057 total_stake_page = total_stake_page. saturating_add ( exposure. total ) ;
1014- // set or update staker exposure for this era.
10151058 total_backers += exposure. others . len ( ) as u32 ;
1059+ // set or update staker exposure for this era.
10161060 Eras :: < T > :: upsert_exposure ( new_planned_era, & stash, exposure) ;
10171061 } ) ;
10181062
@@ -1025,6 +1069,8 @@ impl<T: Config> EraElectionPlanner<T> {
10251069 Eras :: < T > :: add_total_stake ( new_planned_era, total_stake_page) ;
10261070
10271071 // collect or update the pref of all winners.
1072+ // TODO: rather inefficient, we can do this once at the last page across all entries in
1073+ // `ElectableStashes`.
10281074 for stash in & elected_stashes {
10291075 let pref = Validators :: < T > :: get ( stash) ;
10301076 Eras :: < T > :: set_validator_prefs ( new_planned_era, stash, pref) ;
0 commit comments