diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index b3f8c18f704c..c12ba964b441 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -1436,21 +1436,26 @@ pub mod pallet { Error::::TooManyTargets ); + let mut targets = targets + .into_iter() + .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from)) + .collect::, _>>()?; + + targets.sort(); + targets.dedup(); + let old = Nominators::::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner()); let targets: BoundedVec<_, _> = targets .into_iter() - .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from)) .map(|n| { - n.and_then(|n| { - if old.contains(&n) || !Validators::::get(&n).blocked { - Ok(n) - } else { - Err(Error::::BadTarget.into()) - } - }) + if old.contains(&n) || !Validators::::get(&n).blocked { + Ok(n) + } else { + Err(Error::::BadTarget.into()) + } }) - .collect::, _>>()? + .collect::, DispatchError>>()? .try_into() .map_err(|_| Error::::TooManyNominators)?; diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 6c2335e1aac8..2531cd232954 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -776,6 +776,31 @@ fn nominators_also_get_slashed_pro_rata() { }); } +#[test] +fn nominate_same_account_should_be_filtered() { + ExtBuilder::default().build_and_execute(|| { + let duplicated_targets = vec![11, 21, 31, 11, 21, 31, 11, 11, 21, 21, 31, 31]; + + assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), duplicated_targets.clone())); + assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21, 31]); + + // Make sure it can correct the wrong automatically. + Nominators::::insert( + 101, + Nominations { + targets: BoundedVec::truncate_from(duplicated_targets.clone()), + submitted_in: Default::default(), + suppressed: Default::default(), + }, + ); + assert_eq!(Staking::nominators(101).unwrap().targets, duplicated_targets); + + assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), duplicated_targets)); + assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21, 31]); + }); +} + #[test] fn double_staking_should_fail() { // should test (in the same order):