Skip to content
tests.rs 133 KiB
Newer Older
		let exposure = Staking::eras_stakers(active_era(), 11);
		assert_eq!(Balances::free_balance(101), 2000);
			&[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }],
		assert_eq!(Balances::free_balance(11), 1000);
		assert_eq!(Balances::free_balance(101), 2000);

		// This is the best way to check that the validator was chilled; `get` will
		// return default value.
		for (stash, _) in <Staking as Store>::Validators::iter() {
			assert!(stash != 11);
		}

		let nominations = <Staking as Store>::Nominators::get(&101).unwrap();

		// and make sure that the vote will not be ignored, because the slash was
		// zero.
		let last_slash = <Staking as Store>::SlashingSpans::get(&11).unwrap().last_nonzero_slash();
		assert!(nominations.submitted_in >= last_slash);
	});
}
Gavin Wood's avatar
Gavin Wood committed

#[test]
fn six_session_delay() {
	ExtBuilder::default().initialize_first_session(false).build_and_execute(|| {
Gavin Wood's avatar
Gavin Wood committed
		use pallet_session::SessionManager;

		let val_set = Session::validators();
		let init_session = Session::current_index();
		let init_active_era = active_era();
Gavin Wood's avatar
Gavin Wood committed
		// pallet-session is delaying session by one, thus the next session to plan is +2.
		assert_eq!(<Staking as SessionManager<_>>::new_session(init_session + 2), None);
		assert_eq!(
			<Staking as SessionManager<_>>::new_session(init_session + 3),
			Some(val_set.clone())
		);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq!(<Staking as SessionManager<_>>::new_session(init_session + 4), None);
		assert_eq!(<Staking as SessionManager<_>>::new_session(init_session + 5), None);
		assert_eq!(
			<Staking as SessionManager<_>>::new_session(init_session + 6),
			Some(val_set.clone())
		);
Gavin Wood's avatar
Gavin Wood committed

		<Staking as SessionManager<_>>::end_session(init_session);
		<Staking as SessionManager<_>>::start_session(init_session + 1);
		assert_eq!(active_era(), init_active_era);

Gavin Wood's avatar
Gavin Wood committed
		<Staking as SessionManager<_>>::end_session(init_session + 1);
		<Staking as SessionManager<_>>::start_session(init_session + 2);
		assert_eq!(active_era(), init_active_era);
Gavin Wood's avatar
Gavin Wood committed

		// Reward current era
		Staking::reward_by_ids(vec![(11, 1)]);

		// New active era is triggered here.
		<Staking as SessionManager<_>>::end_session(init_session + 2);
		<Staking as SessionManager<_>>::start_session(init_session + 3);
		assert_eq!(active_era(), init_active_era + 1);

Gavin Wood's avatar
Gavin Wood committed
		<Staking as SessionManager<_>>::end_session(init_session + 3);
		<Staking as SessionManager<_>>::start_session(init_session + 4);
		assert_eq!(active_era(), init_active_era + 1);

Gavin Wood's avatar
Gavin Wood committed
		<Staking as SessionManager<_>>::end_session(init_session + 4);
		<Staking as SessionManager<_>>::start_session(init_session + 5);
		assert_eq!(active_era(), init_active_era + 1);
Gavin Wood's avatar
Gavin Wood committed

		// Reward current era
		Staking::reward_by_ids(vec![(21, 2)]);

		// New active era is triggered here.
		<Staking as SessionManager<_>>::end_session(init_session + 5);
		<Staking as SessionManager<_>>::start_session(init_session + 6);
		assert_eq!(active_era(), init_active_era + 2);
Gavin Wood's avatar
Gavin Wood committed

		// That reward are correct
		assert_eq!(Staking::eras_reward_points(init_active_era).total, 1);
		assert_eq!(Staking::eras_reward_points(init_active_era + 1).total, 2);
	});
}

