// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. extern crate alloc; use alloc::vec; use super::*; use crate::extension::{AsPerson, AsPersonInfo}; use core::marker::{Send, Sync}; use frame_benchmarking::{account, v2::*, BenchmarkError}; use frame_support::{ assert_ok, dispatch::RawOrigin, pallet_prelude::{Get, Pays}, traits::{Len, OnIdle, OnPoll}, }; use frame_system::RawOrigin as SystemOrigin; use sp_runtime::{ generic::ExtensionVersion, traits::{AppendZerosInput, AsTransactionAuthorizedOrigin, DispatchTransaction}, Weight, }; const RI_ZERO: RingIndex = 0; const SEED: u32 = 0; type SecretOf = <::Crypto as GenerateVerifiable>::Secret; fn new_member_from(i: u32, seed: u32) -> (SecretOf, MemberOf) { let mut entropy = &(i, seed).encode()[..]; let mut entropy = AppendZerosInput::new(&mut entropy); let secret = T::Crypto::new_secret(Decode::decode(&mut entropy).unwrap()); let public = T::Crypto::member_from_secret(&secret); (secret, public) } fn generate_members_for_ring( seed: u32, ) -> Vec<(SecretOf, MemberOf)> { (0..T::MaxRingSize::get()) .map(|i| new_member_from::(i, seed)) .collect::>() } fn generate_members( seed: u32, start: u32, end: u32, ) -> Vec<(SecretOf, MemberOf)> { (start..end).map(|i| new_member_from::(i, seed)).collect::>() } pub fn recognize_people( members: &[(SecretOf, MemberOf)], ) -> Vec<(PersonalId, MemberOf, SecretOf)> { let mut people = Vec::new(); for (secret, public) in members.iter() { let person = pallet::Pallet::::reserve_new_id(); pallet::Pallet::::recognize_personhood(person, Some(public.clone())).unwrap(); people.push((person, public.clone(), secret.clone())); } people } pub trait BenchmarkHelper { fn valid_account_context() -> Context; fn initialize_chunks() -> Vec; } #[cfg(feature = "std")] impl BenchmarkHelper<()> for () { fn valid_account_context() -> Context { [0u8; 32] } fn initialize_chunks() -> Vec<()> { vec![] } } #[cfg(feature = "std")] impl BenchmarkHelper for () { fn valid_account_context() -> Context { [0u8; 32] } fn initialize_chunks() -> Vec { vec![] } } fn prepare_chunks() { let chunks = T::BenchmarkHelper::initialize_chunks(); let page_size = ::ChunkPageSize::get(); let mut page_idx = 0; let mut chunk_idx = 0; while chunk_idx < chunks.len() { let chunk_idx_end = core::cmp::min(chunk_idx + page_size as usize, chunks.len()); let chunk_page: ChunksOf = chunks[chunk_idx..chunk_idx_end] .to_vec() .try_into() .expect("page size was checked against the array length; qed"); Chunks::::insert(page_idx, chunk_page); page_idx += 1; chunk_idx = chunk_idx_end; } } #[benchmarks( where T: Send + Sync, ::RuntimeCall: Dispatchable + IsSubType> + From> + GetDispatchInfo, ::RuntimeOrigin: AsTransactionAuthorizedOrigin, )] mod benches { use super::*; #[benchmark] fn under_alias() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Create account and alias let account: T::AccountId = whitelisted_caller(); let context = T::BenchmarkHelper::valid_account_context(); let alias_value: Alias = [0u8; 32]; let ra = RevisedContextualAlias { revision: 0, ring: RI_ZERO, ca: ContextualAlias { context, alias: alias_value }, }; // Set up alias account association let block_number = frame_system::Pallet::::block_number(); assert_ok!(pallet::Pallet::::set_alias_account( Origin::PersonalAlias(ra.clone()).into(), account.clone(), block_number )); assert!(AccountToAlias::::contains_key(&account)); assert!(AliasToAccount::::contains_key(&ra.ca)); // A simple call to benchmark with let call = frame_system::Call::::remark { remark: vec![] }; let boxed_call = Box::new(call.into()); #[extrinsic_call] _(RawOrigin::Signed(account), boxed_call); Ok(()) } #[benchmark] fn set_alias_account() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); let block_number = frame_system::Pallet::::block_number(); let alias_value: Alias = [0u8; 32]; let alias = RevisedContextualAlias { ca: ContextualAlias { context: T::BenchmarkHelper::valid_account_context(), alias: alias_value, }, revision: 0, ring: 0, }; // An account had already been assigned to this alias let old_account: T::AccountId = account("test_old", 0, SEED); assert_ok!(pallet::Pallet::::set_alias_account( Origin::PersonalAlias(alias.clone()).into(), old_account.clone(), block_number )); assert!(AccountToAlias::::contains_key(&old_account)); assert!(AliasToAccount::::contains_key(&alias.ca)); let account: T::AccountId = account("test", 0, SEED); #[extrinsic_call] _(Origin::PersonalAlias(alias.clone()), account.clone(), block_number); assert!(!AccountToAlias::::contains_key(&old_account)); assert!(AccountToAlias::::contains_key(&account)); assert!(AliasToAccount::::contains_key(&alias.ca)); assert_eq!(AliasToAccount::::get(&alias.ca), Some(account)); Ok(()) } #[benchmark] fn unset_alias_account() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); let account: T::AccountId = account("test", 0, SEED); let block_number = frame_system::Pallet::::block_number(); let alias_value: Alias = [0u8; 32]; let alias = RevisedContextualAlias { ca: ContextualAlias { context: T::BenchmarkHelper::valid_account_context(), alias: alias_value, }, revision: 0, ring: 0, }; assert_ok!(pallet::Pallet::::set_alias_account( Origin::PersonalAlias(alias.clone()).into(), account.clone(), block_number )); assert!(AccountToAlias::::contains_key(&account)); assert!(AliasToAccount::::contains_key(&alias.ca)); #[extrinsic_call] _(Origin::PersonalAlias(alias.clone())); assert!(!AccountToAlias::::contains_key(&account)); assert!(!AliasToAccount::::contains_key(&alias.ca)); Ok(()) } #[benchmark] fn force_recognize_personhood() -> Result<(), BenchmarkError> { let members = generate_members_for_ring::(SEED); #[extrinsic_call] _(SystemOrigin::Root, members.iter().map(|(_, m)| m.clone()).collect::>()); for person in members { assert!(pallet::Keys::::get(person.1).is_some()); } Ok(()) } #[benchmark] fn set_personal_id_account() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); let people = recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Get one of the generated people's information let (personal_id, _, _): &(PersonalId, MemberOf, SecretOf) = &people[0]; let account: T::AccountId = account("test", 0, SEED); let block_number = frame_system::Pallet::::block_number(); // An account had already been assigned to this personal id let old_account: T::AccountId = frame_benchmarking::account("test_old", 0, SEED); assert_ok!(pallet::Pallet::::set_personal_id_account( Origin::PersonalIdentity(*personal_id).into(), old_account.clone(), block_number )); #[extrinsic_call] _(Origin::PersonalIdentity(*personal_id), account.clone(), block_number); assert_eq!(AccountToPersonalId::::get(&old_account), None); assert_eq!(AccountToPersonalId::::get(&account), Some(*personal_id)); assert!(People::::get(personal_id).is_some()); assert_eq!(People::::get(personal_id).unwrap().account, Some(account)); Ok(()) } #[benchmark] fn unset_personal_id_account() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); let people = recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Get one of the generated people's information let (personal_id, _, _): &(PersonalId, MemberOf, SecretOf) = &people[0]; let account: T::AccountId = account("test", 0, SEED); let block_number = frame_system::Pallet::::block_number(); // An account had already been assigned to this personal id let old_account: T::AccountId = frame_benchmarking::account("test_old", 0, SEED); assert_ok!(pallet::Pallet::::set_personal_id_account( Origin::PersonalIdentity(*personal_id).into(), old_account.clone(), block_number )); #[extrinsic_call] _(Origin::PersonalIdentity(*personal_id)); assert_eq!(AccountToPersonalId::::get(&old_account), None); assert_eq!(AccountToPersonalId::::get(&account), None); assert!(People::::get(personal_id).is_some()); assert_eq!(People::::get(personal_id).unwrap().account, None); Ok(()) } #[benchmark] fn set_onboarding_size() -> Result<(), BenchmarkError> { #[extrinsic_call] _(SystemOrigin::Root, 1); assert_eq!(OnboardingSize::::get(), 1); Ok(()) } #[benchmark] fn merge_rings() -> Result<(), BenchmarkError> { prepare_chunks::(); // Two rings exist let ring_size: u32 = ::MaxRingSize::get(); let members = generate_members::(SEED, 0, ring_size * 2); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); assert_eq!(RingKeysStatus::::get(RI_ZERO).total, ring_size); assert_ok!(pallet::Pallet::::onboard_people()); assert_eq!(RingKeysStatus::::get(1).total, ring_size); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, T::MaxRingSize::get())); assert_eq!(RingKeysStatus::::get(RI_ZERO).included, ring_size); assert_ok!(pallet::Pallet::::build_ring(1, T::MaxRingSize::get())); assert_eq!(RingKeysStatus::::get(1).included, ring_size); // Suspend and remove more than half of the people in both rings assert_ok!(pallet::Pallet::::start_people_set_mutation_session()); let suspensions: Vec = (1..ring_size / 2 + 3) .chain(ring_size + 1..ring_size * 3 / 2 + 3) .map(|i| i as PersonalId) .collect(); assert_ok!(pallet::Pallet::::suspend_personhood(&suspensions)); assert_ok!(pallet::Pallet::::end_people_set_mutation_session()); assert!(PendingSuspensions::::get(RI_ZERO).len() > (ring_size / 2) as usize); assert!(PendingSuspensions::::get(1).len() > (ring_size / 2) as usize); let mut meter = WeightMeter::new(); pallet::Pallet::::migrate_keys(&mut meter); pallet::Pallet::::remove_suspended_keys(RI_ZERO); pallet::Pallet::::remove_suspended_keys(1); assert!(RingKeys::::get(RI_ZERO).len() < (ring_size / 2) as usize); assert!(RingKeys::::get(1).len() < (ring_size / 2) as usize); let keys_left_len = RingKeys::::get(RI_ZERO).len() + RingKeys::::get(1).len(); // The current ring has to have a higher index than the ones being merged CurrentRingIndex::::set(14); let account: T::AccountId = account("caller", 0, SEED); #[extrinsic_call] _(SystemOrigin::Signed(account), RI_ZERO, 1); assert_eq!(RingKeys::::get(RI_ZERO).len(), keys_left_len); assert_eq!(RingKeysStatus::::get(RI_ZERO).total, keys_left_len as u32); assert!(Root::::get(RI_ZERO).is_some()); assert!(Root::::get(1).is_none()); Ok(()) } #[benchmark] fn migrate_included_key() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); let temp_key = new_member_from::(u32::MAX, SEED).1; KeyMigrationQueue::::insert(0, temp_key); let new_key = new_member_from::(u32::MAX - 1, SEED).1; #[extrinsic_call] _(Origin::PersonalIdentity(0u64), new_key.clone()); // Pending suspensions are reflected in the ring status. assert_eq!(KeyMigrationQueue::::get(0), Some(new_key)); Ok(()) } #[benchmark] fn migrate_onboarding_key() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); let temp_key = new_member_from::(u32::MAX, SEED).1; let new_person = pallet::Pallet::::reserve_new_id(); pallet::Pallet::::recognize_personhood(new_person, Some(temp_key.clone())).unwrap(); let new_key = new_member_from::(u32::MAX - 1, SEED).1; #[extrinsic_call] _(Origin::PersonalIdentity(new_person), new_key.clone()); // Pending suspensions are reflected in the ring status. assert!(KeyMigrationQueue::::iter().next().is_none()); assert_eq!(OnboardingQueue::::get(0)[0], new_key); Ok(()) } #[benchmark] fn should_build_ring(n: Linear<1, { T::MaxRingSize::get() }>) -> Result<(), BenchmarkError> { prepare_chunks::(); // One full queue page of people awaiting let queue_page_size: u32 = ::OnboardingQueuePageSize::get(); let ring_size: u32 = ::MaxRingSize::get(); let members = generate_members::(SEED, 0, queue_page_size); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); // No ring built but people onboarded successfully assert!(Root::::get(RI_ZERO).is_none()); assert_eq!(RingKeys::::get(RI_ZERO).len(), ring_size as usize); assert_eq!(RingKeysStatus::::get(RI_ZERO), RingStatus { total: ring_size, included: 0 }); #[block] { let _ = Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()); } Ok(()) } #[benchmark] fn build_ring(n: Linear<1, { T::MaxRingSize::get() }>) -> Result<(), BenchmarkError> { prepare_chunks::(); // One full queue page of people awaiting let queue_page_size: u32 = ::OnboardingQueuePageSize::get(); let ring_size: u32 = ::MaxRingSize::get(); let members = generate_members::(SEED, 0, queue_page_size); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); // No ring built but people onboarded successfully assert!(Root::::get(RI_ZERO).is_none()); assert_eq!(RingKeys::::get(RI_ZERO).len(), ring_size as usize); assert_eq!(RingKeysStatus::::get(RI_ZERO), RingStatus { total: ring_size, included: 0 }); #[block] { assert_ok!(Pallet::::build_ring(RI_ZERO, n)); } // The ring becomes built assert!(Root::::get(RI_ZERO).is_some()); assert_eq!(RingKeys::::get(RI_ZERO).len(), ring_size as usize); assert_eq!(RingKeysStatus::::get(RI_ZERO), RingStatus { total: ring_size, included: n }); Ok(()) } #[benchmark] fn onboard_people() -> Result<(), BenchmarkError> { prepare_chunks::(); // One full ring exists let ring_size: u32 = ::MaxRingSize::get(); let members = generate_members::(SEED, 0, ring_size); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); assert_eq!(RingKeys::::get(RI_ZERO).len(), ring_size as usize); assert_eq!( RingKeysStatus::::get(RI_ZERO), RingStatus { total: ring_size, included: ring_size } ); assert_eq!(QueuePageIndices::::get(), (0, 0)); assert!(OnboardingQueue::::get(0).is_empty()); // 1st onboarding page with fewer people than open slots let keys_len: u32 = Keys::::iter().collect::>().len().try_into().unwrap(); let members = generate_members::(SEED, keys_len, keys_len + ring_size / 2); recognize_people::(&members); assert_eq!(OnboardingQueue::::get(0).len(), (ring_size as u8 / 2) as usize); // To stop adding keys to the first page and start filling the next one QueuePageIndices::::put((0, 1)); assert!(OnboardingQueue::::get(1).is_empty()); // 2nd onboarding page full let keys_len: u32 = Keys::::iter().collect::>().len().try_into().unwrap(); assert_eq!(keys_len, (ring_size + ring_size / 2)); let queue_page_size: u32 = ::OnboardingQueuePageSize::get(); let members = generate_members::(SEED, keys_len, keys_len + queue_page_size); recognize_people::(&members); assert_eq!(QueuePageIndices::::get(), (0, 1)); assert_eq!(OnboardingQueue::::get(0).len(), (ring_size / 2) as usize); assert!(OnboardingQueue::::get(1).is_full()); assert_eq!(RingKeys::::get(1).len(), 0); #[block] { assert_ok!(Pallet::::onboard_people()); } assert_eq!(RingKeys::::get(1).len(), ring_size as usize); assert_eq!(RingKeysStatus::::get(1), RingStatus { total: ring_size, included: 0 }); Ok(()) } #[benchmark] fn pending_suspensions_iteration() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); let max_ring_size = T::MaxRingSize::get(); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, max_ring_size).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Suspend all people in the ring assert_ok!(pallet::Pallet::::start_people_set_mutation_session()); let suspensions: Vec = (0..max_ring_size as PersonalId).collect(); assert_ok!(pallet::Pallet::::suspend_personhood(&suspensions)); assert_ok!(pallet::Pallet::::end_people_set_mutation_session()); let mut meter = WeightMeter::new(); pallet::Pallet::::migrate_keys(&mut meter); // To make sure they are indeed pending suspension assert_eq!(PendingSuspensions::::get(RI_ZERO).len(), max_ring_size as usize); #[block] { assert!(PendingSuspensions::::iter_keys().next().is_some()); } Ok(()) } #[benchmark] fn remove_suspended_keys( n: Linear<1, { T::MaxRingSize::get() }>, ) -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // For later verification let initial_root = Root::::get(RI_ZERO).unwrap(); // Suspend 'n' number of people in the ring assert_ok!(pallet::Pallet::::start_people_set_mutation_session()); let suspensions: Vec = (0..n as PersonalId).collect(); assert_ok!(pallet::Pallet::::suspend_personhood(&suspensions)); assert_ok!(pallet::Pallet::::end_people_set_mutation_session()); let mut meter = WeightMeter::new(); pallet::Pallet::::migrate_keys(&mut meter); // To make sure they are indeed pending suspension assert_eq!(PendingSuspensions::::get(RI_ZERO).len(), n as usize); #[block] { pallet::Pallet::::remove_suspended_keys(RI_ZERO); } // Pending suspensions are cleared for the ring assert!(PendingSuspensions::::get(RI_ZERO).is_empty()); // Ring data becomes modified let ring_size: u32 = ::MaxRingSize::get(); assert_eq!( RingKeysStatus::::get(RI_ZERO), RingStatus { included: 0, total: ring_size - n as u32 } ); assert_eq!(RingKeys::::get(RI_ZERO).len(), (ring_size - n as u32) as usize); assert_ne!(Root::::get(RI_ZERO).unwrap().intermediate, initial_root.intermediate); Ok(()) } #[benchmark] fn migrate_keys_single_included_key() -> Result<(), BenchmarkError> { prepare_chunks::(); let max_members = T::MaxRingSize::get(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Migrate 'n' number of people in the ring for (personal_id, key) in (0..max_members as PersonalId) .map(|i| new_member_from::(u32::MAX - i as u32, SEED).1) .enumerate() { assert_ok!(pallet::Pallet::::migrate_included_key( Origin::PersonalIdentity(personal_id as PersonalId).into(), key )); } assert_ok!(pallet::Pallet::::start_people_set_mutation_session()); assert_ok!(pallet::Pallet::::end_people_set_mutation_session()); assert!(PendingSuspensions::::get(RI_ZERO).is_empty()); // All migrated keys are queued, but we only want one as this function benchmarks just one // iteration of `migrate_keys`. assert_eq!(KeyMigrationQueue::::iter().count(), T::MaxRingSize::get() as usize); let (first_id, first_key) = KeyMigrationQueue::::iter().next().unwrap(); #[block] { assert_ok!(pallet::Pallet::::migrate_keys_single_included_key(first_id, first_key)); } // Pending suspensions are reflected in the ring status. assert_eq!(PendingSuspensions::::get(RI_ZERO).len(), 1); Ok(()) } #[benchmark] fn merge_queue_pages() -> Result<(), BenchmarkError> { prepare_chunks::(); // Two pages exists: first is full, the second contains one member let queue_page_size: u32 = ::OnboardingQueuePageSize::get(); let members = generate_members::(SEED, 0, queue_page_size + 1); recognize_people::(&members); assert_eq!(QueuePageIndices::::get(), (0, 1)); assert!(OnboardingQueue::::get(0).is_full()); assert_eq!(OnboardingQueue::::get(1).len(), 1); // One key is removed from the first page OnboardingQueue::::mutate(0, |keys| { keys.pop(); }); assert_eq!(OnboardingQueue::::get(0).len(), queue_page_size as usize - 1); // Attempt to merge pages succeeds let QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page } = pallet::Pallet::::should_merge_queue_pages() else { panic!("should be mergeable") }; #[block] { pallet::Pallet::::merge_queue_pages( initial_head, new_head, first_key_page, second_key_page, ); } // The queue pages have changed assert_eq!(QueuePageIndices::::get(), (1, 1)); assert!(OnboardingQueue::::get(0).is_empty()); assert!(OnboardingQueue::::get(1).is_full()); Ok(()) } #[benchmark] fn on_poll_base() -> Result<(), BenchmarkError> { // Two pages exists: first is full, the second contains one member let queue_page_size: u32 = ::OnboardingQueuePageSize::get(); let members = generate_members::(SEED, 0, queue_page_size + 1); recognize_people::(&members); assert_eq!(QueuePageIndices::::get(), (0, 1)); assert!(OnboardingQueue::::get(0).is_full()); assert_eq!(OnboardingQueue::::get(1).len(), 1); assert!(RingsState::::get().append_only()); let mut meter = WeightMeter::new(); #[block] { pallet::Pallet::::on_poll(0u32.into(), &mut meter); } assert_eq!(meter.consumed(), T::WeightInfo::on_poll_base()); Ok(()) } #[benchmark] fn on_idle_base() -> Result<(), BenchmarkError> { prepare_chunks::(); // Two pages exists: first is full, the second contains one member let queue_page_size: u32 = ::OnboardingQueuePageSize::get(); let ring_size = T::MaxRingSize::get(); let members = generate_members::(SEED, 0, queue_page_size + 1); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); // No ring built but people onboarded successfully assert!(Root::::get(RI_ZERO).is_none()); assert_eq!(RingKeys::::get(RI_ZERO).len(), ring_size as usize); assert_eq!(RingKeysStatus::::get(RI_ZERO), RingStatus { total: ring_size, included: 0 }); let to_include = Pallet::::should_build_ring(RI_ZERO, ring_size).unwrap(); assert_ok!(Pallet::::build_ring(RI_ZERO, to_include)); // The ring becomes built assert!(Root::::get(RI_ZERO).is_some()); assert_eq!(RingKeys::::get(RI_ZERO).len(), ring_size as usize); assert_eq!( RingKeysStatus::::get(RI_ZERO), RingStatus { total: ring_size, included: ring_size } ); #[block] { pallet::Pallet::::on_idle(0u32.into(), Weight::MAX); } Ok(()) } #[benchmark] fn as_person_alias_with_account() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Create account and alias let account: T::AccountId = account("caller", 0, SEED); let context = T::BenchmarkHelper::valid_account_context(); let alias_value: Alias = [0u8; 32]; let ra = RevisedContextualAlias { revision: 0, ring: RI_ZERO, ca: ContextualAlias { context, alias: alias_value }, }; // Set up alias account association let block_number = frame_system::Pallet::::block_number(); assert_ok!(pallet::Pallet::::set_alias_account( Origin::PersonalAlias(ra.clone()).into(), account.clone(), block_number )); assert!(AccountToAlias::::contains_key(&account)); assert!(AliasToAccount::::contains_key(&ra.ca)); // A simple call to benchmark with let inner = frame_system::Call::::remark { remark: vec![] }; let call: ::RuntimeCall = inner.into(); let ext = AsPerson::new(Some(AsPersonInfo::::AsPersonalAliasWithAccount(T::Nonce::default()))); let info = call.get_dispatch_info(); let post_info = PostDispatchInfo { actual_weight: Some(Weight::from_parts(10, 0)), pays_fee: Pays::Yes, }; let len = call.encoded_size(); #[block] { ext.test_run(RawOrigin::Signed(account).into(), &call, &info, len, 0, |_| { Ok(post_info) }) .unwrap() .unwrap(); } Ok(()) } #[benchmark] fn as_person_identity_with_account() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let members = generate_members_for_ring::(SEED); let recognized_people = recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Select one of the generated people's information let (personal_id, _, _): &(PersonalId, MemberOf, SecretOf) = &recognized_people[0]; // Set up personal ID account association let account: T::AccountId = account("caller", 0, SEED); let block_number = frame_system::Pallet::::block_number(); assert_ok!(pallet::Pallet::::set_personal_id_account( Origin::PersonalIdentity(*personal_id).into(), account.clone(), block_number )); assert!(AccountToPersonalId::::contains_key(&account)); // A simple call to benchmark with let inner = frame_system::Call::::remark { remark: vec![] }; let call: ::RuntimeCall = inner.into(); let ext = AsPerson::new(Some(AsPersonInfo::::AsPersonalIdentityWithAccount( T::Nonce::default(), ))); let info = call.get_dispatch_info(); let post_info = PostDispatchInfo { actual_weight: Some(Weight::from_parts(10, 0)), pays_fee: Pays::Yes, }; let len = call.encoded_size(); #[block] { ext.test_run(RawOrigin::Signed(account).into(), &call, &info, len, 0, |_| { Ok(post_info) }) .unwrap() .unwrap(); } Ok(()) } #[benchmark] fn as_person_alias_with_proof() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let account: T::AccountId = account("caller", 0, SEED); let members = generate_members_for_ring::(SEED); recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // The call to set the alias, the only one valid for this extension code path. let block_number = frame_system::Pallet::::block_number(); let inner = Call::::set_alias_account { account, call_valid_at: block_number }; let call: ::RuntimeCall = inner.into(); let context = T::BenchmarkHelper::valid_account_context(); let ext_version: ExtensionVersion = 0; // Generate a valid proof let proof = (ext_version, &call).using_encoded(|msg| { let (secret, member) = &members[0]; T::Crypto::create( T::Crypto::open(member, members.iter().map(|(_, m)| m.clone())).unwrap(), secret, &context[..], &sp_io::hashing::blake2_256(msg), ) .map(|(p, _)| p) .expect("should create proof") }); let ext = AsPerson::new(Some(AsPersonInfo::::AsPersonalAliasWithProof( proof, RI_ZERO, context, ))); let info = call.get_dispatch_info(); let post_info = PostDispatchInfo { actual_weight: Some(Weight::from_parts(10, 0)), pays_fee: Pays::Yes, }; let len = call.encoded_size(); #[block] { ext.test_run(RawOrigin::None.into(), &call, &info, len, 0, |_| Ok(post_info)) .unwrap() .unwrap(); } Ok(()) } #[benchmark] fn as_person_identity_with_proof() -> Result<(), BenchmarkError> { prepare_chunks::(); // Generate people and build a ring let account: T::AccountId = account("caller", 0, SEED); let members = generate_members_for_ring::(SEED); let recognized_people = recognize_people::(&members); assert_ok!(pallet::Pallet::::onboard_people()); let to_include = pallet::Pallet::::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap(); assert_ok!(pallet::Pallet::::build_ring(RI_ZERO, to_include)); // Select one of the generated people's information let (personal_id, _, secret): &(PersonalId, MemberOf, SecretOf) = &recognized_people[0]; // The call to set the personal ID account, the only one valid for this extension code path. let block_number = frame_system::Pallet::::block_number(); let inner = Call::::set_personal_id_account { account, call_valid_at: block_number }; let call: ::RuntimeCall = inner.into(); let ext_version: ExtensionVersion = 0; let signature = (ext_version, &call).using_encoded(|msg| { ::sign(secret, &sp_io::hashing::blake2_256(msg)) .expect("failed to create signature") }); let ext = AsPerson::new(Some(AsPersonInfo::::AsPersonalIdentityWithProof( signature, *personal_id, ))); let info = call.get_dispatch_info(); let post_info = PostDispatchInfo { actual_weight: Some(Weight::from_parts(10, 0)), pays_fee: Pays::Yes, }; let len = call.encoded_size(); #[block] { ext.test_run(RawOrigin::None.into(), &call, &info, len, 0, |_| Ok(post_info)) .unwrap() .unwrap(); } Ok(()) } // Implements a test for each benchmark. Execute with: // `cargo test -p pallet-people --features runtime-benchmarks`. impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); }