Skip to content
tests.rs 178 KiB
Newer Older
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Gav Wood's avatar
Gav Wood committed

//! Tests for the module.

use frame_election_provider_support::{ElectionProvider, SortedListProvider, Support};
use frame_support::{
	assert_noop, assert_ok, assert_storage_noop, bounded_vec,
	dispatch::{extract_actual_weight, GetDispatchInfo, WithPostDispatchInfo},
	traits::{Currency, Get, ReservableCurrency},
use pallet_balances::Error as BalancesError;
use sp_runtime::{
	assert_eq_error_rate,
	traits::{BadOrigin, Dispatchable},
	Perbill, Percent, Rounding, TokenError,
	offence::{DisableStrategy, OffenceDetails, OnOffenceHandler},
	SessionIndex,
};
use sp_std::prelude::*;
#[test]
fn set_staking_configs_works() {
	ExtBuilder::default().build_and_execute(|| {
		// setting works
		assert_ok!(Staking::set_staking_configs(
			RuntimeOrigin::root(),
			ConfigOp::Set(1_500),
			ConfigOp::Set(2_000),
			ConfigOp::Set(10),
			ConfigOp::Set(20),
			ConfigOp::Set(Percent::from_percent(75)),
			ConfigOp::Set(Zero::zero())
		));
		assert_eq!(MinNominatorBond::<Test>::get(), 1_500);
		assert_eq!(MinValidatorBond::<Test>::get(), 2_000);
		assert_eq!(MaxNominatorsCount::<Test>::get(), Some(10));
		assert_eq!(MaxValidatorsCount::<Test>::get(), Some(20));
		assert_eq!(ChillThreshold::<Test>::get(), Some(Percent::from_percent(75)));
		assert_eq!(MinCommission::<Test>::get(), Perbill::from_percent(0));

		// noop does nothing
		assert_storage_noop!(assert_ok!(Staking::set_staking_configs(
			RuntimeOrigin::root(),
			ConfigOp::Noop,
			ConfigOp::Noop,
			ConfigOp::Noop,
			ConfigOp::Noop,
			ConfigOp::Noop,
			ConfigOp::Noop
		)));

		// removing works
		assert_ok!(Staking::set_staking_configs(
			RuntimeOrigin::root(),
			ConfigOp::Remove,
			ConfigOp::Remove,
			ConfigOp::Remove,
			ConfigOp::Remove,
			ConfigOp::Remove,
			ConfigOp::Remove
		));
		assert_eq!(MinNominatorBond::<Test>::get(), 0);
		assert_eq!(MinValidatorBond::<Test>::get(), 0);
		assert_eq!(MaxNominatorsCount::<Test>::get(), None);
		assert_eq!(MaxValidatorsCount::<Test>::get(), None);
		assert_eq!(ChillThreshold::<Test>::get(), None);
		assert_eq!(MinCommission::<Test>::get(), Perbill::from_percent(0));
	});
}

#[test]
fn force_unstake_works() {
	ExtBuilder::default().build_and_execute(|| {
		// Account 11 (also controller) is stashed and locked
		assert_eq!(Staking::bonded(&11), Some(11));
		// Adds 2 slashing spans
		add_slash(&11);
		// Cant transfer
		assert_noop!(
			Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10),
			TokenError::Frozen,
		);
		// Force unstake requires root.
		assert_noop!(Staking::force_unstake(RuntimeOrigin::signed(11), 11, 2), BadOrigin);
		// Force unstake needs correct number of slashing spans (for weight calculation)
			Staking::force_unstake(RuntimeOrigin::root(), 11, 0),
			Error::<Test>::IncorrectSlashingSpans
		);
		// We now force them to unstake
		assert_ok!(Staking::force_unstake(RuntimeOrigin::root(), 11, 2));
		// No longer bonded.
		assert_eq!(Staking::bonded(&11), None);
		// Transfer works.
		assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10));