#[test]
fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward() {
	ExtBuilder::default().build_and_execute(|| {
		for i in 0..=<Test as Config>::MaxNominatorRewardedPerValidator::get() {
			let stash = 10_000 + i as AccountId;
			let controller = 20_000 + i as AccountId;
			let balance = 10_000 + i as Balance;
Gavin Wood's avatar
Gavin Wood committed
			Balances::make_free_balance_be(&stash, balance);
			assert_ok!(Staking::bond(
				Origin::signed(stash),
				controller,
				balance,
				RewardDestination::Stash
			));
Gavin Wood's avatar
Gavin Wood committed
			assert_ok!(Staking::nominate(Origin::signed(controller), vec![11]));
		}
Gavin Wood's avatar
Gavin Wood committed

		<Pallet<Test>>::reward_by_ids(vec![(11, 1)]);
		// compute and ensure the reward amount is greater than zero.
		let _ = current_total_payout_for_duration(reward_time_per_era());
Gavin Wood's avatar
Gavin Wood committed

Gavin Wood's avatar
Gavin Wood committed
		mock::make_all_reward_payment(1);

		// Assert only nominators from 1 to Max are rewarded
		for i in 0..=<Test as Config>::MaxNominatorRewardedPerValidator::get() {
			let stash = 10_000 + i as AccountId;
			let balance = 10_000 + i as Balance;
Gavin Wood's avatar
Gavin Wood committed
			if stash == 10_000 {
				assert!(Balances::free_balance(&stash) == balance);
			} else {
				assert!(Balances::free_balance(&stash) > balance);
			}
		}
	});
}

#[test]
fn set_history_depth_works() {
	ExtBuilder::default().build_and_execute(|| {
		Staking::set_history_depth(Origin::root(), 20, 0).unwrap();
Gavin Wood's avatar
Gavin Wood committed
		assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
		assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
		Staking::set_history_depth(Origin::root(), 4, 0).unwrap();
Gavin Wood's avatar
Gavin Wood committed
		assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
		assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
		Staking::set_history_depth(Origin::root(), 3, 0).unwrap();
Gavin Wood's avatar
Gavin Wood committed
		assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
		assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
		Staking::set_history_depth(Origin::root(), 8, 0).unwrap();
Gavin Wood's avatar
Gavin Wood committed
		assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
		assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
	});
}
#[test]
fn test_payout_stakers() {
	// Here we will test validator can set `max_nominators_payout` and it works.
	// We also test that `payout_extra_nominators` works.
	ExtBuilder::default().has_stakers(false).build_and_execute(|| {
		let balance = 1000;
		// Create a validator:
		bond_validator(11, 10, balance); // Default(64)

		// Create nominators, targeting stash of validators
		for i in 0..100 {
			bond_nominator(1000 + i, 100 + i, balance + i as Balance, vec![11]);
		Staking::reward_by_ids(vec![(11, 1)]);

		// compute and ensure the reward amount is greater than zero.
		let _ = current_total_payout_for_duration(reward_time_per_era());

		mock::start_active_era(2);
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 1));

		// Top 64 nominators of validator 11 automatically paid out, including the validator
		// Validator payout goes to controller.
		assert!(Balances::free_balance(&10) > balance);
		for i in 36..100 {
			assert!(Balances::free_balance(&(100 + i)) > balance + i as Balance);
		}
		// The bottom 36 do not
		for i in 0..36 {
			assert_eq!(Balances::free_balance(&(100 + i)), balance + i as Balance);
		}

		// We track rewards in `claimed_rewards` vec
		assert_eq!(
			Staking::ledger(&10),
			Some(StakingLedger {
				stash: 11,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: vec![1]
			})
		);

		for i in 3..16 {
			Staking::reward_by_ids(vec![(11, 1)]);

			// compute and ensure the reward amount is greater than zero.
			let _ = current_total_payout_for_duration(reward_time_per_era());

			mock::start_active_era(i);
			assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, i - 1));
		}

		// We track rewards in `claimed_rewards` vec
		assert_eq!(
			Staking::ledger(&10),
			Some(StakingLedger {
				stash: 11,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: (1..=14).collect()
			})
		);

		for i in 16..100 {
			Staking::reward_by_ids(vec![(11, 1)]);
			// compute and ensure the reward amount is greater than zero.
			let _ = current_total_payout_for_duration(reward_time_per_era());
			mock::start_active_era(i);
		}

		// We clean it up as history passes
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 15));
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98));
		assert_eq!(
			Staking::ledger(&10),
			Some(StakingLedger {
				stash: 11,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: vec![15, 98]
			})
		);

		// Out of order claims works.
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 69));
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 23));
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 42));
		assert_eq!(
			Staking::ledger(&10),
			Some(StakingLedger {
				stash: 11,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: vec![15, 23, 42, 69, 98]
			})
		);
	});
}

