1024 lines
33 KiB
Rust
1024 lines
33 KiB
Rust
// 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<T> = <<T as Config>::Crypto as GenerateVerifiable>::Secret;
|
|
|
|
fn new_member_from<T: Config + Send + Sync>(i: u32, seed: u32) -> (SecretOf<T>, MemberOf<T>) {
|
|
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<T: Config + Send + Sync>(
|
|
seed: u32,
|
|
) -> Vec<(SecretOf<T>, MemberOf<T>)> {
|
|
(0..T::MaxRingSize::get())
|
|
.map(|i| new_member_from::<T>(i, seed))
|
|
.collect::<Vec<_>>()
|
|
}
|
|
|
|
fn generate_members<T: Config + Send + Sync>(
|
|
seed: u32,
|
|
start: u32,
|
|
end: u32,
|
|
) -> Vec<(SecretOf<T>, MemberOf<T>)> {
|
|
(start..end).map(|i| new_member_from::<T>(i, seed)).collect::<Vec<_>>()
|
|
}
|
|
|
|
pub fn recognize_people<T: Config + Send + Sync>(
|
|
members: &[(SecretOf<T>, MemberOf<T>)],
|
|
) -> Vec<(PersonalId, MemberOf<T>, SecretOf<T>)> {
|
|
let mut people = Vec::new();
|
|
for (secret, public) in members.iter() {
|
|
let person = pallet::Pallet::<T>::reserve_new_id();
|
|
pallet::Pallet::<T>::recognize_personhood(person, Some(public.clone())).unwrap();
|
|
people.push((person, public.clone(), secret.clone()));
|
|
}
|
|
|
|
people
|
|
}
|
|
|
|
pub trait BenchmarkHelper<Chunk> {
|
|
fn valid_account_context() -> Context;
|
|
fn initialize_chunks() -> Vec<Chunk>;
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl BenchmarkHelper<()> for () {
|
|
fn valid_account_context() -> Context {
|
|
[0u8; 32]
|
|
}
|
|
|
|
fn initialize_chunks() -> Vec<()> {
|
|
vec![]
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl BenchmarkHelper<verifiable::ring_vrf_impl::StaticChunk> for () {
|
|
fn valid_account_context() -> Context {
|
|
[0u8; 32]
|
|
}
|
|
|
|
fn initialize_chunks() -> Vec<verifiable::ring_vrf_impl::StaticChunk> {
|
|
vec![]
|
|
}
|
|
}
|
|
|
|
fn prepare_chunks<T: Config>() {
|
|
let chunks = T::BenchmarkHelper::initialize_chunks();
|
|
|
|
let page_size = <T as Config>::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<T> = chunks[chunk_idx..chunk_idx_end]
|
|
.to_vec()
|
|
.try_into()
|
|
.expect("page size was checked against the array length; qed");
|
|
Chunks::<T>::insert(page_idx, chunk_page);
|
|
page_idx += 1;
|
|
chunk_idx = chunk_idx_end;
|
|
}
|
|
}
|
|
|
|
#[benchmarks(
|
|
where T: Send + Sync,
|
|
<T as frame_system::Config>::RuntimeCall:
|
|
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + From<Call<T>> + GetDispatchInfo,
|
|
<T as frame_system::Config>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
|
|
)]
|
|
mod benches {
|
|
use super::*;
|
|
|
|
#[benchmark]
|
|
fn under_alias() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::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::<T>::block_number();
|
|
assert_ok!(pallet::Pallet::<T>::set_alias_account(
|
|
Origin::PersonalAlias(ra.clone()).into(),
|
|
account.clone(),
|
|
block_number
|
|
));
|
|
assert!(AccountToAlias::<T>::contains_key(&account));
|
|
assert!(AliasToAccount::<T>::contains_key(&ra.ca));
|
|
|
|
// A simple call to benchmark with
|
|
let call = frame_system::Call::<T>::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::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
let block_number = frame_system::Pallet::<T>::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::<T>::set_alias_account(
|
|
Origin::PersonalAlias(alias.clone()).into(),
|
|
old_account.clone(),
|
|
block_number
|
|
));
|
|
assert!(AccountToAlias::<T>::contains_key(&old_account));
|
|
assert!(AliasToAccount::<T>::contains_key(&alias.ca));
|
|
|
|
let account: T::AccountId = account("test", 0, SEED);
|
|
|
|
#[extrinsic_call]
|
|
_(Origin::PersonalAlias(alias.clone()), account.clone(), block_number);
|
|
|
|
assert!(!AccountToAlias::<T>::contains_key(&old_account));
|
|
assert!(AccountToAlias::<T>::contains_key(&account));
|
|
assert!(AliasToAccount::<T>::contains_key(&alias.ca));
|
|
assert_eq!(AliasToAccount::<T>::get(&alias.ca), Some(account));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn unset_alias_account() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
let account: T::AccountId = account("test", 0, SEED);
|
|
let block_number = frame_system::Pallet::<T>::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::<T>::set_alias_account(
|
|
Origin::PersonalAlias(alias.clone()).into(),
|
|
account.clone(),
|
|
block_number
|
|
));
|
|
assert!(AccountToAlias::<T>::contains_key(&account));
|
|
assert!(AliasToAccount::<T>::contains_key(&alias.ca));
|
|
|
|
#[extrinsic_call]
|
|
_(Origin::PersonalAlias(alias.clone()));
|
|
|
|
assert!(!AccountToAlias::<T>::contains_key(&account));
|
|
assert!(!AliasToAccount::<T>::contains_key(&alias.ca));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn force_recognize_personhood() -> Result<(), BenchmarkError> {
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
|
|
#[extrinsic_call]
|
|
_(SystemOrigin::Root, members.iter().map(|(_, m)| m.clone()).collect::<Vec<_>>());
|
|
|
|
for person in members {
|
|
assert!(pallet::Keys::<T>::get(person.1).is_some());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn set_personal_id_account() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
let people = recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
// Get one of the generated people's information
|
|
let (personal_id, _, _): &(PersonalId, MemberOf<T>, SecretOf<T>) = &people[0];
|
|
|
|
let account: T::AccountId = account("test", 0, SEED);
|
|
let block_number = frame_system::Pallet::<T>::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::<T>::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::<T>::get(&old_account), None);
|
|
assert_eq!(AccountToPersonalId::<T>::get(&account), Some(*personal_id));
|
|
assert!(People::<T>::get(personal_id).is_some());
|
|
assert_eq!(People::<T>::get(personal_id).unwrap().account, Some(account));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn unset_personal_id_account() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
let people = recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
// Get one of the generated people's information
|
|
let (personal_id, _, _): &(PersonalId, MemberOf<T>, SecretOf<T>) = &people[0];
|
|
|
|
let account: T::AccountId = account("test", 0, SEED);
|
|
let block_number = frame_system::Pallet::<T>::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::<T>::set_personal_id_account(
|
|
Origin::PersonalIdentity(*personal_id).into(),
|
|
old_account.clone(),
|
|
block_number
|
|
));
|
|
|
|
#[extrinsic_call]
|
|
_(Origin::PersonalIdentity(*personal_id));
|
|
|
|
assert_eq!(AccountToPersonalId::<T>::get(&old_account), None);
|
|
assert_eq!(AccountToPersonalId::<T>::get(&account), None);
|
|
assert!(People::<T>::get(personal_id).is_some());
|
|
assert_eq!(People::<T>::get(personal_id).unwrap().account, None);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn set_onboarding_size() -> Result<(), BenchmarkError> {
|
|
#[extrinsic_call]
|
|
_(SystemOrigin::Root, 1);
|
|
|
|
assert_eq!(OnboardingSize::<T>::get(), 1);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn merge_rings() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Two rings exist
|
|
let ring_size: u32 = <T as Config>::MaxRingSize::get();
|
|
let members = generate_members::<T>(SEED, 0, ring_size * 2);
|
|
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO).total, ring_size);
|
|
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
assert_eq!(RingKeysStatus::<T>::get(1).total, ring_size);
|
|
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, T::MaxRingSize::get()));
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO).included, ring_size);
|
|
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(1, T::MaxRingSize::get()));
|
|
assert_eq!(RingKeysStatus::<T>::get(1).included, ring_size);
|
|
|
|
// Suspend and remove more than half of the people in both rings
|
|
assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
|
|
let suspensions: Vec<PersonalId> = (1..ring_size / 2 + 3)
|
|
.chain(ring_size + 1..ring_size * 3 / 2 + 3)
|
|
.map(|i| i as PersonalId)
|
|
.collect();
|
|
assert_ok!(pallet::Pallet::<T>::suspend_personhood(&suspensions));
|
|
assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
|
|
|
|
assert!(PendingSuspensions::<T>::get(RI_ZERO).len() > (ring_size / 2) as usize);
|
|
assert!(PendingSuspensions::<T>::get(1).len() > (ring_size / 2) as usize);
|
|
|
|
let mut meter = WeightMeter::new();
|
|
pallet::Pallet::<T>::migrate_keys(&mut meter);
|
|
|
|
pallet::Pallet::<T>::remove_suspended_keys(RI_ZERO);
|
|
pallet::Pallet::<T>::remove_suspended_keys(1);
|
|
|
|
assert!(RingKeys::<T>::get(RI_ZERO).len() < (ring_size / 2) as usize);
|
|
assert!(RingKeys::<T>::get(1).len() < (ring_size / 2) as usize);
|
|
|
|
let keys_left_len = RingKeys::<T>::get(RI_ZERO).len() + RingKeys::<T>::get(1).len();
|
|
|
|
// The current ring has to have a higher index than the ones being merged
|
|
CurrentRingIndex::<T>::set(14);
|
|
|
|
let account: T::AccountId = account("caller", 0, SEED);
|
|
|
|
#[extrinsic_call]
|
|
_(SystemOrigin::Signed(account), RI_ZERO, 1);
|
|
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), keys_left_len);
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO).total, keys_left_len as u32);
|
|
assert!(Root::<T>::get(RI_ZERO).is_some());
|
|
assert!(Root::<T>::get(1).is_none());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn migrate_included_key() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
let temp_key = new_member_from::<T>(u32::MAX, SEED).1;
|
|
KeyMigrationQueue::<T>::insert(0, temp_key);
|
|
|
|
let new_key = new_member_from::<T>(u32::MAX - 1, SEED).1;
|
|
|
|
#[extrinsic_call]
|
|
_(Origin::PersonalIdentity(0u64), new_key.clone());
|
|
|
|
// Pending suspensions are reflected in the ring status.
|
|
assert_eq!(KeyMigrationQueue::<T>::get(0), Some(new_key));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn migrate_onboarding_key() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
let temp_key = new_member_from::<T>(u32::MAX, SEED).1;
|
|
|
|
let new_person = pallet::Pallet::<T>::reserve_new_id();
|
|
pallet::Pallet::<T>::recognize_personhood(new_person, Some(temp_key.clone())).unwrap();
|
|
|
|
let new_key = new_member_from::<T>(u32::MAX - 1, SEED).1;
|
|
|
|
#[extrinsic_call]
|
|
_(Origin::PersonalIdentity(new_person), new_key.clone());
|
|
|
|
// Pending suspensions are reflected in the ring status.
|
|
assert!(KeyMigrationQueue::<T>::iter().next().is_none());
|
|
assert_eq!(OnboardingQueue::<T>::get(0)[0], new_key);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn should_build_ring(n: Linear<1, { T::MaxRingSize::get() }>) -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// One full queue page of people awaiting
|
|
let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
|
|
let ring_size: u32 = <T as Config>::MaxRingSize::get();
|
|
let members = generate_members::<T>(SEED, 0, queue_page_size);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
|
|
// No ring built but people onboarded successfully
|
|
assert!(Root::<T>::get(RI_ZERO).is_none());
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: 0 });
|
|
|
|
#[block]
|
|
{
|
|
let _ = Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn build_ring(n: Linear<1, { T::MaxRingSize::get() }>) -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// One full queue page of people awaiting
|
|
let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
|
|
let ring_size: u32 = <T as Config>::MaxRingSize::get();
|
|
let members = generate_members::<T>(SEED, 0, queue_page_size);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
|
|
// No ring built but people onboarded successfully
|
|
assert!(Root::<T>::get(RI_ZERO).is_none());
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: 0 });
|
|
|
|
#[block]
|
|
{
|
|
assert_ok!(Pallet::<T>::build_ring(RI_ZERO, n));
|
|
}
|
|
|
|
// The ring becomes built
|
|
assert!(Root::<T>::get(RI_ZERO).is_some());
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: n });
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn onboard_people() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// One full ring exists
|
|
let ring_size: u32 = <T as Config>::MaxRingSize::get();
|
|
let members = generate_members::<T>(SEED, 0, ring_size);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
|
|
assert_eq!(
|
|
RingKeysStatus::<T>::get(RI_ZERO),
|
|
RingStatus { total: ring_size, included: ring_size }
|
|
);
|
|
|
|
assert_eq!(QueuePageIndices::<T>::get(), (0, 0));
|
|
assert!(OnboardingQueue::<T>::get(0).is_empty());
|
|
|
|
// 1st onboarding page with fewer people than open slots
|
|
let keys_len: u32 = Keys::<T>::iter().collect::<Vec<_>>().len().try_into().unwrap();
|
|
let members = generate_members::<T>(SEED, keys_len, keys_len + ring_size / 2);
|
|
recognize_people::<T>(&members);
|
|
assert_eq!(OnboardingQueue::<T>::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::<T>::put((0, 1));
|
|
assert!(OnboardingQueue::<T>::get(1).is_empty());
|
|
|
|
// 2nd onboarding page full
|
|
let keys_len: u32 = Keys::<T>::iter().collect::<Vec<_>>().len().try_into().unwrap();
|
|
assert_eq!(keys_len, (ring_size + ring_size / 2));
|
|
let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
|
|
let members = generate_members::<T>(SEED, keys_len, keys_len + queue_page_size);
|
|
recognize_people::<T>(&members);
|
|
|
|
assert_eq!(QueuePageIndices::<T>::get(), (0, 1));
|
|
assert_eq!(OnboardingQueue::<T>::get(0).len(), (ring_size / 2) as usize);
|
|
assert!(OnboardingQueue::<T>::get(1).is_full());
|
|
|
|
assert_eq!(RingKeys::<T>::get(1).len(), 0);
|
|
|
|
#[block]
|
|
{
|
|
assert_ok!(Pallet::<T>::onboard_people());
|
|
}
|
|
|
|
assert_eq!(RingKeys::<T>::get(1).len(), ring_size as usize);
|
|
assert_eq!(RingKeysStatus::<T>::get(1), RingStatus { total: ring_size, included: 0 });
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn pending_suspensions_iteration() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
let max_ring_size = T::MaxRingSize::get();
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include = pallet::Pallet::<T>::should_build_ring(RI_ZERO, max_ring_size).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
// Suspend all people in the ring
|
|
assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
|
|
let suspensions: Vec<PersonalId> = (0..max_ring_size as PersonalId).collect();
|
|
assert_ok!(pallet::Pallet::<T>::suspend_personhood(&suspensions));
|
|
assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
|
|
let mut meter = WeightMeter::new();
|
|
pallet::Pallet::<T>::migrate_keys(&mut meter);
|
|
|
|
// To make sure they are indeed pending suspension
|
|
assert_eq!(PendingSuspensions::<T>::get(RI_ZERO).len(), max_ring_size as usize);
|
|
|
|
#[block]
|
|
{
|
|
assert!(PendingSuspensions::<T>::iter_keys().next().is_some());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn remove_suspended_keys(
|
|
n: Linear<1, { T::MaxRingSize::get() }>,
|
|
) -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
// For later verification
|
|
let initial_root = Root::<T>::get(RI_ZERO).unwrap();
|
|
|
|
// Suspend 'n' number of people in the ring
|
|
assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
|
|
let suspensions: Vec<PersonalId> = (0..n as PersonalId).collect();
|
|
assert_ok!(pallet::Pallet::<T>::suspend_personhood(&suspensions));
|
|
assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
|
|
let mut meter = WeightMeter::new();
|
|
pallet::Pallet::<T>::migrate_keys(&mut meter);
|
|
|
|
// To make sure they are indeed pending suspension
|
|
assert_eq!(PendingSuspensions::<T>::get(RI_ZERO).len(), n as usize);
|
|
|
|
#[block]
|
|
{
|
|
pallet::Pallet::<T>::remove_suspended_keys(RI_ZERO);
|
|
}
|
|
|
|
// Pending suspensions are cleared for the ring
|
|
assert!(PendingSuspensions::<T>::get(RI_ZERO).is_empty());
|
|
|
|
// Ring data becomes modified
|
|
let ring_size: u32 = <T as Config>::MaxRingSize::get();
|
|
assert_eq!(
|
|
RingKeysStatus::<T>::get(RI_ZERO),
|
|
RingStatus { included: 0, total: ring_size - n as u32 }
|
|
);
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), (ring_size - n as u32) as usize);
|
|
assert_ne!(Root::<T>::get(RI_ZERO).unwrap().intermediate, initial_root.intermediate);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn migrate_keys_single_included_key() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
let max_members = T::MaxRingSize::get();
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::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::<T>(u32::MAX - i as u32, SEED).1)
|
|
.enumerate()
|
|
{
|
|
assert_ok!(pallet::Pallet::<T>::migrate_included_key(
|
|
Origin::PersonalIdentity(personal_id as PersonalId).into(),
|
|
key
|
|
));
|
|
}
|
|
assert_ok!(pallet::Pallet::<T>::start_people_set_mutation_session());
|
|
assert_ok!(pallet::Pallet::<T>::end_people_set_mutation_session());
|
|
assert!(PendingSuspensions::<T>::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::<T>::iter().count(), T::MaxRingSize::get() as usize);
|
|
let (first_id, first_key) = KeyMigrationQueue::<T>::iter().next().unwrap();
|
|
|
|
#[block]
|
|
{
|
|
assert_ok!(pallet::Pallet::<T>::migrate_keys_single_included_key(first_id, first_key));
|
|
}
|
|
|
|
// Pending suspensions are reflected in the ring status.
|
|
assert_eq!(PendingSuspensions::<T>::get(RI_ZERO).len(), 1);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn merge_queue_pages() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Two pages exists: first is full, the second contains one member
|
|
let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
|
|
let members = generate_members::<T>(SEED, 0, queue_page_size + 1);
|
|
recognize_people::<T>(&members);
|
|
|
|
assert_eq!(QueuePageIndices::<T>::get(), (0, 1));
|
|
assert!(OnboardingQueue::<T>::get(0).is_full());
|
|
assert_eq!(OnboardingQueue::<T>::get(1).len(), 1);
|
|
|
|
// One key is removed from the first page
|
|
OnboardingQueue::<T>::mutate(0, |keys| {
|
|
keys.pop();
|
|
});
|
|
assert_eq!(OnboardingQueue::<T>::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::<T>::should_merge_queue_pages()
|
|
else {
|
|
panic!("should be mergeable")
|
|
};
|
|
|
|
#[block]
|
|
{
|
|
pallet::Pallet::<T>::merge_queue_pages(
|
|
initial_head,
|
|
new_head,
|
|
first_key_page,
|
|
second_key_page,
|
|
);
|
|
}
|
|
|
|
// The queue pages have changed
|
|
assert_eq!(QueuePageIndices::<T>::get(), (1, 1));
|
|
assert!(OnboardingQueue::<T>::get(0).is_empty());
|
|
assert!(OnboardingQueue::<T>::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 = <T as Config>::OnboardingQueuePageSize::get();
|
|
let members = generate_members::<T>(SEED, 0, queue_page_size + 1);
|
|
recognize_people::<T>(&members);
|
|
|
|
assert_eq!(QueuePageIndices::<T>::get(), (0, 1));
|
|
assert!(OnboardingQueue::<T>::get(0).is_full());
|
|
assert_eq!(OnboardingQueue::<T>::get(1).len(), 1);
|
|
assert!(RingsState::<T>::get().append_only());
|
|
|
|
let mut meter = WeightMeter::new();
|
|
|
|
#[block]
|
|
{
|
|
pallet::Pallet::<T>::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::<T>();
|
|
|
|
// Two pages exists: first is full, the second contains one member
|
|
let queue_page_size: u32 = <T as Config>::OnboardingQueuePageSize::get();
|
|
let ring_size = T::MaxRingSize::get();
|
|
let members = generate_members::<T>(SEED, 0, queue_page_size + 1);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
|
|
// No ring built but people onboarded successfully
|
|
assert!(Root::<T>::get(RI_ZERO).is_none());
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
|
|
assert_eq!(RingKeysStatus::<T>::get(RI_ZERO), RingStatus { total: ring_size, included: 0 });
|
|
let to_include = Pallet::<T>::should_build_ring(RI_ZERO, ring_size).unwrap();
|
|
assert_ok!(Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
// The ring becomes built
|
|
assert!(Root::<T>::get(RI_ZERO).is_some());
|
|
assert_eq!(RingKeys::<T>::get(RI_ZERO).len(), ring_size as usize);
|
|
assert_eq!(
|
|
RingKeysStatus::<T>::get(RI_ZERO),
|
|
RingStatus { total: ring_size, included: ring_size }
|
|
);
|
|
|
|
#[block]
|
|
{
|
|
pallet::Pallet::<T>::on_idle(0u32.into(), Weight::MAX);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[benchmark]
|
|
fn as_person_alias_with_account() -> Result<(), BenchmarkError> {
|
|
prepare_chunks::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::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::<T>::block_number();
|
|
assert_ok!(pallet::Pallet::<T>::set_alias_account(
|
|
Origin::PersonalAlias(ra.clone()).into(),
|
|
account.clone(),
|
|
block_number
|
|
));
|
|
assert!(AccountToAlias::<T>::contains_key(&account));
|
|
assert!(AliasToAccount::<T>::contains_key(&ra.ca));
|
|
|
|
// A simple call to benchmark with
|
|
let inner = frame_system::Call::<T>::remark { remark: vec![] };
|
|
let call: <T as frame_system::Config>::RuntimeCall = inner.into();
|
|
|
|
let ext =
|
|
AsPerson::new(Some(AsPersonInfo::<T>::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::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
let recognized_people = recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
// Select one of the generated people's information
|
|
let (personal_id, _, _): &(PersonalId, MemberOf<T>, SecretOf<T>) = &recognized_people[0];
|
|
|
|
// Set up personal ID account association
|
|
let account: T::AccountId = account("caller", 0, SEED);
|
|
let block_number = frame_system::Pallet::<T>::block_number();
|
|
assert_ok!(pallet::Pallet::<T>::set_personal_id_account(
|
|
Origin::PersonalIdentity(*personal_id).into(),
|
|
account.clone(),
|
|
block_number
|
|
));
|
|
assert!(AccountToPersonalId::<T>::contains_key(&account));
|
|
|
|
// A simple call to benchmark with
|
|
let inner = frame_system::Call::<T>::remark { remark: vec![] };
|
|
let call: <T as frame_system::Config>::RuntimeCall = inner.into();
|
|
|
|
let ext = AsPerson::new(Some(AsPersonInfo::<T>::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::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let account: T::AccountId = account("caller", 0, SEED);
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::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::<T>::block_number();
|
|
let inner = Call::<T>::set_alias_account { account, call_valid_at: block_number };
|
|
let call: <T as frame_system::Config>::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::<T>::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::<T>();
|
|
|
|
// Generate people and build a ring
|
|
let account: T::AccountId = account("caller", 0, SEED);
|
|
let members = generate_members_for_ring::<T>(SEED);
|
|
let recognized_people = recognize_people::<T>(&members);
|
|
assert_ok!(pallet::Pallet::<T>::onboard_people());
|
|
let to_include =
|
|
pallet::Pallet::<T>::should_build_ring(RI_ZERO, T::MaxRingSize::get()).unwrap();
|
|
assert_ok!(pallet::Pallet::<T>::build_ring(RI_ZERO, to_include));
|
|
|
|
// Select one of the generated people's information
|
|
let (personal_id, _, secret): &(PersonalId, MemberOf<T>, SecretOf<T>) =
|
|
&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::<T>::block_number();
|
|
let inner = Call::<T>::set_personal_id_account { account, call_valid_at: block_number };
|
|
let call: <T as frame_system::Config>::RuntimeCall = inner.into();
|
|
let ext_version: ExtensionVersion = 0;
|
|
let signature = (ext_version, &call).using_encoded(|msg| {
|
|
<T::Crypto as GenerateVerifiable>::sign(secret, &sp_io::hashing::blake2_256(msg))
|
|
.expect("failed to create signature")
|
|
});
|
|
|
|
let ext = AsPerson::new(Some(AsPersonInfo::<T>::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);
|
|
}
|