Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
07d82af
Recompile runtime.
gavofyork Jun 22, 2018
d467109
Introduce and enforce block time
gavofyork Jun 22, 2018
f714307
Introduce early session ending.
gavofyork Jun 22, 2018
19fe55f
Report most of staking module
gavofyork Jun 23, 2018
b8f2689
rewards, proper early exit and slashing
gavofyork Jun 23, 2018
5ac27b5
Merge remote-tracking branch 'origin/master' into gav-staking-rewards
gavofyork Jun 24, 2018
2ac1ecd
Fix build & session logic, introduce tests
gavofyork Jun 24, 2018
bd41bbc
Fixed staking tests.
gavofyork Jun 24, 2018
9016bc6
Initial test for reward
gavofyork Jun 25, 2018
7a11706
Merge remote-tracking branch 'origin/master' into gav-staking-rewards
gavofyork Jun 25, 2018
f679c22
Fix test
gavofyork Jun 25, 2018
4fb942f
Tests for slashing
gavofyork Jun 25, 2018
4b19d28
Merge remote-tracking branch 'origin/master' into gav-staking-rewards
gavofyork Jun 25, 2018
1ac443b
Update/fix preset configs
gavofyork Jun 25, 2018
31284c3
Fix some tests.
gavofyork Jun 25, 2018
bc9ad04
Fix some staking tests
gavofyork Jun 25, 2018
bd1b4a4
Minor fix
gavofyork Jun 25, 2018
cdfa86b
minor cleanups
gavofyork Jun 25, 2018
3ce3ca7
Nominating.
gavofyork Jun 25, 2018
c2f95ca
Slash/reward nominators
gavofyork Jun 25, 2018
447538c
Tests for nominating + slash/reward
gavofyork Jun 25, 2018
8cd7914
Merge branch 'master' into gav-staking-rewards
gavofyork Jun 26, 2018
353f51c
Merge branch 'gav-staking-rewards' into gav-delegation
gavofyork Jun 26, 2018
fe75f9a
Fix build
gavofyork Jun 26, 2018
75e160f
Merge branch 'gav-staking-rewards' into gav-delegation
gavofyork Jun 26, 2018
5b92dc1
Rename timestamp::Value -> Moment
gavofyork Jun 27, 2018
924ca4b
Merge remote-tracking branch 'origin/master' into gav-staking-rewards
gavofyork Jun 27, 2018
a3e8fe2
Merge branch 'gav-staking-rewards' into gav-delegation
gavofyork Jun 27, 2018
217ecc1
Avoid double-nominating/staking
gavofyork Jun 28, 2018
71f2a9e
Fix comment
gavofyork Jun 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Tests for nominating + slash/reward
  • Loading branch information
