1use super::*;
21use crate::{
22 asset,
23 session_rotation::{Eras, Rotator},
24 Pallet as Staking,
25};
26use alloc::collections::BTreeMap;
27pub use frame_benchmarking::{
28 impl_benchmark_test_suite, v2::*, whitelist_account, whitelisted_caller, BenchmarkError,
29};
30use frame_election_provider_support::SortedListProvider;
31use frame_support::{
32 assert_ok, pallet_prelude::*, storage::bounded_vec::BoundedVec, traits::TryCollect,
33};
34use frame_system::RawOrigin;
35use pallet_staking_async_rc_client as rc_client;
36use sp_runtime::{
37 traits::{Bounded, One, StaticLookup, Zero},
38 Perbill, Percent, Saturating,
39};
40use sp_staking::currency_to_vote::CurrencyToVote;
41use testing_utils::*;
42
43const SEED: u32 = 0;
44
45pub(crate) fn create_validator_with_nominators<T: Config>(
50 n: u32,
51 upper_bound: u32,
52 dead_controller: bool,
53 unique_controller: bool,
54 destination: RewardDestination<T::AccountId>,
55) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>, EraIndex), &'static str> {
56 clear_validators_and_nominators::<T>();
59 let mut points_total = 0;
60 let mut points_individual = Vec::new();
61
62 let (v_stash, v_controller) = if unique_controller {
63 create_unique_stash_controller::<T>(0, 100, destination.clone(), false)?
64 } else {
65 create_stash_controller::<T>(0, 100, destination.clone())?
66 };
67
68 let validator_prefs =
69 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
70 Staking::<T>::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?;
71 let stash_lookup = T::Lookup::unlookup(v_stash.clone());
72
73 points_total += 10;
74 points_individual.push((v_stash.clone(), 10));
75
76 let original_nominator_count = Nominators::<T>::count();
77 let mut nominators = Vec::new();
78
79 for i in 0..upper_bound {
81 let (n_stash, n_controller) = if !dead_controller {
82 create_stash_controller::<T>(u32::MAX - i, 100, destination.clone())?
83 } else {
84 create_unique_stash_controller::<T>(u32::MAX - i, 100, destination.clone(), true)?
85 };
86 if i < n {
87 Staking::<T>::nominate(
88 RawOrigin::Signed(n_controller.clone()).into(),
89 vec![stash_lookup.clone()],
90 )?;
91 nominators.push((n_stash, n_controller));
92 }
93 }
94
95 ValidatorCount::<T>::put(1);
96
97 let new_validators = Rotator::<T>::legacy_insta_plan_era();
99 let planned_era = CurrentEra::<T>::get().unwrap_or_default();
100
101 assert_eq!(new_validators.len(), 1, "New validators is not 1");
102 assert_eq!(new_validators[0], v_stash, "Our validator was not selected");
103 assert_ne!(Validators::<T>::count(), 0, "New validators count wrong");
104 assert_eq!(
105 Nominators::<T>::count(),
106 original_nominator_count + nominators.len() as u32,
107 "New nominators count wrong"
108 );
109
110 let reward = EraRewardPoints::<T> {
112 total: points_total,
113 individual: points_individual.into_iter().try_collect()?,
114 };
115
116 ErasRewardPoints::<T>::insert(planned_era, reward);
117
118 let total_payout = asset::existential_deposit::<T>()
120 .saturating_mul(upper_bound.into())
121 .saturating_mul(1000u32.into());
122 <ErasValidatorReward<T>>::insert(planned_era, total_payout);
123
124 Ok((v_stash, nominators, planned_era))
125}
126
127struct ListScenario<T: Config> {
128 origin_stash1: T::AccountId,
130 origin_controller1: T::AccountId,
132 dest_weight: BalanceOf<T>,
133}
134
135impl<T: Config> ListScenario<T> {
136 fn new(origin_weight: BalanceOf<T>, is_increase: bool) -> Result<Self, &'static str> {
151 ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0");
152
153 let i = asset::burn::<T>(asset::total_issuance::<T>());
155 core::mem::forget(i);
156
157 let validator_account = account("random_validator", 0, SEED);
159 let validator_stake = asset::existential_deposit::<T>() * 1000u32.into();
160 asset::set_stakeable_balance::<T>(&validator_account, validator_stake);
161 assert_ok!(Staking::<T>::bond(
162 RawOrigin::Signed(validator_account.clone()).into(),
163 validator_stake / 2u32.into(),
164 RewardDestination::Staked
165 ));
166 assert_ok!(Staking::<T>::validate(
167 RawOrigin::Signed(validator_account.clone()).into(),
168 Default::default()
169 ));
170
171 let (origin_stash1, origin_controller1) = create_stash_controller_with_balance::<T>(
173 USER_SEED + 2,
174 origin_weight,
175 RewardDestination::Staked,
176 )?;
177 Staking::<T>::nominate(
178 RawOrigin::Signed(origin_controller1.clone()).into(),
179 vec![T::Lookup::unlookup(validator_account.clone())],
181 )?;
182
183 let (_origin_stash2, origin_controller2) = create_stash_controller_with_balance::<T>(
184 USER_SEED + 3,
185 origin_weight,
186 RewardDestination::Staked,
187 )?;
188 Staking::<T>::nominate(
189 RawOrigin::Signed(origin_controller2).into(),
190 vec![T::Lookup::unlookup(validator_account.clone())],
191 )?;
192
193 let dest_weight_as_vote =
195 T::VoterList::score_update_worst_case(&origin_stash1, is_increase);
196
197 let total_issuance = asset::total_issuance::<T>();
198
199 let dest_weight =
200 T::CurrencyToVote::to_currency(dest_weight_as_vote as u128, total_issuance);
201
202 let (_dest_stash1, dest_controller1) = create_stash_controller_with_balance::<T>(
204 USER_SEED + 1,
205 dest_weight,
206 RewardDestination::Staked,
207 )?;
208 Staking::<T>::nominate(
209 RawOrigin::Signed(dest_controller1).into(),
210 vec![T::Lookup::unlookup(validator_account)],
211 )?;
212
213 Ok(ListScenario { origin_stash1, origin_controller1, dest_weight })
214 }
215}
216
217const USER_SEED: u32 = 999666;
218
219#[benchmarks]
220mod benchmarks {
221 use super::*;
222 use alloc::format;
223
224 #[benchmark]
225 fn bond() {
226 let stash = create_funded_user::<T>("stash", USER_SEED, 100);
227 let reward_destination = RewardDestination::Staked;
228 let amount = asset::existential_deposit::<T>() * 10u32.into();
229 whitelist_account!(stash);
230
231 #[extrinsic_call]
232 _(RawOrigin::Signed(stash.clone()), amount, reward_destination);
233
234 assert!(Bonded::<T>::contains_key(stash.clone()));
235 assert!(Ledger::<T>::contains_key(stash));
236 }
237
238 #[benchmark]
239 fn bond_extra() -> Result<(), BenchmarkError> {
240 clear_validators_and_nominators::<T>();
242
243 let origin_weight = Staking::<T>::min_nominator_bond();
244
245 let scenario = ListScenario::<T>::new(origin_weight, true)?;
249
250 let max_additional = scenario.dest_weight - origin_weight;
251
252 let stash = scenario.origin_stash1.clone();
253 let controller = scenario.origin_controller1;
254 let original_bonded: BalanceOf<T> = Ledger::<T>::get(&controller)
255 .map(|l| l.active)
256 .ok_or("ledger not created after")?;
257
258 let _ = asset::mint_into_existing::<T>(
259 &stash,
260 max_additional + asset::existential_deposit::<T>(),
261 )
262 .unwrap();
263
264 whitelist_account!(stash);
265
266 #[extrinsic_call]
267 _(RawOrigin::Signed(stash), max_additional);
268
269 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
270 let new_bonded: BalanceOf<T> = ledger.active;
271 assert!(original_bonded < new_bonded);
272
273 Ok(())
274 }
275
276 #[benchmark]
277 fn unbond() -> Result<(), BenchmarkError> {
278 clear_validators_and_nominators::<T>();
280
281 let origin_weight = BalanceOf::<T>::try_from(952_994_955_240_703u128)
284 .map_err(|_| "balance expected to be a u128")
285 .unwrap();
286 let scenario = ListScenario::<T>::new(origin_weight, false)?;
287
288 let controller = scenario.origin_controller1.clone();
289 let amount = origin_weight - scenario.dest_weight;
290 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
291 let original_bonded: BalanceOf<T> = ledger.active;
292
293 whitelist_account!(controller);
294
295 #[extrinsic_call]
296 _(RawOrigin::Signed(controller.clone()), amount);
297
298 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
299 let new_bonded: BalanceOf<T> = ledger.active;
300 assert!(original_bonded > new_bonded);
301
302 Ok(())
303 }
304
305 #[benchmark]
306 fn withdraw_unbonded_update() -> Result<(), BenchmarkError> {
308 let (_, controller) = create_stash_controller::<T>(0, 100, RewardDestination::Staked)?;
309 let amount = asset::existential_deposit::<T>() * 5u32.into(); Staking::<T>::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?;
311 set_active_era::<T>(EraIndex::max_value());
312 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
313 let original_total: BalanceOf<T> = ledger.total;
314 whitelist_account!(controller);
315
316 #[extrinsic_call]
317 withdraw_unbonded(RawOrigin::Signed(controller.clone()), 0);
318
319 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
320 let new_total: BalanceOf<T> = ledger.total;
321 assert!(original_total > new_total);
322
323 Ok(())
324 }
325
326 #[benchmark]
327 fn withdraw_unbonded_kill() -> Result<(), BenchmarkError> {
329 clear_validators_and_nominators::<T>();
331
332 let origin_weight = Staking::<T>::min_nominator_bond();
333
334 let scenario = ListScenario::<T>::new(origin_weight, true)?;
337 let controller = scenario.origin_controller1.clone();
338 let stash = scenario.origin_stash1;
339 assert!(T::VoterList::contains(&stash));
340
341 let ed = asset::existential_deposit::<T>();
342 let mut ledger = Ledger::<T>::get(&controller).unwrap();
343 ledger.active = ed - One::one();
344 Ledger::<T>::insert(&controller, ledger);
345 set_active_era::<T>(EraIndex::max_value());
346
347 whitelist_account!(controller);
348
349 #[extrinsic_call]
350 withdraw_unbonded(RawOrigin::Signed(controller.clone()), 0);
351
352 assert!(!Ledger::<T>::contains_key(controller));
353 assert!(!T::VoterList::contains(&stash));
354
355 Ok(())
356 }
357
358 #[benchmark]
359 fn validate() -> Result<(), BenchmarkError> {
360 let (stash, controller) = create_stash_controller::<T>(
361 MaxNominationsOf::<T>::get() - 1,
362 100,
363 RewardDestination::Staked,
364 )?;
365 assert!(!T::VoterList::contains(&stash));
367
368 let prefs = ValidatorPrefs::default();
369 whitelist_account!(controller);
370
371 #[extrinsic_call]
372 _(RawOrigin::Signed(controller), prefs);
373
374 assert!(Validators::<T>::contains_key(&stash));
375 assert!(T::VoterList::contains(&stash));
376
377 Ok(())
378 }
379
380 #[benchmark]
381 fn kick(
382 k: Linear<1, 128>,
387 ) -> Result<(), BenchmarkError> {
388 let rest_of_validators =
391 create_validators_with_seed::<T>(MaxNominationsOf::<T>::get() - 1, 100, 415)?;
392
393 let (stash, controller) = create_stash_controller::<T>(
395 MaxNominationsOf::<T>::get() - 1,
396 100,
397 RewardDestination::Staked,
398 )?;
399 let stash_lookup = T::Lookup::unlookup(stash.clone());
400
401 Staking::<T>::validate(RawOrigin::Signed(controller.clone()).into(), Default::default())?;
403
404 let mut nominator_stashes = Vec::with_capacity(k as usize);
407 for i in 0..k {
408 let (n_stash, n_controller) = create_stash_controller::<T>(
410 MaxNominationsOf::<T>::get() + i,
411 100,
412 RewardDestination::Staked,
413 )?;
414
415 let mut nominations = rest_of_validators.clone();
417 nominations.insert(i as usize % (nominations.len() + 1), stash_lookup.clone());
420 Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), nominations)?;
422
423 nominator_stashes.push(n_stash);
424 }
425
426 for n in nominator_stashes.iter() {
428 assert!(Nominators::<T>::get(n).unwrap().targets.contains(&stash));
429 }
430
431 let kicks = nominator_stashes
433 .iter()
434 .map(|n| T::Lookup::unlookup(n.clone()))
435 .collect::<Vec<_>>();
436
437 whitelist_account!(controller);
438
439 #[extrinsic_call]
440 _(RawOrigin::Signed(controller), kicks);
441
442 for n in nominator_stashes.iter() {
444 assert!(!Nominators::<T>::get(n).unwrap().targets.contains(&stash));
445 }
446
447 Ok(())
448 }
449
450 #[benchmark]
451 fn nominate(n: Linear<1, { MaxNominationsOf::<T>::get() }>) -> Result<(), BenchmarkError> {
453 clear_validators_and_nominators::<T>();
455
456 let origin_weight = Staking::<T>::min_nominator_bond();
457
458 ListScenario::<T>::new(origin_weight, true)?;
461 let (stash, controller) = create_stash_controller_with_balance::<T>(
462 SEED + MaxNominationsOf::<T>::get() + 1, origin_weight,
465 RewardDestination::Staked,
466 )
467 .unwrap();
468
469 assert!(!Nominators::<T>::contains_key(&stash));
470 assert!(!T::VoterList::contains(&stash));
471
472 let validators = create_validators::<T>(n, 100).unwrap();
473 whitelist_account!(controller);
474
475 #[extrinsic_call]
476 _(RawOrigin::Signed(controller), validators);
477
478 assert!(Nominators::<T>::contains_key(&stash));
479 assert!(T::VoterList::contains(&stash));
480
481 Ok(())
482 }
483
484 #[benchmark]
485 fn chill() -> Result<(), BenchmarkError> {
486 clear_validators_and_nominators::<T>();
488
489 let origin_weight = Staking::<T>::min_nominator_bond();
490
491 let scenario = ListScenario::<T>::new(origin_weight, true)?;
494 let controller = scenario.origin_controller1.clone();
495 let stash = scenario.origin_stash1;
496 assert!(T::VoterList::contains(&stash));
497
498 whitelist_account!(controller);
499
500 #[extrinsic_call]
501 _(RawOrigin::Signed(controller));
502
503 assert!(!T::VoterList::contains(&stash));
504
505 Ok(())
506 }
507
508 #[benchmark]
509 fn set_payee() -> Result<(), BenchmarkError> {
510 let (stash, controller) =
511 create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
512 assert_eq!(Payee::<T>::get(&stash), Some(RewardDestination::Staked));
513 whitelist_account!(controller);
514
515 #[extrinsic_call]
516 _(RawOrigin::Signed(controller.clone()), RewardDestination::Account(controller.clone()));
517
518 assert_eq!(Payee::<T>::get(&stash), Some(RewardDestination::Account(controller)));
519
520 Ok(())
521 }
522
523 #[benchmark]
524 fn update_payee() -> Result<(), BenchmarkError> {
525 let (stash, controller) =
526 create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
527 Payee::<T>::insert(&stash, {
528 #[allow(deprecated)]
529 RewardDestination::Controller
530 });
531 whitelist_account!(controller);
532
533 #[extrinsic_call]
534 _(RawOrigin::Signed(controller.clone()), controller.clone());
535
536 assert_eq!(Payee::<T>::get(&stash), Some(RewardDestination::Account(controller)));
537
538 Ok(())
539 }
540
541 #[benchmark]
542 fn set_controller() -> Result<(), BenchmarkError> {
543 let (stash, ctlr) =
544 create_unique_stash_controller::<T>(9000, 100, RewardDestination::Staked, false)?;
545 assert!(!Ledger::<T>::contains_key(&stash));
547 assert!(Ledger::<T>::contains_key(&ctlr));
548 assert_eq!(Bonded::<T>::get(&stash), Some(ctlr.clone()));
549
550 whitelist_account!(stash);
551
552 #[extrinsic_call]
553 _(RawOrigin::Signed(stash.clone()));
554
555 assert!(Ledger::<T>::contains_key(&stash));
556
557 Ok(())
558 }
559
560 #[benchmark]
561 fn set_validator_count() {
562 let validator_count = T::MaxValidatorSet::get() - 1;
563
564 #[extrinsic_call]
565 _(RawOrigin::Root, validator_count);
566
567 assert_eq!(ValidatorCount::<T>::get(), validator_count);
568 }
569
570 #[benchmark]
571 fn force_no_eras() {
572 #[extrinsic_call]
573 _(RawOrigin::Root);
574
575 assert_eq!(ForceEra::<T>::get(), Forcing::ForceNone);
576 }
577
578 #[benchmark]
579 fn force_new_era() {
580 #[extrinsic_call]
581 _(RawOrigin::Root);
582
583 assert_eq!(ForceEra::<T>::get(), Forcing::ForceNew);
584 }
585
586 #[benchmark]
587 fn force_new_era_always() {
588 #[extrinsic_call]
589 _(RawOrigin::Root);
590
591 assert_eq!(ForceEra::<T>::get(), Forcing::ForceAlways);
592 }
593
594 #[benchmark]
595 fn set_invulnerables(v: Linear<0, { T::MaxInvulnerables::get() }>) {
597 let mut invulnerables = Vec::new();
598 for i in 0..v {
599 invulnerables.push(account("invulnerable", i, SEED));
600 }
601
602 #[extrinsic_call]
603 _(RawOrigin::Root, invulnerables);
604
605 assert_eq!(Invulnerables::<T>::get().len(), v as usize);
606 }
607
608 #[benchmark]
609 fn deprecate_controller_batch(
610 u: Linear<0, { T::MaxControllersInDeprecationBatch::get() }>,
613 ) -> Result<(), BenchmarkError> {
614 let mut controllers: Vec<_> = vec![];
615 let mut stashes: Vec<_> = vec![];
616 for i in 0..u as u32 {
617 let (stash, controller) =
618 create_unique_stash_controller::<T>(i, 100, RewardDestination::Staked, false)?;
619 controllers.push(controller);
620 stashes.push(stash);
621 }
622 let bounded_controllers: BoundedVec<_, T::MaxControllersInDeprecationBatch> =
623 BoundedVec::try_from(controllers.clone()).unwrap();
624
625 #[extrinsic_call]
626 _(RawOrigin::Root, bounded_controllers);
627
628 for i in 0..u as u32 {
629 let stash = &stashes[i as usize];
630 let controller = &controllers[i as usize];
631 assert_eq!(Ledger::<T>::get(controller), None);
633 assert_eq!(Bonded::<T>::get(stash), Some(stash.clone()));
635 assert_eq!(Ledger::<T>::get(stash).unwrap().stash, *stash);
637 }
638
639 Ok(())
640 }
641
642 #[benchmark]
643 fn force_unstake() -> Result<(), BenchmarkError> {
644 clear_validators_and_nominators::<T>();
646
647 let origin_weight = Staking::<T>::min_nominator_bond();
648
649 let scenario = ListScenario::<T>::new(origin_weight, true)?;
652 let controller = scenario.origin_controller1.clone();
653 let stash = scenario.origin_stash1;
654 assert!(T::VoterList::contains(&stash));
655
656 #[extrinsic_call]
657 _(RawOrigin::Root, stash.clone(), 0);
658
659 assert!(!Ledger::<T>::contains_key(&controller));
660 assert!(!T::VoterList::contains(&stash));
661
662 Ok(())
663 }
664
665 #[benchmark]
666 fn cancel_deferred_slash(s: Linear<1, { T::MaxValidatorSet::get() }>) {
667 let era = EraIndex::one();
668
669 let validators: Vec<_> = (0..s)
671 .map(|i| {
672 let validator: T::AccountId = account("validator", i, SEED);
673
674 let slash_key = (validator.clone(), Perbill::from_percent(10), 0);
676 let unapplied_slash = UnappliedSlash::<T> {
677 validator: validator.clone(),
678 own: Zero::zero(),
679 others: WeakBoundedVec::default(),
680 reporter: Default::default(),
681 payout: Zero::zero(),
682 };
683 UnappliedSlashes::<T>::insert(era, slash_key, unapplied_slash);
684
685 validator
686 })
687 .collect();
688
689 let validator_slashes: Vec<_> =
691 validators.into_iter().map(|v| (v, Perbill::from_percent(10))).collect();
692
693 #[extrinsic_call]
694 _(RawOrigin::Root, era, validator_slashes.clone());
695
696 let cancelled_slashes = CancelledSlashes::<T>::get(era);
698 assert_eq!(cancelled_slashes.len(), s as usize);
699 }
700
701 #[benchmark]
702 fn payout_stakers_alive_staked(
703 n: Linear<0, { T::MaxExposurePageSize::get() as u32 }>,
704 ) -> Result<(), BenchmarkError> {
705 let (validator, nominators, current_era) = create_validator_with_nominators::<T>(
706 n,
707 T::MaxExposurePageSize::get() as u32,
708 false,
709 true,
710 RewardDestination::Staked,
711 )?;
712
713 <ErasValidatorPrefs<T>>::insert(
715 current_era,
716 validator.clone(),
717 Validators::<T>::get(&validator),
718 );
719
720 let caller = whitelisted_caller();
721 let balance_before = asset::stakeable_balance::<T>(&validator);
722 let mut nominator_balances_before = Vec::new();
723 for (stash, _) in &nominators {
724 let balance = asset::stakeable_balance::<T>(stash);
725 nominator_balances_before.push(balance);
726 }
727
728 #[extrinsic_call]
729 payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era);
730
731 let balance_after = asset::stakeable_balance::<T>(&validator);
732 ensure!(
733 balance_before < balance_after,
734 "Balance of validator stash should have increased after payout.",
735 );
736 for ((stash, _), balance_before) in nominators.iter().zip(nominator_balances_before.iter())
737 {
738 let balance_after = asset::stakeable_balance::<T>(stash);
739 ensure!(
740 balance_before < &balance_after,
741 "Balance of nominator stash should have increased after payout.",
742 );
743 }
744
745 Ok(())
746 }
747
748 #[benchmark]
749 fn rebond(l: Linear<1, { T::MaxUnlockingChunks::get() as u32 }>) -> Result<(), BenchmarkError> {
750 clear_validators_and_nominators::<T>();
752
753 let origin_weight = Pallet::<T>::min_nominator_bond()
754 .max(100u32.into());
756
757 let scenario = ListScenario::<T>::new(origin_weight, true)?;
759 let dest_weight = scenario.dest_weight;
760
761 let rebond_amount = dest_weight - origin_weight;
763
764 let value = rebond_amount / l.into();
766 assert_ne!(value, Zero::zero());
768 assert!(value * l.into() + origin_weight > origin_weight);
770 assert!(value * l.into() + origin_weight <= dest_weight);
771 let unlock_chunk = UnlockChunk::<BalanceOf<T>> { value, era: EraIndex::zero() };
772
773 let controller = scenario.origin_controller1;
774 let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
775
776 for _ in 0..l {
777 staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap()
778 }
779 Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
780 let original_bonded: BalanceOf<T> = staking_ledger.active;
781
782 whitelist_account!(controller);
783
784 #[extrinsic_call]
785 _(RawOrigin::Signed(controller.clone()), rebond_amount);
786
787 let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
788 let new_bonded: BalanceOf<T> = ledger.active;
789 assert!(original_bonded < new_bonded);
790
791 Ok(())
792 }
793
794 #[benchmark]
795 fn reap_stash() -> Result<(), BenchmarkError> {
796 clear_validators_and_nominators::<T>();
798
799 let origin_weight = Staking::<T>::min_nominator_bond();
800
801 let scenario = ListScenario::<T>::new(origin_weight, true)?;
804 let controller = scenario.origin_controller1.clone();
805 let stash = scenario.origin_stash1;
806
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(), 0);
818
819 assert!(!Bonded::<T>::contains_key(&stash));
820 assert!(!T::VoterList::contains(&stash));
821
822 Ok(())
823 }
824
825 #[benchmark]
826 fn set_staking_configs_all_set() {
827 #[extrinsic_call]
828 set_staking_configs(
829 RawOrigin::Root,
830 ConfigOp::Set(BalanceOf::<T>::max_value()),
831 ConfigOp::Set(BalanceOf::<T>::max_value()),
832 ConfigOp::Set(u32::MAX),
833 ConfigOp::Set(u32::MAX),
834 ConfigOp::Set(Percent::max_value()),
835 ConfigOp::Set(Perbill::max_value()),
836 ConfigOp::Set(Percent::max_value()),
837 );
838
839 assert_eq!(MinNominatorBond::<T>::get(), BalanceOf::<T>::max_value());
840 assert_eq!(MinValidatorBond::<T>::get(), BalanceOf::<T>::max_value());
841 assert_eq!(MaxNominatorsCount::<T>::get(), Some(u32::MAX));
842 assert_eq!(MaxValidatorsCount::<T>::get(), Some(u32::MAX));
843 assert_eq!(ChillThreshold::<T>::get(), Some(Percent::from_percent(100)));
844 assert_eq!(MinCommission::<T>::get(), Perbill::from_percent(100));
845 assert_eq!(MaxStakedRewards::<T>::get(), Some(Percent::from_percent(100)));
846 }
847
848 #[benchmark]
849 fn set_staking_configs_all_remove() {
850 #[extrinsic_call]
851 set_staking_configs(
852 RawOrigin::Root,
853 ConfigOp::Remove,
854 ConfigOp::Remove,
855 ConfigOp::Remove,
856 ConfigOp::Remove,
857 ConfigOp::Remove,
858 ConfigOp::Remove,
859 ConfigOp::Remove,
860 );
861
862 assert!(!MinNominatorBond::<T>::exists());
863 assert!(!MinValidatorBond::<T>::exists());
864 assert!(!MaxNominatorsCount::<T>::exists());
865 assert!(!MaxValidatorsCount::<T>::exists());
866 assert!(!ChillThreshold::<T>::exists());
867 assert!(!MinCommission::<T>::exists());
868 assert!(!MaxStakedRewards::<T>::exists());
869 }
870
871 #[benchmark]
872 fn chill_other() -> Result<(), BenchmarkError> {
873 clear_validators_and_nominators::<T>();
875
876 let origin_weight = Staking::<T>::min_nominator_bond();
877
878 let scenario = ListScenario::<T>::new(origin_weight, true)?;
881 let stash = scenario.origin_stash1;
882 assert!(T::VoterList::contains(&stash));
883
884 Staking::<T>::set_staking_configs(
885 RawOrigin::Root.into(),
886 ConfigOp::Set(BalanceOf::<T>::max_value()),
887 ConfigOp::Set(BalanceOf::<T>::max_value()),
888 ConfigOp::Set(0),
889 ConfigOp::Set(0),
890 ConfigOp::Set(Percent::from_percent(0)),
891 ConfigOp::Set(Zero::zero()),
892 ConfigOp::Noop,
893 )?;
894
895 let caller = whitelisted_caller();
896
897 #[extrinsic_call]
898 _(RawOrigin::Signed(caller), stash.clone());
899
900 assert!(!T::VoterList::contains(&stash));
901
902 Ok(())
903 }
904
905 #[benchmark]
906 fn force_apply_min_commission() -> Result<(), BenchmarkError> {
907 clear_validators_and_nominators::<T>();
909
910 let (stash, controller) = create_stash_controller::<T>(1, 1, RewardDestination::Staked)?;
912 let validator_prefs =
913 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
914 Staking::<T>::validate(RawOrigin::Signed(controller).into(), validator_prefs)?;
915
916 assert_eq!(
918 Validators::<T>::get(&stash),
919 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }
920 );
921
922 MinCommission::<T>::set(Perbill::from_percent(75));
924 let caller = whitelisted_caller();
925
926 #[extrinsic_call]
927 _(RawOrigin::Signed(caller), stash.clone());
928
929 assert_eq!(
931 Validators::<T>::get(&stash),
932 ValidatorPrefs { commission: Perbill::from_percent(75), ..Default::default() }
933 );
934
935 Ok(())
936 }
937
938 #[benchmark]
939 fn set_min_commission() {
940 let min_commission = Perbill::max_value();
941
942 #[extrinsic_call]
943 _(RawOrigin::Root, min_commission);
944
945 assert_eq!(MinCommission::<T>::get(), Perbill::from_percent(100));
946 }
947
948 #[benchmark]
949 fn restore_ledger() -> Result<(), BenchmarkError> {
950 let (stash, controller) = create_stash_controller::<T>(0, 100, RewardDestination::Staked)?;
951 Ledger::<T>::remove(controller);
953
954 #[extrinsic_call]
955 _(RawOrigin::Root, stash.clone(), None, None, None);
956
957 assert_eq!(Staking::<T>::inspect_bond_state(&stash), Ok(LedgerIntegrityState::Ok));
958
959 Ok(())
960 }
961
962 #[benchmark]
963 fn migrate_currency() -> Result<(), BenchmarkError> {
964 let (stash, _ctrl) =
965 create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
966 let stake = asset::staked::<T>(&stash);
967 migrate_to_old_currency::<T>(stash.clone());
968 assert!(asset::staked::<T>(&stash).is_zero());
970 whitelist_account!(stash);
971
972 #[extrinsic_call]
973 _(RawOrigin::Signed(stash.clone()), stash.clone());
974
975 assert_eq!(asset::staked::<T>(&stash), stake);
976 Ok(())
977 }
978
979 #[benchmark]
980 fn apply_slash() -> Result<(), BenchmarkError> {
981 let era = EraIndex::one();
982 ActiveEra::<T>::put(ActiveEraInfo { index: era, start: None });
983 let (validator, nominators, _current_era) = create_validator_with_nominators::<T>(
984 T::MaxExposurePageSize::get() as u32,
985 T::MaxExposurePageSize::get() as u32,
986 false,
987 true,
988 RewardDestination::Staked,
989 )?;
990 let slash_fraction = Perbill::from_percent(10);
991 let page_index = 0;
992 let slashed_balance = BalanceOf::<T>::from(10u32);
993
994 let slash_key = (validator.clone(), slash_fraction, page_index);
995 let slashed_nominators =
996 nominators.iter().map(|(n, _)| (n.clone(), slashed_balance)).collect::<Vec<_>>();
997
998 let unapplied_slash = UnappliedSlash::<T> {
999 validator: validator.clone(),
1000 own: slashed_balance,
1001 others: WeakBoundedVec::force_from(slashed_nominators, None),
1002 reporter: Default::default(),
1003 payout: Zero::zero(),
1004 };
1005
1006 UnappliedSlashes::<T>::insert(era, slash_key.clone(), unapplied_slash);
1008
1009 #[extrinsic_call]
1010 _(RawOrigin::Signed(validator.clone()), era, slash_key.clone());
1011
1012 assert!(UnappliedSlashes::<T>::get(era, &slash_key).is_none());
1014
1015 Ok(())
1016 }
1017
1018 #[benchmark]
1019 fn process_offence_queue() -> Result<(), BenchmarkError> {
1020 #[cfg(test)]
1023 crate::mock::SlashDeferDuration::set(77);
1024
1025 let all_validators = crate::testing_utils::create_validators_with_nominators_for_era::<T>(
1027 ValidatorCount::<T>::get().max(1),
1029 2 * T::MaxExposurePageSize::get(),
1031 16,
1032 false,
1033 Some(1),
1034 )?;
1035 let offender =
1036 T::Lookup::lookup(all_validators.first().cloned().expect("must exist")).unwrap();
1037
1038 let _new_validators = Rotator::<T>::legacy_insta_plan_era();
1040 Rotator::<T>::start_era(
1042 crate::ActiveEraInfo { index: Rotator::<T>::planned_era() - 1, start: Some(1) },
1043 42, 2, );
1046
1047 let offender_exposure =
1049 Eras::<T>::get_full_exposure(Rotator::<T>::planned_era(), &offender);
1050 ensure!(
1051 offender_exposure.others.len() as u32 == 2 * T::MaxExposurePageSize::get(),
1052 "exposure not created"
1053 );
1054
1055 let slash_session = 42;
1057 let offences = vec![rc_client::Offence {
1058 offender: offender.clone(),
1059 reporters: Default::default(),
1060 slash_fraction: Perbill::from_percent(50),
1061 }];
1062 <crate::Pallet<T> as rc_client::AHStakingInterface>::on_new_offences(
1063 slash_session,
1064 offences,
1065 );
1066
1067 ensure!(
1069 ValidatorSlashInEra::<T>::contains_key(Rotator::<T>::active_era(), offender),
1070 "offence not submitted"
1071 );
1072 ensure!(
1073 OffenceQueueEras::<T>::get().unwrap_or_default() == vec![Rotator::<T>::active_era()],
1074 "offence should be queued"
1075 );
1076
1077 #[block]
1078 {
1079 slashing::process_offence::<T>();
1080 }
1081
1082 ensure!(OffenceQueueEras::<T>::get().is_none(), "offence should not be queued");
1083
1084 Ok(())
1085 }
1086
1087 #[benchmark]
1088 fn rc_on_offence(
1089 v: Linear<2, { T::MaxValidatorSet::get() / 2 }>,
1090 ) -> Result<(), BenchmarkError> {
1091 let initial_era = Rotator::<T>::planned_era();
1092 let _ = crate::testing_utils::create_validators_with_nominators_for_era::<T>(
1093 2 * v,
1094 1000,
1096 16,
1097 false,
1098 None,
1099 )?;
1100
1101 let new_validators = Rotator::<T>::legacy_insta_plan_era();
1103 ensure!(Rotator::<T>::planned_era() == initial_era + 1, "era should be incremented");
1104 Rotator::<T>::start_era(
1106 crate::ActiveEraInfo { index: initial_era, start: Some(1) },
1107 42, 2, );
1110
1111 ensure!(Rotator::<T>::active_era_start_session_index() == 42, "BondedEra not set");
1113
1114 let to_slash_count = new_validators.len() / 2;
1116 let to_slash = new_validators.into_iter().take(to_slash_count).collect::<Vec<_>>();
1117 let one_slashed = to_slash.first().cloned().unwrap();
1118 let offences = to_slash
1119 .into_iter()
1120 .map(|offender| rc_client::Offence {
1121 offender,
1122 reporters: Default::default(),
1123 slash_fraction: Perbill::from_percent(50),
1124 })
1125 .collect::<Vec<_>>();
1126 let slash_session = 42;
1127
1128 ensure!(
1130 !ValidatorSlashInEra::<T>::contains_key(initial_era + 1, &one_slashed),
1131 "offence submitted???"
1132 );
1133
1134 #[block]
1135 {
1136 <crate::Pallet<T> as rc_client::AHStakingInterface>::on_new_offences(
1137 slash_session,
1138 offences,
1139 );
1140 }
1141
1142 ensure!(
1144 ValidatorSlashInEra::<T>::contains_key(initial_era + 1, one_slashed),
1145 "offence not submitted"
1146 );
1147
1148 Ok(())
1149 }
1150
1151 #[benchmark]
1152 fn rc_on_session_report() -> Result<(), BenchmarkError> {
1153 let initial_planned_era = Rotator::<T>::planned_era();
1154 let initial_active_era = Rotator::<T>::active_era();
1155
1156 crate::testing_utils::create_validators_with_nominators_for_era::<T>(
1159 10, 50, 2, false, None,
1160 )?;
1161
1162 let _new_validators = Rotator::<T>::legacy_insta_plan_era();
1164 ensure!(
1165 CurrentEra::<T>::get().unwrap() == initial_planned_era + 1,
1166 "era should be incremented"
1167 );
1168
1169 let validator_points = (0..T::MaxValidatorSet::get())
1171 .map(|v| (account::<T::AccountId>("random", v, SEED), v))
1172 .collect::<Vec<_>>();
1173 let activation_timestamp = Some((1u64, initial_planned_era + 1));
1174 let report = rc_client::SessionReport {
1175 end_index: 42,
1176 leftover: false,
1177 validator_points,
1178 activation_timestamp,
1179 };
1180
1181 #[block]
1182 {
1183 <crate::Pallet<T> as rc_client::AHStakingInterface>::on_relay_session_report(report);
1184 }
1185
1186 ensure!(Rotator::<T>::active_era() == initial_active_era + 1, "active era not bumped");
1187 Ok(())
1188 }
1189
1190 fn setup_era_for_pruning<T: Config>(v: u32) -> EraIndex {
1192 let validators = v;
1193 let era = 7;
1194
1195 let history_depth = T::HistoryDepth::get();
1198 let active_era = era + history_depth + 1;
1199 crate::ActiveEra::<T>::put(crate::ActiveEraInfo { index: active_era, start: Some(0) });
1200
1201 let max_total_nominators_per_validator =
1205 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
1206 let exposed_nominators_per_validator = max_total_nominators_per_validator / validators;
1207
1208 for i in 0..validators {
1210 let validator = account::<T::AccountId>("validator", i, SEED);
1211 ErasValidatorPrefs::<T>::insert(era, validator.clone(), ValidatorPrefs::default())
1212 }
1213
1214 let pages: WeakBoundedVec<_, _> = (0..crate::ClaimedRewardsBound::<T>::get())
1216 .collect::<Vec<_>>()
1217 .try_into()
1218 .unwrap();
1219 for i in 0..validators {
1220 let validator = account::<T::AccountId>("validator", i, SEED);
1221 ClaimedRewards::<T>::insert(era, validator.clone(), pages.clone())
1222 }
1223
1224 (0..validators)
1226 .map(|validator_index| account::<T::AccountId>("validator", validator_index, SEED))
1227 .for_each(|validator| {
1228 let exposure = sp_staking::Exposure::<T::AccountId, BalanceOf<T>> {
1229 own: BalanceOf::<T>::max_value(),
1230 total: BalanceOf::<T>::max_value(),
1231 others: (0..exposed_nominators_per_validator)
1232 .map(|n| {
1233 let nominator = account::<T::AccountId>("nominator", n, SEED);
1234 IndividualExposure {
1235 who: nominator,
1236 value: BalanceOf::<T>::max_value(),
1237 }
1238 })
1239 .collect::<Vec<_>>(),
1240 };
1241 Eras::<T>::upsert_exposure(era, &validator, exposure);
1242 });
1243
1244 ErasValidatorReward::<T>::insert(era, BalanceOf::<T>::max_value());
1246
1247 let reward_points = crate::EraRewardPoints::<T> {
1249 total: 77777,
1250 individual: (0..validators)
1251 .map(|v| account::<T::AccountId>("validator", v, SEED))
1252 .map(|v| (v, 7))
1253 .collect::<BTreeMap<_, _>>()
1254 .try_into()
1255 .unwrap(),
1256 };
1257 ErasRewardPoints::<T>::insert(era, reward_points);
1258
1259 ErasTotalStake::<T>::insert(era, BalanceOf::<T>::max_value());
1261
1262 era
1263 }
1264
1265 fn validate_pruning_weight<T: Config>(
1267 result: &frame_support::dispatch::DispatchResultWithPostInfo,
1268 step_name: &str,
1269 validator_count: u32,
1270 ) {
1271 assert!(
1272 result.is_ok(),
1273 "Benchmark {} should succeed with v={}",
1274 step_name,
1275 validator_count
1276 );
1277
1278 let post_info = result.unwrap();
1279 let actual_ref_time = post_info
1280 .actual_weight
1281 .expect(&format!(
1282 "Should report actual weight for {} with v={}",
1283 step_name, validator_count
1284 ))
1285 .ref_time();
1286
1287 assert!(
1288 actual_ref_time > 0,
1289 "Should report non-zero ref_time for {} with v={}",
1290 step_name,
1291 validator_count
1292 );
1293 }
1295
1296 #[benchmark(pov_mode = Measured)]
1298 fn prune_era_stakers_paged(
1299 v: Linear<1, { T::MaxValidatorSet::get() }>,
1300 ) -> Result<(), BenchmarkError> {
1301 let era = setup_era_for_pruning::<T>(v);
1302 EraPruningState::<T>::insert(era, PruningStep::ErasStakersPaged);
1303
1304 let caller: T::AccountId = whitelisted_caller();
1305
1306 let result;
1307 #[block]
1308 {
1309 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1310 }
1311
1312 validate_pruning_weight::<T>(&result, "ErasStakersPaged", v);
1313
1314 Ok(())
1315 }
1316
1317 #[benchmark(pov_mode = Measured)]
1319 fn prune_era_stakers_overview(
1320 v: Linear<1, { T::MaxValidatorSet::get() }>,
1321 ) -> Result<(), BenchmarkError> {
1322 let era = setup_era_for_pruning::<T>(v);
1323 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview);
1324
1325 let caller: T::AccountId = whitelisted_caller();
1326
1327 let result;
1328 #[block]
1329 {
1330 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1331 }
1332
1333 validate_pruning_weight::<T>(&result, "ErasStakersOverview", v);
1334
1335 Ok(())
1336 }
1337
1338 #[benchmark(pov_mode = Measured)]
1340 fn prune_era_validator_prefs(
1341 v: Linear<1, { T::MaxValidatorSet::get() }>,
1342 ) -> Result<(), BenchmarkError> {
1343 let era = setup_era_for_pruning::<T>(v);
1344 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs);
1345
1346 let caller: T::AccountId = whitelisted_caller();
1347
1348 let result;
1349 #[block]
1350 {
1351 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1352 }
1353
1354 validate_pruning_weight::<T>(&result, "ErasValidatorPrefs", v);
1355
1356 Ok(())
1357 }
1358
1359 #[benchmark(pov_mode = Measured)]
1361 fn prune_era_claimed_rewards(
1362 v: Linear<1, { T::MaxValidatorSet::get() }>,
1363 ) -> Result<(), BenchmarkError> {
1364 let era = setup_era_for_pruning::<T>(v);
1365 EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards);
1366
1367 let caller: T::AccountId = whitelisted_caller();
1368
1369 let result;
1370 #[block]
1371 {
1372 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1373 }
1374
1375 validate_pruning_weight::<T>(&result, "ClaimedRewards", v);
1376
1377 Ok(())
1378 }
1379
1380 #[benchmark(pov_mode = Measured)]
1382 fn prune_era_validator_reward() -> Result<(), BenchmarkError> {
1383 let era = setup_era_for_pruning::<T>(1);
1384 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward);
1385
1386 let caller: T::AccountId = whitelisted_caller();
1387
1388 let result;
1389 #[block]
1390 {
1391 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1392 }
1393
1394 validate_pruning_weight::<T>(&result, "ErasValidatorReward", 1);
1395
1396 Ok(())
1397 }
1398
1399 #[benchmark(pov_mode = Measured)]
1401 fn prune_era_reward_points() -> Result<(), BenchmarkError> {
1402 let era = setup_era_for_pruning::<T>(1);
1403 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1404
1405 let caller: T::AccountId = whitelisted_caller();
1406
1407 let result;
1408 #[block]
1409 {
1410 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1411 }
1412
1413 validate_pruning_weight::<T>(&result, "ErasRewardPoints", 1);
1414
1415 Ok(())
1416 }
1417
1418 #[benchmark(pov_mode = Measured)]
1420 fn prune_era_total_stake() -> Result<(), BenchmarkError> {
1421 let era = setup_era_for_pruning::<T>(1);
1422 EraPruningState::<T>::insert(era, PruningStep::ErasTotalStake);
1423
1424 let caller: T::AccountId = whitelisted_caller();
1425
1426 let result;
1427 #[block]
1428 {
1429 result = Pallet::<T>::prune_era_step(RawOrigin::Signed(caller).into(), era);
1430 }
1431
1432 validate_pruning_weight::<T>(&result, "ErasTotalStake", 1);
1433
1434 Ok(())
1435 }
1436
1437 impl_benchmark_test_suite!(
1438 Staking,
1439 crate::mock::ExtBuilder::default().has_stakers(true),
1440 crate::mock::Test,
1441 exec_name = build_and_execute
1442 );
1443}
1444
1445#[cfg(test)]
1446mod tests {
1447 use super::*;
1448 use crate::mock::{ExtBuilder, RuntimeOrigin, Staking, Test};
1449 use frame_support::assert_ok;
1450
1451 #[test]
1452 fn create_validators_with_nominators_for_era_works() {
1453 ExtBuilder::default().build_and_execute(|| {
1454 let v = 10;
1455 let n = 100;
1456
1457 create_validators_with_nominators_for_era::<Test>(
1458 v,
1459 n,
1460 MaxNominationsOf::<Test>::get() as usize,
1461 false,
1462 None,
1463 )
1464 .unwrap();
1465
1466 let count_validators = Validators::<Test>::iter().count();
1467 let count_nominators = Nominators::<Test>::iter().count();
1468
1469 assert_eq!(count_validators, Validators::<Test>::count() as usize);
1470 assert_eq!(count_nominators, Nominators::<Test>::count() as usize);
1471
1472 assert_eq!(count_validators, v as usize);
1473 assert_eq!(count_nominators, n as usize);
1474 });
1475 }
1476
1477 #[test]
1478 fn create_validator_with_nominators_works() {
1479 ExtBuilder::default().build_and_execute(|| {
1480 let n = 10;
1481
1482 let (validator_stash, nominators, current_era) =
1483 create_validator_with_nominators::<Test>(
1484 n,
1485 <<Test as Config>::MaxExposurePageSize as Get<_>>::get(),
1486 false,
1487 false,
1488 RewardDestination::Staked,
1489 )
1490 .unwrap();
1491
1492 assert_eq!(nominators.len() as u32, n);
1493
1494 let original_stakeable_balance = asset::stakeable_balance::<Test>(&validator_stash);
1495 assert_ok!(Staking::payout_stakers_by_page(
1496 RuntimeOrigin::signed(1337),
1497 validator_stash,
1498 current_era,
1499 0
1500 ));
1501 let new_stakeable_balance = asset::stakeable_balance::<Test>(&validator_stash);
1502
1503 assert!(original_stakeable_balance < new_stakeable_balance);
1505 });
1506 }
1507}