#[test]
fn payout_stakers_handles_basic_errors() {
	// Here we will test payouts handle all errors.
	ExtBuilder::default().has_stakers(false).build_and_execute(|| {
		// Consumed weight for all payout_stakers dispatches that fail
		let err_weight = weights::SubstrateWeight::<Test>::payout_stakers_alive_staked(0);

		// Same setup as the test above
		let balance = 1000;
		bond_validator(11, 10, balance); // Default(64)

		// Create nominators, targeting stash
		for i in 0..100 {
			bond_nominator(1000 + i, 100 + i, balance + i as Balance, vec![11]);
		Staking::reward_by_ids(vec![(11, 1)]);

		// compute and ensure the reward amount is greater than zero.
		let _ = current_total_payout_for_duration(reward_time_per_era());

		mock::start_active_era(2);

		// Wrong Era, too big
		assert_noop!(
			Staking::payout_stakers(Origin::signed(1337), 11, 2),
			Error::<Test>::InvalidEraToReward.with_weight(err_weight)
		);
		// Wrong Staker
		assert_noop!(
			Staking::payout_stakers(Origin::signed(1337), 10, 1),
			Error::<Test>::NotStash.with_weight(err_weight)
		);

		for i in 3..100 {
			Staking::reward_by_ids(vec![(11, 1)]);
			// compute and ensure the reward amount is greater than zero.
			let _ = current_total_payout_for_duration(reward_time_per_era());
			mock::start_active_era(i);
		}
		// We are at era 99, with history depth of 84
		// We should be able to payout era 15 through 98 (84 total eras), but not 14 or 99.
		assert_noop!(
			Staking::payout_stakers(Origin::signed(1337), 11, 14),
			Error::<Test>::InvalidEraToReward.with_weight(err_weight)
		);
		assert_noop!(
			Staking::payout_stakers(Origin::signed(1337), 11, 99),
			Error::<Test>::InvalidEraToReward.with_weight(err_weight)
		);
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 15));
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98));

		// Can't claim again
		assert_noop!(
			Staking::payout_stakers(Origin::signed(1337), 11, 15),
			Error::<Test>::AlreadyClaimed.with_weight(err_weight)
		);
		assert_noop!(
			Staking::payout_stakers(Origin::signed(1337), 11, 98),
			Error::<Test>::AlreadyClaimed.with_weight(err_weight)
		);
	});
}

