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;
202
203 fn current_era() -> EraIndex;
207
208 fn stake(who: &Self::AccountId) -> Result<Stake<Self::Balance>, DispatchError>;
210
211 fn total_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
213 Self::stake(who).map(|s| s.total)
214 }
215
216 fn active_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
218 Self::stake(who).map(|s| s.active)
219 }
220
221 fn is_unbonding(who: &Self::AccountId) -> Result<bool, DispatchError> {
223 Self::stake(who).map(|s| s.active != s.total)
224 }
225
226 fn fully_unbond(who: &Self::AccountId) -> DispatchResult {
228 Self::unbond(who, Self::stake(who)?.active)
229 }
230
231 fn bond(who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId)
233 -> DispatchResult;
234
235 fn nominate(who: &Self::AccountId, validators: Vec<Self::AccountId>) -> DispatchResult;
237
238 fn chill(who: &Self::AccountId) -> DispatchResult;
240
241 fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult;
245
246 fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult;
256
257 fn set_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult;
259
260 fn withdraw_unbonded(
264 stash: Self::AccountId,
265 num_slashing_spans: u32,
266 ) -> Result<bool, DispatchError>;
267
268 fn desired_validator_count() -> u32;
270
271 fn election_ongoing() -> bool;
273
274 fn force_unstake(who: Self::AccountId) -> DispatchResult;
276
277 fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool;
279
280 fn status(who: &Self::AccountId) -> Result<StakerStatus<Self::AccountId>, DispatchError>;
282
283 fn is_validator(who: &Self::AccountId) -> bool {
285 Self::status(who).map(|s| matches!(s, StakerStatus::Validator)).unwrap_or(false)
286 }
287
288 fn is_virtual_staker(who: &Self::AccountId) -> bool;
294
295 fn nominations(who: &Self::AccountId) -> Option<Vec<Self::AccountId>> {
297 match Self::status(who) {
298 Ok(StakerStatus::Nominator(t)) => Some(t),
299 _ => None,
300 }
301 }
302
303 fn slash_reward_fraction() -> Perbill;
305
306 #[cfg(feature = "runtime-benchmarks")]
307 fn max_exposure_page_size() -> Page;
308
309 #[cfg(feature = "runtime-benchmarks")]
310 fn add_era_stakers(
311 current_era: &EraIndex,
312 stash: &Self::AccountId,
313 exposures: Vec<(Self::AccountId, Self::Balance)>,
314 );
315
316 #[cfg(feature = "runtime-benchmarks")]
317 fn set_current_era(era: EraIndex);
318}
319
320pub trait StakingUnchecked: StakingInterface {
325 fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult;
329
330 fn virtual_bond(
336 keyless_who: &Self::AccountId,
337 value: Self::Balance,
338 payee: &Self::AccountId,
339 ) -> DispatchResult;
340
341 #[cfg(feature = "runtime-benchmarks")]
345 fn migrate_to_direct_staker(who: &Self::AccountId);
346}
347
348#[derive(
350 PartialEq,
351 Eq,
352 PartialOrd,
353 Ord,
354 Clone,
355 Encode,
356 Decode,
357 DecodeWithMemTracking,
358 RuntimeDebug,
359 TypeInfo,
360 Copy,
361)]
362pub struct IndividualExposure<AccountId, Balance: HasCompact> {
363 pub who: AccountId,
365 #[codec(compact)]
367 pub value: Balance,
368}
369
370#[derive(
372 PartialEq,
373 Eq,
374 PartialOrd,
375 Ord,
376 Clone,
377 Encode,
378 Decode,
379 DecodeWithMemTracking,
380 RuntimeDebug,
381 TypeInfo,
382)]
383pub struct Exposure<AccountId, Balance: HasCompact> {
384 #[codec(compact)]
386 pub total: Balance,
387 #[codec(compact)]
389 pub own: Balance,
390 pub others: Vec<IndividualExposure<AccountId, Balance>>,
392}
393
394impl<AccountId, Balance: Default + HasCompact> Default for Exposure<AccountId, Balance> {
395 fn default() -> Self {
396 Self { total: Default::default(), own: Default::default(), others: vec![] }
397 }
398}
399
400impl<
401 AccountId: Clone,
402 Balance: HasCompact + AtLeast32BitUnsigned + Copy + codec::MaxEncodedLen,
403 > Exposure<AccountId, Balance>
404{
405 pub fn split_others(&mut self, n_others: u32) -> Self {
413 let head_others: Vec<_> =
414 self.others.drain(..(n_others as usize).min(self.others.len())).collect();
415
416 let total_others_head: Balance = head_others
417 .iter()
418 .fold(Zero::zero(), |acc: Balance, o| acc.saturating_add(o.value));
419
420 self.total = self.total.saturating_sub(total_others_head);
421
422 Self {
423 total: total_others_head.saturating_add(self.own),
424 own: self.own,
425 others: head_others,
426 }
427 }
428
429 pub fn into_pages(
432 self,
433 page_size: Page,
434 ) -> (PagedExposureMetadata<Balance>, Vec<ExposurePage<AccountId, Balance>>) {
435 let individual_chunks = self.others.chunks(page_size as usize);
436 let mut exposure_pages: Vec<ExposurePage<AccountId, Balance>> =
437 Vec::with_capacity(individual_chunks.len());
438
439 for chunk in individual_chunks {
440 let mut page_total: Balance = Zero::zero();
441 let mut others: Vec<IndividualExposure<AccountId, Balance>> =
442 Vec::with_capacity(chunk.len());
443 for individual in chunk.iter() {
444 page_total.saturating_accrue(individual.value);
445 others.push(IndividualExposure {
446 who: individual.who.clone(),
447 value: individual.value,
448 })
449 }
450 exposure_pages.push(ExposurePage { page_total, others });
451 }
452
453 (
454 PagedExposureMetadata {
455 total: self.total,
456 own: self.own,
457 nominator_count: self.others.len() as u32,
458 page_count: exposure_pages.len() as Page,
459 },
460 exposure_pages,
461 )
462 }
463}
464
465#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
467pub struct ExposurePage<AccountId, Balance: HasCompact> {
468 #[codec(compact)]
470 pub page_total: Balance,
471 pub others: Vec<IndividualExposure<AccountId, Balance>>,
473}
474
475impl<A, B: Default + HasCompact> Default for ExposurePage<A, B> {
476 fn default() -> Self {
477 ExposurePage { page_total: Default::default(), others: vec![] }
478 }
479}
480
481impl<A, B: HasCompact + Default + AddAssign + SubAssign + Clone> From<Vec<IndividualExposure<A, B>>>
483 for ExposurePage<A, B>
484{
485 fn from(exposures: Vec<IndividualExposure<A, B>>) -> Self {
486 exposures.into_iter().fold(ExposurePage::default(), |mut page, e| {
487 page.page_total += e.value.clone();
488 page.others.push(e);
489 page
490 })
491 }
492}
493
494#[derive(
500 PartialEq,
501 Eq,
502 PartialOrd,
503 Ord,
504 Clone,
505 Encode,
506 Decode,
507 RuntimeDebug,
508 TypeInfo,
509 Default,
510 MaxEncodedLen,
511 Copy,
512)]
513pub struct PagedExposureMetadata<Balance: HasCompact + codec::MaxEncodedLen> {
514 #[codec(compact)]
516 pub total: Balance,
517 #[codec(compact)]
519 pub own: Balance,
520 pub nominator_count: u32,
522 pub page_count: Page,
524}
525
526impl<Balance> PagedExposureMetadata<Balance>
527where
528 Balance: HasCompact
529 + codec::MaxEncodedLen
530 + Add<Output = Balance>
531 + Sub<Output = Balance>
532 + sp_runtime::Saturating
533 + PartialEq
534 + Copy
535 + sp_runtime::traits::Debug,
536{
537 pub fn update_with<Max: sp_core::Get<u32>>(
542 self,
543 others_balance: Balance,
544 others_num: u32,
545 ) -> Self {
546 let page_limit = Max::get().max(1);
547 let new_nominator_count = self.nominator_count.saturating_add(others_num);
548 let new_page_count = new_nominator_count
549 .saturating_add(page_limit)
550 .saturating_sub(1)
551 .saturating_div(page_limit);
552
553 Self {
554 total: self.total.saturating_add(others_balance),
555 own: self.own,
556 nominator_count: new_nominator_count,
557 page_count: new_page_count,
558 }
559 }
560}
561
562#[derive(Clone, Debug)]
572pub struct Agent<T>(T);
573impl<T> From<T> for Agent<T> {
574 fn from(acc: T) -> Self {
575 Agent(acc)
576 }
577}
578
579impl<T> Agent<T> {
580 pub fn get(self) -> T {
581 self.0
582 }
583}
584
585#[derive(Clone, Debug)]
590pub struct Delegator<T>(T);
591impl<T> From<T> for Delegator<T> {
592 fn from(acc: T) -> Self {
593 Delegator(acc)
594 }
595}
596
597impl<T> Delegator<T> {
598 pub fn get(self) -> T {
599 self.0
600 }
601}
602
603pub trait DelegationInterface {
605 type Balance: Sub<Output = Self::Balance>
607 + Ord
608 + PartialEq
609 + Default
610 + Copy
611 + MaxEncodedLen
612 + FullCodec
613 + TypeInfo
614 + Saturating;
615
616 type AccountId: Clone + core::fmt::Debug;
618
619 fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
623
624 fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
627
628 fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance>;
630
631 fn register_agent(
633 agent: Agent<Self::AccountId>,
634 reward_account: &Self::AccountId,
635 ) -> DispatchResult;
636
637 fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult;
641
642 fn delegate(
644 delegator: Delegator<Self::AccountId>,
645 agent: Agent<Self::AccountId>,
646 amount: Self::Balance,
647 ) -> DispatchResult;
648
649 fn withdraw_delegation(
654 delegator: Delegator<Self::AccountId>,
655 agent: Agent<Self::AccountId>,
656 amount: Self::Balance,
657 num_slashing_spans: u32,
658 ) -> DispatchResult;
659
660 fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance>;
665
666 fn delegator_slash(
671 agent: Agent<Self::AccountId>,
672 delegator: Delegator<Self::AccountId>,
673 value: Self::Balance,
674 maybe_reporter: Option<Self::AccountId>,
675 ) -> DispatchResult;
676}
677
678pub trait DelegationMigrator {
681 type Balance: Sub<Output = Self::Balance>
683 + Ord
684 + PartialEq
685 + Default
686 + Copy
687 + MaxEncodedLen
688 + FullCodec
689 + TypeInfo
690 + Saturating;
691
692 type AccountId: Clone + core::fmt::Debug;
694
695 fn migrate_nominator_to_agent(
700 agent: Agent<Self::AccountId>,
701 reward_account: &Self::AccountId,
702 ) -> DispatchResult;
703
704 fn migrate_delegation(
709 agent: Agent<Self::AccountId>,
710 delegator: Delegator<Self::AccountId>,
711 value: Self::Balance,
712 ) -> DispatchResult;
713
714 #[cfg(feature = "runtime-benchmarks")]
718 fn force_kill_agent(agent: Agent<Self::AccountId>);
719}
720
721sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);
722
723#[cfg(test)]
724mod tests {
725 use sp_core::ConstU32;
726
727 use super::*;
728
729 #[test]
730 fn update_with_works() {
731 let metadata = PagedExposureMetadata::<u32> {
732 total: 1000,
733 own: 0, nominator_count: 10,
735 page_count: 1,
736 };
737
738 assert_eq!(
739 metadata.update_with::<ConstU32<10>>(1, 1),
740 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 2 },
741 );
742
743 assert_eq!(
744 metadata.update_with::<ConstU32<5>>(1, 1),
745 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
746 );
747
748 assert_eq!(
749 metadata.update_with::<ConstU32<4>>(1, 1),
750 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 3 },
751 );
752
753 assert_eq!(
754 metadata.update_with::<ConstU32<1>>(1, 1),
755 PagedExposureMetadata { total: 1001, own: 0, nominator_count: 11, page_count: 11 },
756 );
757 }
758
759 #[test]
760 fn individual_exposures_to_exposure_works() {
761 let exposure_1 = IndividualExposure { who: 1, value: 10u32 };
762 let exposure_2 = IndividualExposure { who: 2, value: 20 };
763 let exposure_3 = IndividualExposure { who: 3, value: 30 };
764
765 let exposure_page: ExposurePage<u32, u32> = vec![exposure_1, exposure_2, exposure_3].into();
766
767 assert_eq!(
768 exposure_page,
769 ExposurePage { page_total: 60, others: vec![exposure_1, exposure_2, exposure_3] },
770 );
771 }
772
773 #[test]
774 fn empty_individual_exposures_to_exposure_works() {
775 let empty_exposures: Vec<IndividualExposure<u32, u32>> = vec![];
776
777 let exposure_page: ExposurePage<u32, u32> = empty_exposures.into();
778 assert_eq!(exposure_page, ExposurePage { page_total: 0, others: vec![] });
779 }
780
781 #[test]
782 fn exposure_split_others_works() {
783 let exposure = Exposure {
784 total: 100,
785 own: 20,
786 others: vec![
787 IndividualExposure { who: 1, value: 20u32 },
788 IndividualExposure { who: 2, value: 20 },
789 IndividualExposure { who: 3, value: 20 },
790 IndividualExposure { who: 4, value: 20 },
791 ],
792 };
793
794 let mut exposure_0 = exposure.clone();
795 let split_exposure = exposure_0.split_others(0);
798 assert_eq!(exposure_0, exposure);
799 assert_eq!(split_exposure, Exposure { total: 20, own: 20, others: vec![] });
800
801 let mut exposure_1 = exposure.clone();
802 let split_exposure = exposure_1.split_others(1);
804 assert_eq!(exposure_1.own, 20);
805 assert_eq!(exposure_1.total, 20 + 3 * 20);
806 assert_eq!(exposure_1.others.len(), 3);
807
808 assert_eq!(split_exposure.own, 20);
809 assert_eq!(split_exposure.total, 20 + 1 * 20);
810 assert_eq!(split_exposure.others.len(), 1);
811
812 let mut exposure_3 = exposure.clone();
813 let split_exposure = exposure_3.split_others(3);
816 assert_eq!(exposure_3.own, 20);
817 assert_eq!(exposure_3.total, 20 + 1 * 20);
818 assert_eq!(exposure_3.others.len(), 1);
819
820 assert_eq!(split_exposure.own, 20);
821 assert_eq!(split_exposure.total, 20 + 3 * 20);
822 assert_eq!(split_exposure.others.len(), 3);
823
824 let mut exposure_max = exposure.clone();
825 let split_exposure = exposure_max.split_others(u32::MAX);
829 assert_eq!(split_exposure, exposure);
830 assert_eq!(exposure_max, Exposure { total: 20, own: 20, others: vec![] });
831 }
832}