Newer
Older
// Copyright 2017-2018 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 srml_support::{assert_ok, assert_noop};
use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext, Origin};
use srml_support::traits::Currency;
fn note_null_offline_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
assert_eq!(Staking::offline_slash_grace(), 0);
assert_eq!(Staking::slash_count(&10), 0);
assert_eq!(Balances::free_balance(&10), 1);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
assert_eq!(Balances::free_balance(&10), 1);
assert!(Staking::forcing_new_era().is_none());
});
}
#[test]
fn invulnerability_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
assert_ok!(Staking::set_invulnerables(vec![10]));
Balances::set_free_balance(&10, 70);
assert_eq!(Staking::offline_slash_grace(), 0);
assert_eq!(Staking::slash_count(&10), 0);
assert_eq!(Balances::free_balance(&10), 70);
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Staking::slash_count(&10), 0);
assert_eq!(Balances::free_balance(&10), 70);
assert!(Staking::forcing_new_era().is_none());
});
}
fn note_offline_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
Balances::set_free_balance(&10, 70);
assert_eq!(Staking::offline_slash_grace(), 0);
assert_eq!(Staking::slash_count(&10), 0);
assert_eq!(Balances::free_balance(&10), 70);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Balances::free_balance(&10), 50);
assert!(Staking::forcing_new_era().is_none());
});
}
#[test]
fn note_offline_exponent_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
Balances::set_free_balance(&10, 150);
assert_eq!(Staking::offline_slash_grace(), 0);
assert_eq!(Staking::slash_count(&10), 0);
assert_eq!(Balances::free_balance(&10), 150);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Balances::free_balance(&10), 130);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Balances::free_balance(&10), 90);
assert!(Staking::forcing_new_era().is_none());
});
}
#[test]
fn note_offline_grace_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
Balances::set_free_balance(&10, 70);
Balances::set_free_balance(&20, 70);
assert_ok!(Staking::set_offline_slash_grace(1));
assert_eq!(Staking::offline_slash_grace(), 1);
assert_eq!(Staking::slash_count(&10), 0);
assert_eq!(Balances::free_balance(&10), 70);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Balances::free_balance(&10), 70);
assert_eq!(Balances::free_balance(&20), 70);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
Staking::on_offline_validator(20, 1);
assert_eq!(Balances::free_balance(&10), 50);
assert_eq!(Balances::free_balance(&20), 70);
assert!(Staking::forcing_new_era().is_none());
});
}
#[test]
fn note_offline_force_unstake_session_change_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
Balances::set_free_balance(&10, 70);
Balances::set_free_balance(&20, 70);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_eq!(Balances::free_balance(&10), 70);
assert_eq!(Staking::intentions(), vec![10, 20, 1]);
assert_eq!(Session::validators(), vec![10, 20]);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Balances::free_balance(&10), 50);
assert_eq!(Staking::slash_count(&10), 1);
assert_eq!(Staking::intentions(), vec![10, 20, 1]);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
assert_eq!(Balances::free_balance(&10), 10);
assert!(Staking::forcing_new_era().is_some());
});
}
#[test]
fn note_offline_auto_unstake_session_change_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
Balances::set_free_balance(&10, 7000);
Balances::set_free_balance(&20, 7000);
assert_ok!(Staking::register_preferences(Origin::signed(10), 0, ValidatorPrefs { unstake_threshold: 1, validator_payment: 0 }));
assert_eq!(Staking::intentions(), vec![10, 20]);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
Staking::on_offline_validator(20, 1);
assert_eq!(Balances::free_balance(&10), 6980);
assert_eq!(Balances::free_balance(&20), 6980);
assert_eq!(Staking::intentions(), vec![10, 20]);
assert!(Staking::forcing_new_era().is_none());
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
Staking::on_offline_validator(20, 1);
assert_eq!(Balances::free_balance(&10), 6940);
assert_eq!(Balances::free_balance(&20), 6940);
assert_eq!(Staking::intentions(), vec![20]);
assert!(Staking::forcing_new_era().is_some());
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(20, 1);
assert_eq!(Balances::free_balance(&10), 6940);
assert_eq!(Balances::free_balance(&20), 6860);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(20, 1);
assert_eq!(Balances::free_balance(&10), 6940);
assert_eq!(Balances::free_balance(&20), 6700);
assert_eq!(Staking::intentions(), vec![0u64; 0]);
});
}
#[test]
fn rewards_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
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!(Balances::total_balance(&10), 1);
System::set_block_number(3);
Timestamp::set_timestamp(15); // on time.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 1);
assert_eq!(Balances::total_balance(&10), 11);
System::set_block_number(6);
Timestamp::set_timestamp(31); // a little late
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 2);
assert_eq!(Balances::total_balance(&10), 20); // less reward
System::set_block_number(9);
Timestamp::set_timestamp(50); // very late
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), 27); // much less reward
});
}
#[test]
fn slashing_should_work() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
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!(Balances::total_balance(&10), 1);
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 1);
assert_eq!(Balances::total_balance(&10), 11);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 2);
assert_eq!(Balances::total_balance(&10), 21);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(10, 1);
Staking::on_offline_validator(20, 1);
assert_eq!(Balances::total_balance(&10), 1);
});
}
#[test]
fn staking_should_work() {
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
assert_eq!(Staking::era_length(), 2);
assert_eq!(Staking::validator_count(), 2);
assert_eq!(Session::validators(), vec![10, 20]);
assert_ok!(Staking::set_bonding_duration(2));
assert_eq!(Staking::bonding_duration(), 2);
// Block 1: Add three validators. No obvious change.
System::set_block_number(1);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_ok!(Staking::stake(Origin::signed(2)));
assert_ok!(Staking::stake(Origin::signed(4)));
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::validators(), vec![10, 20]);
// Block 2: New validator set now.
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::validators(), vec![4, 2]);
// Block 3: Unstake highest, introduce another staker. No change yet.
System::set_block_number(3);
assert_ok!(Staking::stake(Origin::signed(3)));
assert_ok!(Staking::unstake(Origin::signed(4), (Staking::intentions().iter().position(|&x| x == 4).unwrap() as u32).into()));
assert_eq!(Staking::current_era(), 1);
Session::check_rotate_session(System::block_number());
// Block 4: New era - validators change.
System::set_block_number(4);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 2);
assert_eq!(Session::validators(), vec![3, 2]);
// Block 5: Transfer stake from highest to lowest. No change yet.
System::set_block_number(5);
assert_ok!(Balances::transfer(Origin::signed(4), 1, 40));
Session::check_rotate_session(System::block_number());
// Block 6: Lowest now validator.
System::set_block_number(6);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![1, 3]);
// Block 7: Unstake three. No change yet.
System::set_block_number(7);
assert_ok!(Staking::unstake(Origin::signed(3), (Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32).into()));
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![1, 3]);
// Block 8: Back to one and two.
System::set_block_number(8);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![1, 2]);
});
}
#[test]
fn nominating_and_rewards_should_work() {
with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || {
assert_eq!(Staking::era_length(), 1);
assert_eq!(Staking::validator_count(), 2);
assert_eq!(Staking::bonding_duration(), 3);
assert_eq!(Session::validators(), vec![10, 20]);
System::set_block_number(1);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_ok!(Staking::stake(Origin::signed(2)));
assert_ok!(Staking::stake(Origin::signed(3)));
assert_ok!(Staking::nominate(Origin::signed(4), 1));
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3
assert_eq!(Balances::total_balance(&1), 10);
assert_eq!(Balances::total_balance(&2), 20);
assert_eq!(Balances::total_balance(&3), 30);
assert_eq!(Balances::total_balance(&4), 40);
assert_ok!(Staking::unnominate(Origin::signed(4), 0));
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 2);
assert_eq!(Session::validators(), vec![3, 2]);
assert_eq!(Balances::total_balance(&1), 16);
assert_eq!(Balances::total_balance(&2), 20);
assert_eq!(Balances::total_balance(&3), 60);
assert_eq!(Balances::total_balance(&4), 64);
assert_ok!(Staking::stake(Origin::signed(4)));
assert_ok!(Staking::unstake(Origin::signed(3), (Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32).into()));
assert_ok!(Staking::nominate(Origin::signed(3), 1));
Session::check_rotate_session(System::block_number());
assert_eq!(Balances::total_balance(&1), 16);
assert_eq!(Balances::total_balance(&2), 40);
assert_eq!(Balances::total_balance(&3), 80);
assert_eq!(Balances::total_balance(&4), 64);
Session::check_rotate_session(System::block_number());
assert_eq!(Balances::total_balance(&1), 26);
assert_eq!(Balances::total_balance(&2), 40);
assert_eq!(Balances::total_balance(&3), 133);
assert_eq!(Balances::total_balance(&4), 128);
#[test]
fn rewards_with_off_the_table_should_work() {
with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || {
System::set_block_number(1);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_ok!(Staking::nominate(Origin::signed(2), 1));
assert_ok!(Staking::stake(Origin::signed(3)));
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 2, 3
assert_eq!(Balances::total_balance(&1), 10);
assert_eq!(Balances::total_balance(&2), 20);
assert_eq!(Balances::total_balance(&3), 30);
System::set_block_number(2);
assert_ok!(Staking::register_preferences(
Origin::signed(1),
(Staking::intentions().into_iter().position(|i| i == 1).unwrap() as u32).into(),
ValidatorPrefs { unstake_threshold: 3, validator_payment: 4 }
));
assert_eq!(Balances::total_balance(&1), 22);
assert_eq!(Balances::total_balance(&2), 37);
assert_eq!(Balances::total_balance(&3), 60);
#[test]
fn nominating_slashes_should_work() {
with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || {
assert_eq!(Staking::era_length(), 4);
assert_eq!(Staking::validator_count(), 2);
assert_eq!(Staking::bonding_duration(), 12);
assert_eq!(Session::validators(), vec![10, 20]);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
Timestamp::set_timestamp(15);
System::set_block_number(4);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_ok!(Staking::stake(Origin::signed(3)));
assert_ok!(Staking::nominate(Origin::signed(2), 3));
assert_ok!(Staking::nominate(Origin::signed(4), 1));
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2
assert_eq!(Balances::total_balance(&1), 10);
assert_eq!(Balances::total_balance(&2), 20);
assert_eq!(Balances::total_balance(&3), 30);
assert_eq!(Balances::total_balance(&4), 40);
Svyatoslav Nikolsky
committed
System::set_extrinsic_index(1);
Staking::on_offline_validator(1, 1);
Staking::on_offline_validator(3, 1);
assert_eq!(Balances::total_balance(&1), 0); //slashed
assert_eq!(Balances::total_balance(&2), 20); //not slashed
assert_eq!(Balances::total_balance(&3), 10); //slashed
assert_eq!(Balances::total_balance(&4), 30); //slashed
});
}
#[test]
fn double_staking_should_fail() {
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
System::set_block_number(1);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_noop!(Staking::stake(Origin::signed(1)), "Cannot stake if already staked.");
assert_noop!(Staking::nominate(Origin::signed(1), 1), "Cannot nominate if already staked.");
assert_ok!(Staking::nominate(Origin::signed(2), 1));
assert_noop!(Staking::stake(Origin::signed(2)), "Cannot stake if already nominating.");
assert_noop!(Staking::nominate(Origin::signed(2), 1), "Cannot nominate if already nominating.");
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
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 staking_balance_transfer_when_bonded_should_not_work() {
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
Balances::set_free_balance(&1, 111);
assert_ok!(Staking::stake(Origin::signed(1)));
assert_noop!(Balances::transfer(Origin::signed(1), 2, 69), "cannot transfer illiquid funds");
});
}
#[test]
fn deducting_balance_when_bonded_should_not_work() {
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
Balances::set_free_balance(&1, 111);
<Bondage<Test>>::insert(1, 2);
System::set_block_number(1);
assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2));
assert_noop!(Balances::reserve(&1, 69), "cannot transfer illiquid funds");
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
#[test]
fn slash_value_calculation_does_not_overflow() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
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!(Balances::total_balance(&10), 1);
assert_eq!(Staking::intentions(), vec![10, 20]);
assert_eq!(Staking::offline_slash_grace(), 0);
// set validator preferences so the validator doesn't back down after
// slashing.
<ValidatorPreferences<Test>>::insert(10, ValidatorPrefs {
unstake_threshold: u32::max_value(),
validator_payment: 0,
});
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 1);
assert_eq!(Balances::total_balance(&10), 11);
// the balance type is u64, so after slashing 64 times,
// the slash value should have overflowed. add a couple extra for
// good measure with the slash grace.
trait TypeEq {}
impl<A> TypeEq for (A, A) {}
fn assert_type_eq<A: TypeEq>() {}
assert_type_eq::<(u64, <Test as balances::Trait>::Balance)>();
Staking::on_offline_validator(10, 100);
});
}
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
#[test]
fn next_slash_value_calculation_does_not_overflow() {
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
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!(Balances::total_balance(&10), 1);
assert_eq!(Staking::intentions(), vec![10, 20]);
assert_eq!(Staking::offline_slash_grace(), 0);
// set validator preferences so the validator doesn't back down after
// slashing.
<ValidatorPreferences<Test>>::insert(10, ValidatorPrefs {
unstake_threshold: u32::max_value(),
validator_payment: 0,
});
// we have enough balance to cover the last slash before overflow
Balances::set_free_balance(&10, u64::max_value());
assert_eq!(Balances::total_balance(&10), u64::max_value());
// the balance type is u64, so after slashing 64 times,
// the slash value should have overflowed. add a couple extra for
// good measure with the slash grace.
trait TypeEq {}
impl<A> TypeEq for (A, A) {}
fn assert_type_eq<A: TypeEq>() {}
assert_type_eq::<(u64, <Test as balances::Trait>::Balance)>();
// the total slash value should overflow the balance type
// therefore the total validator balance should be slashed
Staking::on_offline_validator(10, 100);
assert_eq!(Balances::total_balance(&10), 0);
});
}