#[test]
fn payout_stakers_handles_weight_refund() {
	// Note: this test relies on the assumption that `payout_stakers_alive_staked` is solely used by
	// `payout_stakers` to calculate the weight of each payout op.
	ExtBuilder::default().has_stakers(false).build_and_execute(|| {
		let max_nom_rewarded = <Test as Config>::MaxNominatorRewardedPerValidator::get();
		// Make sure the configured value is meaningful for our use.
		assert!(max_nom_rewarded >= 4);
		let half_max_nom_rewarded = max_nom_rewarded / 2;
		// Sanity check our max and half max nominator quantities.
		assert!(half_max_nom_rewarded > 0);
		assert!(max_nom_rewarded > half_max_nom_rewarded);

		let max_nom_rewarded_weight =
			<Test as Config>::WeightInfo::payout_stakers_alive_staked(max_nom_rewarded);
		let half_max_nom_rewarded_weight =
			<Test as Config>::WeightInfo::payout_stakers_alive_staked(half_max_nom_rewarded);
		let zero_nom_payouts_weight = <Test as Config>::WeightInfo::payout_stakers_alive_staked(0);
		assert!(zero_nom_payouts_weight > 0);
		assert!(half_max_nom_rewarded_weight > zero_nom_payouts_weight);
		assert!(max_nom_rewarded_weight > half_max_nom_rewarded_weight);

		let balance = 1000;
		bond_validator(11, 10, balance);

		start_active_era(1);

		// Reward just the validator.
		Staking::reward_by_ids(vec![(11, 1)]);

		// Add some `half_max_nom_rewarded` nominators who will start backing the validator in the
		// next era.
		for i in 0..half_max_nom_rewarded {
			bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]);
		}

		start_active_era(2);

		// Collect payouts when there are no nominators
		let call =
			TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 1 });
		let info = call.get_dispatch_info();
		let result = call.dispatch(Origin::signed(20));
		assert_ok!(result);
		assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
		// The validator is not rewarded in this era; so there will be zero payouts to claim for
		// this era.
		start_active_era(3);

		// Collect payouts for an era where the validator did not receive any points.
		let call =
			TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 2 });
		let info = call.get_dispatch_info();
		let result = call.dispatch(Origin::signed(20));
		assert_ok!(result);
		assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);

		// Reward the validator and its nominators.
		Staking::reward_by_ids(vec![(11, 1)]);

		start_active_era(4);

		// Collect payouts when the validator has `half_max_nom_rewarded` nominators.
		let call =
			TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 3 });
		let info = call.get_dispatch_info();
		let result = call.dispatch(Origin::signed(20));
		assert_ok!(result);
		assert_eq!(extract_actual_weight(&result, &info), half_max_nom_rewarded_weight);

		// Add enough nominators so that we are at the limit. They will be active nominators
		// in the next era.
		for i in half_max_nom_rewarded..max_nom_rewarded {
			bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]);
		}

		start_active_era(5);
		// We now have `max_nom_rewarded` nominators actively nominating our validator.

		// Reward the validator so we can collect for everyone in the next era.
		Staking::reward_by_ids(vec![(11, 1)]);

		start_active_era(6);

		// Collect payouts when the validator had `half_max_nom_rewarded` nominators.
		let call =
			TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 });
		let info = call.get_dispatch_info();
		let result = call.dispatch(Origin::signed(20));
		assert_ok!(result);
		assert_eq!(extract_actual_weight(&result, &info), max_nom_rewarded_weight);

		// Try and collect payouts for an era that has already been collected.
		let call =
			TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 });
		let info = call.get_dispatch_info();
		let result = call.dispatch(Origin::signed(20));
		assert!(result.is_err());
		// When there is an error the consumed weight == weight when there are 0 nominator payouts.
		assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
	});
}

#[test]
fn bond_during_era_correctly_populates_claimed_rewards() {
	ExtBuilder::default().has_stakers(false).build_and_execute(|| {
		// Era = None
		bond_validator(9, 8, 1000);
		assert_eq!(
			Staking::ledger(&8),
			Some(StakingLedger {
				stash: 9,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: vec![],
			})
		);
		bond_validator(11, 10, 1000);
		assert_eq!(
			Staking::ledger(&10),
			Some(StakingLedger {
				stash: 11,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: (0..5).collect(),
			})
		);
		bond_validator(13, 12, 1000);
		assert_eq!(
			Staking::ledger(&12),
			Some(StakingLedger {
				stash: 13,
				total: 1000,
				active: 1000,
				unlocking: vec![],
				claimed_rewards: (15..99).collect(),
			})
		);
	});
}

