Newer
Older
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use runtime_io::with_externalities;
use primitives::PerU128;
use srml_support::{assert_ok, assert_noop, assert_eq_uvec, EnumerableStorageMap};
use mock::{Balances, Session, Staking, System, Timestamp, Test, ExtBuilder, Origin};
use srml_support::traits::{Currency, ReservableCurrency};
fn basic_setup_works() {
// Verifies initial conditions of mock
with_externalities(&mut ExtBuilder::default()
.build(),
|| {
assert_eq!(Staking::bonded(&11), Some(10)); // Account 11 is stashed and locked, and account 10 is the controller
assert_eq!(Staking::bonded(&21), Some(20)); // Account 21 is stashed and locked, and account 20 is the controller
assert_eq!(Staking::bonded(&1), None); // Account 1 is not a stashed
// Account 10 controls the stash from account 11, which is 100 * balance_factor units
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
// Account 20 controls the stash from account 21, which is 200 * balance_factor units
assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] }));
// Account 1 does not control any stash
assert_eq!(Staking::ledger(&1), None);
// ValidatorPrefs are default, thus unstake_threshold is 3, other values are default for their type
assert_eq!(<Validators<Test>>::enumerate().collect::<Vec<_>>(), vec![
(21, ValidatorPrefs { unstake_threshold: 3, validator_payment: 0 }),
(11, ValidatorPrefs { unstake_threshold: 3, validator_payment: 0 })
// Account 100 is the default nominator
assert_eq!(Staking::ledger(100), Some(StakingLedger { stash: 101, total: 500, active: 500, unlocking: vec![] }));
assert_eq!(Staking::nominators(101), vec![11, 21]);
// Account 10 is exposed by 1000 * balance_factor from their own stash in account 11 + the default nominator vote
assert_eq!(Staking::stakers(11), Exposure { total: 1124, own: 1000, others: vec![ IndividualExposure { who: 101, value: 124 }] });
// Account 20 is exposed by 1000 * balance_factor from their own stash in account 21 + the default nominator vote
assert_eq!(Staking::stakers(21), Exposure { total: 1375, own: 1000, others: vec![ IndividualExposure { who: 101, value: 375 }] });
// The number of validators required.
assert_eq!(Staking::validator_count(), 2);
// Initial Era and session
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 0);
// initial rewards
assert_eq!(Staking::current_session_reward(), 10);
// initial slot_stake
assert_eq!(Staking::slot_stake(), 1124); // Naive
// initial slash_count of validators
assert_eq!(Staking::slash_count(&11), 0);
assert_eq!(Staking::slash_count(&21), 0);
});
}
#[test]
fn no_offline_should_work() {
// Test the staking module works when no validators are offline
with_externalities(&mut ExtBuilder::default().build(),
|| {
// Slashing begins for validators immediately if found offline
// Account 10 has not been reported offline
// Account 10 has `balance_factor` free balance
assert_eq!(Balances::free_balance(&10), 1);
// Nothing happens to Account 10, as expected
assert_eq!(Balances::free_balance(&10), 1);
assert!(Staking::forcing_new_era().is_none());
});
}
#[test]
fn invulnerability_should_work() {
// Test that users can be invulnerable from slashing and being kicked
with_externalities(&mut ExtBuilder::default().build(),
|| {
// Make account 11 invulnerable
assert_ok!(Staking::set_invulnerables(vec![11]));
// Give account 11 some funds
let _ = Balances::make_free_balance_be(&11, 70);
// There is no slash grace -- slash immediately.
assert_eq!(Staking::offline_slash_grace(), 0);
// Account 11 has not been slashed
assert_eq!(Staking::slash_count(&11), 0);
// Account 11 has the 70 funds we gave it above
assert_eq!(Balances::free_balance(&11), 70);
// Account 11 should be a validator
assert!(<Validators<Test>>::exists(&11));
// Set account 11 as an offline validator with a large number of reports
// Should exit early if invulnerable
Staking::on_offline_validator(10, 100);
// Show that account 11 has not been touched
assert_eq!(Staking::slash_count(&11), 0);
assert_eq!(Balances::free_balance(&11), 70);
assert!(<Validators<Test>>::exists(&11));
// NOTE: new era is always forced once slashing happens -> new validators need to be chosen.
assert!(Staking::forcing_new_era().is_none());
});
}
#[test]
fn offline_should_slash_and_kick() {
// Test that an offline validator gets slashed and kicked
with_externalities(&mut ExtBuilder::default().build(), || {
// Give account 10 some balance
let _ = Balances::make_free_balance_be(&11, 1000);
// Confirm account 10 is a validator
assert!(<Validators<Test>>::exists(&11));
// Validators get slashed immediately
assert_eq!(Staking::validators(&11).unstake_threshold, 3);
// Account 10 has not been slashed before
assert_eq!(Staking::slash_count(&11), 0);
// Account 10 has the funds we just gave it
assert_eq!(Balances::free_balance(&11), 1000);
// Report account 10 as offline, one greater than unstake threshold
Staking::on_offline_validator(10, 4);
assert_eq!(Staking::slash_count(&11), 4);
// Confirm balance has been reduced by 2^unstake_threshold * offline_slash() * amount_at_stake.
let slash_base = Staking::offline_slash() * Staking::stakers(11).total;
assert_eq!(Balances::free_balance(&11), 1000 - 2_u64.pow(3) * slash_base);
// Confirm account 10 has been removed as a validator
assert!(!<Validators<Test>>::exists(&11));
// A new era is forced due to slashing
assert!(Staking::forcing_new_era().is_some());
fn offline_grace_should_delay_slashing() {
// Tests that with grace, slashing is delayed
with_externalities(&mut ExtBuilder::default().build(), || {
// Initialize account 10 with balance
let _ = Balances::make_free_balance_be(&11, 70);
// Verify account 11 has balance
assert_eq!(Balances::free_balance(&11), 70);
// Set offline slash grace
let offline_slash_grace = 1;
assert_ok!(Staking::set_offline_slash_grace(offline_slash_grace));
assert_eq!(Staking::offline_slash_grace(), 1);
// Check unstake_threshold is 3 (default)
let default_unstake_threshold = 3;
assert_eq!(Staking::validators(&11), ValidatorPrefs { unstake_threshold: default_unstake_threshold, validator_payment: 0 });
assert_eq!(Staking::slash_count(&11), 0);
// Report account 10 up to the threshold
Staking::on_offline_validator(10, default_unstake_threshold as usize + offline_slash_grace as usize);
assert_eq!(Staking::slash_count(&11), 4);
assert_eq!(Balances::free_balance(&11), 70);
// Report account 10 one more time
Staking::on_offline_validator(10, 1);
assert_eq!(Staking::slash_count(&11), 5);
assert!(Balances::free_balance(&11) < 70);
assert!(Staking::forcing_new_era().is_some());
});
}
#[test]
fn max_unstake_threshold_works() {
// Tests that max_unstake_threshold gets used when prefs.unstake_threshold is large
with_externalities(&mut ExtBuilder::default().build(), || {
const MAX_UNSTAKE_THRESHOLD: u32 = 10;
// Two users with maximum possible balance
let _ = Balances::make_free_balance_be(&11, u64::max_value());
let _ = Balances::make_free_balance_be(&21, u64::max_value());
// Give them full exposure as a staker
<Stakers<Test>>::insert(&11, Exposure { total: 1000000, own: 1000000, others: vec![]});
<Stakers<Test>>::insert(&21, Exposure { total: 2000000, own: 2000000, others: vec![]});
// Check things are initialized correctly
assert_eq!(Balances::free_balance(&11), u64::max_value());
assert_eq!(Balances::free_balance(&21), u64::max_value());
assert_eq!(Staking::offline_slash_grace(), 0);
// Account 10 will have max unstake_threshold
assert_ok!(Staking::validate(Origin::signed(10), ValidatorPrefs {
unstake_threshold: MAX_UNSTAKE_THRESHOLD,
validator_payment: 0,
}));
// Account 20 could not set their unstake_threshold past 10
assert_noop!(Staking::validate(Origin::signed(20), ValidatorPrefs {
unstake_threshold: MAX_UNSTAKE_THRESHOLD + 1,
validator_payment: 0}),
"unstake threshold too large"
);
// Give Account 20 unstake_threshold 11 anyway, should still be limited to 10
<Validators<Test>>::insert(21, ValidatorPrefs {
unstake_threshold: MAX_UNSTAKE_THRESHOLD + 1,
<OfflineSlash<Test>>::put(Perbill::from_fraction(0.0001));
// Report each user 1 more than the max_unstake_threshold
Staking::on_offline_validator(10, MAX_UNSTAKE_THRESHOLD as usize + 1);
Staking::on_offline_validator(20, MAX_UNSTAKE_THRESHOLD as usize + 1);
// Show that each balance only gets reduced by 2^max_unstake_threshold times 10%
// of their total stake.
assert_eq!(Balances::free_balance(&11), u64::max_value() - 2_u64.pow(MAX_UNSTAKE_THRESHOLD) * 100);
assert_eq!(Balances::free_balance(&21), u64::max_value() - 2_u64.pow(MAX_UNSTAKE_THRESHOLD) * 200);
});
}
#[test]
fn slashing_does_not_cause_underflow() {
// Tests that slashing more than a user has does not underflow
with_externalities(&mut ExtBuilder::default().build(), || {
// Verify initial conditions
assert_eq!(Balances::free_balance(&11), 1000);
assert_eq!(Staking::offline_slash_grace(), 0);
// Set validator preference so that 2^unstake_threshold would cause overflow (greater than 64)
<Validators<Test>>::insert(11, ValidatorPrefs {
unstake_threshold: 10,
validator_payment: 0,
});
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// Should not panic
Staking::on_offline_validator(10, 100);
// Confirm that underflow has not occurred, and account balance is set to zero
assert_eq!(Balances::free_balance(&11), 0);
#[test]
fn rewards_should_work() {
// should check that:
// * rewards get recorded per session
// * rewards get paid per Era
// * Check that nominators are also rewarded
with_externalities(&mut ExtBuilder::default()
.session_length(3)
.sessions_per_era(3)
.build(),
// this test is only in the scope of one era. Since this variable changes
// at the last block/new era, we'll save it.
let session_reward = 10;
// Set payee to controller
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
// Initial config should be correct
assert_eq!(Staking::era_length(), 9);
assert_eq!(Staking::sessions_per_era(), 3);
assert_eq!(Staking::last_era_length_change(), 0);
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 0);
assert_eq!(Staking::current_session_reward(), 10);
// check the balance of a validator accounts.
assert_eq!(Balances::total_balance(&11), 1000);
let _ = Balances::make_free_balance_be(&2, 500);
assert_eq!(Balances::total_balance(&2), 500);
// add a dummy nominator.
// NOTE: this nominator is being added 'manually'. a Further test (nomination_and_reward..) will add it via '.nominate()'
<Stakers<Test>>::insert(&11, Exposure {
own: 500, // equal division indicates that the reward will be equally divided among validator and nominator.
total: 1000,
others: vec![IndividualExposure {who: 2, value: 500 }]
});
<Payee<Test>>::insert(&2, RewardDestination::Stash);
assert_eq!(Staking::payee(2), RewardDestination::Stash);
assert_eq!(Staking::payee(11), RewardDestination::Controller);
let mut block = 3;
// Block 3 => Session 1 => Era 0
System::set_block_number(block);
Timestamp::set_timestamp(block*5); // on time.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 1);
// session triggered: the reward value stashed should be 10 -- defined in ExtBuilder genesis.
assert_eq!(Staking::current_session_reward(), session_reward);
assert_eq!(Staking::current_era_reward(), session_reward);
block = 6; // Block 6 => Session 2 => Era 0
System::set_block_number(block);
Timestamp::set_timestamp(block*5 + delay); // a little late.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 2);
// session reward is the same,
assert_eq!(Staking::current_session_reward(), session_reward);
// though 2 will be deducted while stashed in the era reward due to delay
assert_eq!(Staking::current_era_reward(), 2*session_reward - delay);
block = 9; // Block 9 => Session 3 => Era 1
System::set_block_number(block);
Timestamp::set_timestamp(block*5); // back to being on time. no delays
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::current_index(), 3);
assert_eq!(Balances::total_balance(&10), 1 + (3*session_reward - delay)/2);
assert_eq!(Balances::total_balance(&2), 500 + (3*session_reward - delay)/2);
});
}
#[test]
fn multi_era_reward_should_work() {
// should check that:
// The value of current_session_reward is set at the end of each era, based on
// slot_stake and session_reward. Check and verify this.
with_externalities(&mut ExtBuilder::default()
.session_length(3)
.sessions_per_era(3)
.nominate(false)
.build(),
|| {
let delay = 0;
let session_reward = 10;
// This is set by the test config builder.
assert_eq!(Staking::current_session_reward(), session_reward);
// check the balance of a validator accounts.
assert_eq!(Balances::total_balance(&10), 1);
// Set payee to controller
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
let mut block = 3;
// Block 3 => Session 1 => Era 0
System::set_block_number(block);
Timestamp::set_timestamp(block*5); // on time.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 1);
// session triggered: the reward value stashed should be 10 -- defined in ExtBuilder genesis.
assert_eq!(Staking::current_session_reward(), session_reward);
assert_eq!(Staking::current_era_reward(), session_reward);
block = 6; // Block 6 => Session 2 => Era 0
System::set_block_number(block);
Timestamp::set_timestamp(block*5 + delay); // a little late.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 2);
assert_eq!(Staking::current_session_reward(), session_reward);
assert_eq!(Staking::current_era_reward(), 2*session_reward - delay);
block = 9; // Block 9 => Session 3 => Era 1
System::set_block_number(block);
Timestamp::set_timestamp(block*5); // back to being punktlisch. no delayss
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::current_index(), 3);
// 1 + sum of of the session rewards accumulated
let recorded_balance = 1 + 3*session_reward - delay;
assert_eq!(Balances::total_balance(&10), recorded_balance);
// the reward for next era will be: session_reward * slot_stake
let new_session_reward = Staking::session_reward() * Staking::slot_stake();
assert_eq!(Staking::current_session_reward(), new_session_reward);
// fast forward to next era:
block=12;System::set_block_number(block);Timestamp::set_timestamp(block*5);Session::check_rotate_session(System::block_number());
block=15;System::set_block_number(block);Timestamp::set_timestamp(block*5);Session::check_rotate_session(System::block_number());
// intermediate test.
assert_eq!(Staking::current_era_reward(), 2*new_session_reward);
block=18;System::set_block_number(block);Timestamp::set_timestamp(block*5);Session::check_rotate_session(System::block_number());
// pay time
assert_eq!(Balances::total_balance(&10), 3*new_session_reward + recorded_balance);
});
}
// * new validators can be added to the default set
// * new ones will be chosen per era
// * either one can unlock the stash and back-down from being a validator via `chill`ing.
with_externalities(&mut ExtBuilder::default()
.sessions_per_era(3)
.nominate(false)
.fare(false) // to give 20 more staked value
// remember + compare this along with the test.
assert_eq_uvec!(Session::validators(), vec![20, 10]);
assert_ok!(Staking::set_bonding_duration(2));
assert_eq!(Staking::bonding_duration(), 2);
// put some money in account that we'll use.
for i in 1..5 { let _ = Balances::make_free_balance_be(&i, 2000); }
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
// add a new candidate for being a validator. account 3 controlled by 4.
assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default()));
// No effects will be seen so far.
assert_eq_uvec!(Session::validators(), vec![20, 10]);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
// No effects will be seen so far. Era has not been yet triggered.
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// --- Block 3: the validators will now change.
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators().len(), 2);
assert_eq_uvec!(Session::validators(), vec![20, 4]);
assert_eq!(Staking::current_era(), 1);
// --- Block 4: Unstake 4 as a validator, freeing up the balance stashed in 3
Session::check_rotate_session(System::block_number());
Staking::chill(Origin::signed(4)).unwrap();
// nothing should be changed so far.
assert_eq_uvec!(Session::validators(), vec![20, 4]);
assert_eq!(Staking::current_era(), 1);
// --- Block 5: nothing. 4 is still there.
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![20, 4]);
assert_eq!(Staking::current_era(), 1);
// --- Block 6: 4 will not be a validator.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 2);
assert_eq!(Session::validators().contains(&4), false);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// Note: the stashed value of 4 is still lock
assert_eq!(Staking::ledger(&4), Some(StakingLedger { stash: 3, total: 1500, active: 1500, unlocking: vec![] }));
// e.g. it cannot spend more than 500 that it has free from the total 2000
assert_noop!(Balances::reserve(&3, 501), "account liquidity restrictions prevent withdrawal");
assert_ok!(Balances::reserve(&3, 409));
#[test]
fn less_than_needed_candidates_works() {
// Test the situation where the number of validators are less than `ValidatorCount` but more than <MinValidators>
// The expected behavior is to choose all the candidates that have some vote.
with_externalities(&mut ExtBuilder::default()
.minimum_validator_count(1)
.validator_count(3)
.nominate(false)
|| {
assert_eq!(Staking::era_length(), 1);
assert_eq!(Staking::validator_count(), 3);
assert_eq!(Staking::minimum_validator_count(), 1);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// 10 and 20 are now valid candidates.
// trigger era
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
// both validators will be chosen again. NO election algorithm is even executed.
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// But the exposure is updated in a simple way. No external votes exists. This is purely self-vote.
assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![]);
assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![]);
});
}
#[test]
fn no_candidate_emergency_condition() {
// Test the situation where the number of validators are less than `ValidatorCount` and less than <MinValidators>
// The expected behavior is to choose all candidates from the previous era.
with_externalities(&mut ExtBuilder::default()
.minimum_validator_count(10)
.validator_count(15)
.nominate(false)
.build(),
|| {
assert_eq!(Staking::era_length(), 1);
assert_eq!(Staking::validator_count(), 15);
assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]);
// trigger era
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
// No one nominates => no one has a proper vote => no change
assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]);
#[test]
fn nominating_and_rewards_should_work() {
// PHRAGMEN OUTPUT: running this test with the reference impl gives:
//
// Votes [('10', 1000, ['10']), ('20', 1000, ['20']), ('30', 1000, ['30']), ('40', 1000, ['40']), ('2', 1000, ['10', '20', '30']), ('4', 1000, ['10', '20', '40'])]
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
// 10 is elected with stake 2200.0 and score 0.0003333333333333333
// 20 is elected with stake 1800.0 and score 0.0005555555555555556
// 10 has load 0.0003333333333333333 and supported
// 10 with stake 1000.0
// 20 has load 0.0005555555555555556 and supported
// 20 with stake 1000.0
// 30 has load 0 and supported
// 30 with stake 0
// 40 has load 0 and supported
// 40 with stake 0
// 2 has load 0.0005555555555555556 and supported
// 10 with stake 600.0 20 with stake 400.0 30 with stake 0.0
// 4 has load 0.0005555555555555556 and supported
// 10 with stake 600.0 20 with stake 400.0 40 with stake 0.0
// Sequential Phragmén with post processing gives
// 10 is elected with stake 2000.0 and score 0.0003333333333333333
// 20 is elected with stake 2000.0 and score 0.0005555555555555556
// 10 has load 0.0003333333333333333 and supported
// 10 with stake 1000.0
// 20 has load 0.0005555555555555556 and supported
// 20 with stake 1000.0
// 30 has load 0 and supported
// 30 with stake 0
// 40 has load 0 and supported
// 40 with stake 0
// 2 has load 0.0005555555555555556 and supported
// 10 with stake 400.0 20 with stake 600.0 30 with stake 0
// 4 has load 0.0005555555555555556 and supported
// 10 with stake 600.0 20 with stake 400.0 40 with stake 0.0
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.validator_pool(true)
.build(),
// initial validators -- everyone is actually even.
assert_eq_uvec!(Session::validators(), vec![40, 30]);
// Set payee to controller
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller));
assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller));
assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller));
// default reward for the first session.
assert_eq!(Staking::current_session_reward(), session_reward);
for i in [1, 2, 3, 4, 5, 10, 11, 20, 21].iter() {
let _ = Balances::make_free_balance_be(i, initial_balance);
// bond two account pairs and state interest in nomination.
assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21, 31]));
assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41]));
assert_eq!(Staking::current_era(), 1);
// 10 and 20 have more votes, they will be chosen by phragmen.
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// OLD validators must have already received some rewards.
assert_eq!(Balances::total_balance(&40), 1 + session_reward);
assert_eq!(Balances::total_balance(&30), 1 + session_reward);
// ------ check the staked value of all parties.
// total expo of 10, with 1200 coming from nominators (externals), according to phragmen.
assert_eq!(Staking::stakers(11).own, 1000);
assert_eq!(Staking::stakers(11).total, 1000 + 798);
// 2 and 4 supported 10, each with stake 600, according to phragmen.
assert_eq!(Staking::stakers(11).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(), vec![399, 399]);
assert_eq!(Staking::stakers(11).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![3, 1]);
// total expo of 20, with 500 coming from nominators (externals), according to phragmen.
assert_eq!(Staking::stakers(21).own, 1000);
assert_eq!(Staking::stakers(21).total, 1000 + 1200);
// 2 and 4 supported 20, each with stake 250, according to phragmen.
assert_eq!(Staking::stakers(21).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(), vec![600, 600]);
assert_eq!(Staking::stakers(21).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![3, 1]);
// They are not chosen anymore
assert_eq!(Staking::stakers(31).total, 0);
assert_eq!(Staking::stakers(41).total, 0);
Session::check_rotate_session(System::block_number());
// next session reward.
let new_session_reward = Staking::session_reward() * Staking::slot_stake();
// nothing else will happen, era ends and rewards are paid again,
// it is expected that nominators will also be paid. See below
// Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11
assert_eq!(Balances::total_balance(&2), initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11));
// Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11
assert_eq!(Balances::total_balance(&4), initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11));
// 10 got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9
assert_eq!(Balances::total_balance(&10), initial_balance + 5*new_session_reward/9 + 2) ;
// 10 got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11
assert_eq!(Balances::total_balance(&20), initial_balance + 5*new_session_reward/11);
fn nominators_also_get_slashed() {
// A nominator should be slashed if the validator they nominated is slashed
with_externalities(&mut ExtBuilder::default().nominate(false).build(), || {
assert_eq!(Staking::era_length(), 1);
// slash happens immediately.
assert_eq!(Staking::offline_slash_grace(), 0);
// Account 10 has not been reported offline
assert_eq!(Staking::slash_count(&10), 0);
// initial validators
assert_eq_uvec!(Session::validators(), vec![20, 10]);
<OfflineSlash<Test>>::put(Perbill::from_percent(12));
// Set payee to controller
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
// give the man some money.
let initial_balance = 1000;
for i in [1, 2, 3, 10].iter() {
let _ = Balances::make_free_balance_be(i, initial_balance);
// 2 will nominate for 10
let nominator_stake = 500;
assert_ok!(Staking::bond(Origin::signed(1), 2, nominator_stake, RewardDestination::default()));
assert_ok!(Staking::nominate(Origin::signed(2), vec![20, 10]));
Kian Peymani
committed
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// Nominator stash didn't collect any.
assert_eq!(Balances::total_balance(&2), initial_balance);
// 10 goes offline
Staking::on_offline_validator(10, 4);
let expo = Staking::stakers(10);
let slash_value = Staking::offline_slash() * expo.total * 2_u64.pow(3);
let total_slash = expo.total.min(slash_value);
let validator_slash = expo.own.min(total_slash);
let nominator_slash = nominator_stake.min(total_slash - validator_slash);
// initial + first era reward + slash
assert_eq!(Balances::total_balance(&10), initial_balance + 10 - validator_slash);
assert_eq!(Balances::total_balance(&2), initial_balance - nominator_slash);
assert!(Staking::forcing_new_era().is_some());
});
}
#[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.
with_externalities(&mut ExtBuilder::default()
|| {
let arbitrary_value = 5;
// 2 = controller, 1 stashed => ok
assert_ok!(Staking::bond(Origin::signed(1), 2, arbitrary_value, RewardDestination::default()));
// 4 = not used so far, 1 stashed => not allowed.
assert_noop!(Staking::bond(Origin::signed(1), 4, arbitrary_value, RewardDestination::default()), "stash already bonded");
// 1 = stashed => attempting to nominate should fail.
assert_noop!(Staking::nominate(Origin::signed(1), vec![1]), "not a controller");
// 2 = controller => nominating should work.
assert_ok!(Staking::nominate(Origin::signed(2), vec![1]));
});
}
#[test]
fn double_controlling_should_fail() {
// should test (in the same order):
// * an account already bonded as controller CANNOT be reused as the controller of another account.
with_externalities(&mut ExtBuilder::default()
.sessions_per_era(2)
.build(),
|| {
let arbitrary_value = 5;
// 2 = controller, 1 stashed => ok
assert_ok!(Staking::bond(Origin::signed(1), 2, arbitrary_value, RewardDestination::default()));
// 2 = controller, 3 stashed (Note that 2 is reused.) => no-op
assert_noop!(Staking::bond(Origin::signed(3), 2, arbitrary_value, RewardDestination::default()), "controller already paired");
});
fn session_and_eras_work() {
with_externalities(&mut ExtBuilder::default()
.sessions_per_era(2)
assert_eq!(Staking::era_length(), 2);
assert_eq!(Staking::sessions_per_era(), 2);
assert_eq!(Staking::last_era_length_change(), 0);
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 0);
// Block 1: No change.
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 1);
assert_eq!(Staking::sessions_per_era(), 2);
assert_eq!(Staking::last_era_length_change(), 0);
assert_eq!(Staking::current_era(), 0);
// Block 2: Simple era change.
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 2);
assert_eq!(Staking::sessions_per_era(), 2);
assert_eq!(Staking::last_era_length_change(), 0);
assert_eq!(Staking::current_era(), 1);
// Block 3: Schedule an era length change; no visible changes.
System::set_block_number(3);
assert_ok!(Staking::set_sessions_per_era(3));
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 3);
assert_eq!(Staking::sessions_per_era(), 2);
assert_eq!(Staking::last_era_length_change(), 0);
assert_eq!(Staking::current_era(), 1);
// Block 4: Era change kicks in.
System::set_block_number(4);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 4);
assert_eq!(Staking::sessions_per_era(), 3);
assert_eq!(Staking::last_era_length_change(), 4);
assert_eq!(Staking::current_era(), 2);
// Block 5: No change.
System::set_block_number(5);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 5);
assert_eq!(Staking::sessions_per_era(), 3);
assert_eq!(Staking::last_era_length_change(), 4);
assert_eq!(Staking::current_era(), 2);
// Block 6: No change.
System::set_block_number(6);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 6);
assert_eq!(Staking::sessions_per_era(), 3);
assert_eq!(Staking::last_era_length_change(), 4);
assert_eq!(Staking::current_era(), 2);
// Block 7: Era increment.
System::set_block_number(7);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::current_index(), 7);
assert_eq!(Staking::sessions_per_era(), 3);
assert_eq!(Staking::last_era_length_change(), 4);
assert_eq!(Staking::current_era(), 3);
});
}
#[test]
fn cannot_transfer_staked_balance() {
// Tests that a stash account cannot transfer funds
with_externalities(&mut ExtBuilder::default().nominate(false).build(), || {
// Confirm account 11 is stashed
assert_eq!(Staking::bonded(&11), Some(10));
// Confirm account 11 has some free balance
assert_eq!(Balances::free_balance(&11), 1000);
// Confirm account 11 (via controller 10) is totally staked
assert_eq!(Staking::stakers(&11).total, 1000);
// Confirm account 11 cannot transfer as a result
assert_noop!(Balances::transfer(Origin::signed(11), 20, 1), "account liquidity restrictions prevent withdrawal");
// 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(Origin::signed(11), 20, 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
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.fare(true)
.build(),
|| {
// Confirm account 21 is stashed
assert_eq!(Staking::bonded(&21), Some(20));
// Confirm account 21 has some free balance
assert_eq!(Balances::free_balance(&21), 2000);
// Confirm account 21 (via controller 20) is totally staked
assert_eq!(Staking::stakers(&21).total, 1000);
// Confirm account 21 can transfer at most 1000
assert_noop!(Balances::transfer(Origin::signed(21), 20, 1001), "account liquidity restrictions prevent withdrawal");
assert_ok!(Balances::transfer(Origin::signed(21), 20, 1000));
});
}
fn cannot_reserve_staked_balance() {
// Checks that a bonded account cannot reserve balance from free balance
with_externalities(&mut ExtBuilder::default().build(), || {
// Confirm account 11 is stashed
assert_eq!(Staking::bonded(&11), Some(10));
// Confirm account 11 has some free balance
assert_eq!(Balances::free_balance(&11), 1000);
// Confirm account 11 (via controller 10) is totally staked
assert_eq!(Staking::stakers(&11).own, 1000);
// Confirm account 11 cannot transfer as a result
assert_noop!(Balances::reserve(&11, 1), "account liquidity restrictions prevent withdrawal");
// Give account 11 extra free balance
let _ = Balances::make_free_balance_be(&11, 10000);
// Confirm account 11 can now reserve balance
assert_ok!(Balances::reserve(&11, 1));
#[test]
fn reward_destination_works() {
// Rewards go to the correct destination as determined in Payee
with_externalities(&mut ExtBuilder::default().nominate(false).build(), || {
// Check that account 11 is a validator
assert!(Staking::current_elected().contains(&11));
// Check the balance of the validator account
assert_eq!(Balances::free_balance(&10), 1);
// Check the balance of the stash account
assert_eq!(Balances::free_balance(&11), 1000);
// Check how much is at stake
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
// Check current session reward is 10
let session_reward0 = Staking::current_session_reward(); // 10
// Move forward the system for payment
System::set_block_number(1);
Timestamp::set_timestamp(5);
Session::check_rotate_session(System::block_number());
// Check that RewardDestination is Staked (default)
assert_eq!(Staking::payee(&11), RewardDestination::Staked);
// Check that reward went to the stash account of validator
assert_eq!(Balances::free_balance(&11), 1000 + session_reward0);
// Check that amount at stake increased accordingly
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
let session_reward1 = Staking::current_session_reward(); // 1010 (1* slot_stake)
//Change RewardDestination to Stash
<Payee<Test>>::insert(&11, RewardDestination::Stash);
// Move forward the system for payment
System::set_block_number(2);
Timestamp::set_timestamp(10);
Session::check_rotate_session(System::block_number());
// Check that RewardDestination is Stash
assert_eq!(Staking::payee(&11), RewardDestination::Stash);
// Check that reward went to the stash account
assert_eq!(Balances::free_balance(&11), 1000 + session_reward0 + session_reward1);
let recorded_stash_balance = 1000 + session_reward0 + session_reward1;
// Check that amount at stake is NOT increased
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
// Change RewardDestination to Controller
<Payee<Test>>::insert(&11, RewardDestination::Controller);
// Check controller balance
assert_eq!(Balances::free_balance(&10), 1);
// Move forward the system for payment
Session::check_rotate_session(System::block_number());
let session_reward2 = Staking::current_session_reward(); // 1010 (1* slot_stake)
// Check that RewardDestination is Controller
assert_eq!(Staking::payee(&11), RewardDestination::Controller);
// Check that reward went to the controller account
assert_eq!(Balances::free_balance(&10), 1 + session_reward2);
// Check that amount at stake is NOT increased
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
// Check that amount in staked account is NOT increased.
assert_eq!(Balances::free_balance(&11), recorded_stash_balance);
fn validator_payment_prefs_work() {
// Test that validator preferences are correctly honored
// Note: unstake threshold is being directly tested in slashing tests.
// This test will focus on validator payment.
with_externalities(&mut ExtBuilder::default()
.session_length(3)
.sessions_per_era(3)
.build(),
let session_reward = 10;
let validator_cut = 5;
let stash_initial_balance = Balances::total_balance(&11);
assert_eq!(Staking::current_session_reward(), session_reward);