Newer
Older
// check the balance of a validator's stash accounts.
assert_eq!(Balances::total_balance(&11), stash_initial_balance);
let _ = Balances::make_free_balance_be(&2, 500);
<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);
<Validators<Test>>::insert(&11, ValidatorPrefs {
unstake_threshold: 3,
validator_payment: validator_cut
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);
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);
block = 9; // Block 9 => Session 3 => Era 1
System::set_block_number(block);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::current_index(), 3);
// whats left to be shared is the sum of 3 rounds minus the validator's cut.
let shared_cut = 3 * session_reward - validator_cut;
// Validator's payee is Staked account, 11, reward will be paid here.
assert_eq!(Balances::total_balance(&11), stash_initial_balance + shared_cut/2 + validator_cut);
// Controller account will not get any reward.
assert_eq!(Balances::total_balance(&10), 1);
// Rest of the reward will be shared and paid to the nominator in stake.
assert_eq!(Balances::total_balance(&2), 500 + shared_cut/2);
#[test]
fn bond_extra_works() {
// Tests that extra `free_balance` in the stash can be added to stake
// NOTE: this tests only verifies `StakingLedger` for correct updates
// See `bond_extra_and_withdraw_unbonded_works` for more details and updates on `Exposure`.
with_externalities(&mut ExtBuilder::default().build(),
|| {
// Check that account 10 is a validator
assert!(<Validators<Test>>::exists(11));
// Check that account 10 is bonded to account 11
assert_eq!(Staking::bonded(&11), Some(10));
// Check how much is at stake
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
// Give account 11 some large free balance greater than total
let _ = Balances::make_free_balance_be(&11, 1000000);
// Call the bond_extra function from controller, add only 100
assert_ok!(Staking::bond_extra(Origin::signed(11), 100));
// There should be 100 more `total` and `active` in the ledger
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] }));
// Call the bond_extra function with a large number, should handle it
assert_ok!(Staking::bond_extra(Origin::signed(11), u64::max_value()));
// The full amount of the funds should now be in the total and active
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000000, active: 1000000, unlocking: vec![] }));
});
}
#[test]
fn bond_extra_and_withdraw_unbonded_works() {
// * Should test
// * Given an account being bonded [and chosen as a validator](not mandatory)
// * It can add extra funds to the bonded account.
// * it can unbond a portion of its funds from the stash account.
// * Once the unbonding period is done, it can actually take the funds out of the stash.
with_externalities(&mut ExtBuilder::default()
.nominate(false)
|| {
// Set payee to controller. avoids confusion
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
// Set unbonding era (bonding_duration) to 2
assert_ok!(Staking::set_bonding_duration(2));
// Give account 11 some large free balance greater than total
let _ = Balances::make_free_balance_be(&11, 1000000);
// Initial config should be correct
assert_eq!(Staking::sessions_per_era(), 1);
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(&10), 1);
// confirm that 10 is a normal validator and gets paid at the end of the era.
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// Initial state of 10
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
assert_eq!(Staking::stakers(&11), Exposure { total: 1000, own: 1000, others: vec![] });
// deposit the extra 100 units
Staking::bond_extra(Origin::signed(11), 100).unwrap();
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] }));
// Exposure is a snapshot! only updated after the next era update.
assert_ne!(Staking::stakers(&11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] });
System::set_block_number(2);Timestamp::set_timestamp(10);Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 2);
assert_eq!(Session::current_index(), 2);
// ledger should be the same.
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] }));
// Exposure is now updated.
assert_eq!(Staking::stakers(&11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] });
// Unbond almost all of the funds in stash.
Staking::unbond(Origin::signed(10), 1000).unwrap();
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] })
);
// Attempting to free the balances now will fail. 2 eras need to pass.
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] }));
// trigger next era.
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 3);
assert_eq!(Session::current_index(), 3);
// nothing yet
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] }));
// trigger next era.
System::set_block_number(4);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 4);
assert_eq!(Session::current_index(), 4);
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
// Now the value is free and the staking ledger is updated.
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
stash: 11, total: 100, active: 100, unlocking: vec![] }));
})
}
fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment() {
// Test that slot_stake is determined by the least staked validator
// Test that slot_stake is the maximum punishment that can happen to a validator
with_externalities(&mut ExtBuilder::default()
.nominate(false)
// Confirm validator count is 2
assert_eq!(Staking::validator_count(), 2);
// Confirm account 10 and 20 are validators
assert!(<Validators<Test>>::exists(&11) && <Validators<Test>>::exists(&21));
assert_eq!(Staking::stakers(&11).total, 1000);
assert_eq!(Staking::stakers(&21).total, 2000);
// Give the man some money.
let _ = Balances::make_free_balance_be(&10, 1000);
let _ = Balances::make_free_balance_be(&20, 1000);
// We confirm initialized slot_stake is this value
assert_eq!(Staking::slot_stake(), Staking::stakers(&11).total);
// Now lets lower account 20 stake
<Stakers<Test>>::insert(&21, Exposure { total: 69, own: 69, others: vec![] });
assert_eq!(Staking::stakers(&21).total, 69);
<Ledger<Test>>::insert(&20, StakingLedger { stash: 22, total: 69, active: 69, unlocking: vec![] });
// New era --> rewards are paid --> stakes are changed
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Staking::stakers(&11).total, 1000 + 10);
assert_eq!(Staking::stakers(&21).total, 69 + 10);
// -- slot stake should also be updated.
assert_eq!(Staking::slot_stake(), 79);
// If 10 gets slashed now, it will be slashed by 5% of exposure.total * 2.pow(unstake_thresh)
Staking::on_offline_validator(10, 4);
// Confirm user has been reported
assert_eq!(Staking::slash_count(&11), 4);
// check the balance of 10 (slash will be deducted from free balance.)
assert_eq!(Balances::free_balance(&11), 1000 + 10 - 50 /*5% of 1000*/ * 8 /*2**3*/);
#[test]
fn on_free_balance_zero_stash_removes_validator() {
// Tests that validator storage items are cleaned up when stash is empty
// Tests that storage items are untouched when controller is empty
with_externalities(&mut ExtBuilder::default()
.existential_deposit(10)
.build(),
|| {
// Check the balance of the validator account
assert_eq!(Balances::free_balance(&10), 256);
// Check the balance of the stash account
assert_eq!(Balances::free_balance(&11), 256000);
// Check these two accounts are bonded
assert_eq!(Staking::bonded(&11), Some(10));
// Set some storage items which we expect to be cleaned up
// Initiate slash count storage item
Staking::on_offline_validator(10, 1);
// Set payee information
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash));
// Check storage items that should be cleaned up
assert!(<Ledger<Test>>::exists(&10));
assert!(<Bonded<Test>>::exists(&11));
assert!(<Validators<Test>>::exists(&11));
assert!(<SlashCount<Test>>::exists(&11));
assert!(<Payee<Test>>::exists(&11));
// Reduce free_balance of controller to 0
Balances::slash(&10, u64::max_value());
// Check the balance of the stash account has not been touched
assert_eq!(Balances::free_balance(&11), 256000);
// Check these two accounts are still bonded
assert_eq!(Staking::bonded(&11), Some(10));
// Check storage items have not changed
assert!(<Ledger<Test>>::exists(&10));
assert!(<Bonded<Test>>::exists(&11));
assert!(<Validators<Test>>::exists(&11));
assert!(<SlashCount<Test>>::exists(&11));
assert!(<Payee<Test>>::exists(&11));
// Reduce free_balance of stash to 0
Balances::slash(&11, u64::max_value());
assert_eq!(Balances::total_balance(&11), 0);
// Check storage items do not exist
assert!(!<Ledger<Test>>::exists(&10));
assert!(!<Bonded<Test>>::exists(&11));
assert!(!<Validators<Test>>::exists(&11));
assert!(!<Nominators<Test>>::exists(&11));
assert!(!<SlashCount<Test>>::exists(&11));
assert!(!<Payee<Test>>::exists(&11));
});
}
#[test]
fn on_free_balance_zero_stash_removes_nominator() {
// Tests that nominator storage items are cleaned up when stash is empty
// Tests that storage items are untouched when controller is empty
with_externalities(&mut ExtBuilder::default()
.existential_deposit(10)
.build(),
|| {
// Make 10 a nominator
assert_ok!(Staking::nominate(Origin::signed(10), vec![20]));
// Check that account 10 is a nominator
assert!(<Nominators<Test>>::exists(11));
// Check the balance of the nominator account
assert_eq!(Balances::free_balance(&10), 256);
// Check the balance of the stash account
assert_eq!(Balances::free_balance(&11), 256000);
// Set payee information
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash));
// Check storage items that should be cleaned up
assert!(<Ledger<Test>>::exists(&10));
assert!(<Bonded<Test>>::exists(&11));
assert!(<Nominators<Test>>::exists(&11));
assert!(<Payee<Test>>::exists(&11));
// Reduce free_balance of controller to 0
Balances::slash(&10, u64::max_value());
// Check total balance of account 10
assert_eq!(Balances::total_balance(&10), 0);
// Check the balance of the stash account has not been touched
assert_eq!(Balances::free_balance(&11), 256000);
// Check these two accounts are still bonded
assert_eq!(Staking::bonded(&11), Some(10));
// Check storage items have not changed
assert!(<Ledger<Test>>::exists(&10));
assert!(<Bonded<Test>>::exists(&11));
assert!(<Nominators<Test>>::exists(&11));
assert!(<Payee<Test>>::exists(&11));
// Reduce free_balance of stash to 0
Balances::slash(&11, u64::max_value());
assert_eq!(Balances::total_balance(&11), 0);
// Check storage items do not exist
assert!(!<Ledger<Test>>::exists(&10));
assert!(!<Bonded<Test>>::exists(&11));
assert!(!<Validators<Test>>::exists(&11));
assert!(!<Nominators<Test>>::exists(&11));
assert!(!<SlashCount<Test>>::exists(&11));
assert!(!<Payee<Test>>::exists(&11));
}
#[test]
fn phragmen_poc_works() {
// Tests the POC test of the phragmen, mentioned in the paper and reference implementation.
// Initial votes:
// Votes [
// ('2', 500, ['10', '20', '30']),
// ('4', 500, ['10', '20', '40']),
// ('10', 1000, ['10']),
// ('20', 1000, ['20']),
// ('30', 1000, ['30']),
// ('40', 1000, ['40'])]
//
// Sequential Phragmén gives
// 10 is elected with stake 1666.6666666666665 and score 0.0005
// 20 is elected with stake 1333.3333333333333 and score 0.00075
// 2 has load 0.00075 and supported
// 10 with stake 333.3333333333333 20 with stake 166.66666666666666 30 with stake 0.0
// 4 has load 0.00075 and supported
// 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0
// 10 has load 0.0005 and supported
// 10 with stake 1000.0
// 20 has load 0.00075 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
// Sequential Phragmén with post processing gives
// 10 is elected with stake 1500.0 and score 0.0005
// 20 is elected with stake 1500.0 and score 0.00075
// 10 has load 0.0005 and supported
// 10 with stake 1000.0
// 20 has load 0.00075 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.00075 and supported
// 10 with stake 166.66666666666674 20 with stake 333.33333333333326 30 with stake 0
// 4 has load 0.00075 and supported
// 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0
with_externalities(&mut ExtBuilder::default()
.nominate(false)
// We don't really care about this. At this point everything is even.
assert_eq_uvec!(Session::validators(), vec![40, 30]);
// Set payees 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));
// no one is a nominator
assert_eq!(<Nominators<Test>>::enumerate().count(), 0 as usize);
// bond [2,1] / [4,3] a nominator
let _ = Balances::deposit_creating(&1, 1000);
let _ = Balances::deposit_creating(&3, 1000);
assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default()));
assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21, 31]));
assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::default()));
assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41]));
// New era => election algorithm will trigger
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// with stake 1666 and 1333 respectively
assert_eq!(Staking::stakers(11).own, 1000);
assert_eq!(Staking::stakers(11).total, 1000 + 332);
assert_eq!(Staking::stakers(21).own, 1000);
assert_eq!(Staking::stakers(21).total, 1000 + 666);
// Nominator's stake distribution.
assert_eq!(Staking::stakers(11).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(), vec![166, 166]);
assert_eq!(Staking::stakers(11).others.iter().map(|e| e.value).sum::<BalanceOf<Test>>(), 332);
assert_eq!(Staking::stakers(11).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![3, 1]);
assert_eq!(Staking::stakers(21).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(), vec![333, 333]);
assert_eq!(Staking::stakers(21).others.iter().map(|e| e.value).sum::<BalanceOf<Test>>(), 666);
assert_eq!(Staking::stakers(21).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![3, 1]);
fn phragmen_election_works_with_post_processing() {
// tests the encapsulated phragmen::elect function.
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
// Votes [
// ('10', 1000, ['10']),
// ('20', 1000, ['20']),
// ('30', 1000, ['30']),
// ('2', 50, ['10', '20']),
// ('4', 1000, ['10', '30'])
// ]
// Sequential Phragmén gives
// 10 is elected with stake 1705.7377049180327 and score 0.0004878048780487805
// 30 is elected with stake 1344.2622950819673 and score 0.0007439024390243903
// 10 has load 0.0004878048780487805 and supported
// 10 with stake 1000.0
// 20 has load 0 and supported
// 20 with stake 0
// 30 has load 0.0007439024390243903 and supported
// 30 with stake 1000.0
// 2 has load 0.0004878048780487805 and supported
// 10 with stake 50.0 20 with stake 0.0
// 4 has load 0.0007439024390243903 and supported
// 10 with stake 655.7377049180328 30 with stake 344.26229508196724
// Sequential Phragmén with post processing gives
// 10 is elected with stake 1525.0 and score 0.0004878048780487805
// 30 is elected with stake 1525.0 and score 0.0007439024390243903
// 10 has load 0.0004878048780487805 and supported
// 10 with stake 1000.0
// 20 has load 0 and supported
// 20 with stake 0
// 30 has load 0.0007439024390243903 and supported
// 30 with stake 1000.0
// 2 has load 0.0004878048780487805 and supported
// 10 with stake 50.0 20 with stake 0.0
// 4 has load 0.0007439024390243903 and supported
// 10 with stake 475.0 30 with stake 525.0
with_externalities(&mut ExtBuilder::default().nominate(false).build(), || {
// initial setup of 10 and 20, both validators
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// Bond [30, 31] as the third validator
assert_ok!(Staking::bond_extra(Origin::signed(31), 999));
assert_ok!(Staking::validate(Origin::signed(30), ValidatorPrefs::default()));
// bond [2,1](A), [4,3](B), as 2 nominators
for i in &[1, 3] { let _ = Balances::deposit_creating(i, 2000); }
assert_ok!(Staking::bond(Origin::signed(1), 2, 50, RewardDestination::default()));
assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21]));
assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::default()));
assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 31]));
let winners = phragmen::elect::<Test, _, _, _>(
2,
Staking::minimum_validator_count() as usize,
<Validators<Test>>::enumerate(),
<Nominators<Test>>::enumerate(),
Staking::slashable_balance_of,
ElectionConfig::<BalanceOf<Test>> {
tolerance: <BalanceOf<Test>>::sa(10 as u64),
iterations: 10,
}
let winners = winners.unwrap();
assert_eq!(winners.iter().map(|w| w.who).collect::<Vec<BalanceOf<Test>>>(), vec![11, 31]);
let winner_10 = winners.iter().filter(|w| w.who == 11).nth(0).unwrap();
let winner_30 = winners.iter().filter(|w| w.who == 31).nth(0).unwrap();
assert_eq!(winner_10.exposure.total, 1000 + 525);
assert_eq!(winner_10.score, PerU128::from_max_value(165991398498018762665060784113057664));
assert_eq!(winner_10.exposure.others[0].value, 475);
assert_eq!(winner_10.exposure.others[1].value, 50);
assert_eq!(winner_30.exposure.total, 1000 + 525);
assert_eq!(winner_30.score, PerU128::from_max_value(253136882709478613064217695772412937));
assert_eq!(winner_30.exposure.others[0].value, 525);
})
}
#[test]
fn switching_roles() {
// Test that it should be possible to switch between roles (nominator, validator, idle) with minimal overhead.
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.sessions_per_era(3)
.build(),
|| {
// Reset reward destination
for i in &[10, 20] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); }
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// put some money in account that we'll use.
for i in 1..7 { let _ = Balances::deposit_creating(&i, 5000); }
assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 5]));
assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(4), vec![21, 1]));
// add a new validator candidate
assert_ok!(Staking::bond(Origin::signed(5), 6, 1000, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(6), ValidatorPrefs::default()));
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![20, 10]);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// new block --> ne era --> new validators
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
// with current nominators 10 and 5 have the most stake
assert_eq_uvec!(Session::validators(), vec![6, 10]);
// 2 decides to be a validator. Consequences:
assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default()));
// new stakes:
// 10: 1000 self vote
// 20: 1000 self vote + 500 vote
// 6 : 1000 self vote
// 2 : 2000 self vote + 500 vote.
// Winners: 20 and 2
System::set_block_number(4);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![6, 10]);
System::set_block_number(5);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![6, 10]);
System::set_block_number(6);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![2, 20]);
});
}
#[test]
fn wrong_vote_is_null() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.validator_pool(true)
.build(),
|| {
assert_eq_uvec!(Session::validators(), vec![40, 30]);
// put some money in account that we'll use.
for i in 1..3 { let _ = Balances::deposit_creating(&i, 5000); }
// add 1 nominators
assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::default()));
assert_ok!(Staking::nominate(Origin::signed(2), vec![
1, 2, 15, 1000, 25 // crap votes. No effect.
]));
// new block
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![20, 10]);
});
}
#[test]
fn bond_with_no_staked_value() {
// Behavior when someone bonds with no staked value.
// Particularly when she votes and the candidate is elected.
with_externalities(&mut ExtBuilder::default()
.validator_count(3)
.nominate(false)
.minimum_validator_count(1)
.build(), || {
// setup
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
let _ = Balances::deposit_creating(&3, 1000);
let initial_balance_2 = Balances::free_balance(&2);
let initial_balance_4 = Balances::free_balance(&4);
// Stingy validator.
assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default()));
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// Not elected even though we want 3.
assert_eq_uvec!(Session::validators(), vec![30, 20, 10]);
// min of 10, 20 and 30 (30 got a payout into staking so it raised it from 1 to 11).
assert_eq!(Staking::slot_stake(), 11);
// let's make the stingy one elected.
assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(4), vec![1]));
// no rewards paid to 2 and 4 yet
assert_eq!(Balances::free_balance(&2), initial_balance_2);
assert_eq!(Balances::free_balance(&4), initial_balance_4);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
// Stingy one is selected
assert_eq_uvec!(Session::validators(), vec![20, 10, 2]);
assert_eq!(Staking::stakers(1), Exposure { own: 1, total: 501, others: vec![IndividualExposure { who: 3, value: 500}]});
assert_eq!(Staking::slot_stake(), 501);
// no rewards paid to 2 and 4 yet
assert_eq!(Balances::free_balance(&2), initial_balance_2);
assert_eq!(Balances::free_balance(&4), initial_balance_4);
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
let reward = Staking::current_session_reward();
// 2 will not get a reward of only 1
// 4 will get the rest
assert_eq!(Balances::free_balance(&2), initial_balance_2 + 1);
assert_eq!(Balances::free_balance(&4), initial_balance_4 + reward - 1);
});
}
#[test]
fn bond_with_little_staked_value_bounded_by_slot_stake() {
// Behavior when someone bonds with little staked value.
// Particularly when she votes and the candidate is elected.
with_externalities(&mut ExtBuilder::default()
.validator_count(3)
.nominate(false)
.minimum_validator_count(1)
.build(),
|| {
assert_ok!(Staking::chill(Origin::signed(30)));
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
let initial_balance_2 = Balances::free_balance(&2);
let initial_balance_10 = Balances::free_balance(&10);
// Stingy validator.
assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default()));
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// 2 is elected.
// and fucks up the slot stake.
assert_eq_uvec!(Session::validators(), vec![20, 10, 2]);
assert_eq!(Staking::slot_stake(), 1);
// Old ones are rewarded.
assert_eq!(Balances::free_balance(&10), initial_balance_10 + 10);
// no rewards paid to 2. This was initial election.
assert_eq!(Balances::free_balance(&2), initial_balance_2);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![20, 10, 2]);
assert_eq!(Staking::slot_stake(), 1);
let reward = Staking::current_session_reward();
// 2 will not get the full reward, practically 1
assert_eq!(Balances::free_balance(&2), initial_balance_2 + reward.max(1));
// same for 10
assert_eq!(Balances::free_balance(&10), initial_balance_10 + 10 + reward.max(1));
#[test]
#[ignore] // Enable this once post-processing is on.
fn phragmen_linear_worse_case_equalize() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.validator_pool(true)
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
.build(),
|| {
let bond_validator = |a, b| {
let _ = Balances::deposit_creating(&(a-1), b);
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(a), ValidatorPrefs::default()));
};
let bond_nominator = |a, b, v| {
let _ = Balances::deposit_creating(&(a-1), b);
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(a), v));
};
for i in &[10, 20, 30, 40] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); }
bond_validator(50, 1000);
bond_validator(60, 1000);
bond_validator(70, 1000);
bond_nominator(2, 2000, vec![11]);
bond_nominator(4, 1000, vec![11, 21]);
bond_nominator(6, 1000, vec![21, 31]);
bond_nominator(8, 1000, vec![31, 41]);
bond_nominator(110, 1000, vec![41, 51]);
bond_nominator(112, 1000, vec![51, 61]);
bond_nominator(114, 1000, vec![61, 71]);
assert_eq_uvec!(Session::validators(), vec![40, 30]);
assert_ok!(Staking::set_validator_count(7));
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![10, 60, 40, 20, 50, 30, 70]);
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
// Sequential Phragmén with post processing gives
// 10 is elected with stake 3000.0 and score 0.00025
// 30 is elected with stake 2008.8712884829595 and score 0.0003333333333333333
// 50 is elected with stake 2000.0001049958742 and score 0.0003333333333333333
// 60 is elected with stake 1991.128921508789 and score 0.0004444444444444444
// 20 is elected with stake 2017.7421569824219 and score 0.0005277777777777777
// 40 is elected with stake 2000.0001049958742 and score 0.0005555555555555556
// 70 is elected with stake 1982.2574230340813 and score 0.0007222222222222222
assert_eq!(Staking::stakers(11).total, 3000);
assert_eq!(Staking::stakers(31).total, 2035);
assert_eq!(Staking::stakers(51).total, 2000);
assert_eq!(Staking::stakers(61).total, 1968);
assert_eq!(Staking::stakers(21).total, 2035);
assert_eq!(Staking::stakers(41).total, 2024);
assert_eq!(Staking::stakers(71).total, 1936);
})
}
#[test]
fn phragmen_chooses_correct_number_of_validators() {
with_externalities(&mut ExtBuilder::default()
.nominate(true)
.validator_pool(true)
.validator_count(1)
.build(),
|| {
assert_eq!(Staking::validator_count(), 1);
assert_eq!(Session::validators().len(), 1);
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators().len(), 1);
})
}
Kian Peymani
committed
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
#[test]
fn phragmen_score_should_be_accurate_on_large_stakes() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build()
, || {
let bond_validator = |a, b| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(a), ValidatorPrefs::default()));
};
for i in 1..=8 {
let _ = Balances::make_free_balance_be(&i, u64::max_value());
}
bond_validator(2, u64::max_value());
bond_validator(4, u64::max_value());
bond_validator(6, u64::max_value()-1);
bond_validator(8, u64::max_value()-2);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![4, 2]);
})
}
Kian Peymani
committed
#[test]
fn phragmen_should_not_overflow_validators() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build()
, || {
let bond_validator = |a, b| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(a), ValidatorPrefs::default()));
};
let bond_nominator = |a, b, v| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(a), v));
};
let check_exposure = |a| {
let expo = Staking::stakers(&a);
assert_eq!(expo.total, expo.own + expo.others.iter().map(|e| e.value).sum::<u64>());
};
Kian Peymani
committed
for i in 1..=8 {
let _ = Balances::make_free_balance_be(&i, u64::max_value());
}
let _ = Staking::chill(Origin::signed(10));
let _ = Staking::chill(Origin::signed(20));
bond_validator(2, u64::max_value());
bond_validator(4, u64::max_value());
bond_nominator(6, u64::max_value()/2, vec![1, 3]);
bond_nominator(8, u64::max_value()/2, vec![1, 3]);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![4, 2]);
check_exposure(4);
check_exposure(2);
Kian Peymani
committed
})
}
#[test]
fn phragmen_should_not_overflow_nominators() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build()
, || {
let bond_validator = |a, b| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(a), ValidatorPrefs::default()));
};
let bond_nominator = |a, b, v| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(a), v));
};
let check_exposure = |a| {
let expo = Staking::stakers(&a);
assert_eq!(expo.total, expo.own + expo.others.iter().map(|e| e.value).sum::<u64>());
};
Kian Peymani
committed
let _ = Staking::chill(Origin::signed(10));
let _ = Staking::chill(Origin::signed(20));
for i in 1..=8 {
let _ = Balances::make_free_balance_be(&i, u64::max_value());
}
bond_validator(2, u64::max_value()/2);
bond_validator(4, u64::max_value()/2);
bond_nominator(6, u64::max_value(), vec![1, 3]);
bond_nominator(8, u64::max_value(), vec![1, 3]);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![4, 2]);
check_exposure(4);
check_exposure(2);
Kian Peymani
committed
})
}
#[test]
fn phragmen_should_not_overflow_ultimate() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build()
, || {
let bond_validator = |a, b| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::validate(Origin::signed(a), ValidatorPrefs::default()));
};
let bond_nominator = |a, b, v| {
assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(a), v));
};
let check_exposure = |a| {
let expo = Staking::stakers(&a);
assert_eq!(expo.total, expo.own + expo.others.iter().map(|e| e.value).sum::<u64>());
};
Kian Peymani
committed
for i in 1..=8 {
let _ = Balances::make_free_balance_be(&i, u64::max_value());
}
bond_validator(2, u64::max_value());
bond_validator(4, u64::max_value());
bond_nominator(6, u64::max_value(), vec![1, 3]);
bond_nominator(8, u64::max_value(), vec![1, 3]);
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![4, 2]);
check_exposure(4);
check_exposure(2);
Kian Peymani
committed
})