@@ -135,7 +135,9 @@ decl_module! {
135
135
pub enum Call where aux: T :: PublicAux {
136
136
fn transfer( aux, dest: RawAddress <T :: AccountId , T :: AccountIndex >, value: T :: Balance ) -> Result = 0 ;
137
137
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 ;
139
141
}
140
142
141
143
#[ cfg_attr( feature = "std" , derive( Serialize , Deserialize ) ) ]
@@ -157,6 +159,7 @@ decl_storage! {
157
159
// The length of a staking era in sessions.
158
160
pub SessionsPerEra get( sessions_per_era) : b"sta:spe" => required T :: BlockNumber ;
159
161
// The total amount of stake on the system.
162
+ // TODO: this doesn't actually track total stake yet - it should do.
160
163
pub TotalStake get( total_stake) : b"sta:tot" => required T :: Balance ;
161
164
// The fee to be paid for making a transaction; the base.
162
165
pub TransactionBaseFee get( transaction_base_fee) : b"sta:basefee" => required T :: Balance ;
@@ -172,7 +175,6 @@ decl_storage! {
172
175
pub CreationFee get( creation_fee) : b"sta:creation_fee" => required T :: Balance ;
173
176
// The fee required to create a contract. At least as big as ReclaimRebate.
174
177
pub ContractFee get( contract_fee) : b"sta:contract_fee" => required T :: Balance ;
175
-
176
178
// Maximum reward, per validator, that is provided per acceptable session.
177
179
pub SessionReward get( session_reward) : b"sta:session_reward" => required T :: Balance ;
178
180
// Slash, per validator that is taken per abnormal era end.
@@ -181,11 +183,19 @@ decl_storage! {
181
183
// The current era index.
182
184
pub CurrentEra get( current_era) : b"sta:era" => required T :: BlockNumber ;
183
185
// 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 > ] ;
185
193
// The next value of sessions per era.
186
194
pub NextSessionsPerEra get( next_sessions_per_era) : b"sta:nse" => T :: BlockNumber ;
187
195
// The session index at which the era length last changed.
188
196
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 ;
189
199
190
200
// The next free enumeration set.
191
201
pub NextEnumSet get( next_enum_set) : b"sta:next_enum" => required T :: AccountIndex ;
@@ -305,27 +315,82 @@ impl<T: Trait> Module<T> {
305
315
///
306
316
/// Effects will be felt at the beginning of the next era.
307
317
fn stake ( aux : & T :: PublicAux ) -> Result {
318
+ let aux = aux. ref_into ( ) ;
319
+ ensure ! ( Self :: nominating( aux) . is_none( ) , "Cannot stake if already nominating." ) ;
308
320
let mut intentions = <Intentions < T > >:: get ( ) ;
309
321
// 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 ( ) ) ;
312
324
<Intentions < T > >:: put ( intentions) ;
313
- <Bondage < T > >:: insert ( aux. ref_into ( ) , T :: BlockNumber :: max_value ( ) ) ;
325
+ <Bondage < T > >:: insert ( aux, T :: BlockNumber :: max_value ( ) ) ;
314
326
Ok ( ( ) )
315
327
}
316
328
317
329
/// Retract the desire to stake for the transactor.
318
330
///
319
331
/// 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 ;
321
335
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
+ }
323
340
intentions. swap_remove ( position) ;
324
341
<Intentions < T > >:: put ( intentions) ;
325
342
<Bondage < T > >:: insert ( aux. ref_into ( ) , Self :: current_era ( ) + Self :: bonding_duration ( ) ) ;
326
343
Ok ( ( ) )
327
344
}
328
345
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
+
329
394
// PRIV DISPATCH
330
395
331
396
/// Set the number of sessions in an era.
@@ -496,20 +561,42 @@ impl<T: Trait> Module<T> {
496
561
let reward = Self :: session_reward ( ) * T :: Balance :: sa ( percent) / T :: Balance :: sa ( 65536usize ) ;
497
562
// apply good session reward
498
563
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
+ }
500
573
}
501
574
} else {
502
575
// slash
503
576
let early_era_slash = Self :: early_era_slash ( ) ;
504
577
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
+ }
506
588
}
507
589
}
508
590
if ( ( session_index - Self :: last_era_length_change ( ) ) % Self :: sessions_per_era ( ) ) . is_zero ( ) || !normal_rotation {
509
591
Self :: new_era ( ) ;
510
592
}
511
593
}
512
594
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
+
513
600
/// The era has changed - enact new staking set.
514
601
///
515
602
/// NOTE: This always happens immediately before a session change to ensure that new validators
@@ -530,17 +617,30 @@ impl<T: Trait> Module<T> {
530
617
// combination of validators, then use session::internal::set_validators().
531
618
// for now, this just orders would-be stakers by their balances and chooses the top-most
532
619
// <ValidatorCount<T>>::get() of them.
620
+ // TODO: this is not sound. this should be moved to an off-chain solution mechanism.
533
621
let mut intentions = <Intentions < T > >:: get ( )
534
622
. into_iter ( )
535
- . map ( |v| ( Self :: voting_balance ( & v) , v) )
623
+ . map ( |v| ( Self :: voting_balance ( & v) + Self :: nomination_balance ( & v ) , v) )
536
624
. collect :: < Vec < _ > > ( ) ;
537
625
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 ( )
540
634
. map ( |( _, v) | v)
541
635
. 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) ;
544
644
}
545
645
546
646
fn enum_set_size ( ) -> T :: AccountIndex {
0 commit comments