# UNDF: UNDF-2026-000000303 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -2081,12 +2081,21 @@ impl sp_staking::StakingInterface for Pallet { fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { - // look in the non paged exposures - // FIXME: Can be cleaned up once non paged exposures are cleared (https://github.com/paritytech/polkadot-sdk/issues/433) - ErasStakers::::iter_prefix(era).any(|(validator, exposures)| { - validator == *who || exposures.others.iter().any(|i| i.who == *who) - }) - || - // look in the paged exposures - ErasStakersPaged::::iter_prefix((era,)).any(|((validator, _), exposure_page)| { - validator == *who || exposure_page.others.iter().any(|i| i.who == *who) - }) + // Fast path: check if `who` was an active validator in this era. + // ErasValidatorPrefs is keyed (era, validator) — O(1) storage read. + if ErasValidatorPrefs::::contains_key(era, who) { + return true; + } + + // Nominator path: still requires scanning exposure pages. + // FIXME: A nominator→validator reverse index would make this O(1). + // See https://github.com/paritytech/polkadot-sdk/issues/433 + // + // look in the non-paged exposures (legacy) + ErasStakers::::iter_prefix(era).any(|(_, exposures)| { + exposures.others.iter().any(|i| i.who == *who) + }) + || + // look in the paged exposures + ErasStakersPaged::::iter_prefix((era,)).any(|(_, exposure_page)| { + exposure_page.others.iter().any(|i| i.who == *who) + }) }