1#![cfg(feature = "runtime-benchmarks")]
19
20extern crate alloc;
21
22use super::*;
23use crate::Pallet;
24use frame::{benchmarking::prelude::*, traits::fungible::Mutate};
25
26const SEED: u32 = 0;
27
28fn assert_last_event<T: Config>(generic_event: crate::Event<T>) {
29 frame_system::Pallet::<T>::assert_last_event(generic_event.into());
30}
31
32fn fund_account<T: Config>(who: &T::AccountId) {
33 let balance = BalanceOf::<T>::max_value() / 100000u32.into();
34 T::Currency::mint_into(who, balance).expect("funding account");
35}
36
37fn generate_friends<T: Config>(seed: u32, num: u32) -> Vec<T::AccountId> {
38 let mut friends = (0..num).map(|x| account("friend", x, seed)).collect::<Vec<_>>();
39 friends.sort();
40
41 for friend in &friends {
42 fund_account::<T>(friend);
43 }
44
45 friends
46}
47
48fn last_friend<T: Config>(seed: u32, num: u32) -> T::AccountId {
50 let friends = generate_friends::<T>(seed, num);
51 friends.into_iter().last().expect("at least one friend")
52}
53
54fn create_friend_group<T: Config>(
55 seed: u32,
56 num_friends: u32,
57 threshold: u32,
58 inheritance_priority: InheritancePriority,
59 inheritance_delay: ProvidedBlockNumberOf<T>,
60 cancel_delay: ProvidedBlockNumberOf<T>,
61) -> FriendGroupOf<T> {
62 let friends = generate_friends::<T>(num_friends * seed + 1, num_friends);
63 let inheritor: T::AccountId = account("inheritor", inheritance_priority, SEED);
64 fund_account::<T>(&inheritor);
65
66 FriendGroupOf::<T> {
67 friends: friends.try_into().unwrap(),
68 friends_needed: threshold,
69 inheritor,
70 inheritance_delay,
71 inheritance_priority,
72 cancel_delay,
73 }
74}
75
76fn create_friend_groups<T: Config>(num_friends: u32, seed: u32) -> FriendGroupsOf<T> {
77 let mut friend_groups = Vec::new();
78
79 for i in 0..MAX_GROUPS_PER_ACCOUNT {
80 friend_groups.push(create_friend_group::<T>(
81 seed + i,
82 num_friends,
83 1,
84 0,
85 10u32.into(),
86 10u32.into(),
87 ));
88 }
89
90 friend_groups.try_into().unwrap()
91}
92
93fn setup_friend_groups<T: Config>(
94 lost: &T::AccountId,
95 num_friends: u32,
96 seed: u32,
97) -> FriendGroupsOf<T> {
98 let friend_groups = create_friend_groups::<T>(num_friends, seed);
99
100 let footprint = Pallet::<T>::friend_group_footprint(&friend_groups);
101 let ticket = T::FriendGroupsConsideration::new(lost, footprint).unwrap();
102 FriendGroups::<T>::insert(lost, (&friend_groups, ticket));
103
104 friend_groups
105}
106
107#[benchmarks]
108mod benchmarks {
109 use super::*;
110
111 #[benchmark]
112 fn control_inherited_account() {
113 let inheritor: T::AccountId = whitelisted_caller();
114 let recovered: T::AccountId = account("recovered", 0, SEED);
115 let recovered_lookup = T::Lookup::unlookup(recovered.clone());
116
117 fund_account::<T>(&inheritor);
118
119 let ticket = Pallet::<T>::inheritor_ticket(&inheritor).unwrap();
120 Inheritor::<T>::insert(&recovered, (0u32, &inheritor, ticket));
121
122 let call: <T as Config>::RuntimeCall =
123 frame_system::Call::<T>::remark { remark: Vec::new() }.into();
124 let call_hash = call.using_encoded(&T::Hashing::hash);
125
126 #[extrinsic_call]
127 _(RawOrigin::Signed(inheritor.clone()), recovered_lookup, Box::new(call));
128
129 assert_last_event::<T>(
130 Event::<T>::RecoveredAccountControlled {
131 recovered,
132 inheritor,
133 call_hash,
134 call_result: Ok(()),
135 }
136 .into(),
137 );
138 }
139
140 #[benchmark]
141 fn revoke_inheritor() {
142 let lost: T::AccountId = whitelisted_caller();
143 let depositor: T::AccountId = account("depositor", 0, SEED);
144
145 fund_account::<T>(&lost);
146 fund_account::<T>(&depositor);
147
148 let ticket = Pallet::<T>::inheritor_ticket(&depositor).unwrap();
149 Inheritor::<T>::insert(
150 &lost,
151 (0u32, &account::<T::AccountId>("inheritor", 0, SEED), ticket),
152 );
153
154 #[extrinsic_call]
155 _(RawOrigin::Signed(lost.clone()));
156
157 assert_last_event::<T>(Event::<T>::InheritorRevoked { lost }.into());
158 }
159
160 #[benchmark]
161 fn set_friend_groups() {
162 let lost: T::AccountId = whitelisted_caller();
163 fund_account::<T>(&lost);
164
165 let _ = setup_friend_groups::<T>(&lost, T::MaxFriendsPerConfig::get(), 0);
166 let new_friend_groups =
167 create_friend_groups::<T>(T::MaxFriendsPerConfig::get(), 1).into_inner();
168
169 #[extrinsic_call]
170 _(RawOrigin::Signed(lost.clone()), new_friend_groups);
171
172 assert_last_event::<T>(Event::<T>::FriendGroupsChanged { lost }.into());
173 }
174
175 #[benchmark]
176 fn initiate_attempt() {
177 let lost: T::AccountId = whitelisted_caller();
178 let lost_lookup = T::Lookup::unlookup(lost.clone());
179 let initiator: T::AccountId = last_friend::<T>(1, T::MaxFriendsPerConfig::get());
181
182 fund_account::<T>(&lost);
183
184 let friend_groups =
185 setup_friend_groups::<T>(&lost, T::MaxFriendsPerConfig::get(), 0).into_inner();
186
187 crate::pallet::Pallet::<T>::set_friend_groups(
188 RawOrigin::Signed(lost.clone()).into(),
189 friend_groups,
190 )
191 .unwrap();
192
193 #[extrinsic_call]
194 _(RawOrigin::Signed(initiator.clone()), lost_lookup, 0);
195
196 assert_last_event::<T>(
197 Event::<T>::AttemptApproved { lost, friend_group_index: 0, friend: initiator }.into(),
198 );
199 }
200
201 #[benchmark]
202 fn approve_attempt() {
203 let lost: T::AccountId = whitelisted_caller();
204 let lost_lookup = T::Lookup::unlookup(lost.clone());
205 let friend: T::AccountId = last_friend::<T>(1, T::MaxFriendsPerConfig::get());
207
208 fund_account::<T>(&lost);
209
210 let friend_groups =
211 setup_friend_groups::<T>(&lost, T::MaxFriendsPerConfig::get(), 0).into_inner();
212 crate::pallet::Pallet::<T>::set_friend_groups(
213 RawOrigin::Signed(lost.clone()).into(),
214 friend_groups,
215 )
216 .unwrap();
217 crate::pallet::Pallet::<T>::initiate_attempt(
218 RawOrigin::Signed(friend.clone()).into(),
219 lost_lookup.clone(),
220 0,
221 )
222 .unwrap();
223
224 crate::pallet::Attempt::<T>::mutate(&lost, 0, |maybe_attempt| {
227 let (attempt, _, _) = maybe_attempt.as_mut().expect("attempt exists after initiation");
228 attempt.approvals = ApprovalBitfieldOf::<T>::default();
229 });
230
231 #[extrinsic_call]
232 _(RawOrigin::Signed(friend.clone()), lost_lookup, 0);
233
234 assert_last_event::<T>(
235 Event::<T>::AttemptApproved { lost, friend_group_index: 0, friend }.into(),
236 );
237 }
238
239 #[benchmark]
240 fn finish_attempt() {
241 let lost: T::AccountId = whitelisted_caller();
242 let lost_lookup = T::Lookup::unlookup(lost.clone());
243 let initiator: T::AccountId = last_friend::<T>(1, T::MaxFriendsPerConfig::get());
245 let inheritor: T::AccountId = account("inheritor", 0, SEED);
246 let previous_inheritor: T::AccountId = account("old_inheritor", 0, SEED);
247
248 fund_account::<T>(&lost);
249 fund_account::<T>(&previous_inheritor);
250
251 let ticket = Pallet::<T>::inheritor_ticket(&previous_inheritor).unwrap();
253 Inheritor::<T>::insert(&lost, (1u32, &previous_inheritor, ticket));
254
255 let friend_groups =
257 setup_friend_groups::<T>(&lost, T::MaxFriendsPerConfig::get(), 0).into_inner();
258 crate::pallet::Pallet::<T>::set_friend_groups(
259 RawOrigin::Signed(lost.clone()).into(),
260 friend_groups,
261 )
262 .unwrap();
263 crate::pallet::Pallet::<T>::initiate_attempt(
264 RawOrigin::Signed(initiator.clone()).into(),
265 lost_lookup.clone(),
266 0,
267 )
268 .unwrap();
269 frame_system::Pallet::<T>::set_block_number(100u32.into());
270
271 #[extrinsic_call]
272 _(RawOrigin::Signed(initiator.clone()), lost_lookup, 0);
273
274 assert_last_event::<T>(
275 Event::<T>::AttemptFinished {
276 lost,
277 friend_group_index: 0,
278 inheritor,
279 previous_inheritor: Some(previous_inheritor),
280 }
281 .into(),
282 );
283 }
284
285 #[benchmark]
286 fn cancel_attempt() {
287 let lost: T::AccountId = whitelisted_caller();
288 let lost_lookup = T::Lookup::unlookup(lost.clone());
289 let initiator: T::AccountId = account("friend", 0, 1);
290
291 fund_account::<T>(&lost);
292 fund_account::<T>(&initiator);
293
294 let friend_groups =
295 setup_friend_groups::<T>(&lost, T::MaxFriendsPerConfig::get(), 0).into_inner();
296 crate::pallet::Pallet::<T>::set_friend_groups(
297 RawOrigin::Signed(lost.clone()).into(),
298 friend_groups,
299 )
300 .unwrap();
301 crate::pallet::Pallet::<T>::initiate_attempt(
302 RawOrigin::Signed(initiator.clone()).into(),
303 lost_lookup.clone(),
304 0,
305 )
306 .unwrap();
307 frame_system::Pallet::<T>::set_block_number(100u32.into());
308
309 #[extrinsic_call]
310 _(RawOrigin::Signed(initiator.clone()), lost_lookup, 0);
311
312 assert_last_event::<T>(
313 Event::<T>::AttemptCanceled { lost, friend_group_index: 0, canceler: initiator }.into(),
314 );
315 }
316
317 #[benchmark]
318 fn slash_attempt() {
319 let lost: T::AccountId = whitelisted_caller();
320 let lost_lookup = T::Lookup::unlookup(lost.clone());
321 let initiator: T::AccountId = account("friend", 0, 1);
322
323 fund_account::<T>(&lost);
324 fund_account::<T>(&initiator);
325
326 let friend_groups =
327 setup_friend_groups::<T>(&lost, T::MaxFriendsPerConfig::get(), 0).into_inner();
328 crate::pallet::Pallet::<T>::set_friend_groups(
329 RawOrigin::Signed(lost.clone()).into(),
330 friend_groups,
331 )
332 .unwrap();
333 crate::pallet::Pallet::<T>::initiate_attempt(
334 RawOrigin::Signed(initiator.clone()).into(),
335 lost_lookup.clone(),
336 0,
337 )
338 .unwrap();
339
340 #[extrinsic_call]
341 _(RawOrigin::Signed(lost.clone()), 0);
342 assert_last_event::<T>(Event::<T>::AttemptSlashed { lost, friend_group_index: 0 }.into());
343 }
344
345 impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
346}