#[test]
fn kill_stash_works() {
	ExtBuilder::default().build_and_execute(|| {
		// Account 11 (also controller) is stashed and locked
		assert_eq!(Staking::bonded(&11), Some(11));
		// Adds 2 slashing spans
		add_slash(&11);
		// Only can kill a stash account
		assert_noop!(Staking::kill_stash(&12, 0), Error::<Test>::NotStash);
		// Respects slashing span count
		assert_noop!(Staking::kill_stash(&11, 0), Error::<Test>::IncorrectSlashingSpans);
		// Correct inputs, everything works
		assert_ok!(Staking::kill_stash(&11, 2));
		// No longer bonded.
		assert_eq!(Staking::bonded(&11), None);
	});
}

Gav Wood's avatar
Gav Wood committed
#[test]
fn basic_setup_works() {
	// Verifies initial conditions of mock
	ExtBuilder::default().build_and_execute(|| {
		// Account 11 is stashed and locked, and is the controller
		assert_eq!(Staking::bonded(&11), Some(11));
		// Account 21 is stashed and locked and is the controller
		assert_eq!(Staking::bonded(&21), Some(21));
		// Account 1 is not a stashed
		assert_eq!(Staking::bonded(&1), None);
		// Account 11 controls its own stash, which is 100 * balance_factor units
			StakingLedger {
				stash: 11,
				total: 1000,
				active: 1000,
				unlocking: Default::default(),
				claimed_rewards: bounded_vec![],
			}
		// Account 21 controls its own stash, which is 200 * balance_factor units
			Some(StakingLedger {
				stash: 21,
				total: 1000,
				active: 1000,
				unlocking: Default::default(),
				claimed_rewards: bounded_vec![],
		// Account 1 does not control any stash
		assert_eq!(Staking::ledger(&1), None);

		// ValidatorPrefs are default
		assert_eq_uvec!(
			<Validators<Test>>::iter().collect::<Vec<_>>(),
			vec![
				(31, ValidatorPrefs::default()),
				(21, ValidatorPrefs::default()),
				(11, ValidatorPrefs::default())
			]
		);
			Some(StakingLedger {
				stash: 101,
				total: 500,
				active: 500,
				unlocking: Default::default(),
				claimed_rewards: bounded_vec![],
		assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]);
thiolliere's avatar
thiolliere committed
		assert_eq!(
			Staking::eras_stakers(active_era(), 11),
Gavin Wood's avatar
Gavin Wood committed
			Exposure {
				total: 1125,
				own: 1000,
				others: vec![IndividualExposure { who: 101, value: 125 }]
Gavin Wood's avatar
Gavin Wood committed
			},
thiolliere's avatar
thiolliere committed
		);
		assert_eq!(
			Staking::eras_stakers(active_era(), 21),
Gavin Wood's avatar
Gavin Wood committed
			Exposure {
				total: 1375,
				own: 1000,
				others: vec![IndividualExposure { who: 101, value: 375 }]
Gavin Wood's avatar
Gavin Wood committed
			},
thiolliere's avatar
thiolliere committed
		);

		// initial total stake = 1125 + 1375
		assert_eq!(Staking::eras_total_stake(active_era()), 2500);
		// The number of validators required.
		assert_eq!(Staking::validator_count(), 2);

		// Initial Era and session
		assert_eq!(active_era(), 0);
		// Account 10 has `balance_factor` free balance
		assert_eq!(Balances::free_balance(10), 1);
		assert_eq!(Balances::free_balance(10), 1);
		// New era is not being forced
		assert_eq!(Staking::force_era(), Forcing::NotForcing);
#[test]
fn change_controller_works() {
	ExtBuilder::default().build_and_execute(|| {
		let (stash, controller) = testing_utils::create_unique_stash_controller::<Test>(
			0,
			100,
			RewardDestination::Staked,
			false,
		)
		.unwrap();

		// ensure `stash` and `controller` are bonded as stash controller pair.
		assert_eq!(Staking::bonded(&stash), Some(controller));

		// `controller` can control `stash` who is initially a validator.
		assert_ok!(Staking::chill(RuntimeOrigin::signed(controller)));

		// sets controller back to `stash`.
		assert_ok!(Staking::set_controller(RuntimeOrigin::signed(stash)));
		assert_eq!(Staking::bonded(&stash), Some(stash));
		mock::start_active_era(1);

		// `controller` is no longer in control. `stash` is now controller.
		assert_noop!(
			Staking::validate(RuntimeOrigin::signed(controller), ValidatorPrefs::default()),
			Error::<Test>::NotController,
		);
		assert_ok!(Staking::validate(RuntimeOrigin::signed(stash), ValidatorPrefs::default()));
	})
}

#[test]
fn change_controller_already_paired_once_stash() {
	ExtBuilder::default().build_and_execute(|| {
		// 10 and 11 are bonded as controller and stash respectively.
		assert_eq!(Staking::bonded(&11), Some(11));
		// 11 is initially a validator.
		assert_ok!(Staking::chill(RuntimeOrigin::signed(11)));
		// Controller cannot change once matching with stash.
		assert_noop!(
			Staking::set_controller(RuntimeOrigin::signed(11)),
			Error::<Test>::AlreadyPaired
		);
		assert_eq!(Staking::bonded(&11), Some(11));
		// 10 is no longer in control.
		assert_noop!(
			Staking::validate(RuntimeOrigin::signed(10), ValidatorPrefs::default()),
			Error::<Test>::NotController,
		assert_ok!(Staking::validate(RuntimeOrigin::signed(11), ValidatorPrefs::default()));
#[test]
fn rewards_should_work() {
	ExtBuilder::default().nominate(true).session_per_era(3).build_and_execute(|| {
		let init_balance_11 = Balances::total_balance(&11);
Gavin Wood's avatar
Gavin Wood committed
		let init_balance_21 = Balances::total_balance(&21);
		let init_balance_101 = Balances::total_balance(&101);
Gavin Wood's avatar
Gavin Wood committed
		Payee::<Test>::insert(11, RewardDestination::Controller);
		Payee::<Test>::insert(21, RewardDestination::Controller);
		Payee::<Test>::insert(101, RewardDestination::Controller);
		Pallet::<Test>::reward_by_ids(vec![(11, 50)]);
		Pallet::<Test>::reward_by_ids(vec![(11, 50)]);
		// This is the second validator of the current elected set.
		Pallet::<Test>::reward_by_ids(vec![(21, 50)]);
		// Compute total payout now for whole duration of the session.
		let total_payout_0 = current_total_payout_for_duration(reward_time_per_era());
		let maximum_payout = maximum_payout_for_duration(reward_time_per_era());
Gavin Wood's avatar
Gavin Wood committed
		start_session(1);
		assert_eq_uvec!(Session::validators(), vec![11, 21]);
		assert_eq!(Balances::total_balance(&11), init_balance_11);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq!(Balances::total_balance(&21), init_balance_21);
		assert_eq!(Balances::total_balance(&101), init_balance_101);
			Staking::eras_reward_points(active_era()),
			EraRewardPoints {
				total: 50 * 3,
				individual: vec![(11, 100), (21, 50)].into_iter().collect(),
			}
		);
		let part_for_11 = Perbill::from_rational::<u32>(1000, 1125);
		let part_for_21 = Perbill::from_rational::<u32>(1000, 1375);
		let part_for_101_from_11 = Perbill::from_rational::<u32>(125, 1125);
		let part_for_101_from_21 = Perbill::from_rational::<u32>(375, 1375);
		start_session(2);
		assert_eq!(active_era(), 1);
		assert_eq!(mock::RewardRemainderUnbalanced::get(), maximum_payout - total_payout_0,);
		assert_eq!(
			*mock::staking_events().last().unwrap(),
			Event::EraPaid {
				era_index: 0,
				validator_payout: total_payout_0,
				remainder: maximum_payout - total_payout_0
			}
Gavin Wood's avatar
Gavin Wood committed
		mock::make_all_reward_payment(0);

			Balances::total_balance(&11),
			init_balance_11 + part_for_11 * total_payout_0 * 2 / 3,
			Balances::total_balance(&21),
			init_balance_21 + part_for_21 * total_payout_0 * 1 / 3,
Gavin Wood's avatar
Gavin Wood committed
		assert_eq_error_rate!(
			Balances::total_balance(&101),
			init_balance_101 +
				part_for_101_from_11 * total_payout_0 * 2 / 3 +
				part_for_101_from_21 * total_payout_0 * 1 / 3,
Gavin Wood's avatar
Gavin Wood committed
			2
		);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq_uvec!(Session::validators(), vec![11, 21]);
		Pallet::<Test>::reward_by_ids(vec![(11, 1)]);
Gavin Wood's avatar
Gavin Wood committed
		// Compute total payout now for whole duration as other parameter won't change
		let total_payout_1 = current_total_payout_for_duration(reward_time_per_era());
		mock::start_active_era(2);
		assert_eq!(
			mock::RewardRemainderUnbalanced::get(),
			maximum_payout * 2 - total_payout_0 - total_payout_1,
		);
		assert_eq!(
			*mock::staking_events().last().unwrap(),
			Event::EraPaid {
				era_index: 1,
				validator_payout: total_payout_1,
				remainder: maximum_payout - total_payout_1
			}
Gavin Wood's avatar
Gavin Wood committed
		mock::make_all_reward_payment(1);

			Balances::total_balance(&11),
			init_balance_11 + part_for_11 * (total_payout_0 * 2 / 3 + total_payout_1),
			Balances::total_balance(&21),
			init_balance_21 + part_for_21 * total_payout_0 * 1 / 3,
Gavin Wood's avatar
Gavin Wood committed
		assert_eq_error_rate!(
			Balances::total_balance(&101),
			init_balance_101 +
				part_for_101_from_11 * (total_payout_0 * 2 / 3 + total_payout_1) +
				part_for_101_from_21 * total_payout_0 * 1 / 3,
Gavin Wood's avatar
Gavin Wood committed
			2
		);
Gav Wood's avatar
Gav Wood committed

#[test]
fn staking_should_work() {
	ExtBuilder::default().nominate(false).build_and_execute(|| {
		// remember + compare this along with the test.
		assert_eq_uvec!(validator_controllers(), vec![21, 11]);
		// put some money in account that we'll use.
		for i in 1..5 {
			let _ = Balances::make_free_balance_be(&i, 2000);
		}
		// --- Block 2:
		start_session(2);
		// add a new candidate for being a validator. account 3 controlled by 4.
		assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Controller));
		assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()));
		assert_ok!(Session::set_keys(
			SessionKeys { other: 4.into() },
			vec![]
		));
		// No effects will be seen so far.
		assert_eq_uvec!(validator_controllers(), vec![21, 11]);
		// --- Block 3:
		start_session(3);
		// No effects will be seen so far. Era has not been yet triggered.
		assert_eq_uvec!(validator_controllers(), vec![21, 11]);
		// --- Block 4: the validators will now be queued.
		start_session(4);
		assert_eq!(active_era(), 1);
Gavin Wood's avatar
Gavin Wood committed

		// --- Block 5: the validators are still in queue.
		start_session(5);
Gavin Wood's avatar
Gavin Wood committed

		// --- Block 6: the validators will now be changed.
		start_session(6);
		assert_eq_uvec!(validator_controllers(), vec![21, 3]);
		// --- Block 6: Unstake 4 as a validator, freeing up the balance stashed in 3
		// 4 will chill
		Staking::chill(RuntimeOrigin::signed(3)).unwrap();
		// --- Block 7: nothing. 3 is still there.
		start_session(7);
		assert_eq_uvec!(validator_controllers(), vec![21, 3]);
		// --- Block 8:
		start_session(8);
Gavin Wood's avatar
Gavin Wood committed

		// --- Block 9: 4 will not be a validator.
		start_session(9);
		assert_eq_uvec!(validator_controllers(), vec![21, 11]);
		// Note: the stashed value of 4 is still lock
		assert_eq!(
			Some(StakingLedger {
				stash: 3,
				total: 1500,
				active: 1500,
				unlocking: Default::default(),
				claimed_rewards: bounded_vec![0],
			})
		);
		// e.g. it cannot reserve more than 500 that it has free from the total 2000
		assert_noop!(Balances::reserve(&3, 501), BalancesError::<Test, _>::LiquidityRestrictions);
		assert_ok!(Balances::reserve(&3, 409));
	});
#[test]
fn blocking_and_kicking_works() {
	ExtBuilder::default()
		.minimum_validator_count(1)
		.validator_count(4)
		.nominate(true)
		.build_and_execute(|| {
			assert_ok!(Staking::validate(
				ValidatorPrefs { blocked: true, ..Default::default() }
			));
			// attempt to nominate from 100/101...
			assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![11]));
			// should have worked since we're already nominated them
			assert_eq!(Nominators::<Test>::get(&101).unwrap().targets, vec![11]);
			// kick the nominator
			assert_ok!(Staking::kick(RuntimeOrigin::signed(11), vec![101]));
			// should have been kicked now
			assert!(Nominators::<Test>::get(&101).unwrap().targets.is_empty());
			// attempt to nominate from 100/101...
				Staking::nominate(RuntimeOrigin::signed(101), vec![11]),
				Error::<Test>::BadTarget
			);
#[test]
fn less_than_needed_candidates_works() {
	ExtBuilder::default()
		.minimum_validator_count(1)
		.validator_count(4)
		.nominate(false)
		.build_and_execute(|| {
			assert_eq!(Staking::validator_count(), 4);
			assert_eq!(Staking::minimum_validator_count(), 1);
			assert_eq_uvec!(validator_controllers(), vec![31, 21, 11]);

			// Previous set is selected. NO election algorithm is even executed.
			assert_eq_uvec!(validator_controllers(), vec![31, 21, 11]);

			// But the exposure is updated in a simple way. No external votes exists.
			// This is purely self-vote.
			assert!(ErasStakers::<Test>::iter_prefix_values(active_era())
				.all(|exposure| exposure.others.is_empty()));
}

#[test]
fn no_candidate_emergency_condition() {
	ExtBuilder::default()
		.minimum_validator_count(1)
		.validator_count(15)
		.set_status(41, StakerStatus::Validator)
		.nominate(false)
		.build_and_execute(|| {
			// initial validators
			assert_eq_uvec!(validator_controllers(), vec![11, 21, 31, 41]);
			let prefs = ValidatorPrefs { commission: Perbill::one(), ..Default::default() };
			Validators::<Test>::insert(11, prefs.clone());
			// set the minimum validator count.
			MinimumValidatorCount::<Test>::put(11);
Gavin Wood's avatar
Gavin Wood committed
			// try to chill
			let res = Staking::chill(RuntimeOrigin::signed(11));
			let current_era = CurrentEra::<Test>::get();

			// try trigger new era
			assert_eq!(*staking_events().last().unwrap(), Event::StakingElectionFailed);
			// No new era is created
			assert_eq!(current_era, CurrentEra::<Test>::get());

			// Go to far further session to see if validator have changed
			mock::run_to_block(100);
			// Previous ones are elected. chill is not effective in active era (as era hasn't
			// changed)
			assert_eq_uvec!(validator_controllers(), vec![11, 21, 31, 41]);
			assert!(!Validators::<Test>::contains_key(11));
			// No new era is created.
			assert_eq!(current_era, CurrentEra::<Test>::get());
Gav Wood's avatar
Gav Wood committed
#[test]
fn nominating_and_rewards_should_work() {
	ExtBuilder::default()
		.nominate(false)
		.set_status(41, StakerStatus::Validator)
		.set_status(11, StakerStatus::Idle)
		.set_status(31, StakerStatus::Idle)
		.build_and_execute(|| {
			// initial validators.
			assert_eq_uvec!(validator_controllers(), vec![41, 21]);

			// re-validate with 11 and 31.
			assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default()));
			assert_ok!(Staking::validate(RuntimeOrigin::signed(31), Default::default()));
			// Set payee to controller.
			assert_ok!(Staking::set_payee(
				RewardDestination::Controller
			));
			assert_ok!(Staking::set_payee(
				RewardDestination::Controller
			));
			assert_ok!(Staking::set_payee(
				RewardDestination::Controller
			));
			assert_ok!(Staking::set_payee(
				RewardDestination::Controller
			));

			// give the man some money
			let initial_balance = 1000;
			for i in [1, 3, 5, 11, 21].iter() {
				let _ = Balances::make_free_balance_be(i, initial_balance);
			}

			// bond two account pairs and state interest in nomination.
			assert_ok!(Staking::bond(
				RuntimeOrigin::signed(1),
				1000,
				RewardDestination::Controller
			));
			assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 21, 31]));
			assert_ok!(Staking::bond(
				RuntimeOrigin::signed(3),
				1000,
				RewardDestination::Controller
			));
			assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![11, 21, 41]));

			// the total reward for era 0
			let total_payout_0 = current_total_payout_for_duration(reward_time_per_era());
			Pallet::<Test>::reward_by_ids(vec![(41, 1)]);
			Pallet::<Test>::reward_by_ids(vec![(21, 1)]);
			// 10 and 20 have more votes, they will be chosen.
			assert_eq_uvec!(validator_controllers(), vec![21, 11]);
			// old validators must have already received some rewards.
			let initial_balance_41 = Balances::total_balance(&41);
			let mut initial_balance_21 = Balances::total_balance(&21);
Gavin Wood's avatar
Gavin Wood committed
			mock::make_all_reward_payment(0);
			assert_eq!(Balances::total_balance(&41), initial_balance_41 + total_payout_0 / 2);
			assert_eq!(Balances::total_balance(&21), initial_balance_21 + total_payout_0 / 2);
			initial_balance_21 = Balances::total_balance(&21);
			assert_eq!(ErasStakers::<Test>::iter_prefix_values(active_era()).count(), 2);
thiolliere's avatar
thiolliere committed
			assert_eq!(
				Staking::eras_stakers(active_era(), 11),
Gavin Wood's avatar
Gavin Wood committed
				Exposure {
					total: 1000 + 800,
					own: 1000,
					others: vec![
						IndividualExposure { who: 1, value: 400 },
						IndividualExposure { who: 3, value: 400 },
Gavin Wood's avatar
Gavin Wood committed
					]
				},
thiolliere's avatar
thiolliere committed
			);
			assert_eq!(
				Staking::eras_stakers(active_era(), 21),
Gavin Wood's avatar
Gavin Wood committed
				Exposure {
					total: 1000 + 1200,
					own: 1000,
					others: vec![
						IndividualExposure { who: 1, value: 600 },
						IndividualExposure { who: 3, value: 600 },
Gavin Wood's avatar
Gavin Wood committed
					]
				},
thiolliere's avatar
thiolliere committed
			);

			// the total reward for era 1
			let total_payout_1 = current_total_payout_for_duration(reward_time_per_era());
			Pallet::<Test>::reward_by_ids(vec![(21, 2)]);
			Pallet::<Test>::reward_by_ids(vec![(11, 1)]);
			// nothing else will happen, era ends and rewards are paid again, it is expected that
			// nominators will also be paid. See below
Gavin Wood's avatar
Gavin Wood committed
			mock::make_all_reward_payment(1);
			let payout_for_11 = total_payout_1 / 3;
			let payout_for_21 = 2 * total_payout_1 / 3;
			// Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==>
thiolliere's avatar
thiolliere committed
			assert_eq_error_rate!(
				Balances::total_balance(&1),
				initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11),
thiolliere's avatar
thiolliere committed
			);
			// Nominator 3: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==>
thiolliere's avatar
thiolliere committed
			assert_eq_error_rate!(
				Balances::total_balance(&3),
				initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11),
			// Validator 11: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9
thiolliere's avatar
thiolliere committed
			assert_eq_error_rate!(
				Balances::total_balance(&11),
				initial_balance + 5 * payout_for_11 / 9,
thiolliere's avatar
thiolliere committed
			);
			// Validator 21: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share =
thiolliere's avatar
thiolliere committed
			assert_eq_error_rate!(
				Balances::total_balance(&21),
				initial_balance_21 + 5 * payout_for_21 / 11,
thiolliere's avatar
thiolliere committed
			);
Gav Wood's avatar
Gav Wood committed
#[test]
fn nominators_also_get_slashed_pro_rata() {
	ExtBuilder::default().build_and_execute(|| {
		let slash_percent = Perbill::from_percent(5);
		let initial_exposure = Staking::eras_stakers(active_era(), 11);
		// 101 is a nominator for 11
		assert_eq!(initial_exposure.others.first().unwrap().who, 101);
		// staked values;
		let nominator_stake = Staking::ledger(101).unwrap().active;
		let nominator_balance = balances(&101).0;
		let validator_stake = Staking::ledger(11).unwrap().active;
		let validator_balance = balances(&11).0;
		let exposed_stake = initial_exposure.total;
		let exposed_validator = initial_exposure.own;
		let exposed_nominator = initial_exposure.others.first().unwrap().value;
		// 11 goes offline
		on_offence_now(
			&[OffenceDetails { offender: (11, initial_exposure.clone()), reporters: vec![] }],
			&[slash_percent],
		// both stakes must have been decreased.
		assert!(Staking::ledger(101).unwrap().active < nominator_stake);
		assert!(Staking::ledger(11).unwrap().active < validator_stake);

		let slash_amount = slash_percent * exposed_stake;
		let validator_share =
			Perbill::from_rational(exposed_validator, exposed_stake) * slash_amount;
		let nominator_share =
			Perbill::from_rational(exposed_nominator, exposed_stake) * slash_amount;

		// both slash amounts need to be positive for the test to make sense.
		assert!(validator_share > 0);
		assert!(nominator_share > 0);
		// both stakes must have been decreased pro-rata.
		assert_eq!(Staking::ledger(101).unwrap().active, nominator_stake - nominator_share);
		assert_eq!(Staking::ledger(11).unwrap().active, validator_stake - validator_share);
		assert_eq!(
			balances(&101).0, // free balance
			nominator_balance - nominator_share,
		);
		assert_eq!(
			balances(&11).0, // free balance
			validator_balance - validator_share,
		);
		// Because slashing happened.
Gav Wood's avatar
Gav Wood committed
	});
}

#[test]
fn double_staking_should_fail() {
	// should test (in the same order):
	// * an account already bonded as stash cannot be be stashed again.
	// * an account already bonded as stash cannot nominate.
	// * an account already bonded as controller can nominate.
	ExtBuilder::default().build_and_execute(|| {
		let arbitrary_value = 5;
		let (stash, controller) = testing_utils::create_unique_stash_controller::<Test>(
			0,
			RewardDestination::default(),
			false,
		)
		.unwrap();

		// 4 = not used so far,  stash => not allowed.
			Staking::bond(
				RuntimeOrigin::signed(stash),
				arbitrary_value.into(),
				RewardDestination::default()
			),
			Error::<Test>::AlreadyBonded,
		// stash => attempting to nominate should fail.
		assert_noop!(
			Staking::nominate(RuntimeOrigin::signed(stash), vec![1]),
			Error::<Test>::NotController
		);
		// controller => nominating should work.
		assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![1]));
fn double_controlling_attempt_should_fail() {
	// should test (in the same order):
	// * an account already bonded as controller CANNOT be reused as the controller of another
	//   account.
	ExtBuilder::default().build_and_execute(|| {
		let arbitrary_value = 5;
		let (stash, _) = testing_utils::create_unique_stash_controller::<Test>(
			0,
			arbitrary_value,
			RewardDestination::default(),
			false,
		)
		.unwrap();

		// Note that controller (same as stash) is reused => no-op.
			Staking::bond(
				RuntimeOrigin::signed(stash),
				arbitrary_value.into(),
				RewardDestination::default()
			),
Gav Wood's avatar
Gav Wood committed
#[test]
fn session_and_eras_work_simple() {
	ExtBuilder::default().period(1).build_and_execute(|| {
		assert_eq!(active_era(), 0);
		assert_eq!(current_era(), 0);
		assert_eq!(Session::current_index(), 1);
		assert_eq!(System::block_number(), 1);
		// Session 1: this is basically a noop. This has already been started.
Gavin Wood's avatar
Gavin Wood committed
		start_session(1);
		assert_eq!(Session::current_index(), 1);
		assert_eq!(active_era(), 0);
		assert_eq!(System::block_number(), 1);
		// Session 2: No change.
		start_session(2);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq!(Session::current_index(), 2);
		assert_eq!(active_era(), 0);
		assert_eq!(System::block_number(), 2);
		// Session 3: Era increment.
		start_session(3);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq!(Session::current_index(), 3);
		assert_eq!(active_era(), 1);
		assert_eq!(System::block_number(), 3);
Gavin Wood's avatar
Gavin Wood committed

		// Session 4: No change.
Gavin Wood's avatar
Gavin Wood committed
		start_session(4);
		assert_eq!(Session::current_index(), 4);
		assert_eq!(active_era(), 1);
		assert_eq!(System::block_number(), 4);
		// Session 5: No change.
		start_session(5);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq!(Session::current_index(), 5);
		assert_eq!(active_era(), 1);
		assert_eq!(System::block_number(), 5);
		// Session 6: Era increment.
		start_session(6);
Gavin Wood's avatar
Gavin Wood committed
		assert_eq!(Session::current_index(), 6);
		assert_eq!(active_era(), 2);
		assert_eq!(System::block_number(), 6);
	});
}

#[test]
fn session_and_eras_work_complex() {
	ExtBuilder::default().period(5).build_and_execute(|| {
		assert_eq!(active_era(), 0);
		assert_eq!(Session::current_index(), 0);
		assert_eq!(System::block_number(), 1);

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

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

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

		start_session(4);
		assert_eq!(Session::current_index(), 4);
		assert_eq!(active_era(), 1);
		assert_eq!(System::block_number(), 20);

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

		start_session(6);
		assert_eq!(Session::current_index(), 6);
		assert_eq!(active_era(), 2);
		assert_eq!(System::block_number(), 30);
#[test]
fn forcing_new_era_works() {
	ExtBuilder::default().build_and_execute(|| {
		// normal flow of session.
		start_session(1);
Gavin Wood's avatar
Gavin Wood committed
		start_session(3);
		Staking::set_force_era(Forcing::ForceNone);
Gavin Wood's avatar
Gavin Wood committed
		start_session(7);
		// back to normal.
		// this immediately starts a new session.
		Staking::set_force_era(Forcing::NotForcing);
		start_session(9);
		assert_eq!(active_era(), 2);
		// forceful change
		Staking::set_force_era(Forcing::ForceAlways);
		start_session(10);
		start_session(11);
Gavin Wood's avatar
Gavin Wood committed
		start_session(12);

		// just one forceful change
		Staking::set_force_era(Forcing::ForceNew);
		start_session(13);
		assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing);
Gavin Wood's avatar
Gavin Wood committed
		start_session(14);
Gavin Wood's avatar
Gavin Wood committed
		start_session(15);
Gav Wood's avatar
Gav Wood committed
#[test]
fn cannot_transfer_staked_balance() {
	// Tests that a stash account cannot transfer funds
	ExtBuilder::default().nominate(false).build_and_execute(|| {
		// Confirm account 11 is stashed
		assert_eq!(Staking::bonded(&11), Some(11));
		// Confirm account 11 has some free balance
		assert_eq!(Balances::free_balance(11), 1000);
		// Confirm account 11 (via controller) is totally staked
		assert_eq!(Staking::eras_stakers(active_era(), 11).total, 1000);
		// Confirm account 11 cannot transfer as a result
		assert_noop!(
			Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1),

		// Give account 11 extra free balance
		let _ = Balances::make_free_balance_be(&11, 10000);
		// Confirm that account 11 can now transfer some balance
		assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1));
#[test]
fn cannot_transfer_staked_balance_2() {
	// Tests that a stash account cannot transfer funds
	// Same test as above but with 20, and more accurate.
	// 21 has 2000 free balance but 1000 at stake
	ExtBuilder::default().nominate(false).build_and_execute(|| {
		// Confirm account 21 is stashed