gavofyork committed Jun 25, 2018
commit 447538c72fe76e731a6ebd7f8a1e9599cf356cd8
34 changes: 20 additions & 14 deletions substrate/runtime/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ decl_storage! {
pub Nominating get(nominating): b"sta:nominating" => map [ T::AccountId => T::AccountId ];
// Nominators for a particular account.
pub NominatorsFor get(nominators_for): b"sta:nominators_for" => default map [ T::AccountId => Vec<T::AccountId> ];
// Nominators for a particular account that is in action right now.
pub CurrentNominatorsFor get(current_nominators_for): b"sta:current_nominators_for" => default map [ T::AccountId => Vec<T::AccountId> ];
// The next value of sessions per era.
pub NextSessionsPerEra get(next_sessions_per_era): b"sta:nse" => T::BlockNumber;
// The session index at which the era length last changed.
Expand Down Expand Up @@ -382,8 +384,6 @@ impl<T: Trait> Module<T> {
Ok(())
}

// TODO: force_unnominate and max_nominators

// PRIV DISPATCH

/// Set the number of sessions in an era.
Expand Down Expand Up @@ -554,22 +554,22 @@ impl<T: Trait> Module<T> {
let reward = Self::session_reward() * T::Balance::sa(percent) / T::Balance::sa(65536usize);
// apply good session reward
for v in <session::Module<T>>::validators().iter() {
let noms = Self::nominators_for(v);
let noms = Self::current_nominators_for(v);
let total = noms.iter().map(Self::voting_balance).fold(Self::voting_balance(v), |acc, x| acc + x);
for n in noms.iter() {
//let r = Self::voting_balance(n) * reward / total; // correct formula, but might overflow with large reard * total.
let quant = T::Balance::sa(1usize << 31);
let r = (Self::voting_balance(n) * quant / total) * reward / quant; // avoid overflow by using quant as a denominator.
let _ = Self::reward(n, r); // will never fail as nominator accounts must be created, but even if it did, it's just a missed reward.
if !total.is_zero() {
let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow
for n in noms.iter() {
let _ = Self::reward(n, safe_mul_rational(Self::voting_balance(n)));
}
let _ = Self::reward(v, safe_mul_rational(Self::voting_balance(v)));
}
let _ = Self::reward(v, reward); // will never fail as validator accounts must be created, but even if it did, it's just a missed reward.
}
} else {
// slash
let early_era_slash = Self::early_era_slash();
for v in <session::Module<T>>::validators().iter() {
if let Some(rem) = Self::slash(v, early_era_slash) {
let noms = Self::nominators_for(v);
let noms = Self::current_nominators_for(v);
let total = noms.iter().map(Self::voting_balance).fold(Zero::zero(), |acc, x| acc + x);
for n in noms.iter() {
//let r = Self::voting_balance(n) * reward / total; // correct formula, but might overflow with large reard * total.
Expand All @@ -585,6 +585,7 @@ impl<T: Trait> Module<T> {
}
}

/// Balance of a (potential) validator that includes all nominators.
fn nomination_balance(who: &T::AccountId) -> T::Balance {
Self::nominators_for(who).iter().map(Self::voting_balance).fold(Zero::zero(), |acc, x| acc + x)
}
Expand Down Expand Up @@ -622,12 +623,17 @@ impl<T: Trait> Module<T> {
intentions[i].0.clone()
} else { Zero::zero() }
);
<session::Module<T>>::set_validators(
&intentions.into_iter()
let vals = &intentions.into_iter()
.map(|(_, v)| v)
.take(<ValidatorCount<T>>::get() as usize)
.collect::<Vec<_>>()
);
.collect::<Vec<_>>();
for v in <session::Module<T>>::validators().iter() {
<CurrentNominatorsFor<T>>::remove(v);
}
for v in vals.iter() {
<CurrentNominatorsFor<T>>::insert(v, Self::nominators_for(v));
}
<session::Module<T>>::set_validators(vals);
}

fn enum_set_size() -> T::AccountIndex {
Expand Down
2 changes: 1 addition & 1 deletion substrate/runtime/staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64
contract_fee: 0,
reclaim_rebate: 0,
session_reward: reward,
early_era_slash: if monied { 10 } else { 0 },
early_era_slash: if monied { 20 } else { 0 },
}.build_storage());
t.extend(timestamp::GenesisConfig::<Test>{
period: 5
Expand Down
102 changes: 100 additions & 2 deletions substrate/runtime/staking/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,18 @@ fn slashing_should_work() {
assert_eq!(Session::current_index(), 1);
assert_eq!(Staking::voting_balance(&10), 11);

System::set_block_number(4);
System::set_block_number(6);
Timestamp::set_timestamp(30); // on time.
Session::check_rotate_session();
assert_eq!(Staking::current_era(), 0);
assert_eq!(Session::current_index(), 2);
assert_eq!(Staking::voting_balance(&10), 21);

System::set_block_number(7);
Timestamp::set_timestamp(100); // way too late - early exit.
Session::check_rotate_session();
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::current_index(), 2);
assert_eq!(Session::current_index(), 3);
assert_eq!(Staking::voting_balance(&10), 1);
});
}
Expand Down Expand Up @@ -225,6 +232,97 @@ fn staking_should_work() {
});
}

