referrerpolicy=no-referrer-when-downgrade

pallet_people/
benchmarking.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18extern crate alloc;
19
20use alloc::vec;
21
22use super::*;
23use crate::extension::{AsPerson, AsPersonInfo};
24
25use core::marker::{Send, Sync};
26use frame_benchmarking::{account, v2::*, BenchmarkError};
27use frame_support::{
28	assert_ok,
29	dispatch::RawOrigin,
30	pallet_prelude::{Get, Pays},
31	traits::{Len, OnIdle, OnPoll},
32};
33use frame_system::RawOrigin as SystemOrigin;
34use sp_runtime::{
35	generic::ExtensionVersion,
36	traits::{AppendZerosInput, AsTransactionAuthorizedOrigin, DispatchTransaction},
37	Weight,
38};
39
40const RI_ZERO: RingIndex = 0;
41const SEED: u32 = 0;
42
43type SecretOf<T> = <<T as Config>::Crypto as GenerateVerifiable>::Secret;
44
45fn new_member_from<T: Config + Send + Sync>(i: u32, seed: u32) -> (SecretOf<T>, MemberOf<T>) {
46	let mut entropy = &(i, seed).encode()[..];
47	let mut entropy = AppendZerosInput::new(&mut entropy);
48	let secret = T::Crypto::new_secret(Decode::decode(&mut entropy).unwrap());
49	let public = T::Crypto::member_from_secret(&secret);
50	(secret, public)
51}
52
53fn generate_members_for_ring<T: Config + Send + Sync>(
54	seed: u32,
55) -> Vec<(SecretOf<T>, MemberOf<T>)> {
56	(0..T::MaxRingSize::get())
57		.map(|i| new_member_from::<T>(i, seed))
58		.collect::<Vec<_>>()
59}
60
61fn generate_members<T: Config + Send + Sync>(
62	seed: u32,
63	start: u32,
64	end: u32,
65) -> Vec<(SecretOf<T>, MemberOf<T>)> {
66	(start..end).map(|i| new_member_from::<T>(i, seed)).collect::<Vec<_>>()
67}
68
69pub fn recognize_people<T: Config + Send + Sync>(
70	members: &[(SecretOf<T>, MemberOf<T>)],
71) -> Vec<(PersonalId, MemberOf<T>, SecretOf<T>)> {
72	let mut people = Vec::new();
73	for (secret, public) in members.iter() {
74		let person = pallet::Pallet::<T>::reserve_new_id();
75		pallet::Pallet::<T>::recognize_personhood(person, Some(public.clone())).unwrap();
76		people.push((person, public.clone(), secret.clone()));
77	}
78
79	people
80}
81
82pub trait BenchmarkHelper<Chunk> {
83	fn valid_account_context() -> Context;
84	fn initialize_chunks() -> Vec<Chunk>;
85}
86
87#[cfg(feature = "std")]
88impl BenchmarkHelper<()> for () {
89	fn valid_account_context() -> Context {
90		[0u8; 32]
91	}
92
93	fn initialize_chunks() -> Vec<()> {
94		vec![]
95	}
96}
97
98#[cfg(feature = "std")]
99impl BenchmarkHelper<verifiable::ring_vrf_impl::StaticChunk> for () {
100	fn valid_account_context() -> Context {
101		[0u8; 32]
102	}
103
104	fn initialize_chunks() -> Vec<verifiable::ring_vrf_impl::StaticChunk> {
105		vec![]
106	}
107}
108
109fn prepare_chunks<T: Config>() {
110	let chunks = T::BenchmarkHelper::initialize_chunks();
111
112	let page_size = <T as Config>::ChunkPageSize::get();
113
114	let mut page_idx = 0;
115	let mut chunk_idx = 0;
116	while chunk_idx < chunks.len() {
117		let chunk_idx_end = core::cmp::min(chunk_idx + page_size as usize, chunks.len());
118		let chunk_page: ChunksOf<T> = chunks[chunk_idx..chunk_idx_end]
119			.to_vec()
120			.try_into()
121			.expect("page size was checked against the array length; qed");
122		Chunks::<T>::insert(page_idx, chunk_page);
123		page_idx += 1;
124		chunk_idx = chunk_idx_end;
125	}
126}
127
128#[benchmarks(
129	where T: Send + Sync,
130		<T as frame_system::Config>::RuntimeCall:
131			Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + From<Call<T>> + GetDispatchInfo,
132		<T as frame_system::Config>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
133)]
134mod benches {
135	use super::*;
136
137	#[benchmark]
138	fn under_alias() -> Result<(), BenchmarkError> {
139		prepare_chunks::<T>();
140
141		// Generate people and build a ring
142		let members = generate_members_for_ring::<T>(SEED);
143		recognize_people::<T>(&members);
144		assert_ok!(pallet::Pallet::<T>::onboard_people());
145		let to_include =
146			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
147		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
148
149		// Create account and alias
150		let account: T::AccountId = whitelisted_caller();
151		let context = T::BenchmarkHelper::valid_account_context();
152		let alias_value: Alias = [0u8; 32];
153		let ra = RevisedContextualAlias {
154			revision: 0,
155			ring: RI_ZERO,
156			ca: ContextualAlias { context, alias: alias_value },
157		};
158
159		// Set up alias account association
160		let block_number = frame_system::Pallet::<T>::block_number();
161		assert_ok!(pallet::Pallet::<T>::set_alias_account(
162			Origin::PersonalAlias(ra.clone()).into(),
163			account.clone(),
164			block_number
165		));
166		assert!(AccountToAlias::<T>::contains_key(&account));
167		assert!(AliasToAccount::<T>::contains_key(&ra.ca));
168
169		// A simple call to benchmark with
170		let call = frame_system::Call::<T>::remark { remark: vec![] };
171		let boxed_call = Box::new(call.into());
172
173		#[extrinsic_call]
174		_(RawOrigin::Signed(account), boxed_call);
175
176		Ok(())
177	}
178
179	#[benchmark]
180	fn set_alias_account() -> Result<(), BenchmarkError> {
181		prepare_chunks::<T>();
182
183		// Generate people and build a ring
184		let members = generate_members_for_ring::<T>(SEED);
185		recognize_people::<T>(&members);
186		assert_ok!(pallet::Pallet::<T>::onboard_people());
187		let to_include =
188			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
189		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
190
191		let block_number = frame_system::Pallet::<T>::block_number();
192
193		let alias_value: Alias = [0u8; 32];
194		let alias = RevisedContextualAlias {
195			ca: ContextualAlias {
196				context: T::BenchmarkHelper::valid_account_context(),
197				alias: alias_value,
198			},
199			revision: 0,
200			ring: 0,
201		};
202
203		// An account had already been assigned to this alias
204		let old_account: T::AccountId = account("test_old", 0, SEED);
205		assert_ok!(pallet::Pallet::<T>::set_alias_account(
206			Origin::PersonalAlias(alias.clone()).into(),
207			old_account.clone(),
208			block_number
209		));
210		assert!(AccountToAlias::<T>::contains_key(&old_account));
211		assert!(AliasToAccount::<T>::contains_key(&alias.ca));
212
213		let account: T::AccountId = account("test", 0, SEED);
214
215		#[extrinsic_call]
216		_(Origin::PersonalAlias(alias.clone()), account.clone(), block_number);
217
218		assert!(!AccountToAlias::<T>::contains_key(&old_account));
219		assert!(AccountToAlias::<T>::contains_key(&account));
220		assert!(AliasToAccount::<T>::contains_key(&alias.ca));
221		assert_eq!(AliasToAccount::<T>::get(&alias.ca), Some(account));
222
223		Ok(())
224	}
225
226	#[benchmark]
227	fn unset_alias_account() -> Result<(), BenchmarkError> {
228		prepare_chunks::<T>();
229
230		// Generate people and build a ring
231		let members = generate_members_for_ring::<T>(SEED);
232		recognize_people::<T>(&members);
233		assert_ok!(pallet::Pallet::<T>::onboard_people());
234		let to_include =
235			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
236		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
237
238		let account: T::AccountId = account("test", 0, SEED);
239		let block_number = frame_system::Pallet::<T>::block_number();
240
241		let alias_value: Alias = [0u8; 32];
242		let alias = RevisedContextualAlias {
243			ca: ContextualAlias {
244				context: T::BenchmarkHelper::valid_account_context(),
245				alias: alias_value,
246			},
247			revision: 0,
248			ring: 0,
249		};
250
251		assert_ok!(pallet::Pallet::<T>::set_alias_account(
252			Origin::PersonalAlias(alias.clone()).into(),
253			account.clone(),
254			block_number
255		));
256		assert!(AccountToAlias::<T>::contains_key(&account));
257		assert!(AliasToAccount::<T>::contains_key(&alias.ca));
258
259		#[extrinsic_call]
260		_(Origin::PersonalAlias(alias.clone()));
261
262		assert!(!AccountToAlias::<T>::contains_key(&account));
263		assert!(!AliasToAccount::<T>::contains_key(&alias.ca));
264
265		Ok(())
266	}
267
268	#[benchmark]
269	fn force_recognize_personhood() -> Result<(), BenchmarkError> {
270		let members = generate_members_for_ring::<T>(SEED);
271
272		#[extrinsic_call]
273		_(SystemOrigin::Root, members.iter().map(|(_, m)| m.clone()).collect::<Vec<_>>());
274
275		for person in members {
276			assert!(pallet::Keys::<T>::get(person.1).is_some());
277		}
278
279		Ok(())
280	}
281
282	#[benchmark]
283	fn set_personal_id_account() -> Result<(), BenchmarkError> {
284		prepare_chunks::<T>();
285
286		// Generate people and build a ring
287		let members = generate_members_for_ring::<T>(SEED);
288		let people = recognize_people::<T>(&members);
289		assert_ok!(pallet::Pallet::<T>::onboard_people());
290		let to_include =
291			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
292		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
293
294		// Get one of the generated people's information
295		let (personal_id, _, _): &(PersonalId, MemberOf<T>, SecretOf<T>) = &people[0];
296
297		let account: T::AccountId = account("test", 0, SEED);
298		let block_number = frame_system::Pallet::<T>::block_number();
299
300		// An account had already been assigned to this personal id
301		let old_account: T::AccountId = frame_benchmarking::account("test_old", 0, SEED);
302		assert_ok!(pallet::Pallet::<T>::set_personal_id_account(
303			Origin::PersonalIdentity(*personal_id).into(),
304			old_account.clone(),
305			block_number
306		));
307
308		#[extrinsic_call]
309		_(Origin::PersonalIdentity(*personal_id), account.clone(), block_number);
310
311		assert_eq!(AccountToPersonalId::<T>::get(&old_account), None);
312		assert_eq!(AccountToPersonalId::<T>::get(&account), Some(*personal_id));
313		assert!(People::<T>::get(personal_id).is_some());
314		assert_eq!(People::<T>::get(personal_id).unwrap().account, Some(account));
315
316		Ok(())
317	}
318
319	#[benchmark]
320	fn unset_personal_id_account() -> Result<(), BenchmarkError> {
321		prepare_chunks::<T>();
322
323		// Generate people and build a ring
324		let members = generate_members_for_ring::<T>(SEED);
325		let people = recognize_people::<T>(&members);
326		assert_ok!(pallet::Pallet::<T>::onboard_people());
327		let to_include =
328			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
329		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
330
331		// Get one of the generated people's information
332		let (personal_id, _, _): &(PersonalId, MemberOf<T>, SecretOf<T>) = &people[0];
333
334		let account: T::AccountId = account("test", 0, SEED);
335		let block_number = frame_system::Pallet::<T>::block_number();
336
337		// An account had already been assigned to this personal id
338		let old_account: T::AccountId = frame_benchmarking::account("test_old", 0, SEED);
339		assert_ok!(pallet::Pallet::<T>::set_personal_id_account(
340			Origin::PersonalIdentity(*personal_id).into(),
341			old_account.clone(),
342			block_number
343		));
344
345		#[extrinsic_call]
346		_(Origin::PersonalIdentity(*personal_id));
347
348		assert_eq!(AccountToPersonalId::<T>::get(&old_account), None);
349		assert_eq!(AccountToPersonalId::<T>::get(&account), None);
350		assert!(People::<T>::get(personal_id).is_some());
351		assert_eq!(People::<T>::get(personal_id).unwrap().account, None);
352
353		Ok(())
354	}
355
356	#[benchmark]
357	fn set_onboarding_size() -> Result<(), BenchmarkError> {
358		#[extrinsic_call]
359		_(SystemOrigin::Root, 1);
360
361		assert_eq!(OnboardingSize::<T>::get(), 1);
362
363		Ok(())
364	}
365
366	#[benchmark]
367	fn merge_rings() -> Result<(), BenchmarkError> {
368		prepare_chunks::<T>();
369
370		// Two rings exist
371		let ring_size: u32 = <T as Config>::MaxRingSize::get();
372		let members = generate_members::<T>(SEED, 0, ring_size * 2);
373
374		recognize_people::<T>(&members);
375		assert_ok!(pallet::Pallet::<T>::onboard_people());
376		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO).total, ring_size);
377
378		assert_ok!(pallet::Pallet::<T>::onboard_people());
379		assert_eq!(RingKeysStatus::<T>::get(1).total, ring_size);
380
381		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, T::MaxRingSize::get()));
382		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO).included, ring_size);
383
384		assert_ok!(pallet::Pallet::<T>::build_ring(1, T::MaxRingSize::get()));
385		assert_eq!(RingKeysStatus::<T>::get(1).included, ring_size);
386
387		// Suspend and remove more than half of the people in both rings
388		assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
389		let suspensions: Vec<PersonalId> = (1..ring_size / 2 + 3)
390			.chain(ring_size + 1..ring_size * 3 / 2 + 3)
391			.map(|i| i as PersonalId)
392			.collect();
393		assert_ok!(pallet::Pallet::<T>::suspend_personhood(&suspensions));
394		assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
395
396		assert!(PendingSuspensions::<T>::get(RI_ZERO).len() > (ring_size / 2) as usize);
397		assert!(PendingSuspensions::<T>::get(1).len() > (ring_size / 2) as usize);
398
399		let mut meter = WeightMeter::new();
400		pallet::Pallet::<T>::migrate_keys(&mut meter);
401
402		pallet::Pallet::<T>::remove_suspended_keys(RI_ZERO);
403		pallet::Pallet::<T>::remove_suspended_keys(1);
404
405		assert!(RingKeys::<T>::get(RI_ZERO).len() < (ring_size / 2) as usize);
406		assert!(RingKeys::<T>::get(1).len() < (ring_size / 2) as usize);
407
408		let keys_left_len = RingKeys::<T>::get(RI_ZERO).len() + RingKeys::<T>::get(1).len();
409
410		// The current ring has to have a higher index than the ones being merged
411		CurrentRingIndex::<T>::set(14);
412
413		let account: T::AccountId = account("caller", 0, SEED);
414
415		#[extrinsic_call]
416		_(SystemOrigin::Signed(account), RI_ZERO, 1);
417
418		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), keys_left_len);
419		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO).total, keys_left_len as u32);
420		assert!(Root::<T>::get(RI_ZERO).is_some());
421		assert!(Root::<T>::get(1).is_none());
422
423		Ok(())
424	}
425
426	#[benchmark]
427	fn migrate_included_key() -> Result<(), BenchmarkError> {
428		prepare_chunks::<T>();
429
430		// Generate people and build a ring
431		let members = generate_members_for_ring::<T>(SEED);
432		recognize_people::<T>(&members);
433		assert_ok!(pallet::Pallet::<T>::onboard_people());
434		let to_include =
435			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
436		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
437
438		let temp_key = new_member_from::<T>(u32::MAX, SEED).1;
439		KeyMigrationQueue::<T>::insert(0, temp_key);
440
441		let new_key = new_member_from::<T>(u32::MAX - 1, SEED).1;
442
443		#[extrinsic_call]
444		_(Origin::PersonalIdentity(0u64), new_key.clone());
445
446		// Pending suspensions are reflected in the ring status.
447		assert_eq!(KeyMigrationQueue::<T>::get(0), Some(new_key));
448
449		Ok(())
450	}
451
452	#[benchmark]
453	fn migrate_onboarding_key() -> Result<(), BenchmarkError> {
454		prepare_chunks::<T>();
455
456		// Generate people and build a ring
457		let members = generate_members_for_ring::<T>(SEED);
458		recognize_people::<T>(&members);
459		assert_ok!(pallet::Pallet::<T>::onboard_people());
460		let to_include =
461			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
462		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
463
464		let temp_key = new_member_from::<T>(u32::MAX, SEED).1;
465
466		let new_person = pallet::Pallet::<T>::reserve_new_id();
467		pallet::Pallet::<T>::recognize_personhood(new_person, Some(temp_key.clone())).unwrap();
468
469		let new_key = new_member_from::<T>(u32::MAX - 1, SEED).1;
470
471		#[extrinsic_call]
472		_(Origin::PersonalIdentity(new_person), new_key.clone());
473
474		// Pending suspensions are reflected in the ring status.
475		assert!(KeyMigrationQueue::<T>::iter().next().is_none());
476		assert_eq!(OnboardingQueue::<T>::get(0)[0], new_key);
477
478		Ok(())
479	}
480
481	#[benchmark]
482	fn should_build_ring(n: Linear<1, { T::MaxRingSize::get() }>) -> Result<(), BenchmarkError> {
483		prepare_chunks::<T>();
484
485		// One full queue page of people awaiting
486		let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
487		let ring_size: u32 = <T as Config>::MaxRingSize::get();
488		let members = generate_members::<T>(SEED, 0, queue_page_size);
489		recognize_people::<T>(&members);
490		assert_ok!(pallet::Pallet::<T>::onboard_people());
491
492		// No ring built but people onboarded successfully
493		assert!(Root::<T>::get(RI_ZERO).is_none());
494		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
495		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: 0 });
496
497		#[block]
498		{
499			let _ = Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get());
500		}
501
502		Ok(())
503	}
504
505	#[benchmark]
506	fn build_ring(n: Linear<1, { T::MaxRingSize::get() }>) -> Result<(), BenchmarkError> {
507		prepare_chunks::<T>();
508
509		// One full queue page of people awaiting
510		let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
511		let ring_size: u32 = <T as Config>::MaxRingSize::get();
512		let members = generate_members::<T>(SEED, 0, queue_page_size);
513		recognize_people::<T>(&members);
514		assert_ok!(pallet::Pallet::<T>::onboard_people());
515
516		// No ring built but people onboarded successfully
517		assert!(Root::<T>::get(RI_ZERO).is_none());
518		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
519		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: 0 });
520
521		#[block]
522		{
523			assert_ok!(Pallet::<T>::build_ring(RI_ZERO, n));
524		}
525
526		// The ring becomes built
527		assert!(Root::<T>::get(RI_ZERO).is_some());
528		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
529		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: n });
530
531		Ok(())
532	}
533
534	#[benchmark]
535	fn onboard_people() -> Result<(), BenchmarkError> {
536		prepare_chunks::<T>();
537
538		// One full ring exists
539		let ring_size: u32 = <T as Config>::MaxRingSize::get();
540		let members = generate_members::<T>(SEED, 0, ring_size);
541		recognize_people::<T>(&members);
542		assert_ok!(pallet::Pallet::<T>::onboard_people());
543		let to_include =
544			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
545		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
546		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
547		assert_eq!(
548			RingKeysStatus::<T>::get(RI_ZERO),
549			RingStatus { total: ring_size, included: ring_size }
550		);
551
552		assert_eq!(QueuePageIndices::<T>::get(), (0, 0));
553		assert!(OnboardingQueue::<T>::get(0).is_empty());
554
555		// 1st onboarding page with fewer people than open slots
556		let keys_len: u32 = Keys::<T>::iter().collect::<Vec<_>>().len().try_into().unwrap();
557		let members = generate_members::<T>(SEED, keys_len, keys_len + ring_size / 2);
558		recognize_people::<T>(&members);
559		assert_eq!(OnboardingQueue::<T>::get(0).len(), (ring_size as u8 / 2) as usize);
560
561		// To stop adding keys to the first page and start filling the next one
562		QueuePageIndices::<T>::put((0, 1));
563		assert!(OnboardingQueue::<T>::get(1).is_empty());
564
565		// 2nd onboarding page full
566		let keys_len: u32 = Keys::<T>::iter().collect::<Vec<_>>().len().try_into().unwrap();
567		assert_eq!(keys_len, (ring_size + ring_size / 2));
568		let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
569		let members = generate_members::<T>(SEED, keys_len, keys_len + queue_page_size);
570		recognize_people::<T>(&members);
571
572		assert_eq!(QueuePageIndices::<T>::get(), (0, 1));
573		assert_eq!(OnboardingQueue::<T>::get(0).len(), (ring_size / 2) as usize);
574		assert!(OnboardingQueue::<T>::get(1).is_full());
575
576		assert_eq!(RingKeys::<T>::get(1).len(), 0);
577
578		#[block]
579		{
580			assert_ok!(Pallet::<T>::onboard_people());
581		}
582
583		assert_eq!(RingKeys::<T>::get(1).len(), ring_size as usize);
584		assert_eq!(RingKeysStatus::<T>::get(1), RingStatus { total: ring_size, included: 0 });
585
586		Ok(())
587	}
588
589	#[benchmark]
590	fn pending_suspensions_iteration() -> Result<(), BenchmarkError> {
591		prepare_chunks::<T>();
592
593		// Generate people and build a ring
594		let members = generate_members_for_ring::<T>(SEED);
595		let max_ring_size = T::MaxRingSize::get();
596		recognize_people::<T>(&members);
597		assert_ok!(pallet::Pallet::<T>::onboard_people());
598		let to_include = pallet::Pallet::<T>::should_build_ring(RI_ZERO, max_ring_size).unwrap();
599		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
600
601		// Suspend all people in the ring
602		assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
603		let suspensions: Vec<PersonalId> = (0..max_ring_size as PersonalId).collect();
604		assert_ok!(pallet::Pallet::<T>::suspend_personhood(&suspensions));
605		assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
606		let mut meter = WeightMeter::new();
607		pallet::Pallet::<T>::migrate_keys(&mut meter);
608
609		// To make sure they are indeed pending suspension
610		assert_eq!(PendingSuspensions::<T>::get(RI_ZERO).len(), max_ring_size as usize);
611
612		#[block]
613		{
614			assert!(PendingSuspensions::<T>::iter_keys().next().is_some());
615		}
616
617		Ok(())
618	}
619
620	#[benchmark]
621	fn remove_suspended_keys(
622		n: Linear<1, { T::MaxRingSize::get() }>,
623	) -> Result<(), BenchmarkError> {
624		prepare_chunks::<T>();
625
626		// Generate people and build a ring
627		let members = generate_members_for_ring::<T>(SEED);
628		recognize_people::<T>(&members);
629		assert_ok!(pallet::Pallet::<T>::onboard_people());
630		let to_include =
631			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
632		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
633
634		// For later verification
635		let initial_root = Root::<T>::get(RI_ZERO).unwrap();
636
637		// Suspend 'n' number of people in the ring
638		assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
639		let suspensions: Vec<PersonalId> = (0..n as PersonalId).collect();
640		assert_ok!(pallet::Pallet::<T>::suspend_personhood(&suspensions));
641		assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
642		let mut meter = WeightMeter::new();
643		pallet::Pallet::<T>::migrate_keys(&mut meter);
644
645		// To make sure they are indeed pending suspension
646		assert_eq!(PendingSuspensions::<T>::get(RI_ZERO).len(), n as usize);
647
648		#[block]
649		{
650			pallet::Pallet::<T>::remove_suspended_keys(RI_ZERO);
651		}
652
653		// Pending suspensions are cleared for the ring
654		assert!(PendingSuspensions::<T>::get(RI_ZERO).is_empty());
655
656		// Ring data becomes modified
657		let ring_size: u32 = <T as Config>::MaxRingSize::get();
658		assert_eq!(
659			RingKeysStatus::<T>::get(RI_ZERO),
660			RingStatus { included: 0, total: ring_size - n as u32 }
661		);
662		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), (ring_size - n as u32) as usize);
663		assert_ne!(Root::<T>::get(RI_ZERO).unwrap().intermediate, initial_root.intermediate);
664
665		Ok(())
666	}
667
668	#[benchmark]
669	fn migrate_keys_single_included_key() -> Result<(), BenchmarkError> {
670		prepare_chunks::<T>();
671
672		let max_members = T::MaxRingSize::get();
673		// Generate people and build a ring
674		let members = generate_members_for_ring::<T>(SEED);
675		recognize_people::<T>(&members);
676		assert_ok!(pallet::Pallet::<T>::onboard_people());
677		let to_include =
678			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
679		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
680
681		// Migrate 'n' number of people in the ring
682		for (personal_id, key) in (0..max_members as PersonalId)
683			.map(|i| new_member_from::<T>(u32::MAX - i as u32, SEED).1)
684			.enumerate()
685		{
686			assert_ok!(pallet::Pallet::<T>::migrate_included_key(
687				Origin::PersonalIdentity(personal_id as PersonalId).into(),
688				key
689			));
690		}
691		assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
692		assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
693		assert!(PendingSuspensions::<T>::get(RI_ZERO).is_empty());
694		// All migrated keys are queued, but we only want one as this function benchmarks just one
695		// iteration of `migrate_keys`.
696		assert_eq!(KeyMigrationQueue::<T>::iter().count(), T::MaxRingSize::get() as usize);
697		let (first_id, first_key) = KeyMigrationQueue::<T>::iter().next().unwrap();
698
699		#[block]
700		{
701			assert_ok!(pallet::Pallet::<T>::migrate_keys_single_included_key(first_id, first_key));
702		}
703
704		// Pending suspensions are reflected in the ring status.
705		assert_eq!(PendingSuspensions::<T>::get(RI_ZERO).len(), 1);
706
707		Ok(())
708	}
709
710	#[benchmark]
711	fn merge_queue_pages() -> Result<(), BenchmarkError> {
712		prepare_chunks::<T>();
713
714		// Two pages exists: first is full, the second contains one member
715		let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
716		let members = generate_members::<T>(SEED, 0, queue_page_size + 1);
717		recognize_people::<T>(&members);
718
719		assert_eq!(QueuePageIndices::<T>::get(), (0, 1));
720		assert!(OnboardingQueue::<T>::get(0).is_full());
721		assert_eq!(OnboardingQueue::<T>::get(1).len(), 1);
722
723		// One key is removed from the first page
724		OnboardingQueue::<T>::mutate(0, |keys| {
725			keys.pop();
726		});
727		assert_eq!(OnboardingQueue::<T>::get(0).len(), queue_page_size as usize - 1);
728
729		// Attempt to merge pages succeeds
730		let QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page } =
731			pallet::Pallet::<T>::should_merge_queue_pages()
732		else {
733			panic!("should be mergeable")
734		};
735
736		#[block]
737		{
738			pallet::Pallet::<T>::merge_queue_pages(
739				initial_head,
740				new_head,
741				first_key_page,
742				second_key_page,
743			);
744		}
745
746		// The queue pages have changed
747		assert_eq!(QueuePageIndices::<T>::get(), (1, 1));
748		assert!(OnboardingQueue::<T>::get(0).is_empty());
749		assert!(OnboardingQueue::<T>::get(1).is_full());
750
751		Ok(())
752	}
753
754	#[benchmark]
755	fn on_poll_base() -> Result<(), BenchmarkError> {
756		// Two pages exists: first is full, the second contains one member
757		let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
758		let members = generate_members::<T>(SEED, 0, queue_page_size + 1);
759		recognize_people::<T>(&members);
760
761		assert_eq!(QueuePageIndices::<T>::get(), (0, 1));
762		assert!(OnboardingQueue::<T>::get(0).is_full());
763		assert_eq!(OnboardingQueue::<T>::get(1).len(), 1);
764		assert!(RingsState::<T>::get().append_only());
765
766		let mut meter = WeightMeter::new();
767
768		#[block]
769		{
770			pallet::Pallet::<T>::on_poll(0u32.into(), &mut meter);
771		}
772
773		assert_eq!(meter.consumed(), T::WeightInfo::on_poll_base());
774		Ok(())
775	}
776
777	#[benchmark]
778	fn on_idle_base() -> Result<(), BenchmarkError> {
779		prepare_chunks::<T>();
780
781		// Two pages exists: first is full, the second contains one member
782		let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
783		let ring_size = T::MaxRingSize::get();
784		let members = generate_members::<T>(SEED, 0, queue_page_size + 1);
785		recognize_people::<T>(&members);
786		assert_ok!(pallet::Pallet::<T>::onboard_people());
787
788		// No ring built but people onboarded successfully
789		assert!(Root::<T>::get(RI_ZERO).is_none());
790		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
791		assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: 0 });
792		let to_include = Pallet::<T>::should_build_ring(RI_ZERO, ring_size).unwrap();
793		assert_ok!(Pallet::<T>::build_ring(RI_ZERO, to_include));
794		// The ring becomes built
795		assert!(Root::<T>::get(RI_ZERO).is_some());
796		assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
797		assert_eq!(
798			RingKeysStatus::<T>::get(RI_ZERO),
799			RingStatus { total: ring_size, included: ring_size }
800		);
801
802		#[block]
803		{
804			pallet::Pallet::<T>::on_idle(0u32.into(), Weight::MAX);
805		}
806
807		Ok(())
808	}
809
810	#[benchmark]
811	fn as_person_alias_with_account() -> Result<(), BenchmarkError> {
812		prepare_chunks::<T>();
813
814		// Generate people and build a ring
815		let members = generate_members_for_ring::<T>(SEED);
816		recognize_people::<T>(&members);
817		assert_ok!(pallet::Pallet::<T>::onboard_people());
818		let to_include =
819			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
820		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
821
822		// Create account and alias
823		let account: T::AccountId = account("caller", 0, SEED);
824		let context = T::BenchmarkHelper::valid_account_context();
825		let alias_value: Alias = [0u8; 32];
826		let ra = RevisedContextualAlias {
827			revision: 0,
828			ring: RI_ZERO,
829			ca: ContextualAlias { context, alias: alias_value },
830		};
831
832		// Set up alias account association
833		let block_number = frame_system::Pallet::<T>::block_number();
834		assert_ok!(pallet::Pallet::<T>::set_alias_account(
835			Origin::PersonalAlias(ra.clone()).into(),
836			account.clone(),
837			block_number
838		));
839		assert!(AccountToAlias::<T>::contains_key(&account));
840		assert!(AliasToAccount::<T>::contains_key(&ra.ca));
841
842		// A simple call to benchmark with
843		let inner = frame_system::Call::<T>::remark { remark: vec![] };
844		let call: <T as frame_system::Config>::RuntimeCall = inner.into();
845
846		let ext =
847			AsPerson::new(Some(AsPersonInfo::<T>::AsPersonalAliasWithAccount(T::Nonce::default())));
848		let info = call.get_dispatch_info();
849		let post_info = PostDispatchInfo {
850			actual_weight: Some(Weight::from_parts(10, 0)),
851			pays_fee: Pays::Yes,
852		};
853		let len = call.encoded_size();
854
855		#[block]
856		{
857			ext.test_run(RawOrigin::Signed(account).into(), &call, &info, len, 0, |_| {
858				Ok(post_info)
859			})
860			.unwrap()
861			.unwrap();
862		}
863
864		Ok(())
865	}
866
867	#[benchmark]
868	fn as_person_identity_with_account() -> Result<(), BenchmarkError> {
869		prepare_chunks::<T>();
870
871		// Generate people and build a ring
872		let members = generate_members_for_ring::<T>(SEED);
873		let recognized_people = recognize_people::<T>(&members);
874		assert_ok!(pallet::Pallet::<T>::onboard_people());
875		let to_include =
876			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
877		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
878
879		// Select one of the generated people's information
880		let (personal_id, _, _): &(PersonalId, MemberOf<T>, SecretOf<T>) = &recognized_people[0];
881
882		// Set up personal ID account association
883		let account: T::AccountId = account("caller", 0, SEED);
884		let block_number = frame_system::Pallet::<T>::block_number();
885		assert_ok!(pallet::Pallet::<T>::set_personal_id_account(
886			Origin::PersonalIdentity(*personal_id).into(),
887			account.clone(),
888			block_number
889		));
890		assert!(AccountToPersonalId::<T>::contains_key(&account));
891
892		// A simple call to benchmark with
893		let inner = frame_system::Call::<T>::remark { remark: vec![] };
894		let call: <T as frame_system::Config>::RuntimeCall = inner.into();
895
896		let ext = AsPerson::new(Some(AsPersonInfo::<T>::AsPersonalIdentityWithAccount(
897			T::Nonce::default(),
898		)));
899		let info = call.get_dispatch_info();
900		let post_info = PostDispatchInfo {
901			actual_weight: Some(Weight::from_parts(10, 0)),
902			pays_fee: Pays::Yes,
903		};
904		let len = call.encoded_size();
905
906		#[block]
907		{
908			ext.test_run(RawOrigin::Signed(account).into(), &call, &info, len, 0, |_| {
909				Ok(post_info)
910			})
911			.unwrap()
912			.unwrap();
913		}
914
915		Ok(())
916	}
917
918	#[benchmark]
919	fn as_person_alias_with_proof() -> Result<(), BenchmarkError> {
920		prepare_chunks::<T>();
921
922		// Generate people and build a ring
923		let account: T::AccountId = account("caller", 0, SEED);
924		let members = generate_members_for_ring::<T>(SEED);
925		recognize_people::<T>(&members);
926		assert_ok!(pallet::Pallet::<T>::onboard_people());
927		let to_include =
928			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
929		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
930
931		// The call to set the alias, the only one valid for this extension code path.
932		let block_number = frame_system::Pallet::<T>::block_number();
933		let inner = Call::<T>::set_alias_account { account, call_valid_at: block_number };
934		let call: <T as frame_system::Config>::RuntimeCall = inner.into();
935
936		let context = T::BenchmarkHelper::valid_account_context();
937		let ext_version: ExtensionVersion = 0;
938
939		// Generate a valid proof
940		let proof = (ext_version, &call).using_encoded(|msg| {
941			let (secret, member) = &members[0];
942			T::Crypto::create(
943				T::Crypto::open(member, members.iter().map(|(_, m)| m.clone())).unwrap(),
944				secret,
945				&context[..],
946				&sp_io::hashing::blake2_256(msg),
947			)
948			.map(|(p, _)| p)
949			.expect("should create proof")
950		});
951
952		let ext = AsPerson::new(Some(AsPersonInfo::<T>::AsPersonalAliasWithProof(
953			proof, RI_ZERO, context,
954		)));
955		let info = call.get_dispatch_info();
956		let post_info = PostDispatchInfo {
957			actual_weight: Some(Weight::from_parts(10, 0)),
958			pays_fee: Pays::Yes,
959		};
960		let len = call.encoded_size();
961
962		#[block]
963		{
964			ext.test_run(RawOrigin::None.into(), &call, &info, len, 0, |_| Ok(post_info))
965				.unwrap()
966				.unwrap();
967		}
968
969		Ok(())
970	}
971
972	#[benchmark]
973	fn as_person_identity_with_proof() -> Result<(), BenchmarkError> {
974		prepare_chunks::<T>();
975
976		// Generate people and build a ring
977		let account: T::AccountId = account("caller", 0, SEED);
978		let members = generate_members_for_ring::<T>(SEED);
979		let recognized_people = recognize_people::<T>(&members);
980		assert_ok!(pallet::Pallet::<T>::onboard_people());
981		let to_include =
982			pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
983		assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
984
985		// Select one of the generated people's information
986		let (personal_id, _, secret): &(PersonalId, MemberOf<T>, SecretOf<T>) =
987			&recognized_people[0];
988
989		// The call to set the personal ID account, the only one valid for this extension code path.
990		let block_number = frame_system::Pallet::<T>::block_number();
991		let inner = Call::<T>::set_personal_id_account { account, call_valid_at: block_number };
992		let call: <T as frame_system::Config>::RuntimeCall = inner.into();
993		let ext_version: ExtensionVersion = 0;
994		let signature = (ext_version, &call).using_encoded(|msg| {
995			<T::Crypto as GenerateVerifiable>::sign(secret, &sp_io::hashing::blake2_256(msg))
996				.expect("failed to create signature")
997		});
998
999		let ext = AsPerson::new(Some(AsPersonInfo::<T>::AsPersonalIdentityWithProof(
1000			signature,
1001			*personal_id,
1002		)));
1003		let info = call.get_dispatch_info();
1004		let post_info = PostDispatchInfo {
1005			actual_weight: Some(Weight::from_parts(10, 0)),
1006			pays_fee: Pays::Yes,
1007		};
1008		let len = call.encoded_size();
1009
1010		#[block]
1011		{
1012			ext.test_run(RawOrigin::None.into(), &call, &info, len, 0, |_| Ok(post_info))
1013				.unwrap()
1014				.unwrap();
1015		}
1016
1017		Ok(())
1018	}
1019
1020	// Implements a test for each benchmark. Execute with:
1021	// `cargo test -p pallet-people --features runtime-benchmarks`.
1022	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
1023}