use super::*;
use crate::session_rotation::{EraElectionPlanner, Eras};
use frame_support::assert_ok;
use sp_npos_elections::Support;
use substrate_test_utils::assert_eq_uvec;
use crate::tests::session_mock::ReceivedValidatorSets;
#[test]
fn planning_era_offset_less_0() {
ExtBuilder::default()
.session_per_era(6)
.planning_era_offset(0)
.no_flush_events()
.build_and_execute(|| {
assert_eq!(Session::current_index(), 8);
assert_eq!(active_era(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 6, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 7, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 20000, remainder: 20000 },
Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 }
]
);
Session::roll_until_active_era(2);
assert_eq!(Session::current_index(), 16);
assert_eq!(active_era(), 2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 12, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 13, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 14, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 15, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 20000, remainder: 20000 },
Event::SessionRotated { starting_session: 16, active_era: 2, planned_era: 2 }
]
);
});
}
#[test]
fn planning_era_offset_works_1() {
ExtBuilder::default()
.session_per_era(6)
.planning_era_offset(1)
.no_flush_events()
.build_and_execute(|| {
assert_eq!(Session::current_index(), 7);
assert_eq!(active_era(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 6, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 17500, remainder: 17500 },
Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 }
]
);
Session::roll_until_active_era(2);
assert_eq!(Session::current_index(), 14);
assert_eq!(active_era(), 2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 12, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 13, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 17500, remainder: 17500 },
Event::SessionRotated { starting_session: 14, active_era: 2, planned_era: 2 }
]
);
});
}
#[test]
fn planning_era_offset_works_2() {
ExtBuilder::default()
.session_per_era(6)
.planning_era_offset(2)
.no_flush_events()
.build_and_execute(|| {
assert_eq!(Session::current_index(), 6);
assert_eq!(active_era(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 }
]
);
Session::roll_until_active_era(2);
assert_eq!(Session::current_index(), 12);
assert_eq!(active_era(), 2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 }
]
);
});
}
#[test]
fn planning_era_offset_works_smart() {
ExtBuilder::default()
.session_per_era(6)
.smart_era_planner()
.no_flush_events()
.build_and_execute(|| {
assert_eq!(Session::current_index(), 6);
assert_eq!(active_era(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 }
]
);
Session::roll_until_active_era(2);
assert_eq!(Session::current_index(), 12);
assert_eq!(active_era(), 2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 }
]
);
});
}
#[test]
fn planning_era_offset_works_smart_with_delay() {
ExtBuilder::default()
.session_per_era(6)
.election_delay(7)
.smart_era_planner()
.no_flush_events()
.build_and_execute(|| {
assert_eq!(Session::current_index(), 6);
assert_eq!(active_era(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 1 },
Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 }
]
);
Session::roll_until_active_era(2);
assert_eq!(Session::current_index(), 12);
assert_eq!(active_era(), 2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 2 },
Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 }
]
);
});
}
#[test]
fn new_era_elects_correct_number_of_validators() {
ExtBuilder::default().nominate(true).validator_count(1).build_and_execute(|| {
assert_eq!(ValidatorCount::<Test>::get(), 1);
assert_eq!(session_validators().len(), 1);
})
}
#[test]
fn less_than_needed_candidates_works() {
ExtBuilder::default().validator_count(4).nominate(false).build_and_execute(|| {
assert_eq_uvec!(Session::validators(), vec![31, 21, 11]);
Session::roll_until_active_era(2);
assert_eq_uvec!(Session::validators(), vec![31, 21, 11]);
assert!(ErasStakersPaged::<T>::iter_prefix_values((active_era(),))
.all(|exposure| exposure.others.is_empty()));
});
}
mod paged_exposures {
use crate::session_rotation::Rotator;
use super::*;
#[test]
fn can_page_exposure() {
let mut others: Vec<IndividualExposure<AccountId, Balance>> = vec![];
let mut total_stake: Balance = 0;
for i in 1..20 {
let individual_stake: Balance = 100 * i as Balance;
others.push(IndividualExposure { who: i, value: individual_stake });
total_stake += individual_stake;
}
let own_stake: Balance = 500;
total_stake += own_stake;
assert_eq!(total_stake, 19_500);
let exposure: Exposure<AccountId, Balance> =
Exposure { total: total_stake, own: own_stake, others };
let (exposure_metadata, exposure_page): (
PagedExposureMetadata<Balance>,
Vec<ExposurePage<AccountId, Balance>>,
) = exposure.clone().into_pages(3);
assert_eq!(exposure_page.len(), 7);
assert_eq!(exposure_metadata.page_count, 7);
assert!(matches!(exposure_page[0], ExposurePage { page_total: 600, .. }));
assert!(matches!(exposure_page[1], ExposurePage { page_total: 1500, .. }));
assert_eq!(exposure_metadata.total, 19_500);
assert_eq!(
exposure_page.iter().map(|a| a.page_total).reduce(|a, b| a + b).unwrap(),
19_500 - exposure_metadata.own
);
assert_eq!(exposure_metadata.own, 500);
assert_eq!(exposure_page.iter().map(|a| a.others.len()).reduce(|a, b| a + b).unwrap(), 19);
assert_eq!(exposure_metadata.nominator_count, 19);
}
fn exposure_pages(era: u32, who: AccountId) -> Vec<(u32, (Balance, u32))> {
let mut res = ErasStakersPaged::<T>::iter_prefix((era, who))
.map(|(page, expo)| (page, (expo.page_total, expo.others.len() as u32)))
.collect::<Vec<_>>();
res.sort_by_key(|(page, _)| *page);
res
}
#[test]
fn store_stakers_info_elect_works_page_size_1() {
ExtBuilder::default().exposures_page_size(1).build_and_execute(|| {
Session::roll_to_next_session();
assert_eq!(Rotator::<T>::planned_era(), 2);
assert_eq!(Rotator::<T>::active_era(), 1);
assert_eq!(ErasTotalStake::<T>::get(Rotator::<T>::planned_era()), 0);
assert_eq!(
ErasStakersOverview::<T>::iter_prefix(Rotator::<T>::planned_era()).count(),
0
);
assert_eq!(
ErasStakersPaged::<T>::iter_prefix((Rotator::<T>::planned_era(),)).count(),
0
);
let exposures_page1 = bounded_vec![
(
1,
Exposure::<AccountId, Balance> {
total: 1000 + 250,
own: 1000,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
2,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
3,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
5,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
];
let exposure_page2: BoundedExposuresOf<Test> = bounded_vec![
(
1,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
2,
Exposure::<AccountId, Balance> {
total: 1000 + 250,
own: 1000,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
3,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
4,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
];
let current_era = current_era();
assert_eq!(ErasTotalStake::<T>::get(current_era), 0);
assert_eq!(
EraElectionPlanner::<T>::store_stakers_info(exposures_page1, current_era).to_vec(),
vec![1, 2, 3, 5]
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &1).unwrap(),
PagedExposureMetadata { total: 1250, own: 1000, nominator_count: 1, page_count: 1 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &2).unwrap(),
PagedExposureMetadata { total: 250, own: 0, nominator_count: 1, page_count: 1 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &3).unwrap(),
PagedExposureMetadata { total: 250, own: 0, nominator_count: 1, page_count: 1 },
);
assert!(ErasStakersOverview::<T>::get(current_era, &4).is_none());
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &5).unwrap(),
PagedExposureMetadata { total: 250, own: 0, nominator_count: 1, page_count: 1 },
);
assert_eq!(ErasTotalStake::<T>::get(current_era), 2000);
assert_eq!(exposure_pages(current_era, 1), vec![(0, (250, 1))]);
assert_eq!(exposure_pages(current_era, 2), vec![(0, (250, 1))]);
assert_eq!(exposure_pages(current_era, 3), vec![(0, (250, 1))]);
assert_eq!(exposure_pages(current_era, 4), vec![]);
assert_eq!(exposure_pages(current_era, 5), vec![(0, (250, 1))]);
assert_eq!(
EraElectionPlanner::<T>::store_stakers_info(exposure_page2, current_era).to_vec(),
vec![1, 2, 3, 4]
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &1).unwrap(),
PagedExposureMetadata { total: 1500, own: 1000, nominator_count: 2, page_count: 2 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &2).unwrap(),
PagedExposureMetadata { total: 1500, own: 1000, nominator_count: 2, page_count: 2 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &3).unwrap(),
PagedExposureMetadata { total: 500, own: 0, nominator_count: 2, page_count: 2 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &4).unwrap(),
PagedExposureMetadata { total: 250, own: 0, nominator_count: 1, page_count: 1 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &5).unwrap(),
PagedExposureMetadata { total: 250, own: 0, nominator_count: 1, page_count: 1 },
);
assert_eq!(ErasTotalStake::<T>::get(current_era), 4000);
assert_eq!(exposure_pages(current_era, 1), vec![(0, (250, 1)), (1, (250, 1))]);
assert_eq!(exposure_pages(current_era, 2), vec![(0, (250, 1)), (1, (250, 1))]);
assert_eq!(exposure_pages(current_era, 3), vec![(0, (250, 1)), (1, (250, 1))]);
assert_eq!(exposure_pages(current_era, 4), vec![(0, (250, 1))]);
assert_eq!(exposure_pages(current_era, 5), vec![(0, (250, 1))]);
})
}
#[test]
fn store_stakers_info_elect_works_page_size_2() {
ExtBuilder::default().exposures_page_size(2).build_and_execute(|| {
Session::roll_to_next_session();
assert_eq!(Rotator::<T>::planned_era(), 2);
assert_eq!(Rotator::<T>::active_era(), 1);
assert_eq!(ErasTotalStake::<T>::get(Rotator::<T>::planned_era()), 0);
assert_eq!(
ErasStakersOverview::<T>::iter_prefix(Rotator::<T>::planned_era()).count(),
0
);
assert_eq!(
ErasStakersPaged::<T>::iter_prefix((Rotator::<T>::planned_era(),)).count(),
0
);
let exposures_page1 = bounded_vec![
(
1,
Exposure::<AccountId, Balance> {
total: 1000 + 750,
own: 1000,
others: vec![
IndividualExposure { who: 101, value: 250 },
IndividualExposure { who: 102, value: 250 },
IndividualExposure { who: 103, value: 250 },
]
}
),
(
2,
Exposure::<AccountId, Balance> {
total: 500,
own: 0,
others: vec![
IndividualExposure { who: 101, value: 250 },
IndividualExposure { who: 102, value: 250 },
]
}
),
(
3,
Exposure::<AccountId, Balance> {
total: 750,
own: 0,
others: vec![
IndividualExposure { who: 101, value: 250 },
IndividualExposure { who: 102, value: 250 },
IndividualExposure { who: 103, value: 250 }
]
}
),
(
5,
Exposure::<AccountId, Balance> {
total: 500,
own: 0,
others: vec![
IndividualExposure { who: 101, value: 250 },
IndividualExposure { who: 102, value: 250 },
]
}
),
];
let exposure_page2: BoundedExposuresOf<Test> = bounded_vec![
(
1,
Exposure::<AccountId, Balance> {
total: 500,
own: 0,
others: vec![
IndividualExposure { who: 104, value: 250 },
IndividualExposure { who: 105, value: 250 }
]
}
),
(
2,
Exposure::<AccountId, Balance> {
total: 1000 + 750,
own: 1000,
others: vec![
IndividualExposure { who: 103, value: 250 },
IndividualExposure { who: 104, value: 250 },
IndividualExposure { who: 105, value: 250 },
]
}
),
(
3,
Exposure::<AccountId, Balance> {
total: 250,
own: 0,
others: vec![IndividualExposure { who: 103, value: 250 },]
}
),
(
4,
Exposure::<AccountId, Balance> {
total: 750,
own: 0,
others: vec![
IndividualExposure { who: 101, value: 250 },
IndividualExposure { who: 102, value: 250 },
IndividualExposure { who: 103, value: 250 }
]
}
),
];
let current_era = current_era();
assert_eq!(
EraElectionPlanner::<T>::store_stakers_info(exposures_page1, current_era).to_vec(),
vec![1, 2, 3, 5]
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &1).unwrap(),
PagedExposureMetadata { total: 1750, own: 1000, nominator_count: 3, page_count: 2 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &2).unwrap(),
PagedExposureMetadata { total: 500, own: 0, nominator_count: 2, page_count: 1 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &3).unwrap(),
PagedExposureMetadata { total: 750, own: 0, nominator_count: 3, page_count: 2 },
);
assert!(ErasStakersOverview::<T>::get(current_era, &4).is_none());
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &5).unwrap(),
PagedExposureMetadata { total: 500, own: 0, nominator_count: 2, page_count: 1 },
);
assert_eq!(ErasTotalStake::<T>::get(current_era), 3500);
assert_eq!(exposure_pages(current_era, 1), vec![(0, (500, 2)), (1, (250, 1))]);
assert_eq!(exposure_pages(current_era, 2), vec![(0, (500, 2))]);
assert_eq!(exposure_pages(current_era, 3), vec![(0, (500, 2)), (1, (250, 1))]);
assert_eq!(exposure_pages(current_era, 4), vec![]);
assert_eq!(exposure_pages(current_era, 5), vec![(0, (500, 2))]);
assert_eq!(
EraElectionPlanner::<T>::store_stakers_info(exposure_page2, current_era).to_vec(),
vec![1, 2, 3, 4]
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &1).unwrap(),
PagedExposureMetadata { total: 2250, own: 1000, nominator_count: 5, page_count: 3 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &2).unwrap(),
PagedExposureMetadata { total: 2250, own: 1000, nominator_count: 5, page_count: 3 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &3).unwrap(),
PagedExposureMetadata { total: 1000, own: 0, nominator_count: 4, page_count: 2 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &4).unwrap(),
PagedExposureMetadata { total: 750, own: 0, nominator_count: 3, page_count: 2 },
);
assert_eq!(
ErasStakersOverview::<T>::get(current_era, &5).unwrap(),
PagedExposureMetadata { total: 500, own: 0, nominator_count: 2, page_count: 1 },
);
assert_eq!(ErasTotalStake::<T>::get(current_era), 6750);
assert_eq!(
exposure_pages(current_era, 1),
vec![(0, (500, 2)), (1, (500, 2)), (2, (250, 1))]
);
assert_eq!(
exposure_pages(current_era, 2),
vec![(0, (500, 2)), (1, (500, 2)), (2, (250, 1))]
);
assert_eq!(exposure_pages(current_era, 3), vec![(0, (500, 2)), (1, (500, 2))]);
assert_eq!(exposure_pages(current_era, 4), vec![(0, (500, 2)), (1, (250, 1))]);
assert_eq!(exposure_pages(current_era, 5), vec![(0, (500, 2))]);
})
}
}
mod electable_stashes {
use super::*;
#[test]
fn add_electable_stashes_work() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
MaxValidatorSet::set(5);
assert_eq!(MaxValidatorSet::get(), 5);
assert!(ElectableStashes::<Test>::get().is_empty());
assert_ok!(EraElectionPlanner::<T>::add_electables(vec![1u64, 2, 3].into_iter()));
assert_eq!(
ElectableStashes::<Test>::get().into_inner().into_iter().collect::<Vec<_>>(),
vec![1, 2, 3]
);
assert_ok!(EraElectionPlanner::<T>::add_electables(vec![1u64, 2, 4].into_iter()));
assert_eq!(
ElectableStashes::<Test>::get().into_inner().into_iter().collect::<Vec<_>>(),
vec![1, 2, 3, 4]
);
})
}
#[test]
fn add_electable_stashes_overflow_works() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
MaxValidatorSet::set(5);
assert_eq!(MaxValidatorSet::get(), 5);
assert!(ElectableStashes::<Test>::get().is_empty());
let expected_idx_not_included = 5; assert_eq!(
EraElectionPlanner::<T>::add_electables(
vec![1u64, 2, 3, 4, 5, 6, 7, 8].into_iter()
),
Err(expected_idx_not_included)
);
assert_eq!(
ElectableStashes::<Test>::get().into_inner().into_iter().collect::<Vec<_>>(),
vec![1, 2, 3, 4, 5]
);
})
}
#[test]
fn overflow_electable_stashes_no_exposures_work() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
MaxValidatorSet::set(2);
assert!(ElectableStashes::<Test>::get().is_empty());
let supports = to_bounded_supports(vec![
(1, Support { total: 100, voters: vec![(10, 1_000)] }),
(2, Support { total: 200, voters: vec![(20, 2_000)] }),
(3, Support { total: 300, voters: vec![(30, 3_000)] }),
(4, Support { total: 400, voters: vec![(40, 4_000)] }),
]);
let expected_not_included = 2;
assert_eq!(
EraElectionPlanner::<T>::do_elect_paged_inner(supports),
Err(expected_not_included)
);
assert_eq!(ElectableStashes::<Test>::get().into_iter().collect::<Vec<_>>(), vec![1, 2]);
let exposure_exists = |acc, era| Eras::<Test>::get_full_exposure(era, &acc).total != 0;
assert!(exposure_exists(1, 1));
assert!(exposure_exists(2, 1));
assert!(!exposure_exists(3, 1));
assert!(!exposure_exists(4, 1));
})
}
}
mod paged_on_initialize_era_election_planner {
use pallet_staking_async_rc_client::ValidatorSetReport;
use super::*;
#[test]
fn single_page_election_works() {
ExtBuilder::default()
.validator_count(3)
.build_and_execute(|| {
let pages: BlockNumber = EraElectionPlanner::<T>::election_pages().into();
assert_eq!(pages, 1);
assert_eq!(System::block_number(), 15);
assert_eq!(PlanningEraOffset::get(), 2);
assert_eq!(current_era(), 1);
assert_eq_uvec!(Session::validators(), vec![11, 21, 31]);
assert_ok!(Staking::force_unstake(RuntimeOrigin::root(), 31, 0));
let expected_elected = vec![11, 21];
ValidatorCount::<Test>::set(expected_elected.len() as u32);
Session::roll_until(20);
assert_eq!(NextElectionPage::<Test>::get(), None);
assert!(ElectableStashes::<Test>::get().is_empty());
assert_eq!(VoterSnapshotStatus::<Test>::get(), SnapshotStatus::Waiting);
Session::roll_next();
assert_eq!(NextElectionPage::<Test>::get(), None);
assert!(ElectableStashes::<Test>::get().is_empty());
assert_eq!(
ReceivedValidatorSets::get_last(),
ValidatorSetReport {
id: 2,
leftover: false,
new_validator_set: vec![11, 21],
prune_up_to: None
}
);
assert_eq!(VoterSnapshotStatus::<Test>::get(), SnapshotStatus::Waiting);
assert_eq!(current_era(), 2);
assert_eq!(active_era(), 1);
assert_eq_uvec!(
era_exposures(1),
vec![
(
11,
Exposure {
total: 1250,
own: 1000 as Balance,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
21,
Exposure {
total: 1250,
own: 1000 as Balance,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(31, Exposure { total: 500, own: 500 as Balance, others: vec![] }),
]
);
assert_eq_uvec!(
era_exposures(2),
vec![
(
11,
Exposure {
total: 1250,
own: 1000 as Balance,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(
21,
Exposure {
total: 1250,
own: 1000 as Balance,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
]
);
assert_eq_uvec!(Session::validators(), vec![11, 21, 31]);
Session::roll_until_active_era(2);
assert_eq_uvec!(Session::validators(), vec![11, 21]);
})
}
#[test]
fn multi_page_election_works() {
ExtBuilder::default()
.add_staker(61, 1000, StakerStatus::Validator)
.add_staker(71, 1000, StakerStatus::Validator)
.add_staker(81, 1000, StakerStatus::Validator)
.add_staker(91, 1000, StakerStatus::Validator)
.multi_page_election_provider(3)
.validator_count(6)
.election_bounds(3, 10)
.build_and_execute(|| {
type A = AccountId;
type B = Balance;
assert_eq!(System::block_number(), 15);
assert_eq!(PlanningEraOffset::get(), 2);
Session::roll_until(20);
assert_eq!(
staking_events_since_last_call(),
vec![Event::SessionRotated {
starting_session: 4,
active_era: 1,
planned_era: 2
}]
);
assert_eq!(NextElectionPage::<Test>::get(), None);
assert_eq!(VoterSnapshotStatus::<Test>::get(), SnapshotStatus::Waiting);
assert!(ElectableStashes::<Test>::get().is_empty());
Session::roll_until(21);
assert_eq!(NextElectionPage::<Test>::get(), Some(1));
assert_eq!(VoterSnapshotStatus::<Test>::get(), SnapshotStatus::Ongoing(31));
assert_eq!(
ElectableStashes::<Test>::get().into_iter().collect::<Vec<AccountId>>(),
vec![11, 21, 31]
);
assert_eq_uvec!(
era_exposures(2),
vec![
(11, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(21, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(31, Exposure::<A, B> { total: 500, own: 500, others: vec![] }),
]
);
Session::roll_until(22);
assert_eq_uvec!(
ElectableStashes::<Test>::get().into_iter().collect::<Vec<_>>(),
vec![11, 21, 31, 61, 71, 81]
);
assert_eq!(NextElectionPage::<Test>::get(), Some(0));
assert_eq!(VoterSnapshotStatus::<Test>::get(), SnapshotStatus::Ongoing(81));
assert_eq_uvec!(
era_exposures(2),
vec![
(31, Exposure::<A, B> { total: 500, own: 500, others: vec![] }),
(21, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(81, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(71, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(11, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(61, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] })
]
);
Session::roll_until(23);
assert!(ElectableStashes::<Test>::get().is_empty());
assert_eq!(VoterSnapshotStatus::<Test>::get(), SnapshotStatus::Waiting);
assert_eq!(NextElectionPage::<Test>::get(), None);
assert_eq_uvec!(
era_exposures(2),
vec![
(31, Exposure::<A, B> { total: 500, own: 500, others: vec![] }),
(
21,
Exposure::<A, B> {
total: 1250,
own: 1000,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(81, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(71, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(91, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] }),
(
11,
Exposure::<A, B> {
total: 1250,
own: 1000,
others: vec![IndividualExposure { who: 101, value: 250 }]
}
),
(61, Exposure::<A, B> { total: 1000, own: 1000, others: vec![] })
]
);
assert_eq!(
ReceivedValidatorSets::get_last(),
ValidatorSetReport {
id: 2,
leftover: false,
new_validator_set: vec![11, 21, 31, 61, 71, 81, 91],
prune_up_to: None
}
);
assert_eq!(NextElectionPage::<Test>::get(), None);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::PagedElectionProceeded { page: 2, result: Ok(3) },
Event::PagedElectionProceeded { page: 1, result: Ok(3) },
Event::PagedElectionProceeded { page: 0, result: Ok(1) }
]
);
Session::roll_until_active_era(2);
assert_eq_uvec!(Session::validators(), vec![11, 21, 31, 61, 71, 81, 91]);
})
}
}