Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions prdoc/pr_11647.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
title: 'Fix try-state warning for LastValidatorEra in staking-async'
doc:
- audience: Runtime Dev
description: |-
Fixes a false try-state warning where `LastValidatorEra` was flagged as incorrect for active
validators. After the election for the next era completes but before that era becomes active,
`LastValidatorEra` is correctly set to `active_era + 1`. The previous check only accepted
`active_era`, causing spurious warnings.

Adds a test verifying `LastValidatorEra` transitions from `active_era` to `active_era + 1`
once the next era's election results are stored.

crates:
- name: pallet-staking-async
bump: patch
12 changes: 7 additions & 5 deletions substrate/frame/staking-async/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2011,14 +2011,16 @@ impl<T: Config> Pallet<T> {
let Some(era) = ActiveEra::<T>::get().map(|a| a.index) else { return Ok(()) };
let overview_and_pages = ErasStakersOverview::<T>::iter_prefix(era)
.map(|(validator, metadata)| {
// ensure `LastValidatorEra` is correctly set
if LastValidatorEra::<T>::get(&validator) != Some(era) {
let last_validator_era = LastValidatorEra::<T>::get(&validator).unwrap_or_default();
// If election for next era is finished, last_validator_era is set to next era.
if last_validator_era != era && last_validator_era != era + 1 {
log!(
warn,
"Validator {:?} has incorrect LastValidatorEra (expected {:?}, got {:?})",
error,
"Validator {:?} has incorrect LastValidatorEra (expected {:?} or {:?}, got {:?})",
validator,
era,
LastValidatorEra::<T>::get(&validator)
era + 1,
last_validator_era
);
}

Expand Down
30 changes: 30 additions & 0 deletions substrate/frame/staking-async/src/tests/try_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,36 @@ fn try_state_bad_exposure() {
});
}

#[test]
fn last_validator_era_can_be_one_greater_than_active_era() {
// When the election for the next era has finished but the era is not yet active,
// `LastValidatorEra` is set to `active_era + 1`.
ExtBuilder::default().try_state(false).build_and_execute(|| {
Session::roll_until_active_era(1);
let era = active_era();

// Before election, `LastValidatorEra` equals the active era.
for (validator, _) in ErasStakersOverview::<T>::iter_prefix(era) {
assert_eq!(LastValidatorEra::<T>::get(&validator), Some(era));
}

// Roll session by session until the election for the next era has been stored, i.e.
// `ErasStakersOverview` for the next era is populated.
while ErasStakersOverview::<T>::iter_prefix(era + 1).next().is_none() {
Session::roll_to_next_session();
}

// Election for era 2 is stored but era 2 is not yet active.
assert_eq!(active_era(), 1);
assert_eq!(current_era(), 2);

// After election, `LastValidatorEra` is now 1 greater than active era.
for (validator, _) in ErasStakersOverview::<T>::iter_prefix(era) {
assert_eq!(LastValidatorEra::<T>::get(&validator), Some(era + 1));
}
});
}

#[test]
fn try_state_bad_eras_total_stake() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
Expand Down
Loading