Newer
Older
// 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);
// check the balance of a validator accounts.
assert_eq!(Balances::total_balance(&10), 1);
// 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![] }));
})
}
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
#[test]
fn too_many_unbond_calls_should_not_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
// locked at era 0 until 3
for _ in 0..MAX_UNLOCKING_CHUNKS-1 {
assert_ok!(Staking::unbond(Origin::signed(10), 1));
}
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// locked ar era 1 until 4
assert_ok!(Staking::unbond(Origin::signed(10), 1));
// can't do more.
assert_noop!(Staking::unbond(Origin::signed(10), 1), "can not schedule more unlock chunks");
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
assert_noop!(Staking::unbond(Origin::signed(10), 1), "can not schedule more unlock chunks");
// free up.
assert_ok!(Staking::withdraw_unbonded(Origin::signed(10)));
// Can add again.
assert_ok!(Staking::unbond(Origin::signed(10), 1));
assert_eq!(Staking::ledger(&10).unwrap().unlocking.len(), 2);
})
}
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]);
// tests the encapsulated phragmen::elect function.
// 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
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,
let (winners, assignment) = winners.unwrap();
assert_eq!(winners, vec![11, 31]);
assert_eq!(assignment, vec![
(3, vec![(11, 2816371998), (31, 1478595298)]),
(1, vec![(11, 4294967296)]),
]);
check_exposure_all();
})
}
#[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)
.build(),
|| {
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(120, 1000, vec![51, 61]);
bond_nominator(130, 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]);
// Sequential Phragmén with post processing gives
// 10 is elected with stake 3000.0 and score 0.00025
// 20 is elected with stake 2017.7421569824219 and score 0.0005277777777777777
// 30 is elected with stake 2008.8712884829595 and score 0.0003333333333333333
// 40 is elected with stake 2000.0001049958742 and score 0.0005555555555555556
// 50 is elected with stake 2000.0001049958742 and score 0.0003333333333333333
// 60 is elected with stake 1991.128921508789 and score 0.0004444444444444444
// 70 is elected with stake 1982.2574230340813 and score 0.0007222222222222222
assert_eq!(Staking::stakers(11).total, 3000);
assert_eq!(Staking::stakers(21).total, 2209);
assert_eq!(Staking::stakers(31).total, 2027);
assert_eq!(Staking::stakers(41).total, 2010);
assert_eq!(Staking::stakers(51).total, 2010);
assert_eq!(Staking::stakers(61).total, 1998);
assert_eq!(Staking::stakers(71).total, 1983);
})
}
#[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
#[test]
fn phragmen_score_should_be_accurate_on_large_stakes() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build(),
|| {
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(),
|| {
Kian Peymani
committed
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![3, 5]);
bond_nominator(8, u64::max_value()/2, vec![3, 5]);
Kian Peymani
committed
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![4, 2]);
// This test will fail this. Will saturate.
// check_exposure_all();
assert_eq!(Staking::stakers(3).total, u64::max_value());
assert_eq!(Staking::stakers(5).total, u64::max_value());
Kian Peymani
committed
})
}
#[test]
fn phragmen_should_not_overflow_nominators() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build(),
|| {
Kian Peymani
committed
let _ = Staking::chill(Origin::signed(10));
let _ = Staking::chill(Origin::signed(20));
bond_validator(2, u64::max_value()/2);
bond_validator(4, u64::max_value()/2);
bond_nominator(6, u64::max_value(), vec![3, 5]);
bond_nominator(8, u64::max_value(), vec![3, 5]);
Kian Peymani
committed
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![4, 2]);
// Saturate.
assert_eq!(Staking::stakers(3).total, u64::max_value());
assert_eq!(Staking::stakers(5).total, u64::max_value());
Kian Peymani
committed
})
}
#[test]
fn phragmen_should_not_overflow_ultimate() {
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.build(),
|| {
Kian Peymani
committed
bond_validator(2, u64::max_value());
bond_validator(4, u64::max_value());
bond_nominator(6, u64::max_value(), vec![3, 5]);
bond_nominator(8, u64::max_value(), vec![3, 5]);
Kian Peymani
committed
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq_uvec!(Session::validators(), vec![4, 2]);
// Saturate.
assert_eq!(Staking::stakers(3).total, u64::max_value());
assert_eq!(Staking::stakers(5).total, u64::max_value());
Kian Peymani
committed
})
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.minimum_validator_count(1)
.validator_count(20)
.build(),
|| {
let _ = Staking::chill(Origin::signed(10));
let _ = Staking::chill(Origin::signed(20));
let _ = Staking::chill(Origin::signed(30));
let prefix = 200;
bond_validator(prefix + 2, 1);
bond_validator(prefix + 4, 100);
bond_validator(prefix + 6, 1000000);
bond_validator(prefix + 8, 100000000001000);
bond_validator(prefix + 10, 100000000002000);
bond_validator(prefix + 12, 100000000003000);
bond_validator(prefix + 14, 400000000000000);
bond_validator(prefix + 16, 400000000001000);
bond_validator(prefix + 18, 18000000000000000);
bond_validator(prefix + 20, 20000000000000000);
bond_validator(prefix + 22, 500000000000100000);
bond_validator(prefix + 24, 500000000000200000);
bond_nominator(50, 990000000000000000, vec![
prefix + 3,
prefix + 5,
prefix + 7,
prefix + 9,
prefix + 11,
prefix + 13,
prefix + 15,
prefix + 17,
prefix + 19,
prefix + 21,
prefix + 23,
);
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
// For manual inspection
println!("Validators are {:?}", Session::validators());
println!("Validators are {:#?}",
Session::validators()
.iter()
.map(|v| (v.clone(), Staking::stakers(v+1)))
.collect::<Vec<(u64, Exposure<u64, u64>)>>()
);
// Each exposure => total == own + sum(others)
// aside from some error, stake must be divided correctly
let individual_expo_sum: u128 = Session::validators()
.iter()
.map(|v| Staking::stakers(v+1))
.fold(0u128, |s, v| if v.others.len() > 0 { s + v.others[0].value as u128 } else { s });
990000000000000000 - individual_expo_sum < 100,
format!(
"Nominator stake = {} / SUM(individual expo) = {} / diff = {}",
990000000000000000u64,
individual_expo_sum,
990000000000000000 - individual_expo_sum
)