Extended Balance Type for Staking's Election (#2134)

* First draft of extended balance type

* Test cleanup.

* Update staking docs.

* Add a good failing test case for quintill

* Bring back saturating.

* Some final fixes

* A few more.

* Update wasm; Bump spec;

* Re-bump.

* Custom lossy conversion from currency to vote

* remove print

* Fix reverse conversion issue.

* void. Re-trigger ci.
This commit is contained in:
Kian Peymani
2019-03-29 20:00:15 +04:30
committed by Gav Wood
parent 086d55397b
commit 958cc7efea
10 changed files with 474 additions and 389 deletions
+182 -198
View File
@@ -21,8 +21,8 @@
use super::*;
use runtime_io::with_externalities;
use phragmen;
use primitives::Perquintill;
use srml_support::{assert_ok, assert_noop, EnumerableStorageMap};
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};
@@ -54,7 +54,7 @@ fn basic_setup_works() {
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: 1125, own: 1000, others: vec![ IndividualExposure { who: 101, value: 125 }] });
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 }] });
@@ -69,8 +69,7 @@ fn basic_setup_works() {
assert_eq!(Staking::current_session_reward(), 10);
// initial slot_stake
assert_eq!(Staking::slot_stake(), 1125); // Naive
// assert_eq!(Staking::slot_stake(), 1250); // Post-process
assert_eq!(Staking::slot_stake(), 1124); // Naive
// initial slash_count of validators
assert_eq!(Staking::slash_count(&11), 0);
@@ -173,7 +172,7 @@ fn offline_grace_should_delay_slashing() {
assert_ok!(Staking::set_offline_slash_grace(offline_slash_grace));
assert_eq!(Staking::offline_slash_grace(), 1);
// Check unstaked_threshold is 3 (default)
// 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 });
@@ -192,7 +191,7 @@ fn offline_grace_should_delay_slashing() {
Staking::on_offline_validator(10, 1);
assert_eq!(Staking::slash_count(&11), 5);
// User gets slashed
assert_eq!(Balances::free_balance(&11), 0);
assert!(Balances::free_balance(&11) < 70);
// New era is forced
assert!(Staking::forcing_new_era().is_some());
});
@@ -440,7 +439,7 @@ fn staking_should_work() {
.build(),
|| {
// remember + compare this along with the test.
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
assert_ok!(Staking::set_bonding_duration(2));
assert_eq!(Staking::bonding_duration(), 2);
@@ -458,7 +457,7 @@ fn staking_should_work() {
assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default()));
// No effects will be seen so far.
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// --- Block 2:
System::set_block_number(2);
@@ -466,7 +465,7 @@ fn staking_should_work() {
assert_eq!(Staking::current_era(), 0);
// No effects will be seen so far. Era has not been yet triggered.
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// --- Block 3: the validators will now change.
@@ -475,7 +474,7 @@ fn staking_should_work() {
// 2 only voted for 4 and 20
assert_eq!(Session::validators().len(), 2);
assert_eq!(Session::validators(), vec![20, 4]);
assert_eq_uvec!(Session::validators(), vec![20, 4]);
assert_eq!(Staking::current_era(), 1);
@@ -487,14 +486,14 @@ fn staking_should_work() {
Staking::chill(Origin::signed(4)).unwrap();
// nothing should be changed so far.
assert_eq!(Session::validators(), vec![20, 4]);
assert_eq_uvec!(Session::validators(), vec![20, 4]);
assert_eq!(Staking::current_era(), 1);
// --- Block 5: nothing. 4 is still there.
System::set_block_number(5);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![20, 4]);
assert_eq_uvec!(Session::validators(), vec![20, 4]);
assert_eq!(Staking::current_era(), 1);
@@ -503,7 +502,7 @@ fn staking_should_work() {
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 2);
assert_eq!(Session::validators().contains(&4), false);
assert_eq!(Session::validators(), vec![20, 10]);
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![] }));
@@ -528,7 +527,7 @@ fn less_than_needed_candidates_works() {
assert_eq!(Staking::minimum_validator_count(), 1);
// initial validators
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// 10 and 20 are now valid candidates.
// trigger era
@@ -537,7 +536,7 @@ fn less_than_needed_candidates_works() {
assert_eq!(Staking::current_era(), 1);
// both validators will be chosen again. NO election algorithm is even executed.
assert_eq!(Session::validators(), vec![20, 10]);
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![]);
@@ -560,7 +559,7 @@ fn no_candidate_emergency_condition() {
assert_eq!(Staking::validator_count(), 15);
// initial validators
assert_eq!(Session::validators(), vec![10, 20, 30, 40]);
assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]);
// trigger era
System::set_block_number(1);
@@ -568,15 +567,12 @@ fn no_candidate_emergency_condition() {
assert_eq!(Staking::current_era(), 1);
// No one nominates => no one has a proper vote => no change
assert_eq!(Session::validators(), vec![10, 20, 30, 40]);
assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]);
});
}
#[test]
fn nominating_and_rewards_should_work() {
// For now it tests a functionality which somehow overlaps with other tests:
// the fact that the nominator is rewarded properly.
//
// 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'])]
@@ -620,7 +616,7 @@ fn nominating_and_rewards_should_work() {
.build(),
|| {
// initial validators -- everyone is actually even.
assert_eq!(Session::validators(), vec![40, 30]);
assert_eq_uvec!(Session::validators(), vec![40, 30]);
// Set payee to controller
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
@@ -638,9 +634,6 @@ fn nominating_and_rewards_should_work() {
let _ = Balances::make_free_balance_be(i, initial_balance);
}
// record their balances.
for i in 1..5 { assert_eq!(Balances::total_balance(&i), initial_balance); }
// bond two account pairs and state interest in nomination.
// 2 will nominate for 10, 20, 30
assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller));
@@ -654,7 +647,7 @@ fn nominating_and_rewards_should_work() {
assert_eq!(Staking::current_era(), 1);
// 10 and 20 have more votes, they will be chosen by phragmen.
assert_eq!(Session::validators(), vec![20, 10]);
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);
@@ -664,9 +657,9 @@ fn nominating_and_rewards_should_work() {
// 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 + 800);
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![400, 400]);
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);
@@ -687,13 +680,13 @@ fn nominating_and_rewards_should_work() {
// 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
// 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) ;
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);
});
@@ -710,7 +703,7 @@ fn nominators_also_get_slashed() {
// Account 10 has not been reported offline
assert_eq!(Staking::slash_count(&10), 0);
// initial validators
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
<OfflineSlash<Test>>::put(Perbill::from_percent(12));
// Set payee to controller
@@ -753,8 +746,7 @@ fn nominators_also_get_slashed() {
#[test]
fn double_staking_should_fail() {
// should test (in the same order):
// * an account already bonded as controller CAN be reused as the controller of another account.
// * an account already bonded as stash cannot be the controller of another account.
// * 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()
@@ -762,7 +754,6 @@ fn double_staking_should_fail() {
.build(),
|| {
let arbitrary_value = 5;
System::set_block_number(1);
// 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.
@@ -777,16 +768,12 @@ fn double_staking_should_fail() {
#[test]
fn double_controlling_should_fail() {
// should test (in the same order):
// * an account already bonded as controller CAN be reused as the controller of another account.
// * an account already bonded as stash cannot be the controller of another account.
// * an account already bonded as stash cannot nominate.
// * an account already bonded as controller can nominate.
// * 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;
System::set_block_number(1);
// 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
@@ -888,7 +875,7 @@ fn cannot_transfer_staked_balance() {
#[test]
fn cannot_transfer_staked_balance_2() {
// Tests that a stash account cannot transfer funds
// Same test as above but with 20
// 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)
@@ -916,7 +903,7 @@ fn cannot_reserve_staked_balance() {
// 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, 1125);
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");
@@ -931,20 +918,16 @@ fn cannot_reserve_staked_balance() {
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!(<Validators<Test>>::exists(11));
// 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 these two accounts are bonded
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![] }));
// Track current session reward
let mut current_session_reward = Staking::current_session_reward();
// 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);
@@ -953,14 +936,12 @@ fn reward_destination_works() {
// Check that RewardDestination is Staked (default)
assert_eq!(Staking::payee(&11), RewardDestination::Staked);
// Check current session reward is 10
assert_eq!(current_session_reward, 10);
// Check that reward went to the stash account of validator
assert_eq!(Balances::free_balance(&11), 1000 + current_session_reward);
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 + 10, active: 1000 + 10, unlocking: vec![] }));
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
// Update current session reward
current_session_reward = Staking::current_session_reward(); // 1010 (1* slot_stake)
let session_reward1 = Staking::current_session_reward(); // 1010 (1* slot_stake)
//Change RewardDestination to Stash
<Payee<Test>>::insert(&11, RewardDestination::Stash);
@@ -973,12 +954,11 @@ fn reward_destination_works() {
// 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 + 10 + current_session_reward);
assert_eq!(Balances::free_balance(&11), 1000 + session_reward0 + session_reward1);
// Record this value
let recorded_stash_balance = 1000 + 10 + current_session_reward;
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 + 10, active: 1000 + 10, unlocking: vec![] }));
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);
@@ -990,13 +970,14 @@ fn reward_destination_works() {
System::set_block_number(3);
Timestamp::set_timestamp(15);
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 + 1010);
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 + 10, active: 1000 + 10, unlocking: vec![] }));
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);
});
@@ -1012,27 +993,20 @@ fn validator_payment_prefs_work() {
.sessions_per_era(3)
.build(),
|| {
// Initial config
let session_reward = 10;
let validator_cut = 5;
let validator_initial_balance = Balances::total_balance(&11);
// 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);
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), validator_initial_balance);
assert_eq!(Balances::total_balance(&11), stash_initial_balance);
// and the nominator (to-be)
let _ = Balances::make_free_balance_be(&2, 500);
// add a dummy nominator.
// NOTE: this nominator is being added 'manually', use '.nominate()' to do it realistically.
<Stakers<Test>>::insert(&11, Exposure {
own: 500, // equal division indicates that the reward will be equally divided among validator and nominator.
total: 1000,
@@ -1045,10 +1019,9 @@ fn validator_payment_prefs_work() {
});
// ------------ Fast forward
let mut block = 3;
// Block 3 => Session 1 => Era 0
let mut block = 3;
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);
@@ -1059,7 +1032,6 @@ fn validator_payment_prefs_work() {
block = 6; // Block 6 => Session 2 => Era 0
System::set_block_number(block);
Timestamp::set_timestamp(block*5); // a little late.
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 2);
@@ -1069,7 +1041,6 @@ fn validator_payment_prefs_work() {
block = 9; // Block 9 => Session 3 => Era 1
System::set_block_number(block);
Timestamp::set_timestamp(block*5);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::current_index(), 3);
@@ -1077,7 +1048,7 @@ fn validator_payment_prefs_work() {
// 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), validator_initial_balance + shared_cut/2 + validator_cut);
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.
@@ -1112,7 +1083,6 @@ fn bond_extra_works() {
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![] }));
});
}
@@ -1147,14 +1117,7 @@ fn bond_extra_and_withdraw_unbonded_works() {
// confirm that 10 is a normal validator and gets paid at the end of the era.
System::set_block_number(1);
Timestamp::set_timestamp(5);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::current_index(), 1);
// NOTE: despite having .nominate() in extBuilder, 20 doesn't have a share since
// rewards are paid before election in new_era()
assert_eq!(Balances::total_balance(&10), 1 + 10);
// Initial state of 10
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
@@ -1176,13 +1139,12 @@ fn bond_extra_and_withdraw_unbonded_works() {
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![] });
// Note that by this point 10 also have received more rewards, but we don't care now.
// assert_eq!(Balances::total_balance(&10), 1 + 10 + MORE_REWARD);
// 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}] }));
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();
@@ -1190,7 +1152,9 @@ fn bond_extra_and_withdraw_unbonded_works() {
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] }));
// trigger next era.
System::set_block_number(3);Timestamp::set_timestamp(15);Session::check_rotate_session(System::block_number());
System::set_block_number(3);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 3);
assert_eq!(Session::current_index(), 3);
@@ -1200,7 +1164,8 @@ fn bond_extra_and_withdraw_unbonded_works() {
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] }));
// trigger next era.
System::set_block_number(4);Timestamp::set_timestamp(20);Session::check_rotate_session(System::block_number());
System::set_block_number(4);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 4);
assert_eq!(Session::current_index(), 4);
@@ -1212,17 +1177,14 @@ fn bond_extra_and_withdraw_unbonded_works() {
}
#[test]
fn slot_stake_is_least_staked_validator_and_limits_maximum_punishment() {
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
// Note that rewardDestination is the stash account by default
// Note that unlike reward slash will affect free_balance, not the stash account.
with_externalities(&mut ExtBuilder::default()
.nominate(false)
.fare(false)
.build(),
|| {
// Give the man some money.
// Confirm validator count is 2
assert_eq!(Staking::validator_count(), 2);
// Confirm account 10 and 20 are validators
@@ -1245,26 +1207,21 @@ fn slot_stake_is_least_staked_validator_and_limits_maximum_punishment() {
// New era --> rewards are paid --> stakes are changed
System::set_block_number(1);
Timestamp::set_timestamp(5);
Session::check_rotate_session(System::block_number());
assert_eq!(Staking::current_era(), 1);
// -- new balances + reward
assert_eq!(Staking::stakers(&11).total, 1000 + 10);
assert_eq!(Staking::stakers(&21).total, 69 + 10);
// -- Note that rewards are going directly to stash, not as free balance.
assert_eq!(Balances::free_balance(&10), 1000);
assert_eq!(Balances::free_balance(&20), 1000);
// -- slot stake should also be updated.
assert_eq!(Staking::slot_stake(), 79);
// // If 10 gets slashed now, despite having +1000 in stash, it will be slashed byt 79, which is the slot stake
// 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
// Confirm user has been reported
assert_eq!(Staking::slash_count(&11), 4);
// // check the balance of 10 (slash will be deducted from free balance.)
// 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*/);
});
}
@@ -1277,8 +1234,6 @@ fn on_free_balance_zero_stash_removes_validator() {
.existential_deposit(10)
.build(),
|| {
// Check that account 10 is a validator
assert!(<Validators<Test>>::exists(11));
// Check the balance of the validator account
assert_eq!(Balances::free_balance(&10), 256);
// Check the balance of the stash account
@@ -1345,13 +1300,10 @@ fn on_free_balance_zero_stash_removes_nominator() {
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 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));
@@ -1442,13 +1394,9 @@ fn phragmen_poc_works() {
.build(),
|| {
// We don't really care about this. At this point everything is even.
// assert_eq!(Session::validators(), vec![40, 30]);
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] }));
assert_eq!(Staking::ledger(&30), Some(StakingLedger { stash: 31, total: 1000, active: 1000, unlocking: vec![] }));
assert_eq!(Staking::ledger(&40), Some(StakingLedger { stash: 41, total: 1000, active: 1000, unlocking: vec![] }));
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));
@@ -1471,7 +1419,7 @@ fn phragmen_poc_works() {
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// with stake 1666 and 1333 respectively
assert_eq!(Staking::stakers(11).own, 1000);
@@ -1491,22 +1439,55 @@ fn phragmen_poc_works() {
}
#[test]
fn phragmen_election_works_example_2() {
fn phragmen_election_works_with_post_processing() {
// 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
// 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!(Session::validators(), vec![20, 10]);
// no one is a nominator
assert_eq!(<Nominators<Test>>::enumerate().count(), 0 as usize);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// Bond [30, 31] as the third validator
assert_ok!(Staking::bond(Origin::signed(31), 30, 1000, RewardDestination::default()));
assert_ok!(Staking::validate(Origin::signed(30), ValidatorPrefs::default()));
// bond [2,1](A), [4,3](B), as 2 nominators
// Give all of them some balance to be able to bond properly.
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]));
@@ -1539,62 +1520,21 @@ fn phragmen_election_works_example_2() {
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();
// python implementation output:
/*
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
*/
// Check exposures
assert_eq!(winner_10.exposure.total, 1000 + 525);
assert_eq!(winner_10.score, Perquintill::from_quintillionths(487804878048780));
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, Perquintill::from_quintillionths(743902439024390));
assert_eq!(winner_30.score, PerU128::from_max_value(253136882709478613064217695772412937));
assert_eq!(winner_30.exposure.others[0].value, 525);
})
}
#[test]
fn switching_roles() {
// Show: It should be possible to switch between roles (nominator, validator, idle) with minimal overhead.
// 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)
@@ -1603,7 +1543,7 @@ fn switching_roles() {
// Reset reward destination
for i in &[10, 20] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); }
assert_eq!(Session::validators(), vec![20, 10]);
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); }
@@ -1624,42 +1564,43 @@ fn switching_roles() {
Session::check_rotate_session(System::block_number());
// no change
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// new block
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
// no change
assert_eq!(Session::validators(), vec![20, 10]);
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!(Session::validators(), vec![6, 10]);
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
// 6: 1000 self vote
// 20: 1000 self vote + 500 vote
// 2: 2000 self vote + 500 vote.
assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default()));
// 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!(Session::validators(), vec![6, 10]);
assert_eq_uvec!(Session::validators(), vec![6, 10]);
System::set_block_number(5);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![6, 10]);
assert_eq_uvec!(Session::validators(), vec![6, 10]);
// ne era
System::set_block_number(6);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![2, 20]);
assert_eq_uvec!(Session::validators(), vec![2, 20]);
});
}
@@ -1670,7 +1611,7 @@ fn wrong_vote_is_null() {
.validator_pool(true)
.build(),
|| {
assert_eq!(Session::validators(), vec![40, 30]);
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); }
@@ -1686,7 +1627,7 @@ fn wrong_vote_is_null() {
System::set_block_number(1);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
});
}
@@ -1701,13 +1642,12 @@ fn bond_with_no_staked_value() {
.build(), || {
// setup
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller));
let _ = Balances::deposit_creating(&3, 1000);
let initial_balance_2 = Balances::free_balance(&2);
let initial_balance_4 = Balances::free_balance(&4);
// initial validators
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// Stingy validator.
assert_ok!(Staking::bond(Origin::signed(1), 2, 0, RewardDestination::Controller));
@@ -1717,7 +1657,7 @@ fn bond_with_no_staked_value() {
Session::check_rotate_session(System::block_number());
// Not elected even though we want 3.
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// min of 10 and 20.
assert_eq!(Staking::slot_stake(), 1000);
@@ -1726,17 +1666,17 @@ fn bond_with_no_staked_value() {
assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller));
assert_ok!(Staking::nominate(Origin::signed(4), vec![1]));
assert_eq!(Staking::ledger(4), Some(StakingLedger { stash: 3, active: 500, total: 500, unlocking: vec![]}));
// 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());
assert_eq!(Session::validators(), vec![20, 10, 2]);
// Stingy one is selected
assert_eq_uvec!(Session::validators(), vec![20, 10, 2]);
assert_eq!(Staking::stakers(1), Exposure { own: 0, total: 500, others: vec![IndividualExposure { who: 3, value: 500}]});
// New slot stake.
assert_eq!(Staking::slot_stake(), 500);
// no rewards paid to 2 and 4 yet
@@ -1750,13 +1690,12 @@ fn bond_with_no_staked_value() {
// 2 will not get any reward
// 4 will get all the reward share
assert_eq!(Balances::free_balance(&2), initial_balance_2);
// assert_eq!(Balances::free_balance(&4), initial_balance_4 + reward);
assert_eq!(Balances::free_balance(&4), initial_balance_4 + reward);
});
}
#[test]
fn bond_with_little_staked_value() {
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()
@@ -1767,11 +1706,11 @@ fn bond_with_little_staked_value() {
|| {
// setup
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller));
let initial_balance_2 = Balances::free_balance(&2);
let initial_balance_10 = Balances::free_balance(&10);
// initial validators
assert_eq!(Session::validators(), vec![20, 10]);
assert_eq_uvec!(Session::validators(), vec![20, 10]);
// Stingy validator.
assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller));
@@ -1782,24 +1721,25 @@ fn bond_with_little_staked_value() {
// 2 is elected.
// and fucks up the slot stake.
assert_eq!(Session::validators(), vec![20, 10, 2]);
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), 1 + 10);
assert_eq!(Balances::free_balance(&20), 1 + 10);
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!(Session::validators(), vec![20, 10, 2]);
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));
});
}
@@ -1838,13 +1778,13 @@ fn phragmen_linear_worse_case_equalize() {
bond_nominator(112, 1000, vec![51, 61]);
bond_nominator(114, 1000, vec![61, 71]);
assert_eq!(Session::validators(), vec![40, 30]);
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!(Session::validators(), vec![10, 60, 40, 20, 50, 30, 70]);
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
@@ -1866,7 +1806,7 @@ fn phragmen_linear_worse_case_equalize() {
}
#[test]
fn phragmen_chooses_correct_validators() {
fn phragmen_chooses_correct_number_of_validators() {
with_externalities(&mut ExtBuilder::default()
.nominate(true)
.validator_pool(true)
@@ -1874,8 +1814,6 @@ fn phragmen_chooses_correct_validators() {
.validator_count(1)
.build(),
|| {
// 4 validator candidates
// self vote + default account 100 is nominator.
assert_eq!(Staking::validator_count(), 1);
assert_eq!(Session::validators().len(), 1);
@@ -1886,6 +1824,34 @@ fn phragmen_chooses_correct_validators() {
})
}
#[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]);
})
}
#[test]
fn phragmen_should_not_overflow_validators() {
with_externalities(&mut ExtBuilder::default()
@@ -1900,6 +1866,10 @@ fn phragmen_should_not_overflow_validators() {
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>());
};
for i in 1..=8 {
let _ = Balances::make_free_balance_be(&i, u64::max_value());
@@ -1917,7 +1887,9 @@ fn phragmen_should_not_overflow_validators() {
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![4, 2]);
assert_eq_uvec!(Session::validators(), vec![4, 2]);
check_exposure(4);
check_exposure(2);
})
}
@@ -1935,6 +1907,10 @@ fn phragmen_should_not_overflow_nominators() {
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>());
};
let _ = Staking::chill(Origin::signed(10));
let _ = Staking::chill(Origin::signed(20));
@@ -1952,7 +1928,9 @@ fn phragmen_should_not_overflow_nominators() {
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![4, 2]);
assert_eq_uvec!(Session::validators(), vec![4, 2]);
check_exposure(4);
check_exposure(2);
})
}
@@ -1970,6 +1948,10 @@ fn phragmen_should_not_overflow_ultimate() {
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>());
};
for i in 1..=8 {
let _ = Balances::make_free_balance_be(&i, u64::max_value());
@@ -1984,6 +1966,8 @@ fn phragmen_should_not_overflow_ultimate() {
System::set_block_number(2);
Session::check_rotate_session(System::block_number());
assert_eq!(Session::validators(), vec![4, 2]);
assert_eq_uvec!(Session::validators(), vec![4, 2]);
check_exposure(4);
check_exposure(2);
})
}
}