pallet_offences_benchmarking/
inner.rs1use alloc::{vec, vec::Vec};
21use codec::Decode;
22use frame_benchmarking::v2::*;
23use frame_support::traits::Get;
24use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin};
25use pallet_babe::EquivocationOffence as BabeEquivocationOffence;
26use pallet_balances::Config as BalancesConfig;
27use pallet_grandpa::{
28 EquivocationOffence as GrandpaEquivocationOffence, TimeSlot as GrandpaTimeSlot,
29};
30use pallet_offences::{Config as OffencesConfig, Pallet as Offences};
31use pallet_session::{
32 historical::{Config as HistoricalConfig, IdentificationTuple},
33 Config as SessionConfig, Pallet as Session,
34};
35use pallet_staking::{
36 Config as StakingConfig, Exposure, IndividualExposure, MaxNominationsOf, Pallet as Staking,
37 RewardDestination, ValidatorPrefs,
38};
39use sp_runtime::{
40 traits::{Convert, Saturating, StaticLookup},
41 Perbill,
42};
43use sp_staking::offence::ReportOffence;
44
45const SEED: u32 = 0;
46
47const MAX_NOMINATORS: u32 = 100;
48
49pub struct Pallet<T: Config>(Offences<T>);
50
51pub trait Config:
52 SessionConfig<ValidatorId = <Self as frame_system::Config>::AccountId>
53 + StakingConfig
54 + OffencesConfig
55 + HistoricalConfig
56 + BalancesConfig
57 + IdTupleConvert<Self>
58{
59}
60
61pub trait IdTupleConvert<T: HistoricalConfig + OffencesConfig> {
64 fn convert(id: IdentificationTuple<T>) -> <T as OffencesConfig>::IdentificationTuple;
66}
67
68impl<T: HistoricalConfig + OffencesConfig> IdTupleConvert<T> for T
69where
70 <T as OffencesConfig>::IdentificationTuple: From<IdentificationTuple<T>>,
71{
72 fn convert(id: IdentificationTuple<T>) -> <T as OffencesConfig>::IdentificationTuple {
73 id.into()
74 }
75}
76
77type LookupSourceOf<T> = <<T as SystemConfig>::Lookup as StaticLookup>::Source;
78type BalanceOf<T> = <T as StakingConfig>::CurrencyBalance;
79
80struct Offender<T: Config> {
81 pub controller: T::AccountId,
82 #[allow(dead_code)]
83 pub stash: T::AccountId,
84 #[allow(dead_code)]
85 pub nominator_stashes: Vec<T::AccountId>,
86}
87
88fn bond_amount<T: Config>() -> BalanceOf<T> {
89 pallet_staking::asset::existential_deposit::<T>().saturating_mul(10_000u32.into())
90}
91
92fn create_offender<T: Config>(n: u32, nominators: u32) -> Result<Offender<T>, &'static str> {
93 let stash: T::AccountId = account("stash", n, SEED);
94 let stash_lookup: LookupSourceOf<T> = T::Lookup::unlookup(stash.clone());
95 let reward_destination = RewardDestination::Staked;
96 let amount = bond_amount::<T>();
97 let free_amount = amount.saturating_mul(2u32.into());
99 pallet_staking::asset::set_stakeable_balance::<T>(&stash, free_amount);
100 Staking::<T>::bond(
101 RawOrigin::Signed(stash.clone()).into(),
102 amount,
103 reward_destination.clone(),
104 )?;
105
106 let validator_prefs =
107 ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
108 Staking::<T>::validate(RawOrigin::Signed(stash.clone()).into(), validator_prefs)?;
109
110 let keys =
112 <T as SessionConfig>::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes())
113 .unwrap();
114 let proof: Vec<u8> = vec![0, 1, 2, 3];
115 Session::<T>::set_keys(RawOrigin::Signed(stash.clone()).into(), keys, proof)?;
116
117 let mut individual_exposures = vec![];
118 let mut nominator_stashes = vec![];
119 for i in 0..nominators {
121 let nominator_stash: T::AccountId =
122 account("nominator stash", n * MAX_NOMINATORS + i, SEED);
123 pallet_staking::asset::set_stakeable_balance::<T>(&nominator_stash, free_amount);
124
125 Staking::<T>::bond(
126 RawOrigin::Signed(nominator_stash.clone()).into(),
127 amount,
128 reward_destination.clone(),
129 )?;
130
131 let selected_validators: Vec<LookupSourceOf<T>> = vec![stash_lookup.clone()];
132 Staking::<T>::nominate(
133 RawOrigin::Signed(nominator_stash.clone()).into(),
134 selected_validators,
135 )?;
136
137 individual_exposures
138 .push(IndividualExposure { who: nominator_stash.clone(), value: amount });
139 nominator_stashes.push(nominator_stash.clone());
140 }
141
142 let exposure = Exposure { total: amount * n.into(), own: amount, others: individual_exposures };
143 let current_era = 0u32;
144 Staking::<T>::add_era_stakers(current_era, stash.clone(), exposure);
145
146 Ok(Offender { controller: stash.clone(), stash, nominator_stashes })
147}
148
149fn make_offenders<T: Config>(
150 num_offenders: u32,
151 num_nominators: u32,
152) -> Result<Vec<IdentificationTuple<T>>, &'static str> {
153 let mut offenders = vec![];
154 for i in 0..num_offenders {
155 let offender = create_offender::<T>(i + 1, num_nominators)?;
156 pallet_session::Validators::<T>::mutate(|v| v.push(offender.controller.clone()));
159 offenders.push(offender);
160 }
161
162 let id_tuples = offenders
163 .iter()
164 .map(|offender| {
165 <T as SessionConfig>::ValidatorIdOf::convert(offender.controller.clone())
166 .expect("failed to get validator id from account id")
167 })
168 .map(|validator_id| {
169 <T as HistoricalConfig>::FullIdentificationOf::convert(validator_id.clone())
170 .map(|full_id| (validator_id, full_id))
171 .unwrap()
172 })
173 .collect::<Vec<IdentificationTuple<T>>>();
174
175 if pallet_staking::ActiveEra::<T>::get().is_none() {
176 pallet_staking::ActiveEra::<T>::put(pallet_staking::ActiveEraInfo {
177 index: 0,
178 start: Some(0),
179 });
180 }
181
182 Ok(id_tuples)
183}
184
185#[cfg(test)]
186fn assert_all_slashes_applied<T>(offender_count: usize)
187where
188 T: Config,
189 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet_staking::Event<T>>,
190 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet_balances::Event<T>>,
191 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet_offences::Event>,
192 <T as frame_system::Config>::RuntimeEvent: TryInto<frame_system::Event<T>>,
193{
194 assert_eq!(System::<T>::read_events_for_pallet::<pallet_balances::Event<T>>().len(), 2);
197 assert_eq!(
199 System::<T>::read_events_for_pallet::<pallet_staking::Event<T>>().len(),
200 1 * (offender_count + 1) as usize + 1
201 );
202 assert_eq!(System::<T>::read_events_for_pallet::<pallet_offences::Event>().len(), 1);
204 assert_eq!(System::<T>::read_events_for_pallet::<frame_system::Event<T>>().len(), 1);
206}
207
208#[benchmarks(
209 where
210 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet_staking::Event<T>>,
211 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet_balances::Event<T>>,
212 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet_offences::Event>,
213 <T as frame_system::Config>::RuntimeEvent: TryInto<frame_system::Event<T>>,
214)]
215mod benchmarks {
216 use super::*;
217
218 #[benchmark]
219 pub fn report_offence_grandpa(
220 n: Linear<0, { MAX_NOMINATORS.min(MaxNominationsOf::<T>::get()) }>,
221 ) -> Result<(), BenchmarkError> {
222 let reporters = vec![account("reporter", 1, SEED)];
225
226 Staking::<T>::set_slash_reward_fraction(Perbill::one());
228
229 let mut offenders = make_offenders::<T>(1, n)?;
230 let validator_set_count = Session::<T>::validators().len() as u32;
231
232 let offence = GrandpaEquivocationOffence {
233 time_slot: GrandpaTimeSlot { set_id: 0, round: 0 },
234 session_index: 0,
235 validator_set_count,
236 offender: T::convert(offenders.pop().unwrap()),
237 };
238 assert_eq!(System::<T>::event_count(), 0);
239
240 #[block]
241 {
242 let _ = Offences::<T>::report_offence(reporters, offence);
243 }
244
245 #[cfg(test)]
246 {
247 assert_all_slashes_applied::<T>(n as usize);
248 }
249
250 Ok(())
251 }
252
253 #[benchmark]
254 fn report_offence_babe(
255 n: Linear<0, { MAX_NOMINATORS.min(MaxNominationsOf::<T>::get()) }>,
256 ) -> Result<(), BenchmarkError> {
257 let reporters = vec![account("reporter", 1, SEED)];
260
261 Staking::<T>::set_slash_reward_fraction(Perbill::one());
263
264 let mut offenders = make_offenders::<T>(1, n)?;
265 let validator_set_count = Session::<T>::validators().len() as u32;
266
267 let offence = BabeEquivocationOffence {
268 slot: 0u64.into(),
269 session_index: 0,
270 validator_set_count,
271 offender: T::convert(offenders.pop().unwrap()),
272 };
273 assert_eq!(System::<T>::event_count(), 0);
274
275 #[block]
276 {
277 let _ = Offences::<T>::report_offence(reporters, offence);
278 }
279 #[cfg(test)]
280 {
281 assert_all_slashes_applied::<T>(n as usize);
282 }
283
284 Ok(())
285 }
286
287 impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
288}