Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit abf3761

Browse files
authored
Squashed changes. (#262)
1 parent 0ad8b40 commit abf3761

File tree

3 files changed

+231
-20
lines changed

3 files changed

+231
-20
lines changed

substrate/runtime/staking/src/lib.rs

+115-15
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ decl_module! {
135135
pub enum Call where aux: T::PublicAux {
136136
fn transfer(aux, dest: RawAddress<T::AccountId, T::AccountIndex>, value: T::Balance) -> Result = 0;
137137
fn stake(aux) -> Result = 1;
138-
fn unstake(aux) -> Result = 2;
138+
fn unstake(aux, index: u32) -> Result = 2;
139+
fn nominate(aux, target: RawAddress<T::AccountId, T::AccountIndex>) -> Result = 3;
140+
fn unnominate(aux, target_index: u32) -> Result = 4;
139141
}
140142

141143
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
@@ -157,6 +159,7 @@ decl_storage! {
157159
// The length of a staking era in sessions.
158160
pub SessionsPerEra get(sessions_per_era): b"sta:spe" => required T::BlockNumber;
159161
// The total amount of stake on the system.
162+
// TODO: this doesn't actually track total stake yet - it should do.
160163
pub TotalStake get(total_stake): b"sta:tot" => required T::Balance;
161164
// The fee to be paid for making a transaction; the base.
162165
pub TransactionBaseFee get(transaction_base_fee): b"sta:basefee" => required T::Balance;
@@ -172,7 +175,6 @@ decl_storage! {
172175
pub CreationFee get(creation_fee): b"sta:creation_fee" => required T::Balance;
173176
// The fee required to create a contract. At least as big as ReclaimRebate.
174177
pub ContractFee get(contract_fee): b"sta:contract_fee" => required T::Balance;
175-
176178
// Maximum reward, per validator, that is provided per acceptable session.
177179
pub SessionReward get(session_reward): b"sta:session_reward" => required T::Balance;
178180
// Slash, per validator that is taken per abnormal era end.
@@ -181,11 +183,19 @@ decl_storage! {
181183
// The current era index.
182184
pub CurrentEra get(current_era): b"sta:era" => required T::BlockNumber;
183185
// All the accounts with a desire to stake.
184-
pub Intentions: b"sta:wil:" => default Vec<T::AccountId>;
186+
pub Intentions get(intentions): b"sta:wil:" => default Vec<T::AccountId>;
187+
// All nominator -> nominee relationships.
188+
pub Nominating get(nominating): b"sta:nominating" => map [ T::AccountId => T::AccountId ];
189+
// Nominators for a particular account.
190+
pub NominatorsFor get(nominators_for): b"sta:nominators_for" => default map [ T::AccountId => Vec<T::AccountId> ];
191+
// Nominators for a particular account that is in action right now.
192+
pub CurrentNominatorsFor get(current_nominators_for): b"sta:current_nominators_for" => default map [ T::AccountId => Vec<T::AccountId> ];
185193
// The next value of sessions per era.
186194
pub NextSessionsPerEra get(next_sessions_per_era): b"sta:nse" => T::BlockNumber;
187195
// The session index at which the era length last changed.
188196
pub LastEraLengthChange get(last_era_length_change): b"sta:lec" => default T::BlockNumber;
197+
// The current era stake threshold
198+
pub StakeThreshold get(stake_threshold): b"sta:stake_threshold" => required T::Balance;
189199

190200
// The next free enumeration set.
191201
pub NextEnumSet get(next_enum_set): b"sta:next_enum" => required T::AccountIndex;
@@ -305,27 +315,82 @@ impl<T: Trait> Module<T> {
305315
///
306316
/// Effects will be felt at the beginning of the next era.
307317
fn stake(aux: &T::PublicAux) -> Result {
318+
let aux = aux.ref_into();
319+
ensure!(Self::nominating(aux).is_none(), "Cannot stake if already nominating.");
308320
let mut intentions = <Intentions<T>>::get();
309321
// can't be in the list twice.
310-
ensure!(intentions.iter().find(|&t| t == aux.ref_into()).is_none(), "Cannot stake if already staked.");
311-
intentions.push(aux.ref_into().clone());
322+
ensure!(intentions.iter().find(|&t| t == aux).is_none(), "Cannot stake if already staked.");
323+
intentions.push(aux.clone());
312324
<Intentions<T>>::put(intentions);
313-
<Bondage<T>>::insert(aux.ref_into(), T::BlockNumber::max_value());
325+
<Bondage<T>>::insert(aux, T::BlockNumber::max_value());
314326
Ok(())
315327
}
316328

317329
/// Retract the desire to stake for the transactor.
318330
///
319331
/// Effects will be felt at the beginning of the next era.
320-
fn unstake(aux: &T::PublicAux) -> Result {
332+
fn unstake(aux: &T::PublicAux, position: u32) -> Result {
333+
let aux = aux.ref_into();
334+
let position = position as usize;
321335
let mut intentions = <Intentions<T>>::get();
322-
let position = intentions.iter().position(|t| t == aux.ref_into()).ok_or("Cannot unstake if not already staked.")?;
336+
// let position = intentions.iter().position(|t| t == aux.ref_into()).ok_or("Cannot unstake if not already staked.")?;
337+
if intentions.get(position) != Some(aux) {
338+
return Err("Invalid index")
339+
}
323340
intentions.swap_remove(position);
324341
<Intentions<T>>::put(intentions);
325342
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
326343
Ok(())
327344
}
328345

346+
fn nominate(aux: &T::PublicAux, target: RawAddress<T::AccountId, T::AccountIndex>) -> Result {
347+
let target = Self::lookup(target)?;
348+
let aux = aux.ref_into();
349+
350+
ensure!(Self::nominating(aux).is_none(), "Cannot nominate if already nominating.");
351+
ensure!(Self::intentions().iter().find(|&t| t == aux.ref_into()).is_none(), "Cannot nominate if already staked.");
352+
353+
// update nominators_for
354+
let mut t = Self::nominators_for(&target);
355+
t.push(aux.clone());
356+
<NominatorsFor<T>>::insert(&target, t);
357+
358+
// update nominating
359+
<Nominating<T>>::insert(aux, &target);
360+
361+
// Update bondage
362+
<Bondage<T>>::insert(aux.ref_into(), T::BlockNumber::max_value());
363+
364+
Ok(())
365+
}
366+
367+
/// Will panic if called when source isn't currently nominating target.
368+
/// Updates Nominating, NominatorsFor and NominationBalance.
369+
fn unnominate(aux: &T::PublicAux, target_index: u32) -> Result {
370+
let source = aux.ref_into();
371+
let target_index = target_index as usize;
372+
373+
let target = <Nominating<T>>::get(source).ok_or("Account must be nominating")?;
374+
375+
let mut t = Self::nominators_for(&target);
376+
if t.get(target_index) != Some(source) {
377+
return Err("Invalid target index")
378+
}
379+
380+
// Ok - all valid.
381+
382+
// update nominators_for
383+
t.swap_remove(target_index);
384+
<NominatorsFor<T>>::insert(&target, t);
385+
386+
// update nominating
387+
<Nominating<T>>::remove(source);
388+
389+
// update bondage
390+
<Bondage<T>>::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration());
391+
Ok(())
392+
}
393+
329394
// PRIV DISPATCH
330395

331396
/// Set the number of sessions in an era.
@@ -496,20 +561,42 @@ impl<T: Trait> Module<T> {
496561
let reward = Self::session_reward() * T::Balance::sa(percent) / T::Balance::sa(65536usize);
497562
// apply good session reward
498563
for v in <session::Module<T>>::validators().iter() {
499-
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.
564+
let noms = Self::current_nominators_for(v);
565+
let total = noms.iter().map(Self::voting_balance).fold(Self::voting_balance(v), |acc, x| acc + x);
566+
if !total.is_zero() {
567+
let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow
568+
for n in noms.iter() {
569+
let _ = Self::reward(n, safe_mul_rational(Self::voting_balance(n)));
570+
}
571+
let _ = Self::reward(v, safe_mul_rational(Self::voting_balance(v)));
572+
}
500573
}
501574
} else {
502575
// slash
503576
let early_era_slash = Self::early_era_slash();
504577
for v in <session::Module<T>>::validators().iter() {
505-
Self::slash(v, early_era_slash);
578+
if let Some(rem) = Self::slash(v, early_era_slash) {
579+
let noms = Self::current_nominators_for(v);
580+
let total = noms.iter().map(Self::voting_balance).fold(Zero::zero(), |acc, x| acc + x);
581+
for n in noms.iter() {
582+
//let r = Self::voting_balance(n) * reward / total; // correct formula, but might overflow with large slash * total.
583+
let quant = T::Balance::sa(1usize << 31);
584+
let s = (Self::voting_balance(n) * quant / total) * rem / quant; // avoid overflow by using quant as a denominator.
585+
let _ = Self::slash(n, s); // best effort - not much that can be done on fail.
586+
}
587+
}
506588
}
507589
}
508590
if ((session_index - Self::last_era_length_change()) % Self::sessions_per_era()).is_zero() || !normal_rotation {
509591
Self::new_era();
510592
}
511593
}
512594

595+
/// Balance of a (potential) validator that includes all nominators.
596+
fn nomination_balance(who: &T::AccountId) -> T::Balance {
597+
Self::nominators_for(who).iter().map(Self::voting_balance).fold(Zero::zero(), |acc, x| acc + x)
598+
}
599+
513600
/// The era has changed - enact new staking set.
514601
///
515602
/// NOTE: This always happens immediately before a session change to ensure that new validators
@@ -530,17 +617,30 @@ impl<T: Trait> Module<T> {
530617
// combination of validators, then use session::internal::set_validators().
531618
// for now, this just orders would-be stakers by their balances and chooses the top-most
532619
// <ValidatorCount<T>>::get() of them.
620+
// TODO: this is not sound. this should be moved to an off-chain solution mechanism.
533621
let mut intentions = <Intentions<T>>::get()
534622
.into_iter()
535-
.map(|v| (Self::voting_balance(&v), v))
623+
.map(|v| (Self::voting_balance(&v) + Self::nomination_balance(&v), v))
536624
.collect::<Vec<_>>();
537625
intentions.sort_unstable_by(|&(ref b1, _), &(ref b2, _)| b2.cmp(&b1));
538-
<session::Module<T>>::set_validators(
539-
&intentions.into_iter()
626+
627+
<StakeThreshold<T>>::put(
628+
if intentions.len() > 0 {
629+
let i = (<ValidatorCount<T>>::get() as usize).min(intentions.len() - 1);
630+
intentions[i].0.clone()
631+
} else { Zero::zero() }
632+
);
633+
let vals = &intentions.into_iter()
540634
.map(|(_, v)| v)
541635
.take(<ValidatorCount<T>>::get() as usize)
542-
.collect::<Vec<_>>()
543-
);
636+
.collect::<Vec<_>>();
637+
for v in <session::Module<T>>::validators().iter() {
638+
<CurrentNominatorsFor<T>>::remove(v);
639+
}
640+
for v in vals.iter() {
641+
<CurrentNominatorsFor<T>>::insert(v, Self::nominators_for(v));
642+
}
643+
<session::Module<T>>::set_validators(vals);
544644
}
545645

546646
fn enum_set_size() -> T::AccountIndex {

substrate/runtime/staking/src/mock.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64
9898
contract_fee: 0,
9999
reclaim_rebate: 0,
100100
session_reward: reward,
101-
early_era_slash: if monied { 10 } else { 0 },
101+
early_era_slash: if monied { 20 } else { 0 },
102102
}.build_storage());
103103
t.extend(timestamp::GenesisConfig::<Test>{
104104
period: 5

substrate/runtime/staking/src/tests.rs

+115-4
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,18 @@ fn slashing_should_work() {
7979
assert_eq!(Session::current_index(), 1);
8080
assert_eq!(Staking::voting_balance(&10), 11);
8181

82-
System::set_block_number(4);
82+
System::set_block_number(6);
83+
Timestamp::set_timestamp(30); // on time.
84+
Session::check_rotate_session();
85+
assert_eq!(Staking::current_era(), 0);
86+
assert_eq!(Session::current_index(), 2);
87+
assert_eq!(Staking::voting_balance(&10), 21);
88+
89+
System::set_block_number(7);
8390
Timestamp::set_timestamp(100); // way too late - early exit.
8491
Session::check_rotate_session();
8592
assert_eq!(Staking::current_era(), 1);
86-
assert_eq!(Session::current_index(), 2);
93+
assert_eq!(Session::current_index(), 3);
8794
assert_eq!(Staking::voting_balance(&10), 1);
8895
});
8996
}
@@ -192,7 +199,7 @@ fn staking_should_work() {
192199
// Block 3: Unstake highest, introduce another staker. No change yet.
193200
System::set_block_number(3);
194201
assert_ok!(Staking::stake(&3));
195-
assert_ok!(Staking::unstake(&4));
202+
assert_ok!(Staking::unstake(&4, Staking::intentions().iter().position(|&x| x == 4).unwrap() as u32));
196203
assert_eq!(Staking::current_era(), 1);
197204
Session::check_rotate_session();
198205

@@ -214,7 +221,7 @@ fn staking_should_work() {
214221

215222
// Block 7: Unstake three. No change yet.
216223
System::set_block_number(7);
217-
assert_ok!(Staking::unstake(&3));
224+
assert_ok!(Staking::unstake(&3, Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
218225
Session::check_rotate_session();
219226
assert_eq!(Session::validators(), vec![1, 3]);
220227

@@ -225,6 +232,110 @@ fn staking_should_work() {
225232
});
226233
}
227234

235+
#[test]
236+
fn nominating_and_rewards_should_work() {
237+
with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || {
238+
assert_eq!(Staking::era_length(), 1);
239+
assert_eq!(Staking::validator_count(), 2);
240+
assert_eq!(Staking::bonding_duration(), 3);
241+
assert_eq!(Session::validators(), vec![10, 20]);
242+
243+
System::set_block_number(1);
244+
assert_ok!(Staking::stake(&1));
245+
assert_ok!(Staking::stake(&2));
246+
assert_ok!(Staking::stake(&3));
247+
assert_ok!(Staking::nominate(&4, 1.into()));
248+
Session::check_rotate_session();
249+
assert_eq!(Staking::current_era(), 1);
250+
assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3
251+
assert_eq!(Staking::voting_balance(&1), 10);
252+
assert_eq!(Staking::voting_balance(&2), 20);
253+
assert_eq!(Staking::voting_balance(&3), 30);
254+
assert_eq!(Staking::voting_balance(&4), 40);
255+
256+
System::set_block_number(2);
257+
assert_ok!(Staking::unnominate(&4, 0));
258+
Session::check_rotate_session();
259+
assert_eq!(Staking::current_era(), 2);
260+
assert_eq!(Session::validators(), vec![3, 2]);
261+
assert_eq!(Staking::voting_balance(&1), 12);
262+
assert_eq!(Staking::voting_balance(&2), 20);
263+
assert_eq!(Staking::voting_balance(&3), 40);
264+
assert_eq!(Staking::voting_balance(&4), 48);
265+
266+
System::set_block_number(3);
267+
assert_ok!(Staking::stake(&4));
268+
assert_ok!(Staking::unstake(&3, Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
269+
assert_ok!(Staking::nominate(&3, 1.into()));
270+
Session::check_rotate_session();
271+
assert_eq!(Session::validators(), vec![1, 4]);
272+
assert_eq!(Staking::voting_balance(&1), 12);
273+
assert_eq!(Staking::voting_balance(&2), 30);
274+
assert_eq!(Staking::voting_balance(&3), 50);
275+
assert_eq!(Staking::voting_balance(&4), 48);
276+
277+
System::set_block_number(4);
278+
Session::check_rotate_session();
279+
assert_eq!(Staking::voting_balance(&1), 13);
280+
assert_eq!(Staking::voting_balance(&2), 30);
281+
assert_eq!(Staking::voting_balance(&3), 58);
282+
assert_eq!(Staking::voting_balance(&4), 58);
283+
});
284+
}
285+
286+
#[test]
287+
fn nominating_slashes_should_work() {
288+
with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || {
289+
assert_eq!(Staking::era_length(), 4);
290+
assert_eq!(Staking::validator_count(), 2);
291+
assert_eq!(Staking::bonding_duration(), 3);
292+
assert_eq!(Session::validators(), vec![10, 20]);
293+
294+
System::set_block_number(2);
295+
Session::check_rotate_session();
296+
297+
Timestamp::set_timestamp(15);
298+
System::set_block_number(4);
299+
assert_ok!(Staking::stake(&1));
300+
assert_ok!(Staking::stake(&3));
301+
assert_ok!(Staking::nominate(&2, 3.into()));
302+
assert_ok!(Staking::nominate(&4, 1.into()));
303+
Session::check_rotate_session();
304+
305+
assert_eq!(Staking::current_era(), 1);
306+
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2
307+
assert_eq!(Staking::voting_balance(&1), 10);
308+
assert_eq!(Staking::voting_balance(&2), 20);
309+
assert_eq!(Staking::voting_balance(&3), 30);
310+
assert_eq!(Staking::voting_balance(&4), 40);
311+
312+
System::set_block_number(5);
313+
Timestamp::set_timestamp(100); // late
314+
assert_eq!(Session::blocks_remaining(), 1);
315+
assert!(Session::broken_validation());
316+
Session::check_rotate_session();
317+
318+
assert_eq!(Staking::current_era(), 2);
319+
assert_eq!(Staking::voting_balance(&1), 0);
320+
assert_eq!(Staking::voting_balance(&2), 20);
321+
assert_eq!(Staking::voting_balance(&3), 10);
322+
assert_eq!(Staking::voting_balance(&4), 30);
323+
});
324+
}
325+
326+
#[test]
327+
fn double_staking_should_fail() {
328+
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
329+
System::set_block_number(1);
330+
assert_ok!(Staking::stake(&1));
331+
assert_noop!(Staking::stake(&1), "Cannot stake if already staked.");
332+
assert_noop!(Staking::nominate(&1, 1.into()), "Cannot nominate if already staked.");
333+
assert_ok!(Staking::nominate(&2, 1.into()));
334+
assert_noop!(Staking::stake(&2), "Cannot stake if already nominating.");
335+
assert_noop!(Staking::nominate(&2, 1.into()), "Cannot nominate if already nominating.");
336+
});
337+
}
338+
228339
#[test]
229340
fn staking_eras_work() {
230341
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {

0 commit comments

Comments
 (0)