#[test]
fn nominating_and_rewards_should_work() {
with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || {
assert_eq!(Staking::era_length(), 1);
assert_eq!(Staking::validator_count(), 2);
assert_eq!(Staking::bonding_duration(), 3);
assert_eq!(Session::validators(), vec![10, 20]);

System::set_block_number(1);
assert_ok!(Staking::stake(&1));
assert_ok!(Staking::stake(&2));
assert_ok!(Staking::stake(&3));
assert_ok!(Staking::nominate(&4, 1.into()));
Session::check_rotate_session();
assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3
assert_eq!(Staking::voting_balance(&1), 10);
assert_eq!(Staking::voting_balance(&2), 20);
assert_eq!(Staking::voting_balance(&3), 30);
assert_eq!(Staking::voting_balance(&4), 40);

System::set_block_number(2);
assert_ok!(Staking::unnominate(&4, 0));
Session::check_rotate_session();
assert_eq!(Staking::current_era(), 2);
assert_eq!(Session::validators(), vec![3, 2]);
assert_eq!(Staking::voting_balance(&1), 12);
assert_eq!(Staking::voting_balance(&2), 20);
assert_eq!(Staking::voting_balance(&3), 40);
assert_eq!(Staking::voting_balance(&4), 48);

System::set_block_number(3);
assert_ok!(Staking::stake(&4));
assert_ok!(Staking::unstake(&3, Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
assert_ok!(Staking::nominate(&3, 1.into()));
Session::check_rotate_session();
assert_eq!(Session::validators(), vec![1, 4]);
assert_eq!(Staking::voting_balance(&1), 12);
assert_eq!(Staking::voting_balance(&2), 30);
assert_eq!(Staking::voting_balance(&3), 50);
assert_eq!(Staking::voting_balance(&4), 48);

System::set_block_number(4);
Session::check_rotate_session();
assert_eq!(Staking::voting_balance(&1), 13);
assert_eq!(Staking::voting_balance(&2), 30);
assert_eq!(Staking::voting_balance(&3), 58);
assert_eq!(Staking::voting_balance(&4), 58);
});
}

#[test]
fn nominating_slashes_should_work() {
with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || {
assert_eq!(Staking::era_length(), 4);
assert_eq!(Staking::validator_count(), 2);
assert_eq!(Staking::bonding_duration(), 3);
assert_eq!(Session::validators(), vec![10, 20]);

System::set_block_number(2);
Session::check_rotate_session();

Timestamp::set_timestamp(15);
System::set_block_number(4);
assert_ok!(Staking::stake(&1));
assert_ok!(Staking::stake(&3));
assert_ok!(Staking::nominate(&2, 3.into()));
assert_ok!(Staking::nominate(&4, 1.into()));
Session::check_rotate_session();

assert_eq!(Staking::current_era(), 1);
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2
assert_eq!(Staking::voting_balance(&1), 10);
assert_eq!(Staking::voting_balance(&2), 20);
assert_eq!(Staking::voting_balance(&3), 30);
assert_eq!(Staking::voting_balance(&4), 40);

System::set_block_number(5);
Timestamp::set_timestamp(100); // late
assert_eq!(Session::blocks_remaining(), 1);
assert!(Session::broken_validation());
Session::check_rotate_session();

assert_eq!(Staking::current_era(), 2);
assert_eq!(Staking::voting_balance(&1), 0);
assert_eq!(Staking::voting_balance(&2), 20);
assert_eq!(Staking::voting_balance(&3), 10);
assert_eq!(Staking::voting_balance(&4), 30);
});
}

#[test]
fn staking_eras_work() {
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
Expand Down