#[test]
fn offences_weight_calculated_correctly() {
	ExtBuilder::default().nominate(true).build_and_execute(|| {
		// On offence with zero offenders: 4 Reads, 1 Write
		let zero_offence_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(4, 1);
		assert_eq!(Staking::on_offence(&[], &[Perbill::from_percent(50)], 0), zero_offence_weight);

		// On Offence with N offenders, Unapplied: 4 Reads, 1 Write + 4 Reads, 5 Writes
		let n_offence_unapplied_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(4, 1)
			+ <Test as frame_system::Config>::DbWeight::get().reads_writes(4, 5);
		let offenders: Vec<OffenceDetails<<Test as frame_system::Config>::AccountId, pallet_session::historical::IdentificationTuple<Test>>>
			= (1..10).map(|i|
				OffenceDetails {
					offender: (i, Staking::eras_stakers(active_era(), i)),
					reporters: vec![],
				}
			).collect();
		assert_eq!(Staking::on_offence(&offenders, &[Perbill::from_percent(50)], 0), n_offence_unapplied_weight);

		// On Offence with one offenders, Applied
		let one_offender = [
			OffenceDetails {
				offender: (11, Staking::eras_stakers(active_era(), 11)),
				reporters: vec![1],
			},
		];

		let n = 1; // Number of offenders
		let rw = 3 + 3 * n; // rw reads and writes
		let one_offence_unapplied_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(4, 1)
			+ <Test as frame_system::Config>::DbWeight::get().reads_writes(rw, rw)
			// One `slash_cost`
			+ <Test as frame_system::Config>::DbWeight::get().reads_writes(6, 5)
			// `slash_cost` * nominators (1)
			+ <Test as frame_system::Config>::DbWeight::get().reads_writes(6, 5)
			// `reward_cost` * reporters (1)
			+ <Test as frame_system::Config>::DbWeight::get().reads_writes(2, 2);
		assert_eq!(Staking::on_offence(&one_offender, &[Perbill::from_percent(50)], 0), one_offence_unapplied_weight);
#[test]
fn payout_creates_controller() {
	ExtBuilder::default().has_stakers(false).build_and_execute(|| {
		let balance = 1000;
		// Create a validator:
		bond_validator(11, 10, balance);

		// Create a stash/controller pair
		bond_nominator(1234, 1337, 100, vec![11]);

		// kill controller
		assert_ok!(Balances::transfer(Origin::signed(1337), 1234, 100));
		assert_eq!(Balances::free_balance(1337), 0);

		Staking::reward_by_ids(vec![(11, 1)]);
		// compute and ensure the reward amount is greater than zero.
		let _ = current_total_payout_for_duration(reward_time_per_era());
		mock::start_active_era(2);
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 1));

		// Controller is created
		assert!(Balances::free_balance(1337) > 0);
	})
}

#[test]
fn payout_to_any_account_works() {
	ExtBuilder::default().has_stakers(false).build_and_execute(|| {
		let balance = 1000;
		// Create a validator:
		bond_validator(11, 10, balance); // Default(64)

		// Create a stash/controller pair
		bond_nominator(1234, 1337, 100, vec![11]);

		// Update payout location
		assert_ok!(Staking::set_payee(Origin::signed(1337), RewardDestination::Account(42)));

		// Reward Destination account doesn't exist
		assert_eq!(Balances::free_balance(42), 0);

		Staking::reward_by_ids(vec![(11, 1)]);
		// compute and ensure the reward amount is greater than zero.
		let _ = current_total_payout_for_duration(reward_time_per_era());
		mock::start_active_era(2);
		assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 1));

		// Payment is successful
		assert!(Balances::free_balance(42) > 0);
	})
}
fn session_buffering_with_offset() {
	// similar to live-chains, have some offset for the first session
	ExtBuilder::default()
		.offset(2)
		.period(5)
		.session_per_era(5)
		.build_and_execute(|| {
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 0);

			start_session(1);
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 1);
			assert_eq!(System::block_number(), 2);

			start_session(2);
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 2);
			assert_eq!(System::block_number(), 7);

			start_session(3);
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 3);
			assert_eq!(System::block_number(), 12);

			// active era is lagging behind by one session, because of how session module works.
			start_session(4);
			assert_eq!(current_era(), 1);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 4);
			assert_eq!(System::block_number(), 17);

			start_session(5);
			assert_eq!(current_era(), 1);
			assert_eq!(active_era(), 1);
			assert_eq!(Session::current_index(), 5);
			assert_eq!(System::block_number(), 22);

			// go all the way to active 2.
			start_active_era(2);
			assert_eq!(current_era(), 2);
			assert_eq!(active_era(), 2);
			assert_eq!(Session::current_index(), 10);
		});
}

