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 DispatchError, DispatchResult, Perbill, RuntimeDebug, 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(RuntimeDebug, 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(RuntimeDebug, 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 RuntimeDebug,
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,
390 Eq,
391 PartialOrd,
392 Ord,
393 Clone,
394 Encode,
395 Decode,
396 DecodeWithMemTracking,
397 RuntimeDebug,
398 TypeInfo,
399)]
400pub struct Exposure<AccountId, Balance: HasCompact> {
401 #[codec(compact)]
403 pub total: Balance,
404 #[codec(compact)]
406 pub own: Balance,
407 pub others: Vec<IndividualExposure<AccountId, Balance>>,
409}
410
411impl<AccountId, Balance: Default + HasCompact> Default for Exposure<AccountId, Balance> {
412 fn default() -> Self {
413 Self { total: Default::default(), own: Default::default(), others: vec![] }
414 }
415}
416
417impl<
418 AccountId: Clone,
419 Balance: HasCompact + AtLeast32BitUnsigned + Copy + codec::MaxEncodedLen,
420 > Exposure<AccountId, Balance>
421{
422 pub fn split_others(&mut self, n_others: u32) -> Self {
430 let head_others: Vec<_> =
431 self.others.drain(..(n_others as usize).min(self.others.len())).collect();
432
433 let total_others_head: Balance = head_others
434 .iter()
435 .fold(Zero::zero(), |acc: Balance, o| acc.saturating_add(o.value));
436
437 self.total = self.total.saturating_sub(total_others_head);
438
439 Self {
440 total: total_others_head.saturating_add(self.own),
441 own: self.own,
442 others: head_others,
443 }
444 }
445
446 pub fn into_pages(
449 self,
450 page_size: Page,
451 ) -> (PagedExposureMetadata<Balance>, Vec<ExposurePage<AccountId, Balance>>) {
452 let individual_chunks = self.others.chunks(page_size as usize);
453 let mut exposure_pages: Vec<ExposurePage<AccountId, Balance>> =
454 Vec::with_capacity(individual_chunks.len());
455
456 for chunk in individual_chunks {
457 let mut page_total: Balance = Zero::zero();
458 let mut others: Vec<IndividualExposure<AccountId, Balance>> =
459 Vec::with_capacity(chunk.len());
460 for individual in chunk.iter() {
461 page_total.saturating_accrue(individual.value);
462 others.push(IndividualExposure {
463 who: individual.who.clone(),
464 value: individual.value,
465 })
466 }
467 exposure_pages.push(ExposurePage { page_total, others });
468 }
469
470 (
471 PagedExposureMetadata {
472 total: self.total,
473 own: self.own,
474 nominator_count: self.others.len() as u32,
475 page_count: exposure_pages.len() as Page,
476 },
477 exposure_pages,
478 )
479 }
480}
481
482#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
484pub struct ExposurePage<AccountId, Balance: HasCompact> {
485 #[codec(compact)]
487 pub page_total: Balance,
488 pub others: Vec<IndividualExposure<AccountId, Balance>>,
490}
491
492impl<A, B: Default + HasCompact> Default for ExposurePage<A, B> {
493 fn default() -> Self {
494 ExposurePage { page_total: Default::default(), others: vec![] }
495 }
496}
497
498impl<A, B: HasCompact + Default + AddAssign + SubAssign + Clone> From<Vec<IndividualExposure<A, B>>>
500 for ExposurePage<A, B>
501{
502 fn from(exposures: Vec<IndividualExposure<A, B>>) -> Self {
503 exposures.into_iter().fold(ExposurePage::default(), |mut page, e| {
504 page.page_total += e.value.clone();
505 page.others.push(e);
506 page
507 })
508 }
509}
510
511#[derive(
517 PartialEq,
518 Eq,
519 PartialOrd,
520 Ord,
521 Clone,
522 Encode,
523 Decode,
524 RuntimeDebug,
525 TypeInfo,
526 Default,
527 MaxEncodedLen,
528 Copy,
529)]
530pub struct PagedExposureMetadata<Balance: HasCompact + codec::MaxEncodedLen> {
531 #[codec(compact)]
533 pub total: Balance,
534 #[codec(compact)]
536 pub own: Balance,
537 pub nominator_count: u32,
539 pub page_count: Page,
541}
542
543impl<Balance> PagedExposureMetadata<Balance>
544where
545 Balance: HasCompact
546 + codec::MaxEncodedLen
547 + Add<Output = Balance>
548 + Sub<Output = Balance>
549 + sp_runtime::Saturating
550 + PartialEq
551 + Copy
552 + sp_runtime::traits::Debug,
553{
554 pub fn update_with<Max: sp_core::Get<u32>>(
559 self,
560 others_balance: Balance,
561 others_num: u32,
562 ) -> Self {
563 let page_limit = Max::get().max(1);
564 let new_nominator_count = self.nominator_count.saturating_add(others_num);
565 let new_page_count = new_nominator_count
566 .saturating_add(page_limit)
567 .saturating_sub(1)
568 .saturating_div(page_limit);
569
570 Self {
571 total: self.total.saturating_add(others_balance),
572 own: self.own,
573 nominator_count: new_nominator_count,
574 page_count: new_page_count,
575 }
576 }
577}
578
579#[derive(Clone, Debug)]
589pub struct Agent<T>(T);
590impl<T> From<T> for Agent<T> {
591 fn from(acc: T) -> Self {
592 Agent(acc)
593 }
594}
595
596impl<T> Agent<T> {
597 pub fn get(self) -> T {
598 self.0
599 }
600}
601
602#[derive(Clone, Debug)]
607pub struct Delegator<T>(T);
608impl<T> From<T> for Delegator<T> {
609 fn from(acc: T) -> Self {
610 Delegator(acc)
611 }
612}
613
614impl<T> Delegator<T> {
615 pub fn get(self) -> T {
616 self.0
617 }
618}
619
620pub trait DelegationInterface {
622 type Balance: Sub<Output = Self::Balance>
624 + Ord
625 + PartialEq
626 + Default
627 + Copy
628 + MaxEncodedLen
629 + FullCodec
630 + TypeInfo
631 + Saturating;
632
633 type AccountId: Clone + core::fmt::Debug;
635
636 fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
640
641 fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
644
645 fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance>;
647
648 fn register_agent(
650 agent: Agent<Self::AccountId>,
651 reward_account: &Self::AccountId,
652 ) -> DispatchResult;
653
654 fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult;
658
659 fn delegate(
661 delegator: Delegator<Self::AccountId>,
662 agent: Agent<Self::AccountId>,
663 amount: Self::Balance,
664 ) -> DispatchResult;
665
666 fn withdraw_delegation(
671 delegator: Delegator<Self::AccountId>,
672 agent: Agent<Self::AccountId>,
673 amount: Self::Balance,
674 num_slashing_spans: u32,
675 ) -> DispatchResult;
676
677 fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
682
683 fn delegator_slash(
688 agent: Agent<Self::AccountId>,
689 delegator: Delegator<Self::AccountId>,
690 value: Self::Balance,
691 maybe_reporter: Option<Self::AccountId>,
692 ) -> DispatchResult;
693}
694
695pub trait DelegationMigrator {
698 type Balance: Sub<Output = Self::Balance>
700 + Ord
701 + PartialEq
702 + Default
703 + Copy
704 + MaxEncodedLen
705 + FullCodec
706 + TypeInfo
707 + Saturating;
708
709 type AccountId: Clone + core::fmt::Debug;
711
712 fn migrate_nominator_to_agent(
717 agent: Agent<Self::AccountId>,
718 reward_account: &Self::AccountId,
719 ) -> DispatchResult;
720
721 fn migrate_delegation(
726 agent: Agent<Self::AccountId>,
727 delegator: Delegator<Self::AccountId>,
728 value: Self::Balance,
729 ) -> DispatchResult;
730
731 #[cfg(feature = "runtime-benchmarks")]
735 fn force_kill_agent(agent: Agent<Self::AccountId>);
736}
737
738sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);
739sp_core::generate_feature_enabled_macro!(std_or_benchmarks_enabled, any(feature = "std", feature = "runtime-benchmarks"), $);
740
741#[cfg(test)]
742mod tests {
743 use sp_core::ConstU32;
744
745 use super::*;
746
747 #[test]
748 fn update_with_works() {
749 let metadata = PagedExposureMetadata::<u32> {
750 total: 1000,
751 own: 0, nominator_count: 10,
753 page_count: 1,
754 };
755
756 assert_eq!(
757 metadata.update_with::<ConstU32<10>>(1, 1),
758 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 2 },
759 );
760
761 assert_eq!(
762 metadata.update_with::<ConstU32<5>>(1, 1),
763 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
764 );
765
766 assert_eq!(
767 metadata.update_with::<ConstU32<4>>(1, 1),
768 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
769 );
770
771 assert_eq!(
772 metadata.update_with::<ConstU32<1>>(1, 1),
773 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 11 },
774 );
775 }
776
777 #[test]
778 fn individual_exposures_to_exposure_works() {
779 let exposure_1 = IndividualExposure { who: 1, value: 10u32 };
780 let exposure_2 = IndividualExposure { who: 2, value: 20 };
781 let exposure_3 = IndividualExposure { who: 3, value: 30 };
782
783 let exposure_page: ExposurePage<u32, u32> = vec![exposure_1, exposure_2, exposure_3].into();
784
785 assert_eq!(
786 exposure_page,
787 ExposurePage { page_total: 60, others: vec![exposure_1, exposure_2, exposure_3] },
788 );
789 }
790
791 #[test]
792 fn empty_individual_exposures_to_exposure_works() {
793 let empty_exposures: Vec<IndividualExposure<u32, u32>> = vec![];
794
795 let exposure_page: ExposurePage<u32, u32> = empty_exposures.into();
796 assert_eq!(exposure_page, ExposurePage { page_total: 0, others: vec![] });
797 }
798
799 #[test]
800 fn exposure_split_others_works() {
801 let exposure = Exposure {
802 total: 100,
803 own: 20,
804 others: vec![
805 IndividualExposure { who: 1, value: 20u32 },
806 IndividualExposure { who: 2, value: 20 },
807 IndividualExposure { who: 3, value: 20 },
808 IndividualExposure { who: 4, value: 20 },
809 ],
810 };
811
812 let mut exposure_0 = exposure.clone();
813 let split_exposure = exposure_0.split_others(0);
816 assert_eq!(exposure_0, exposure);
817 assert_eq!(split_exposure, Exposure { total: 20, own: 20, others: vec![] });
818
819 let mut exposure_1 = exposure.clone();
820 let split_exposure = exposure_1.split_others(1);
822 assert_eq!(exposure_1.own, 20);
823 assert_eq!(exposure_1.total, 20 + 3 * 20);
824 assert_eq!(exposure_1.others.len(), 3);
825
826 assert_eq!(split_exposure.own, 20);
827 assert_eq!(split_exposure.total, 20 + 1 * 20);
828 assert_eq!(split_exposure.others.len(), 1);
829
830 let mut exposure_3 = exposure.clone();
831 let split_exposure = exposure_3.split_others(3);
834 assert_eq!(exposure_3.own, 20);
835 assert_eq!(exposure_3.total, 20 + 1 * 20);
836 assert_eq!(exposure_3.others.len(), 1);
837
838 assert_eq!(split_exposure.own, 20);
839 assert_eq!(split_exposure.total, 20 + 3 * 20);
840 assert_eq!(split_exposure.others.len(), 3);
841
842 let mut exposure_max = exposure.clone();
843 let split_exposure = exposure_max.split_others(u32::MAX);
847 assert_eq!(split_exposure, exposure);
848 assert_eq!(exposure_max, Exposure { total: 20, own: 20, others: vec![] });
849 }
850}