1#![cfg_attr(not(feature = "std"), no_std)]
19
20extern crate alloc;
24
25use crate::currency_to_vote::CurrencyToVote;
26use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec};
27use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, HasCompact, MaxEncodedLen};
28use core::ops::{Add, AddAssign, Sub, SubAssign};
29use scale_info::TypeInfo;
30use sp_runtime::{
31 traits::{AtLeast32BitUnsigned, Zero},
32 Debug, DispatchError, DispatchResult, Perbill, Saturating,
33};
34
35pub mod offence;
36
37pub mod currency_to_vote;
38
39pub type SessionIndex = u32;
41
42pub type EraIndex = u32;
44
45pub type Page = u32;
47#[derive(Clone, Debug)]
52pub enum StakingAccount<AccountId> {
53 Stash(AccountId),
54 Controller(AccountId),
55}
56
57#[cfg(feature = "std")]
58impl<AccountId> From<AccountId> for StakingAccount<AccountId> {
59 fn from(account: AccountId) -> Self {
60 StakingAccount::Stash(account)
61 }
62}
63
64#[derive(Debug, TypeInfo)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone))]
67pub enum StakerStatus<AccountId> {
68 Idle,
70 Validator,
72 Nominator(Vec<AccountId>),
74}
75
76#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
79pub struct Stake<Balance> {
80 pub total: Balance,
90 pub active: Balance,
93}
94
95#[impl_trait_for_tuples::impl_for_tuples(10)]
101pub trait OnStakingUpdate<AccountId, Balance> {
102 fn on_stake_update(_who: &AccountId, _prev_stake: Option<Stake<Balance>>) {}
107
108 fn on_nominator_add(_who: &AccountId) {}
112
113 fn on_nominator_update(_who: &AccountId, _prev_nominations: Vec<AccountId>) {}
119
120 fn on_nominator_remove(_who: &AccountId, _nominations: Vec<AccountId>) {}
125
126 fn on_validator_add(_who: &AccountId) {}
130
131 fn on_validator_update(_who: &AccountId) {}
135
136 fn on_validator_remove(_who: &AccountId) {}
138
139 fn on_unstake(_who: &AccountId) {}
141
142 fn on_slash(
150 _stash: &AccountId,
151 _slashed_active: Balance,
152 _slashed_unlocking: &BTreeMap<EraIndex, Balance>,
153 _slashed_total: Balance,
154 ) {
155 }
156
157 fn on_withdraw(_stash: &AccountId, _amount: Balance) {}
159}
160
161pub trait StakingInterface {
166 type Balance: Sub<Output = Self::Balance>
168 + Ord
169 + PartialEq
170 + Default
171 + Copy
172 + MaxEncodedLen
173 + FullCodec
174 + TypeInfo
175 + Saturating;
176
177 type AccountId: Clone + core::fmt::Debug;
179
180 type CurrencyToVote: CurrencyToVote<Self::Balance>;
182
183 fn minimum_nominator_bond() -> Self::Balance;
188
189 fn minimum_validator_bond() -> Self::Balance;
191
192 fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError>;
199
200 fn bonding_duration() -> EraIndex;
204
205 fn nominator_bonding_duration() -> EraIndex {
216 Self::bonding_duration()
217 }
218
219 fn current_era() -> EraIndex;
223
224 fn stake(who: &Self::AccountId) -> Result<Stake<Self::Balance>, DispatchError>;
226
227 fn total_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
229 Self::stake(who).map(|s| s.total)
230 }
231
232 fn active_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
234 Self::stake(who).map(|s| s.active)
235 }
236
237 fn is_unbonding(who: &Self::AccountId) -> Result<bool, DispatchError> {
239 Self::stake(who).map(|s| s.active != s.total)
240 }
241
242 fn fully_unbond(who: &Self::AccountId) -> DispatchResult {
244 Self::unbond(who, Self::stake(who)?.active)
245 }
246
247 fn bond(who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId)
249 -> DispatchResult;
250
251 fn nominate(who: &Self::AccountId, validators: Vec<Self::AccountId>) -> DispatchResult;
253
254 fn chill(who: &Self::AccountId) -> DispatchResult;
256
257 fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult;
261
262 fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult;
272
273 fn set_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult;
275
276 fn withdraw_unbonded(
280 stash: Self::AccountId,
281 num_slashing_spans: u32,
282 ) -> Result<bool, DispatchError>;
283
284 fn desired_validator_count() -> u32;
286
287 fn election_ongoing() -> bool;
289
290 fn force_unstake(who: Self::AccountId) -> DispatchResult;
292
293 fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool;
295
296 fn status(who: &Self::AccountId) -> Result<StakerStatus<Self::AccountId>, DispatchError>;
298
299 fn is_validator(who: &Self::AccountId) -> bool {
301 Self::status(who).map(|s| matches!(s, StakerStatus::Validator)).unwrap_or(false)
302 }
303
304 fn is_virtual_staker(who: &Self::AccountId) -> bool;
310
311 fn nominations(who: &Self::AccountId) -> Option<Vec<Self::AccountId>> {
313 match Self::status(who) {
314 Ok(StakerStatus::Nominator(t)) => Some(t),
315 _ => None,
316 }
317 }
318
319 fn slash_reward_fraction() -> Perbill;
321
322 #[cfg(feature = "runtime-benchmarks")]
323 fn max_exposure_page_size() -> Page;
324
325 #[cfg(feature = "runtime-benchmarks")]
326 fn add_era_stakers(
327 current_era: &EraIndex,
328 stash: &Self::AccountId,
329 exposures: Vec<(Self::AccountId, Self::Balance)>,
330 );
331
332 #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
334 fn set_era(era: EraIndex);
335}
336
337pub trait StakingUnchecked: StakingInterface {
342 fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult;
346
347 fn virtual_bond(
353 keyless_who: &Self::AccountId,
354 value: Self::Balance,
355 payee: &Self::AccountId,
356 ) -> DispatchResult;
357
358 #[cfg(feature = "runtime-benchmarks")]
362 fn migrate_to_direct_staker(who: &Self::AccountId);
363}
364
365#[derive(
367 PartialEq,
368 Eq,
369 PartialOrd,
370 Ord,
371 Clone,
372 Encode,
373 Decode,
374 DecodeWithMemTracking,
375 Debug,
376 TypeInfo,
377 Copy,
378)]
379pub struct IndividualExposure<AccountId, Balance: HasCompact> {
380 pub who: AccountId,
382 #[codec(compact)]
384 pub value: Balance,
385}
386
387#[derive(
389 PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo,
390)]
391pub struct Exposure<AccountId, Balance: HasCompact> {
392 #[codec(compact)]
394 pub total: Balance,
395 #[codec(compact)]
397 pub own: Balance,
398 pub others: Vec<IndividualExposure<AccountId, Balance>>,
400}
401
402impl<AccountId, Balance: Default + HasCompact> Default for Exposure<AccountId, Balance> {
403 fn default() -> Self {
404 Self { total: Default::default(), own: Default::default(), others: vec![] }
405 }
406}
407
408impl<
409 AccountId: Clone,
410 Balance: HasCompact + AtLeast32BitUnsigned + Copy + codec::MaxEncodedLen,
411 > Exposure<AccountId, Balance>
412{
413 pub fn split_others(&mut self, n_others: u32) -> Self {
421 let head_others: Vec<_> =
422 self.others.drain(..(n_others as usize).min(self.others.len())).collect();
423
424 let total_others_head: Balance = head_others
425 .iter()
426 .fold(Zero::zero(), |acc: Balance, o| acc.saturating_add(o.value));
427
428 self.total = self.total.saturating_sub(total_others_head);
429
430 Self {
431 total: total_others_head.saturating_add(self.own),
432 own: self.own,
433 others: head_others,
434 }
435 }
436
437 pub fn into_pages(
440 self,
441 page_size: Page,
442 ) -> (PagedExposureMetadata<Balance>, Vec<ExposurePage<AccountId, Balance>>) {
443 let individual_chunks = self.others.chunks(page_size as usize);
444 let mut exposure_pages: Vec<ExposurePage<AccountId, Balance>> =
445 Vec::with_capacity(individual_chunks.len());
446
447 for chunk in individual_chunks {
448 let mut page_total: Balance = Zero::zero();
449 let mut others: Vec<IndividualExposure<AccountId, Balance>> =
450 Vec::with_capacity(chunk.len());
451 for individual in chunk.iter() {
452 page_total.saturating_accrue(individual.value);
453 others.push(IndividualExposure {
454 who: individual.who.clone(),
455 value: individual.value,
456 })
457 }
458 exposure_pages.push(ExposurePage { page_total, others });
459 }
460
461 (
462 PagedExposureMetadata {
463 total: self.total,
464 own: self.own,
465 nominator_count: self.others.len() as u32,
466 page_count: exposure_pages.len() as Page,
467 },
468 exposure_pages,
469 )
470 }
471}
472
473#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug, TypeInfo)]
475pub struct ExposurePage<AccountId, Balance: HasCompact> {
476 #[codec(compact)]
478 pub page_total: Balance,
479 pub others: Vec<IndividualExposure<AccountId, Balance>>,
481}
482
483impl<A, B: Default + HasCompact> Default for ExposurePage<A, B> {
484 fn default() -> Self {
485 ExposurePage { page_total: Default::default(), others: vec![] }
486 }
487}
488
489impl<A, B: HasCompact + Default + AddAssign + SubAssign + Clone> From<Vec<IndividualExposure<A, B>>>
491 for ExposurePage<A, B>
492{
493 fn from(exposures: Vec<IndividualExposure<A, B>>) -> Self {
494 exposures.into_iter().fold(ExposurePage::default(), |mut page, e| {
495 page.page_total += e.value.clone();
496 page.others.push(e);
497 page
498 })
499 }
500}
501
502#[derive(
508 PartialEq,
509 Eq,
510 PartialOrd,
511 Ord,
512 Clone,
513 Encode,
514 Decode,
515 Debug,
516 TypeInfo,
517 Default,
518 MaxEncodedLen,
519 Copy,
520)]
521pub struct PagedExposureMetadata<Balance: HasCompact + codec::MaxEncodedLen> {
522 #[codec(compact)]
524 pub total: Balance,
525 #[codec(compact)]
527 pub own: Balance,
528 pub nominator_count: u32,
530 pub page_count: Page,
532}
533
534impl<Balance> PagedExposureMetadata<Balance>
535where
536 Balance: HasCompact
537 + codec::MaxEncodedLen
538 + Add<Output = Balance>
539 + Sub<Output = Balance>
540 + sp_runtime::Saturating
541 + PartialEq
542 + Copy
543 + sp_runtime::traits::Debug,
544{
545 pub fn update_with<Max: sp_core::Get<u32>>(
550 self,
551 others_balance: Balance,
552 others_num: u32,
553 ) -> Self {
554 let page_limit = Max::get().max(1);
555 let new_nominator_count = self.nominator_count.saturating_add(others_num);
556 let new_page_count = new_nominator_count
557 .saturating_add(page_limit)
558 .saturating_sub(1)
559 .saturating_div(page_limit);
560
561 Self {
562 total: self.total.saturating_add(others_balance),
563 own: self.own,
564 nominator_count: new_nominator_count,
565 page_count: new_page_count,
566 }
567 }
568}
569
570#[derive(Clone, Debug)]
580pub struct Agent<T>(T);
581impl<T> From<T> for Agent<T> {
582 fn from(acc: T) -> Self {
583 Agent(acc)
584 }
585}
586
587impl<T> Agent<T> {
588 pub fn get(self) -> T {
589 self.0
590 }
591}
592
593#[derive(Clone, Debug)]
598pub struct Delegator<T>(T);
599impl<T> From<T> for Delegator<T> {
600 fn from(acc: T) -> Self {
601 Delegator(acc)
602 }
603}
604
605impl<T> Delegator<T> {
606 pub fn get(self) -> T {
607 self.0
608 }
609}
610
611pub trait DelegationInterface {
613 type Balance: Sub<Output = Self::Balance>
615 + Ord
616 + PartialEq
617 + Default
618 + Copy
619 + MaxEncodedLen
620 + FullCodec
621 + TypeInfo
622 + Saturating;
623
624 type AccountId: Clone + core::fmt::Debug;
626
627 fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
631
632 fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
635
636 fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance>;
638
639 fn register_agent(
641 agent: Agent<Self::AccountId>,
642 reward_account: &Self::AccountId,
643 ) -> DispatchResult;
644
645 fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult;
649
650 fn delegate(
652 delegator: Delegator<Self::AccountId>,
653 agent: Agent<Self::AccountId>,
654 amount: Self::Balance,
655 ) -> DispatchResult;
656
657 fn withdraw_delegation(
662 delegator: Delegator<Self::AccountId>,
663 agent: Agent<Self::AccountId>,
664 amount: Self::Balance,
665 num_slashing_spans: u32,
666 ) -> DispatchResult;
667
668 fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
673
674 fn delegator_slash(
679 agent: Agent<Self::AccountId>,
680 delegator: Delegator<Self::AccountId>,
681 value: Self::Balance,
682 maybe_reporter: Option<Self::AccountId>,
683 ) -> DispatchResult;
684}
685
686pub trait DelegationMigrator {
689 type Balance: Sub<Output = Self::Balance>
691 + Ord
692 + PartialEq
693 + Default
694 + Copy
695 + MaxEncodedLen
696 + FullCodec
697 + TypeInfo
698 + Saturating;
699
700 type AccountId: Clone + core::fmt::Debug;
702
703 fn migrate_nominator_to_agent(
708 agent: Agent<Self::AccountId>,
709 reward_account: &Self::AccountId,
710 ) -> DispatchResult;
711
712 fn migrate_delegation(
717 agent: Agent<Self::AccountId>,
718 delegator: Delegator<Self::AccountId>,
719 value: Self::Balance,
720 ) -> DispatchResult;
721
722 #[cfg(feature = "runtime-benchmarks")]
726 fn force_kill_agent(agent: Agent<Self::AccountId>);
727}
728
729sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);
730sp_core::generate_feature_enabled_macro!(std_or_benchmarks_enabled, any(feature = "std", feature = "runtime-benchmarks"), $);
731
732#[cfg(test)]
733mod tests {
734 use sp_core::ConstU32;
735
736 use super::*;
737
738 #[test]
739 fn update_with_works() {
740 let metadata = PagedExposureMetadata::<u32> {
741 total: 1000,
742 own: 0, nominator_count: 10,
744 page_count: 1,
745 };
746
747 assert_eq!(
748 metadata.update_with::<ConstU32<10>>(1, 1),
749 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 2 },
750 );
751
752 assert_eq!(
753 metadata.update_with::<ConstU32<5>>(1, 1),
754 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
755 );
756
757 assert_eq!(
758 metadata.update_with::<ConstU32<4>>(1, 1),
759 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
760 );
761
762 assert_eq!(
763 metadata.update_with::<ConstU32<1>>(1, 1),
764 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 11 },
765 );
766 }
767
768 #[test]
769 fn individual_exposures_to_exposure_works() {
770 let exposure_1 = IndividualExposure { who: 1, value: 10u32 };
771 let exposure_2 = IndividualExposure { who: 2, value: 20 };
772 let exposure_3 = IndividualExposure { who: 3, value: 30 };
773
774 let exposure_page: ExposurePage<u32, u32> = vec![exposure_1, exposure_2, exposure_3].into();
775
776 assert_eq!(
777 exposure_page,
778 ExposurePage { page_total: 60, others: vec![exposure_1, exposure_2, exposure_3] },
779 );
780 }
781
782 #[test]
783 fn empty_individual_exposures_to_exposure_works() {
784 let empty_exposures: Vec<IndividualExposure<u32, u32>> = vec![];
785
786 let exposure_page: ExposurePage<u32, u32> = empty_exposures.into();
787 assert_eq!(exposure_page, ExposurePage { page_total: 0, others: vec![] });
788 }
789
790 #[test]
791 fn exposure_split_others_works() {
792 let exposure = Exposure {
793 total: 100,
794 own: 20,
795 others: vec![
796 IndividualExposure { who: 1, value: 20u32 },
797 IndividualExposure { who: 2, value: 20 },
798 IndividualExposure { who: 3, value: 20 },
799 IndividualExposure { who: 4, value: 20 },
800 ],
801 };
802
803 let mut exposure_0 = exposure.clone();
804 let split_exposure = exposure_0.split_others(0);
807 assert_eq!(exposure_0, exposure);
808 assert_eq!(split_exposure, Exposure { total: 20, own: 20, others: vec![] });
809
810 let mut exposure_1 = exposure.clone();
811 let split_exposure = exposure_1.split_others(1);
813 assert_eq!(exposure_1.own, 20);
814 assert_eq!(exposure_1.total, 20 + 3 * 20);
815 assert_eq!(exposure_1.others.len(), 3);
816
817 assert_eq!(split_exposure.own, 20);
818 assert_eq!(split_exposure.total, 20 + 1 * 20);
819 assert_eq!(split_exposure.others.len(), 1);
820
821 let mut exposure_3 = exposure.clone();
822 let split_exposure = exposure_3.split_others(3);
825 assert_eq!(exposure_3.own, 20);
826 assert_eq!(exposure_3.total, 20 + 1 * 20);
827 assert_eq!(exposure_3.others.len(), 1);
828
829 assert_eq!(split_exposure.own, 20);
830 assert_eq!(split_exposure.total, 20 + 3 * 20);
831 assert_eq!(split_exposure.others.len(), 3);
832
833 let mut exposure_max = exposure.clone();
834 let split_exposure = exposure_max.split_others(u32::MAX);
838 assert_eq!(split_exposure, exposure);
839 assert_eq!(exposure_max, Exposure { total: 20, own: 20, others: vec![] });
840 }
841}