#[test]
fn session_buffering_no_offset() {
	// no offset, first session starts immediately
	ExtBuilder::default()
		.offset(0)
		.period(5)
		.session_per_era(5)
		.build_and_execute(|| {
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 0);

			start_session(1);
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 1);
			assert_eq!(System::block_number(), 5);

			start_session(2);
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 2);
			assert_eq!(System::block_number(), 10);

			start_session(3);
			assert_eq!(current_era(), 0);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 3);
			assert_eq!(System::block_number(), 15);

			// active era is lagging behind by one session, because of how session module works.
			start_session(4);
			assert_eq!(current_era(), 1);
			assert_eq!(active_era(), 0);
			assert_eq!(Session::current_index(), 4);
			assert_eq!(System::block_number(), 20);

			start_session(5);
			assert_eq!(current_era(), 1);
			assert_eq!(active_era(), 1);
			assert_eq!(Session::current_index(), 5);
			assert_eq!(System::block_number(), 25);

			// go all the way to active 2.
			start_active_era(2);
			assert_eq!(current_era(), 2);
			assert_eq!(active_era(), 2);
			assert_eq!(Session::current_index(), 10);
		});
}

#[test]
fn cannot_rebond_to_lower_than_ed() {
	ExtBuilder::default()
		.existential_deposit(10)
		.balance_factor(10)
		.build_and_execute(|| {
			// initial stuff.
			assert_eq!(
				Staking::ledger(&20).unwrap(),
				StakingLedger {
					stash: 21,
					total: 10 * 1000,
					active: 10 * 1000,
			// unbond all of it. must be chilled first.
			assert_ok!(Staking::chill(Origin::signed(20)));
			assert_ok!(Staking::unbond(Origin::signed(20), 10 * 1000));
			assert_eq!(
				Staking::ledger(&20).unwrap(),
				StakingLedger {
					stash: 21,
					total: 10 * 1000,
					unlocking: vec![UnlockChunk { value: 10 * 1000, era: 3 }],
					claimed_rewards: vec![]
				}
			);

			// now bond a wee bit more
			assert_noop!(Staking::rebond(Origin::signed(20), 5), Error::<Test>::InsufficientBond);
fn cannot_bond_extra_to_lower_than_ed() {
	ExtBuilder::default()
		.existential_deposit(10)
		.balance_factor(10)
		.build_and_execute(|| {
			// initial stuff.
			assert_eq!(
				Staking::ledger(&20).unwrap(),
				StakingLedger {
					stash: 21,
					total: 10 * 1000,
					active: 10 * 1000,
			// unbond all of it. must be chilled first.
			assert_ok!(Staking::chill(Origin::signed(20)));
			assert_ok!(Staking::unbond(Origin::signed(20), 10 * 1000));
			assert_eq!(
				Staking::ledger(&20).unwrap(),
				StakingLedger {
					stash: 21,
					total: 10 * 1000,
					unlocking: vec![UnlockChunk { value: 10 * 1000, era: 3 }],
					claimed_rewards: vec![]
				}
			);

			// now bond a wee bit more
			assert_noop!(
				Staking::bond_extra(Origin::signed(21), 5),
				Error::<Test>::InsufficientBond,

#[test]
fn do_not_die_when_active_is_ed() {
	let ed = 10;
	ExtBuilder::default()
		.existential_deposit(ed)
		.balance_factor(ed)
			assert_eq!(
				Staking::ledger(&20).unwrap(),
				StakingLedger {
					stash: 21,
					total: 1000 * ed,
					active: 1000 * ed,
					unlocking: vec![],
					claimed_rewards: vec![]
				}
			);

			// when unbond all of it except ed.
			assert_ok!(Staking::unbond(Origin::signed(20), 999 * ed));
			start_active_era(3);
			assert_ok!(Staking::withdraw_unbonded(Origin::signed(20), 100));

			assert_eq!(
				Staking::ledger(&20).unwrap(),
				StakingLedger {
					stash: 21,
					total: ed,
					active: ed,
					unlocking: vec![],
					claimed_rewards: vec![]
				}
			);
		})
}
#[test]
fn on_finalize_weight_is_nonzero() {
	ExtBuilder::default().build_and_execute(|| {
		let on_finalize_weight = <Test as frame_system::Config>::DbWeight::get().reads(1);
		assert!(<Staking as Hooks<u64>>::on_initialize(1) >= on_finalize_weight);
mod election_data_provider {
	use super::*;
	use frame_election_provider_support::ElectionDataProvider;
	#[test]
	fn targets_2sec_block() {
		let mut validators = 1000;
		while <Test as Config>::WeightInfo::get_npos_targets(validators) <
			2 * frame_support::weights::constants::WEIGHT_PER_SECOND
		{
			validators += 1;
		}

		println!("Can create a snapshot of {} validators in 2sec block", validators);
	}

	#[test]
	fn voters_2sec_block() {
		// we assume a network only wants up to 1000 validators in most cases, thus having 2000
		// candidates is as high as it gets.
		let validators = 2000;
		// we assume the worse case: each validator also has a slashing span.
		let slashing_spans = validators;
		let mut nominators = 1000;

		while <Test as Config>::WeightInfo::get_npos_voters(validators, nominators, slashing_spans) <
			2 * frame_support::weights::constants::WEIGHT_PER_SECOND
		{
			nominators += 1;
		}

		println!(
			"Can create a snapshot of {} nominators [{} validators, each 1 slashing] in 2sec block",
			nominators, validators
		);
	}

	#[test]
	fn voters_include_self_vote() {
		ExtBuilder::default().nominate(false).build_and_execute(|| {
			assert!(<Validators<Test>>::iter().map(|(x, _)| x).all(|v| Staking::voters(None)
				.unwrap()
				.into_iter()
				.find(|(w, _, t)| { v == *w && t[0] == *w })
				.is_some()))
		})
	}

	#[test]
	fn voters_exclude_slashed() {
		ExtBuilder::default().build_and_execute(|| {
			assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]);
			assert_eq!(
				<Staking as ElectionDataProvider<AccountId, BlockNumber>>::voters(None)
					.unwrap()
					.iter()
					.find(|x| x.0 == 101)
					.unwrap()
					.2,
				vec![11, 21]
			);

			start_active_era(1);
			add_slash(&11);

			// 11 is gone.
			start_active_era(2);
			assert_eq!(
				<Staking as ElectionDataProvider<AccountId, BlockNumber>>::voters(None)
					.unwrap()
					.iter()
					.find(|x| x.0 == 101)
					.unwrap()
					.2,
				vec![21]
			);

			// resubmit and it is back
			assert_ok!(Staking::nominate(Origin::signed(100), vec![11, 21]));
			assert_eq!(
				<Staking as ElectionDataProvider<AccountId, BlockNumber>>::voters(None)
					.unwrap()
	fn respects_snapshot_len_limits() {
		ExtBuilder::default()
			.set_status(41, StakerStatus::Validator)
			.build_and_execute(|| {
				// sum of all nominators who'd be voters (1), plus the self-votes (4).
				assert_eq!(
					<Test as Config>::SortedListProvider::count() +
						<Validators<Test>>::iter().count() as u32,
					5
				);

				// if limits is less..
				assert_eq!(Staking::voters(Some(1)).unwrap().len(), 1);

				// if limit is equal..
				assert_eq!(Staking::voters(Some(5)).unwrap().len(), 5);

				// if limit is more.
				assert_eq!(Staking::voters(Some(55)).unwrap().len(), 5);

				// if target limit is more..
				assert_eq!(Staking::targets(Some(6)).unwrap().len(), 4);
				assert_eq!(Staking::targets(Some(4)).unwrap().len(), 4);

				// if target limit is less, then we return an error.
				assert_eq!(Staking::targets(Some(1)).unwrap_err(), "Target snapshot too big");
			});
	}

	#[test]
	fn only_iterates_max_2_times_nominators_quota() {
		ExtBuilder::default()
			.nominate(true) // add nominator 101, who nominates [11, 21]
			// the other nominators only nominate 21
			.add_staker(61, 60, 2_000, StakerStatus::<AccountId>::Nominator(vec![21]))
			.add_staker(71, 70, 2_000, StakerStatus::<AccountId>::Nominator(vec![21]))
			.add_staker(81, 80, 2_000, StakerStatus::<AccountId>::Nominator(vec![21]))
			.build_and_execute(|| {
				// given our nominators ordered by stake,
				assert_eq!(
					<Test as Config>::SortedListProvider::iter().collect::<Vec<_>>(),
					vec![61, 71, 81, 101]
				);

				// and total voters
				assert_eq!(
					<Test as Config>::SortedListProvider::count() +
						<Validators<Test>>::iter().count() as u32,
					7
				);

				// roll to session 5
				run_to_block(25);

				// slash 21, the only validator nominated by our first 3 nominators
				add_slash(&21);

				// we take 4 voters: 2 validators and 2 nominators (so nominators quota = 2)
				assert_eq!(
					Staking::voters(Some(3))
						.unwrap()
						.iter()
						.map(|(stash, _, _)| stash)
						.copied()
						.collect::<Vec<_>>(),
					vec![31, 11], // 2 validators, but no nominators because we hit the quota
				);
			});
	}

	// Even if some of the higher staked nominators are slashed, we still get up to max len voters
	// by adding more lower staked nominators. In other words, we assert that we keep on adding
	// valid nominators until we reach max len voters; which is opposed to simply stopping after we
	// have iterated max len voters, but not adding all of them to voters due to some nominators not
	// having valid targets.
	#[test]
	fn get_max_len_voters_even_if_some_nominators_are_slashed() {
		ExtBuilder::default()
			.nominate(true) // add nominator 101, who nominates [11, 21]
			.add_staker(61, 60, 20, StakerStatus::<AccountId>::Nominator(vec![21]))
			//                                 61 only nominates validator 21 ^^
			.add_staker(71, 70, 10, StakerStatus::<AccountId>::Nominator(vec![11, 21]))
			.build_and_execute(|| {
				// given our nominators ordered by stake,
				assert_eq!(
					<Test as Config>::SortedListProvider::iter().collect::<Vec<_>>(),
					vec![101, 61, 71]
				);

				// and total voters
				assert_eq!(
					<Test as Config>::SortedListProvider::count() +
						<Validators<Test>>::iter().count() as u32,
					6
				);

				// we take 5 voters
				assert_eq!(
					Staking::voters(Some(5))
						.unwrap()
						.iter()
						.map(|(stash, _, _)| stash)
						.copied()
						.collect::<Vec<_>>(),
					// then
					vec![
						31, 21, 11, // 3 nominators
						101, 61 // 2 validators, and 71 is excluded
					],
				);

				// roll to session 5
				run_to_block(25);

				// slash 21, the only validator nominated by 61
				add_slash(&21);