1use super::*;
21use crate::{asset, ConfigOp, Pallet as Staking};
22use testing_utils::*;
23
24use codec::Decode;
25use frame_election_provider_support::{bounds::DataProviderBounds, SortedListProvider};
26use frame_support::{
27 pallet_prelude::*,
28 storage::bounded_vec::BoundedVec,
29 traits::{Get, Imbalance, UnfilteredDispatchable},
30};
31use sp_runtime::{
32 traits::{Bounded, One, StaticLookup, TrailingZeroInput, Zero},
33 Perbill, Percent, Saturating,
34};
35use sp_staking::{currency_to_vote::CurrencyToVote, SessionIndex};
36
37pub use frame_benchmarking::{
38 impl_benchmark_test_suite, v2::*, whitelist_account, whitelisted_caller, BenchmarkError,
39};
40use frame_system::RawOrigin;
41
42const SEED: u32 = 0;
43const MAX_SPANS: u32 = 100;
44const MAX_SLASHES: u32 = 1000;
45
46type MaxValidators<T> = <<T as Config>::BenchmarkingConfig as BenchmarkingConfig>::MaxValidators;
47type MaxNominators<T> = <<T as Config>::BenchmarkingConfig as BenchmarkingConfig>::MaxNominators;
48
49pub fn add_slashing_spans<T: Config>(who: &T::AccountId, spans: u32) {
52 if spans == 0 {
53 return
54 }
55
56 let mut slashing_spans = crate::slashing::SlashingSpans::new(0);
58 SpanSlash::<T>::insert((who, 0), crate::slashing::SpanRecord::default());
59
60 for i in 1..spans {
61 assert!(slashing_spans.end_span(i));
62 SpanSlash::<T>::insert((who, i), crate::slashing::SpanRecord::default());
63 }
64 SlashingSpans::<T>::insert(who, slashing_spans);
65}
66
67pub fn create_validator_with_nominators<T: Config>(
71 n: u32,
72 upper_bound: u32,
73 dead_controller: bool,
74 unique_controller: bool,
75 destination: RewardDestination<T::AccountId>,
76) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>), &'static str> {
77 clear_validators_and_nominators::<T>();
79 let mut points_total = 0;
80 let mut points_individual = Vec::new();
81
82 let (v_stash, v_controller) = if unique_controller {
83 create_unique_stash_controller::<T>(0, 100, destination.clone(), false)?
84 } else {
85 create_stash_controller::<T>(0, 100, destination.clone())?
86 };
87
88 let validator_prefs =
89 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
90 Staking::<T>::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?;
91 let stash_lookup = T::Lookup::unlookup(v_stash.clone());
92
93 points_total += 10;
94 points_individual.push((v_stash.clone(), 10));
95
96 let original_nominator_count = Nominators::<T>::count();
97 let mut nominators = Vec::new();
98
99 for i in 0..upper_bound {
101 let (n_stash, n_controller) = if !dead_controller {
102 create_stash_controller::<T>(u32::MAX - i, 100, destination.clone())?
103 } else {
104 create_unique_stash_controller::<T>(u32::MAX - i, 100, destination.clone(), true)?
105 };
106 if i < n {
107 Staking::<T>::nominate(
108 RawOrigin::Signed(n_controller.clone()).into(),
109 vec![stash_lookup.clone()],
110 )?;
111 nominators.push((n_stash, n_controller));
112 }
113 }
114
115 ValidatorCount::<T>::put(1);
116
117 let new_validators = Staking::<T>::try_trigger_new_era(SessionIndex::one(), true).unwrap();
119
120 assert_eq!(new_validators.len(), 1);
121 assert_eq!(new_validators[0], v_stash, "Our validator was not selected!");
122 assert_ne!(Validators::<T>::count(), 0);
123 assert_eq!(Nominators::<T>::count(), original_nominator_count + nominators.len() as u32);
124
125 let reward = EraRewardPoints::<T::AccountId> {
127 total: points_total,
128 individual: points_individual.into_iter().collect(),
129 };
130
131 let current_era = CurrentEra::<T>::get().unwrap();
132 ErasRewardPoints::<T>::insert(current_era, reward);
133
134 let total_payout = asset::existential_deposit::<T>()
136 .saturating_mul(upper_bound.into())
137 .saturating_mul(1000u32.into());
138 <ErasValidatorReward<T>>::insert(current_era, total_payout);
139
140 Ok((v_stash, nominators))
141}
142
143struct ListScenario<T: Config> {
144 origin_stash1: T::AccountId,
146 origin_controller1: T::AccountId,
148 dest_weight: BalanceOf<T>,
149}
150
151impl<T: Config> ListScenario<T> {
152 fn new(origin_weight: BalanceOf<T>, is_increase: bool) -> Result<Self, &'static str> {
167 ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0");
168
169 let i = asset::burn::<T>(asset::total_issuance::<T>());
171 core::mem::forget(i);
172
173 let (origin_stash1, origin_controller1) = create_stash_controller_with_balance::<T>(
176 USER_SEED + 2,
177 origin_weight,
178 RewardDestination::Staked,
179 )?;
180 Staking::<T>::nominate(
181 RawOrigin::Signed(origin_controller1.clone()).into(),
182 vec![T::Lookup::unlookup(account("random_validator", 0, SEED))],
184 )?;
185
186 let (_origin_stash2, origin_controller2) = create_stash_controller_with_balance::<T>(
187 USER_SEED + 3,
188 origin_weight,
189 RewardDestination::Staked,
190 )?;
191 Staking::<T>::nominate(
192 RawOrigin::Signed(origin_controller2).into(),
193 vec![T::Lookup::unlookup(account("random_validator", 0, SEED))],
194 )?;
195
196 let dest_weight_as_vote =
198 T::VoterList::score_update_worst_case(&origin_stash1, is_increase);
199
200 let total_issuance = asset::total_issuance::<T>();
201
202 let dest_weight =
203 T::CurrencyToVote::to_currency(dest_weight_as_vote as u128, total_issuance);
204
205 let (_dest_stash1, dest_controller1) = create_stash_controller_with_balance::<T>(
207 USER_SEED + 1,
208 dest_weight,
209 RewardDestination::Staked,
210 )?;
211 Staking::<T>::nominate(
212 RawOrigin::Signed(dest_controller1).into(),
213 vec![T::Lookup::unlookup(account("random_validator", 0, SEED))],
214 )?;
215
216 Ok(ListScenario { origin_stash1, origin_controller1, dest_weight })
217 }
218}
219
220const USER_SEED: u32 = 999666;
221
222#[benchmarks]
223mod benchmarks {
224 use super::*;
225
226 #[benchmark]
227 fn bond() {
228 let stash = create_funded_user::<T>("stash", USER_SEED, 100);
229 let reward_destination = RewardDestination::Staked;
230 let amount = asset::existential_deposit::<T>() * 10u32.into();
231 whitelist_account!(stash);
232
233 #[extrinsic_call]
234 _(RawOrigin::Signed(stash.clone()), amount, reward_destination);
235
236 assert!(Bonded::<T>::contains_key(stash.clone()));
237 assert!(Ledger::<T>::contains_key(stash));
238 }
239
240 #[benchmark]
241 fn bond_extra() -> Result<(), BenchmarkError> {
242 clear_validators_and_nominators::<T>();
244
245 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
246
247 let scenario = ListScenario::<T>::new(origin_weight, true)?;
251
252 let max_additional = scenario.dest_weight - origin_weight;
253
254 let stash = scenario.origin_stash1.clone();
255 let controller = scenario.origin_controller1;
256 let original_bonded: BalanceOf<T> = Ledger::<T>::get(&controller)
257 .map(|l| l.active)
258 .ok_or("ledger not created after")?;
259
260 let _ = asset::mint_into_existing::<T>(
261 &stash,
262 max_additional + asset::existential_deposit::<T>(),
263 )
264 .unwrap();
265
266 whitelist_account!(stash);
267
268 #[extrinsic_call]
269 _(RawOrigin::Signed(stash), max_additional);
270
271 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
272 let new_bonded: BalanceOf<T> = ledger.active;
273 assert!(original_bonded < new_bonded);
274
275 Ok(())
276 }
277
278 #[benchmark]
279 fn unbond() -> Result<(), BenchmarkError> {
280 clear_validators_and_nominators::<T>();
282
283 let origin_weight = BalanceOf::<T>::try_from(952_994_955_240_703u128)
286 .map_err(|_| "balance expected to be a u128")
287 .unwrap();
288 let scenario = ListScenario::<T>::new(origin_weight, false)?;
289
290 let controller = scenario.origin_controller1.clone();
291 let amount = origin_weight - scenario.dest_weight;
292 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
293 let original_bonded: BalanceOf<T> = ledger.active;
294
295 whitelist_account!(controller);
296
297 #[extrinsic_call]
298 _(RawOrigin::Signed(controller.clone()), amount);
299
300 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
301 let new_bonded: BalanceOf<T> = ledger.active;
302 assert!(original_bonded > new_bonded);
303
304 Ok(())
305 }
306
307 #[benchmark]
308 fn withdraw_unbonded_update(
310 s: Linear<0, MAX_SPANS>,
312 ) -> Result<(), BenchmarkError> {
313 let (stash, controller) = create_stash_controller::<T>(0, 100, RewardDestination::Staked)?;
314 add_slashing_spans::<T>(&stash, s);
315 let amount = asset::existential_deposit::<T>() * 5u32.into(); Staking::<T>::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?;
317 CurrentEra::<T>::put(EraIndex::max_value());
318 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
319 let original_total: BalanceOf<T> = ledger.total;
320 whitelist_account!(controller);
321
322 #[extrinsic_call]
323 withdraw_unbonded(RawOrigin::Signed(controller.clone()), s);
324
325 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
326 let new_total: BalanceOf<T> = ledger.total;
327 assert!(original_total > new_total);
328
329 Ok(())
330 }
331
332 #[benchmark]
333 fn withdraw_unbonded_kill(
335 s: Linear<0, MAX_SPANS>,
337 ) -> Result<(), BenchmarkError> {
338 clear_validators_and_nominators::<T>();
340
341 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
342
343 let scenario = ListScenario::<T>::new(origin_weight, true)?;
346 let controller = scenario.origin_controller1.clone();
347 let stash = scenario.origin_stash1;
348 add_slashing_spans::<T>(&stash, s);
349 assert!(T::VoterList::contains(&stash));
350
351 let ed = asset::existential_deposit::<T>();
352 let mut ledger = Ledger::<T>::get(&controller).unwrap();
353 ledger.active = ed - One::one();
354 Ledger::<T>::insert(&controller, ledger);
355 CurrentEra::<T>::put(EraIndex::max_value());
356
357 whitelist_account!(controller);
358
359 #[extrinsic_call]
360 withdraw_unbonded(RawOrigin::Signed(controller.clone()), s);
361
362 assert!(!Ledger::<T>::contains_key(controller));
363 assert!(!T::VoterList::contains(&stash));
364
365 Ok(())
366 }
367
368 #[benchmark]
369 fn validate() -> Result<(), BenchmarkError> {
370 let (stash, controller) = create_stash_controller::<T>(
371 MaxNominationsOf::<T>::get() - 1,
372 100,
373 RewardDestination::Staked,
374 )?;
375 assert!(!T::VoterList::contains(&stash));
377
378 let prefs = ValidatorPrefs::default();
379 whitelist_account!(controller);
380
381 #[extrinsic_call]
382 _(RawOrigin::Signed(controller), prefs);
383
384 assert!(Validators::<T>::contains_key(&stash));
385 assert!(T::VoterList::contains(&stash));
386
387 Ok(())
388 }
389
390 #[benchmark]
391 fn kick(
392 k: Linear<1, 128>,
397 ) -> Result<(), BenchmarkError> {
398 let rest_of_validators =
401 create_validators_with_seed::<T>(MaxNominationsOf::<T>::get() - 1, 100, 415)?;
402
403 let (stash, controller) = create_stash_controller::<T>(
405 MaxNominationsOf::<T>::get() - 1,
406 100,
407 RewardDestination::Staked,
408 )?;
409 let stash_lookup = T::Lookup::unlookup(stash.clone());
410
411 Staking::<T>::validate(RawOrigin::Signed(controller.clone()).into(), Default::default())?;
413
414 let mut nominator_stashes = Vec::with_capacity(k as usize);
417 for i in 0..k {
418 let (n_stash, n_controller) = create_stash_controller::<T>(
420 MaxNominationsOf::<T>::get() + i,
421 100,
422 RewardDestination::Staked,
423 )?;
424
425 let mut nominations = rest_of_validators.clone();
427 nominations.insert(i as usize % (nominations.len() + 1), stash_lookup.clone());
430 Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), nominations)?;
432
433 nominator_stashes.push(n_stash);
434 }
435
436 for n in nominator_stashes.iter() {
438 assert!(Nominators::<T>::get(n).unwrap().targets.contains(&stash));
439 }
440
441 let kicks = nominator_stashes
443 .iter()
444 .map(|n| T::Lookup::unlookup(n.clone()))
445 .collect::<Vec<_>>();
446
447 whitelist_account!(controller);
448
449 #[extrinsic_call]
450 _(RawOrigin::Signed(controller), kicks);
451
452 for n in nominator_stashes.iter() {
454 assert!(!Nominators::<T>::get(n).unwrap().targets.contains(&stash));
455 }
456
457 Ok(())
458 }
459
460 #[benchmark]
461 fn nominate(n: Linear<1, { MaxNominationsOf::<T>::get() }>) -> Result<(), BenchmarkError> {
463 clear_validators_and_nominators::<T>();
465
466 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
467
468 ListScenario::<T>::new(origin_weight, true)?;
471 let (stash, controller) = create_stash_controller_with_balance::<T>(
472 SEED + MaxNominationsOf::<T>::get() + 1, origin_weight,
475 RewardDestination::Staked,
476 )
477 .unwrap();
478
479 assert!(!Nominators::<T>::contains_key(&stash));
480 assert!(!T::VoterList::contains(&stash));
481
482 let validators = create_validators::<T>(n, 100).unwrap();
483 whitelist_account!(controller);
484
485 #[extrinsic_call]
486 _(RawOrigin::Signed(controller), validators);
487
488 assert!(Nominators::<T>::contains_key(&stash));
489 assert!(T::VoterList::contains(&stash));
490
491 Ok(())
492 }
493
494 #[benchmark]
495 fn chill() -> Result<(), BenchmarkError> {
496 clear_validators_and_nominators::<T>();
498
499 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
500
501 let scenario = ListScenario::<T>::new(origin_weight, true)?;
504 let controller = scenario.origin_controller1.clone();
505 let stash = scenario.origin_stash1;
506 assert!(T::VoterList::contains(&stash));
507
508 whitelist_account!(controller);
509
510 #[extrinsic_call]
511 _(RawOrigin::Signed(controller));
512
513 assert!(!T::VoterList::contains(&stash));
514
515 Ok(())
516 }
517
518 #[benchmark]
519 fn set_payee() -> Result<(), BenchmarkError> {
520 let (stash, controller) =
521 create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
522 assert_eq!(Payee::<T>::get(&stash), Some(RewardDestination::Staked));
523 whitelist_account!(controller);
524
525 #[extrinsic_call]
526 _(RawOrigin::Signed(controller.clone()), RewardDestination::Account(controller.clone()));
527
528 assert_eq!(Payee::<T>::get(&stash), Some(RewardDestination::Account(controller)));
529
530 Ok(())
531 }
532
533 #[benchmark]
534 fn update_payee() -> Result<(), BenchmarkError> {
535 let (stash, controller) =
536 create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
537 Payee::<T>::insert(&stash, {
538 #[allow(deprecated)]
539 RewardDestination::Controller
540 });
541 whitelist_account!(controller);
542
543 #[extrinsic_call]
544 _(RawOrigin::Signed(controller.clone()), controller.clone());
545
546 assert_eq!(Payee::<T>::get(&stash), Some(RewardDestination::Account(controller)));
547
548 Ok(())
549 }
550
551 #[benchmark]
552 fn set_controller() -> Result<(), BenchmarkError> {
553 let (stash, ctlr) =
554 create_unique_stash_controller::<T>(9000, 100, RewardDestination::Staked, false)?;
555 assert!(!Ledger::<T>::contains_key(&stash));
557 assert!(Ledger::<T>::contains_key(&ctlr));
558 assert_eq!(Bonded::<T>::get(&stash), Some(ctlr.clone()));
559
560 whitelist_account!(stash);
561
562 #[extrinsic_call]
563 _(RawOrigin::Signed(stash.clone()));
564
565 assert!(Ledger::<T>::contains_key(&stash));
566
567 Ok(())
568 }
569
570 #[benchmark]
571 fn set_validator_count() {
572 let validator_count = MaxValidators::<T>::get();
573
574 #[extrinsic_call]
575 _(RawOrigin::Root, validator_count);
576
577 assert_eq!(ValidatorCount::<T>::get(), validator_count);
578 }
579
580 #[benchmark]
581 fn force_no_eras() {
582 #[extrinsic_call]
583 _(RawOrigin::Root);
584
585 assert_eq!(ForceEra::<T>::get(), Forcing::ForceNone);
586 }
587
588 #[benchmark]
589 fn force_new_era() {
590 #[extrinsic_call]
591 _(RawOrigin::Root);
592
593 assert_eq!(ForceEra::<T>::get(), Forcing::ForceNew);
594 }
595
596 #[benchmark]
597 fn force_new_era_always() {
598 #[extrinsic_call]
599 _(RawOrigin::Root);
600
601 assert_eq!(ForceEra::<T>::get(), Forcing::ForceAlways);
602 }
603
604 #[benchmark]
605 fn set_invulnerables(v: Linear<0, { MaxValidators::<T>::get() }>) {
607 let mut invulnerables = Vec::new();
608 for i in 0..v {
609 invulnerables.push(account("invulnerable", i, SEED));
610 }
611
612 #[extrinsic_call]
613 _(RawOrigin::Root, invulnerables);
614
615 assert_eq!(Invulnerables::<T>::get().len(), v as usize);
616 }
617
618 #[benchmark]
619 fn deprecate_controller_batch(
620 u: Linear<0, { T::MaxControllersInDeprecationBatch::get() }>,
623 ) -> Result<(), BenchmarkError> {
624 let mut controllers: Vec<_> = vec![];
625 let mut stashes: Vec<_> = vec![];
626 for i in 0..u as u32 {
627 let (stash, controller) =
628 create_unique_stash_controller::<T>(i, 100, RewardDestination::Staked, false)?;
629 controllers.push(controller);
630 stashes.push(stash);
631 }
632 let bounded_controllers: BoundedVec<_, T::MaxControllersInDeprecationBatch> =
633 BoundedVec::try_from(controllers.clone()).unwrap();
634
635 #[extrinsic_call]
636 _(RawOrigin::Root, bounded_controllers);
637
638 for i in 0..u as u32 {
639 let stash = &stashes[i as usize];
640 let controller = &controllers[i as usize];
641 assert_eq!(Ledger::<T>::get(controller), None);
643 assert_eq!(Bonded::<T>::get(stash), Some(stash.clone()));
645 assert_eq!(Ledger::<T>::get(stash).unwrap().stash, *stash);
647 }
648
649 Ok(())
650 }
651
652 #[benchmark]
653 fn force_unstake(
654 s: Linear<0, MAX_SPANS>,
656 ) -> Result<(), BenchmarkError> {
657 clear_validators_and_nominators::<T>();
659
660 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
661
662 let scenario = ListScenario::<T>::new(origin_weight, true)?;
665 let controller = scenario.origin_controller1.clone();
666 let stash = scenario.origin_stash1;
667 assert!(T::VoterList::contains(&stash));
668 add_slashing_spans::<T>(&stash, s);
669
670 #[extrinsic_call]
671 _(RawOrigin::Root, stash.clone(), s);
672
673 assert!(!Ledger::<T>::contains_key(&controller));
674 assert!(!T::VoterList::contains(&stash));
675
676 Ok(())
677 }
678
679 #[benchmark]
680 fn cancel_deferred_slash(s: Linear<1, MAX_SLASHES>) {
681 let mut unapplied_slashes = Vec::new();
682 let era = EraIndex::one();
683 let dummy = || T::AccountId::decode(&mut TrailingZeroInput::zeroes()).unwrap();
684 for _ in 0..MAX_SLASHES {
685 unapplied_slashes
686 .push(UnappliedSlash::<T::AccountId, BalanceOf<T>>::default_from(dummy()));
687 }
688 UnappliedSlashes::<T>::insert(era, &unapplied_slashes);
689
690 let slash_indices: Vec<u32> = (0..s).collect();
691
692 #[extrinsic_call]
693 _(RawOrigin::Root, era, slash_indices);
694
695 assert_eq!(UnappliedSlashes::<T>::get(&era).len(), (MAX_SLASHES - s) as usize);
696 }
697
698 #[benchmark]
699 fn payout_stakers_alive_staked(
700 n: Linear<0, { T::MaxExposurePageSize::get() as u32 }>,
701 ) -> Result<(), BenchmarkError> {
702 let (validator, nominators) = create_validator_with_nominators::<T>(
703 n,
704 T::MaxExposurePageSize::get() as u32,
705 false,
706 true,
707 RewardDestination::Staked,
708 )?;
709
710 let current_era = CurrentEra::<T>::get().unwrap();
711 <ErasValidatorPrefs<T>>::insert(
713 current_era,
714 validator.clone(),
715 Validators::<T>::get(&validator),
716 );
717
718 let caller = whitelisted_caller();
719 let balance_before = asset::stakeable_balance::<T>(&validator);
720 let mut nominator_balances_before = Vec::new();
721 for (stash, _) in &nominators {
722 let balance = asset::stakeable_balance::<T>(stash);
723 nominator_balances_before.push(balance);
724 }
725
726 #[extrinsic_call]
727 payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era);
728
729 let balance_after = asset::stakeable_balance::<T>(&validator);
730 ensure!(
731 balance_before < balance_after,
732 "Balance of validator stash should have increased after payout.",
733 );
734 for ((stash, _), balance_before) in nominators.iter().zip(nominator_balances_before.iter())
735 {
736 let balance_after = asset::stakeable_balance::<T>(stash);
737 ensure!(
738 balance_before < &balance_after,
739 "Balance of nominator stash should have increased after payout.",
740 );
741 }
742
743 Ok(())
744 }
745
746 #[benchmark]
747 fn rebond(l: Linear<1, { T::MaxUnlockingChunks::get() as u32 }>) -> Result<(), BenchmarkError> {
748 clear_validators_and_nominators::<T>();
750
751 let origin_weight = MinNominatorBond::<T>::get()
752 .max(asset::existential_deposit::<T>())
753 .max(100u32.into());
755
756 let scenario = ListScenario::<T>::new(origin_weight, true)?;
758 let dest_weight = scenario.dest_weight;
759
760 let rebond_amount = dest_weight - origin_weight;
762
763 let value = rebond_amount / l.into();
765 assert_ne!(value, Zero::zero());
767 assert!(value * l.into() + origin_weight > origin_weight);
769 assert!(value * l.into() + origin_weight <= dest_weight);
770 let unlock_chunk = UnlockChunk::<BalanceOf<T>> { value, era: EraIndex::zero() };
771
772 let controller = scenario.origin_controller1;
773 let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
774
775 for _ in 0..l {
776 staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap()
777 }
778 Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
779 let original_bonded: BalanceOf<T> = staking_ledger.active;
780
781 whitelist_account!(controller);
782
783 #[extrinsic_call]
784 _(RawOrigin::Signed(controller.clone()), rebond_amount);
785
786 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
787 let new_bonded: BalanceOf<T> = ledger.active;
788 assert!(original_bonded < new_bonded);
789
790 Ok(())
791 }
792
793 #[benchmark]
794 fn reap_stash(s: Linear<1, MAX_SPANS>) -> Result<(), BenchmarkError> {
795 clear_validators_and_nominators::<T>();
797
798 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
799
800 let scenario = ListScenario::<T>::new(origin_weight, true)?;
803 let controller = scenario.origin_controller1.clone();
804 let stash = scenario.origin_stash1;
805
806 add_slashing_spans::<T>(&stash, s);
807 let l =
808 StakingLedger::<T>::new(stash.clone(), asset::existential_deposit::<T>() - One::one());
809 Ledger::<T>::insert(&controller, l);
810
811 assert!(Bonded::<T>::contains_key(&stash));
812 assert!(T::VoterList::contains(&stash));
813
814 whitelist_account!(controller);
815
816 #[extrinsic_call]
817 _(RawOrigin::Signed(controller), stash.clone(), s);
818
819 assert!(!Bonded::<T>::contains_key(&stash));
820 assert!(!T::VoterList::contains(&stash));
821
822 Ok(())
823 }
824
825 #[benchmark]
826 fn new_era(v: Linear<1, 10>, n: Linear<0, 100>) -> Result<(), BenchmarkError> {
827 create_validators_with_nominators_for_era::<T>(
828 v,
829 n,
830 MaxNominationsOf::<T>::get() as usize,
831 false,
832 None,
833 )?;
834 let session_index = SessionIndex::one();
835
836 let validators;
837 #[block]
838 {
839 validators =
840 Staking::<T>::try_trigger_new_era(session_index, true).ok_or("`new_era` failed")?;
841 }
842
843 assert!(validators.len() == v as usize);
844
845 Ok(())
846 }
847
848 #[benchmark(extra)]
849 fn payout_all(v: Linear<1, 10>, n: Linear<0, 100>) -> Result<(), BenchmarkError> {
850 create_validators_with_nominators_for_era::<T>(
851 v,
852 n,
853 MaxNominationsOf::<T>::get() as usize,
854 false,
855 None,
856 )?;
857 let new_validators = Staking::<T>::try_trigger_new_era(SessionIndex::one(), true).unwrap();
859 assert!(new_validators.len() == v as usize);
860
861 let current_era = CurrentEra::<T>::get().unwrap();
862 let mut points_total = 0;
863 let mut points_individual = Vec::new();
864 let mut payout_calls_arg = Vec::new();
865
866 for validator in new_validators.iter() {
867 points_total += 10;
868 points_individual.push((validator.clone(), 10));
869 payout_calls_arg.push((validator.clone(), current_era));
870 }
871
872 let reward = EraRewardPoints::<T::AccountId> {
874 total: points_total,
875 individual: points_individual.into_iter().collect(),
876 };
877
878 ErasRewardPoints::<T>::insert(current_era, reward);
879
880 let total_payout = asset::existential_deposit::<T>() * 1000u32.into();
882 <ErasValidatorReward<T>>::insert(current_era, total_payout);
883
884 let caller: T::AccountId = whitelisted_caller();
885 let origin = RawOrigin::Signed(caller);
886 let calls: Vec<_> = payout_calls_arg
887 .iter()
888 .map(|arg| {
889 Call::<T>::payout_stakers_by_page {
890 validator_stash: arg.0.clone(),
891 era: arg.1,
892 page: 0,
893 }
894 .encode()
895 })
896 .collect();
897
898 #[block]
899 {
900 for call in calls {
901 <Call<T> as Decode>::decode(&mut &*call)
902 .expect("call is encoded above, encoding must be correct")
903 .dispatch_bypass_filter(origin.clone().into())?;
904 }
905 }
906
907 Ok(())
908 }
909
910 #[benchmark(extra)]
911 fn do_slash(
912 l: Linear<1, { T::MaxUnlockingChunks::get() as u32 }>,
913 ) -> Result<(), BenchmarkError> {
914 let (stash, controller) = create_stash_controller::<T>(0, 100, RewardDestination::Staked)?;
915 let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
916 let unlock_chunk =
917 UnlockChunk::<BalanceOf<T>> { value: 1u32.into(), era: EraIndex::zero() };
918 for _ in 0..l {
919 staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap();
920 }
921 Ledger::<T>::insert(controller, staking_ledger);
922 let slash_amount = asset::existential_deposit::<T>() * 10u32.into();
923 let balance_before = asset::stakeable_balance::<T>(&stash);
924
925 #[block]
926 {
927 crate::slashing::do_slash::<T>(
928 &stash,
929 slash_amount,
930 &mut BalanceOf::<T>::zero(),
931 &mut NegativeImbalanceOf::<T>::zero(),
932 EraIndex::zero(),
933 );
934 }
935
936 let balance_after = asset::stakeable_balance::<T>(&stash);
937 assert!(balance_before > balance_after);
938
939 Ok(())
940 }
941
942 #[benchmark]
943 fn get_npos_voters(
944 v: Linear<{ MaxValidators::<T>::get() / 2 }, { MaxValidators::<T>::get() }>,
946
947 n: Linear<{ MaxNominators::<T>::get() / 2 }, { MaxNominators::<T>::get() }>,
949 ) -> Result<(), BenchmarkError> {
950 create_validators_with_nominators_for_era::<T>(
951 v,
952 n,
953 MaxNominationsOf::<T>::get() as usize,
954 false,
955 None,
956 )?;
957
958 assert_eq!(Validators::<T>::count(), v);
959 assert_eq!(Nominators::<T>::count(), n);
960
961 let num_voters = (v + n) as usize;
962
963 let voters;
965 #[block]
966 {
967 voters = <Staking<T>>::get_npos_voters(DataProviderBounds::default());
968 }
969
970 assert_eq!(voters.len(), num_voters);
971
972 Ok(())
973 }
974
975 #[benchmark]
976 fn get_npos_targets(
977 v: Linear<{ MaxValidators::<T>::get() / 2 }, { MaxValidators::<T>::get() }>,
979 ) -> Result<(), BenchmarkError> {
980 let n = MaxNominators::<T>::get();
982 create_validators_with_nominators_for_era::<T>(
983 v,
984 n,
985 MaxNominationsOf::<T>::get() as usize,
986 false,
987 None,
988 )?;
989
990 let targets;
991
992 #[block]
993 {
994 targets = <Staking<T>>::get_npos_targets(DataProviderBounds::default());
996 }
997
998 assert_eq!(targets.len() as u32, v);
999
1000 Ok(())
1001 }
1002
1003 #[benchmark]
1004 fn set_staking_configs_all_set() {
1005 #[extrinsic_call]
1006 set_staking_configs(
1007 RawOrigin::Root,
1008 ConfigOp::Set(BalanceOf::<T>::max_value()),
1009 ConfigOp::Set(BalanceOf::<T>::max_value()),
1010 ConfigOp::Set(u32::MAX),
1011 ConfigOp::Set(u32::MAX),
1012 ConfigOp::Set(Percent::max_value()),
1013 ConfigOp::Set(Perbill::max_value()),
1014 ConfigOp::Set(Percent::max_value()),
1015 );
1016
1017 assert_eq!(MinNominatorBond::<T>::get(), BalanceOf::<T>::max_value());
1018 assert_eq!(MinValidatorBond::<T>::get(), BalanceOf::<T>::max_value());
1019 assert_eq!(MaxNominatorsCount::<T>::get(), Some(u32::MAX));
1020 assert_eq!(MaxValidatorsCount::<T>::get(), Some(u32::MAX));
1021 assert_eq!(ChillThreshold::<T>::get(), Some(Percent::from_percent(100)));
1022 assert_eq!(MinCommission::<T>::get(), Perbill::from_percent(100));
1023 assert_eq!(MaxStakedRewards::<T>::get(), Some(Percent::from_percent(100)));
1024 }
1025
1026 #[benchmark]
1027 fn set_staking_configs_all_remove() {
1028 #[extrinsic_call]
1029 set_staking_configs(
1030 RawOrigin::Root,
1031 ConfigOp::Remove,
1032 ConfigOp::Remove,
1033 ConfigOp::Remove,
1034 ConfigOp::Remove,
1035 ConfigOp::Remove,
1036 ConfigOp::Remove,
1037 ConfigOp::Remove,
1038 );
1039
1040 assert!(!MinNominatorBond::<T>::exists());
1041 assert!(!MinValidatorBond::<T>::exists());
1042 assert!(!MaxNominatorsCount::<T>::exists());
1043 assert!(!MaxValidatorsCount::<T>::exists());
1044 assert!(!ChillThreshold::<T>::exists());
1045 assert!(!MinCommission::<T>::exists());
1046 assert!(!MaxStakedRewards::<T>::exists());
1047 }
1048
1049 #[benchmark]
1050 fn chill_other() -> Result<(), BenchmarkError> {
1051 clear_validators_and_nominators::<T>();
1053
1054 let origin_weight = MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>());
1055
1056 let scenario = ListScenario::<T>::new(origin_weight, true)?;
1059 let stash = scenario.origin_stash1;
1060 assert!(T::VoterList::contains(&stash));
1061
1062 Staking::<T>::set_staking_configs(
1063 RawOrigin::Root.into(),
1064 ConfigOp::Set(BalanceOf::<T>::max_value()),
1065 ConfigOp::Set(BalanceOf::<T>::max_value()),
1066 ConfigOp::Set(0),
1067 ConfigOp::Set(0),
1068 ConfigOp::Set(Percent::from_percent(0)),
1069 ConfigOp::Set(Zero::zero()),
1070 ConfigOp::Noop,
1071 )?;
1072
1073 let caller = whitelisted_caller();
1074
1075 #[extrinsic_call]
1076 _(RawOrigin::Signed(caller), stash.clone());
1077
1078 assert!(!T::VoterList::contains(&stash));
1079
1080 Ok(())
1081 }
1082
1083 #[benchmark]
1084 fn force_apply_min_commission() -> Result<(), BenchmarkError> {
1085 clear_validators_and_nominators::<T>();
1087
1088 let (stash, controller) = create_stash_controller::<T>(1, 1, RewardDestination::Staked)?;
1090 let validator_prefs =
1091 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
1092 Staking::<T>::validate(RawOrigin::Signed(controller).into(), validator_prefs)?;
1093
1094 assert_eq!(
1096 Validators::<T>::get(&stash),
1097 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }
1098 );
1099
1100 MinCommission::<T>::set(Perbill::from_percent(75));
1102 let caller = whitelisted_caller();
1103
1104 #[extrinsic_call]
1105 _(RawOrigin::Signed(caller), stash.clone());
1106
1107 assert_eq!(
1109 Validators::<T>::get(&stash),
1110 ValidatorPrefs { commission: Perbill::from_percent(75), ..Default::default() }
1111 );
1112
1113 Ok(())
1114 }
1115
1116 #[benchmark]
1117 fn set_min_commission() {
1118 let min_commission = Perbill::max_value();
1119
1120 #[extrinsic_call]
1121 _(RawOrigin::Root, min_commission);
1122
1123 assert_eq!(MinCommission::<T>::get(), Perbill::from_percent(100));
1124 }
1125
1126 #[benchmark]
1127 fn restore_ledger() -> Result<(), BenchmarkError> {
1128 let (stash, controller) = create_stash_controller::<T>(0, 100, RewardDestination::Staked)?;
1129 Ledger::<T>::remove(controller);
1131
1132 #[extrinsic_call]
1133 _(RawOrigin::Root, stash.clone(), None, None, None);
1134
1135 assert_eq!(Staking::<T>::inspect_bond_state(&stash), Ok(LedgerIntegrityState::Ok));
1136
1137 Ok(())
1138 }
1139
1140 #[benchmark]
1141 fn migrate_currency() -> Result<(), BenchmarkError> {
1142 let (stash, _ctrl) =
1143 create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
1144 let stake = asset::staked::<T>(&stash);
1145 migrate_to_old_currency::<T>(stash.clone());
1146 assert!(asset::staked::<T>(&stash).is_zero());
1148 whitelist_account!(stash);
1149
1150 #[extrinsic_call]
1151 _(RawOrigin::Signed(stash.clone()), stash.clone());
1152
1153 assert_eq!(asset::staked::<T>(&stash), stake);
1154 Ok(())
1155 }
1156
1157 #[benchmark]
1158 fn manual_slash() -> Result<(), BenchmarkError> {
1159 let (validator_stash, _nominators) = create_validator_with_nominators::<T>(
1162 T::MaxExposurePageSize::get() as u32,
1163 T::MaxExposurePageSize::get() as u32,
1164 false,
1165 true,
1166 RewardDestination::Staked,
1167 )?;
1168
1169 let era = CurrentEra::<T>::get().unwrap();
1170 ActiveEra::<T>::put(ActiveEraInfo { index: era, start: None });
1171 let slash_fraction = Perbill::from_percent(10);
1172
1173 #[extrinsic_call]
1174 _(RawOrigin::Root, validator_stash.clone(), era, slash_fraction);
1175
1176 assert!(ValidatorSlashInEra::<T>::get(era, &validator_stash).is_some());
1177
1178 Ok(())
1179 }
1180
1181 impl_benchmark_test_suite!(
1182 Staking,
1183 crate::mock::ExtBuilder::default().has_stakers(true),
1184 crate::mock::Test,
1185 exec_name = build_and_execute
1186 );
1187}
1188
1189#[cfg(test)]
1190mod tests {
1191 use super::*;
1192 use crate::mock::{ExtBuilder, RuntimeOrigin, Staking, Test};
1193 use frame_support::assert_ok;
1194
1195 #[test]
1196 fn create_validators_with_nominators_for_era_works() {
1197 ExtBuilder::default().build_and_execute(|| {
1198 let v = 10;
1199 let n = 100;
1200
1201 create_validators_with_nominators_for_era::<Test>(
1202 v,
1203 n,
1204 MaxNominationsOf::<Test>::get() as usize,
1205 false,
1206 None,
1207 )
1208 .unwrap();
1209
1210 let count_validators = Validators::<Test>::iter().count();
1211 let count_nominators = Nominators::<Test>::iter().count();
1212
1213 assert_eq!(count_validators, Validators::<Test>::count() as usize);
1214 assert_eq!(count_nominators, Nominators::<Test>::count() as usize);
1215
1216 assert_eq!(count_validators, v as usize);
1217 assert_eq!(count_nominators, n as usize);
1218 });
1219 }
1220
1221 #[test]
1222 fn create_validator_with_nominators_works() {
1223 ExtBuilder::default().build_and_execute(|| {
1224 let n = 10;
1225
1226 let (validator_stash, nominators) = create_validator_with_nominators::<Test>(
1227 n,
1228 <<Test as Config>::MaxExposurePageSize as Get<_>>::get(),
1229 false,
1230 false,
1231 RewardDestination::Staked,
1232 )
1233 .unwrap();
1234
1235 assert_eq!(nominators.len() as u32, n);
1236
1237 let current_era = CurrentEra::<Test>::get().unwrap();
1238
1239 let original_stakeable_balance = asset::stakeable_balance::<Test>(&validator_stash);
1240 assert_ok!(Staking::payout_stakers_by_page(
1241 RuntimeOrigin::signed(1337),
1242 validator_stash,
1243 current_era,
1244 0
1245 ));
1246 let new_stakeable_balance = asset::stakeable_balance::<Test>(&validator_stash);
1247
1248 assert!(original_stakeable_balance < new_stakeable_balance);
1250 });
1251 }
1252
1253 #[test]
1254 fn add_slashing_spans_works() {
1255 ExtBuilder::default().build_and_execute(|| {
1256 let n = 10;
1257
1258 let (validator_stash, _nominators) = create_validator_with_nominators::<Test>(
1259 n,
1260 <<Test as Config>::MaxExposurePageSize as Get<_>>::get(),
1261 false,
1262 false,
1263 RewardDestination::Staked,
1264 )
1265 .unwrap();
1266
1267 let num_of_slashing_spans = 20;
1269 add_slashing_spans::<Test>(&validator_stash, num_of_slashing_spans);
1270
1271 let slashing_spans = SlashingSpans::<Test>::get(&validator_stash).unwrap();
1272 assert_eq!(slashing_spans.iter().count(), num_of_slashing_spans as usize);
1273 for i in 0..num_of_slashing_spans {
1274 assert!(SpanSlash::<Test>::contains_key((&validator_stash, i)));
1275 }
1276
1277 assert_ok!(Staking::kill_stash(&validator_stash, num_of_slashing_spans));
1279 assert!(SlashingSpans::<Test>::get(&validator_stash).is_none());
1280 for i in 0..num_of_slashing_spans {
1281 assert!(!SpanSlash::<Test>::contains_key((&validator_stash, i)));
1282 }
1283 });
1284 }
1285}