referrerpolicy=no-referrer-when-downgrade

pallet_people/
lib.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
18//! # People Pallet
19//!
20//! A pallet managing the registry of proven individuals.
21//!
22//! ## Overview
23//!
24//! The People pallet stores and manages identifiers of individuals who have proven their
25//! personhood. It tracks their personal IDs, organizes their cryptographic keys into rings, and
26//! allows them to use contextual aliases through authentication in extensions. When transactions
27//! include cryptographic proofs of belonging to the people set, the pallet's transaction extension
28//! verifies these proofs before allowing the transaction to proceed. This enables other pallets to
29//! check if actions come from unique persons while preserving privacy through the ring-based
30//! structure.
31//!
32//! The pallet accepts new persons after they prove their uniqueness elsewhere, stores their
33//! information, and supports removing persons via suspensions. While other systems (e.g., wallets)
34//! generate the proofs, this pallet handles the storage of all necessary data and verifies the
35//! proofs when used.
36//!
37//! ## Key Features
38//!
39//! - **Stores Identity Data**: Tracks personal IDs and cryptographic keys of proven persons
40//! - **Organizes Keys**: Groups keys into rings to enable privacy-preserving proofs
41//! - **Verifies Proofs**: Checks personhood proofs attached to transactions
42//! - **Links Accounts**: Allows connecting blockchain accounts to contextual aliases
43//! - **Manages Registry**: Adds proven persons and will support removing them
44//!
45//! ## Interface
46//!
47//! ### Dispatchable Functions
48//!
49//! - `set_alias_account(origin, account)`: Link an account to a contextual alias Once linked, this
50//!   allows the account to dispatch transactions as a person with the alias origin using a regular
51//!   signed transaction with a nonce, providing a simpler alternative to attaching full proofs.
52//! - `unset_alias_account(origin)`: Remove an account-alias link.
53//! - `merge_rings`: Merge the people in two rings into a single, new ring.
54//! - `force_recognize_personhood`: Recognize a set of people without any additional checks.
55//! - `set_personal_id_account`: Set a personal id account.
56//! - `unset_personal_id_account`: Unset the personal id account.
57//! - `migrate_included_key`: Migrate the key for a person who was onboarded and is currently
58//!   included in a ring.
59//! - `migrate_onboarding_key`: Migrate the key for a person who is currently onboarding. The
60//!   operation is instant, replacing the old key in the onboarding queue.
61//! - `set_onboarding_size`: Force set the onboarding size for new people. This call requires root
62//!   privileges.
63//! - `build_ring_manual`: Manually build a ring root by including registered people. The
64//!   transaction fee is refunded on a successful call.
65//! - `onboard_people_manual`: Manually onboard people into a ring. The transaction fee is refunded
66//!   on a successful call.
67//!
68//! ### Automated tasks performed by the pallet in hooks
69//!
70//! - Ring building: Build or update a ring's cryptographic commitment. This task processes queued
71//!   keys into a ring commitment that enables proof generation and verification. Since ring
72//!   construction, or rather adding keys to the ring, is computationally expensive, it's performed
73//!   periodically in batches rather than processing each key immediately. The batch size needs to
74//!   be reasonably large to enhance privacy by obscuring the exact timing of when individuals' keys
75//!   were added to the ring, making it more difficult to correlate specific persons with their
76//!   keys.
77//! - People onboarding: Onboard people from the onboarding queue into a ring. This task takes the
78//!   unincluded keys of recognized people from the onboarding queue and registers them into the
79//!   ring. People can be onboarded only in batches of at least `OnboardingSize` and when the
80//!   remaining open slots in a ring are at least `OnboardingSize`. This does not compute the root,
81//!   that is done using `build_ring`.
82//! - Cleaning of suspended people: Remove people's keys marked as suspended or inactive from rings.
83//!   The keys are stored in the `PendingSuspensions` map and they are removed from rings and their
84//!   roots are reset. The ring roots will subsequently be build in the ring building phase from
85//!   scratch. sequentially.
86//! - Key migration: Migrate the keys for people who were onboarded and are currently included in
87//!   rings. The migration is not instant as the key replacement and subsequent inclusion in a new
88//!   ring root will happen only after the next mutation session.
89//! - Onboarding queue page merging: Merge the two pages at the front of the onboarding queue. After
90//!   a round of suspensions, it is possible for the second page of the onboarding queue to be left
91//!   with few members such that, if the first page also has few members, the total count is below
92//!   the required onboarding size, thus stalling the queue. This function fixes this by moving the
93//!   people from the first page to the front of the second page, defragmenting the queue.
94//!
95//! ### Transaction Extension
96//!
97//! The pallet provides the `AsPerson` transaction extension that allows transactions to be
98//! dispatched with special origins: `PersonalIdentity` and `PersonalAlias`. These origins prove the
99//! transaction comes from a unique person, either through their identity or through a contextual
100//! alias. To make use of the personhood system, other pallets should check for these origins.
101//!
102//! The extension verifies the proof of personhood during transaction validation and, if valid,
103//! transforms the transaction's origin into one of these special origins.
104//!
105//! ## Usage
106//!
107//! Other pallets can verify personhood through origin checks:
108//!
109//! - `EnsurePersonalIdentity`: Verifies the origin represents a specific person using their
110//!   PersonalId
111//! - `EnsurePersonalAlias`: Verifies the origin has a valid alias for any context
112//! - `EnsurePersonalAliasInContext`: Verifies the origin has a valid alias for a specific context
113
114#![cfg_attr(not(feature = "std"), no_std)]
115#![recursion_limit = "128"]
116#![allow(clippy::borrowed_box)]
117extern crate alloc;
118use alloc::{boxed::Box, vec::Vec};
119
120#[cfg(test)]
121mod mock;
122#[cfg(test)]
123mod tests;
124
125#[cfg(feature = "runtime-benchmarks")]
126pub mod benchmarking;
127pub mod extension;
128pub mod types;
129pub mod weights;
130pub use pallet::*;
131pub use types::*;
132pub use weights::WeightInfo;
133
134use codec::{Decode, Encode, MaxEncodedLen};
135use core::{
136	cmp::{self},
137	ops::Range,
138};
139use frame_support::{
140	dispatch::{
141		extract_actual_weight, DispatchInfo, DispatchResultWithPostInfo, GetDispatchInfo,
142		PostDispatchInfo,
143	},
144	storage::with_storage_layer,
145	traits::{
146		reality::{
147			AddOnlyPeopleTrait, Context, ContextualAlias, CountedMembers, PeopleTrait, PersonalId,
148			RingIndex,
149		},
150		Defensive, EnsureOriginWithArg, IsSubType, OriginTrait,
151	},
152	transactional,
153	weights::WeightMeter,
154};
155use scale_info::TypeInfo;
156use sp_runtime::{
157	traits::{BadOrigin, Dispatchable},
158	ArithmeticError, RuntimeDebug, SaturatedConversion, Saturating,
159};
160use verifiable::{Alias, GenerateVerifiable};
161
162#[cfg(feature = "runtime-benchmarks")]
163pub use benchmarking::BenchmarkHelper;
164
165#[frame_support::pallet]
166pub mod pallet {
167	use super::*;
168	use frame_support::{pallet_prelude::*, traits::Contains};
169	use frame_system::pallet_prelude::{BlockNumberFor, *};
170
171	const LOG_TARGET: &str = "runtime::people";
172
173	#[pallet::pallet]
174	pub struct Pallet<T>(_);
175
176	#[pallet::config]
177	pub trait Config:
178		frame_system::Config<
179		RuntimeOrigin: From<Origin>
180		                   + From<<Self::RuntimeOrigin as OriginTrait>::PalletsOrigin>
181		                   + OriginTrait<
182			PalletsOrigin: From<Origin>
183			                   + TryInto<
184				Origin,
185				Error = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin,
186			>,
187		>,
188		RuntimeCall: Parameter
189		                 + GetDispatchInfo
190		                 + IsSubType<Call<Self>>
191		                 + Dispatchable<
192			RuntimeOrigin = Self::RuntimeOrigin,
193			Info = DispatchInfo,
194			PostInfo = PostDispatchInfo,
195		>,
196	>
197	{
198		/// Weight information for extrinsics in this pallet.
199		type WeightInfo: WeightInfo;
200
201		/// The runtime event type.
202		#[allow(deprecated)]
203		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
204
205		/// Trait allowing cryptographic proof of membership without exposing the underlying member.
206		/// Normally a Ring-VRF.
207		type Crypto: GenerateVerifiable<
208			Proof: Send + Sync + DecodeWithMemTracking,
209			Signature: Send + Sync + DecodeWithMemTracking,
210			Member: DecodeWithMemTracking,
211		>;
212
213		/// Contexts which may validly have an account alias behind it for everyone.
214		type AccountContexts: Contains<Context>;
215
216		/// Number of chunks per page.
217		#[pallet::constant]
218		type ChunkPageSize: Get<u32>;
219
220		/// Maximum number of people included in a ring before a new one is created.
221		#[pallet::constant]
222		type MaxRingSize: Get<u32>;
223
224		/// Maximum number of people included in an onboarding queue page before a new one is
225		/// created.
226		#[pallet::constant]
227		type OnboardingQueuePageSize: Get<u32>;
228
229		/// Helper for benchmarks.
230		#[cfg(feature = "runtime-benchmarks")]
231		type BenchmarkHelper: BenchmarkHelper<<Self::Crypto as GenerateVerifiable>::StaticChunk>;
232	}
233
234	/// The current individuals we recognise.
235	#[pallet::storage]
236	pub type Root<T> = StorageMap<_, Blake2_128Concat, RingIndex, RingRoot<T>>;
237
238	/// Keeps track of the ring index currently being populated.
239	#[pallet::storage]
240	pub type CurrentRingIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
241
242	/// Maximum number of people queued before onboarding to a ring.
243	#[pallet::storage]
244	pub type OnboardingSize<T: Config> = StorageValue<_, u32, ValueQuery>;
245
246	/// Hint for the maximum number of people that can be included in a ring through a single root
247	/// building call. If no value is set, then the onboarding size will be used instead.
248	#[pallet::storage]
249	pub type RingBuildingPeopleLimit<T: Config> = StorageValue<_, u32, OptionQuery>;
250
251	/// Both the keys that are included in built rings
252	/// and the keys that will be used in future rings.
253	#[pallet::storage]
254	pub type RingKeys<T: Config> = StorageMap<
255		_,
256		Blake2_128Concat,
257		RingIndex,
258		BoundedVec<MemberOf<T>, T::MaxRingSize>,
259		ValueQuery,
260	>;
261
262	/// Stores the meta information for each ring, the number of keys and how many are actually
263	/// included in the root.
264	#[pallet::storage]
265	pub type RingKeysStatus<T: Config> =
266		StorageMap<_, Blake2_128Concat, RingIndex, RingStatus, ValueQuery>;
267
268	/// A map of all rings which currently have pending suspensions and need cleaning, along with
269	/// their respective number of suspended keys which need to be removed.
270	#[pallet::storage]
271	pub type PendingSuspensions<T: Config> =
272		StorageMap<_, Twox64Concat, RingIndex, BoundedVec<u32, T::MaxRingSize>, ValueQuery>;
273
274	/// The number of people currently included in a ring.
275	#[pallet::storage]
276	pub type ActiveMembers<T: Config> = StorageValue<_, u32, ValueQuery>;
277
278	/// The current individuals we recognise, but not necessarily yet included in a ring.
279	///
280	/// Look-up from the crypto (public) key to the immutable ID of the individual (`PersonalId`). A
281	/// person can have two different entries in this map if they queued a key migration which
282	/// hasn't been enacted yet.
283	#[pallet::storage]
284	pub type Keys<T> = CountedStorageMap<_, Blake2_128Concat, MemberOf<T>, PersonalId>;
285
286	/// A map of all the people who have declared their intent to migrate their keys and are waiting
287	/// for the next mutation session.
288	#[pallet::storage]
289	pub type KeyMigrationQueue<T: Config> =
290		StorageMap<_, Blake2_128Concat, PersonalId, MemberOf<T>>;
291
292	/// The current individuals we recognise, but not necessarily yet included in a ring.
293	///
294	/// Immutable ID of the individual (`PersonalId`) to information about their key and status.
295	#[pallet::storage]
296	pub type People<T: Config> =
297		StorageMap<_, Blake2_128Concat, PersonalId, PersonRecord<MemberOf<T>, T::AccountId>>;
298
299	/// Conversion of a contextual alias to an account ID.
300	#[pallet::storage]
301	pub type AliasToAccount<T> = StorageMap<
302		_,
303		Blake2_128Concat,
304		ContextualAlias,
305		<T as frame_system::Config>::AccountId,
306		OptionQuery,
307	>;
308
309	/// Conversion of an account ID to a contextual alias.
310	#[pallet::storage]
311	pub type AccountToAlias<T> = StorageMap<
312		_,
313		Blake2_128Concat,
314		<T as frame_system::Config>::AccountId,
315		RevisedContextualAlias,
316		OptionQuery,
317	>;
318
319	/// Association of an account ID to a personal ID.
320	///
321	/// Managed with `set_personal_id_account` and `unset_personal_id_account`.
322	/// Reverse lookup is inside `People` storage, inside the record.
323	#[pallet::storage]
324	pub type AccountToPersonalId<T> = StorageMap<
325		_,
326		Blake2_128Concat,
327		<T as frame_system::Config>::AccountId,
328		PersonalId,
329		OptionQuery,
330	>;
331
332	/// Paginated collection of static chunks used by the verifiable crypto.
333	#[pallet::storage]
334	pub type Chunks<T> = StorageMap<_, Twox64Concat, PageIndex, ChunksOf<T>, OptionQuery>;
335
336	/// The next free and never reserved personal ID.
337	#[pallet::storage]
338	pub type NextPersonalId<T> = StorageValue<_, PersonalId, ValueQuery>;
339
340	/// The state of the pallet regarding the actions that are currently allowed to be performed on
341	/// all existing rings.
342	#[pallet::storage]
343	pub type RingsState<T> = StorageValue<_, RingMembersState, ValueQuery>;
344
345	/// Candidates' reserved identities which we track.
346	#[pallet::storage]
347	pub type ReservedPersonalId<T: Config> =
348		StorageMap<_, Twox64Concat, PersonalId, (), OptionQuery>;
349
350	/// Keeps track of the page indices of the head and tail of the onboarding queue.
351	#[pallet::storage]
352	pub type QueuePageIndices<T: Config> = StorageValue<_, (PageIndex, PageIndex), ValueQuery>;
353
354	/// Paginated collection of people public keys ready to be included in a ring.
355	#[pallet::storage]
356	pub type OnboardingQueue<T> = StorageMap<
357		_,
358		Twox64Concat,
359		PageIndex,
360		BoundedVec<MemberOf<T>, <T as Config>::OnboardingQueuePageSize>,
361		ValueQuery,
362	>;
363
364	#[pallet::event]
365	#[pallet::generate_deposit(pub(super) fn deposit_event)]
366	pub enum Event<T: Config> {
367		/// An individual has had their personhood recognised and indexed.
368		PersonhoodRecognized { who: PersonalId, key: MemberOf<T> },
369		/// An individual has had their personhood recognised again and indexed.
370		PersonOnboarding { who: PersonalId, key: MemberOf<T> },
371	}
372
373	#[pallet::extra_constants]
374	impl<T: Config> Pallet<T> {
375		/// The amount of block number tolerance we allow for a setup account transaction.
376		///
377		/// `set_alias_account` and `set_personal_id_account` calls contains
378		/// `call_valid_at` as a parameter, those calls are valid if the block number is within
379		/// the tolerance period.
380		pub fn account_setup_time_tolerance() -> BlockNumberFor<T> {
381			600u32.into()
382		}
383	}
384
385	#[pallet::error]
386	pub enum Error<T> {
387		/// The supplied identifier does not represent a person.
388		NotPerson,
389		/// The given person has no associated key.
390		NoKey,
391		/// The context is not a member of those allowed to have account aliases held.
392		InvalidContext,
393		/// The account is not known.
394		InvalidAccount,
395		/// The account is already in use under another alias.
396		AccountInUse,
397		/// The proof is invalid.
398		InvalidProof,
399		/// The signature is invalid.
400		InvalidSignature,
401		/// There are not yet any members of our personhood set.
402		NoMembers,
403		/// The root cannot be finalized as there are still unpushed members.
404		Incomplete,
405		/// The root is still fresh.
406		StillFresh,
407		/// Too many members have been pushed.
408		TooManyMembers,
409		/// Key already in use by another person.
410		KeyAlreadyInUse,
411		/// The old key was not found when expected.
412		KeyNotFound,
413		/// Could not push member into the ring.
414		CouldNotPush,
415		/// The record is already using this key.
416		SameKey,
417		/// Personal Id was not reserved.
418		PersonalIdNotReserved,
419		/// Personal Id has never been reserved.
420		PersonalIdReservationCannotRenew,
421		/// Personal Id was not reserved or not already recognized.
422		PersonalIdNotReservedOrNotRecognized,
423		/// Ring cannot be merged if it's the top ring.
424		InvalidRing,
425		/// Ring cannot be built while there are suspensions pending.
426		SuspensionsPending,
427		/// Ring cannot be merged if it's not below 1/2 capacity.
428		RingAboveMergeThreshold,
429		/// Suspension indices provided are invalid.
430		InvalidSuspensions,
431		/// An mutating action was queued when there was no mutation session in progress.
432		NoMutationSession,
433		/// An mutating session could not be started.
434		CouldNotStartMutationSession,
435		/// Cannot merge rings while a suspension session is in progress.
436		SuspensionSessionInProgress,
437		/// Call is too late or too early.
438		TimeOutOfRange,
439		/// Alias <-> Account is already set and up to date.
440		AliasAccountAlreadySet,
441		/// Personhood cannot be resumed if it is not suspended.
442		NotSuspended,
443		/// Personhood is suspended.
444		Suspended,
445		/// Invalid state for attempted key migration.
446		InvalidKeyMigration,
447		/// Invalid suspension of a key belonging to a person whose index in the ring has already
448		/// been included in the pending suspensions list.
449		KeyAlreadySuspended,
450		/// The onboarding size must not exceed the maximum ring size.
451		InvalidOnboardingSize,
452	}
453
454	#[pallet::origin]
455	#[derive(
456		Clone,
457		PartialEq,
458		Eq,
459		RuntimeDebug,
460		Encode,
461		Decode,
462		MaxEncodedLen,
463		TypeInfo,
464		DecodeWithMemTracking,
465	)]
466	pub enum Origin {
467		PersonalIdentity(PersonalId),
468		PersonalAlias(RevisedContextualAlias),
469	}
470
471	#[pallet::hooks]
472	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
473		fn integrity_test() {
474			assert!(
475				<T as Config>::ChunkPageSize::get() > 0,
476				"chunk page size must hold at least one element"
477			);
478			assert!(<T as Config>::MaxRingSize::get() > 0, "rings must hold at least one person");
479			assert!(
480				<T as Config>::MaxRingSize::get() <= <T as Config>::OnboardingQueuePageSize::get(),
481				"onboarding queue page size must greater than or equal to max ring size"
482			);
483		}
484
485		fn on_poll(_: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
486			// Check if there are any keys to migrate.
487			if weight_meter.try_consume(T::WeightInfo::on_poll_base()).is_err() {
488				return;
489			}
490			if RingsState::<T>::get().key_migration() {
491				Self::migrate_keys(weight_meter);
492			}
493
494			// Check if there are any rings with suspensions and try to clean the first one.
495			if let Some(ring_index) = PendingSuspensions::<T>::iter_keys().next() {
496				if Self::should_remove_suspended_keys(ring_index, true) &&
497					weight_meter.can_consume(T::WeightInfo::remove_suspended_people(
498						T::MaxRingSize::get(),
499					)) {
500					let actual = Self::remove_suspended_keys(ring_index);
501					weight_meter.consume(actual)
502				}
503			}
504
505			let merge_weight = T::WeightInfo::merge_queue_pages();
506			if !weight_meter.can_consume(merge_weight) {
507				return;
508			}
509			let merge_action = Self::should_merge_queue_pages();
510			if let QueueMergeAction::Merge {
511				initial_head,
512				new_head,
513				first_key_page,
514				second_key_page,
515			} = merge_action
516			{
517				Self::merge_queue_pages(initial_head, new_head, first_key_page, second_key_page);
518				weight_meter.consume(merge_weight);
519			}
520		}
521
522		fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
523			let mut weight_meter = WeightMeter::with_limit(limit.saturating_div(2));
524			let on_idle_weight = T::WeightInfo::on_idle_base();
525			if !weight_meter.can_consume(on_idle_weight) {
526				return weight_meter.consumed();
527			}
528			weight_meter.consume(on_idle_weight);
529
530			let max_ring_size = T::MaxRingSize::get();
531			let remove_people_weight = T::WeightInfo::remove_suspended_people(max_ring_size);
532			let rings_state = RingsState::<T>::get();
533
534			// Check if there are any rings with suspensions and try to clean as many as possible.
535			// First check the state of the rings allow for removals.
536			if !rings_state.append_only() {
537				return weight_meter.consumed();
538			}
539			// Account for the first iteration of the loop.
540			let suspension_step_weight = T::WeightInfo::pending_suspensions_iteration();
541			if !weight_meter.can_consume(suspension_step_weight) {
542				return weight_meter.consumed();
543			}
544			// Always renew the iterator because in each iteration we remove a key, which would make
545			// the old iterator unstable.
546			while let Some(ring_index) = PendingSuspensions::<T>::iter_keys().next() {
547				weight_meter.consume(suspension_step_weight);
548				// Break the loop if we run out of weight.
549				if !weight_meter.can_consume(remove_people_weight) {
550					return weight_meter.consumed();
551				}
552				if Self::should_remove_suspended_keys(ring_index, false) {
553					let actual = Self::remove_suspended_keys(ring_index);
554					weight_meter.consume(actual)
555				}
556				// Break the loop if we run out of weight.
557				if !weight_meter.can_consume(suspension_step_weight) {
558					return weight_meter.consumed();
559				}
560			}
561
562			// Ring state must be append only for both onboarding and ring building, but it is
563			// already checked above.
564
565			let onboard_people_weight = T::WeightInfo::onboard_people();
566			if !weight_meter.can_consume(onboard_people_weight) {
567				return weight_meter.consumed();
568			}
569			let op_res = with_storage_layer::<(), DispatchError, _>(|| Self::onboard_people());
570			weight_meter.consume(onboard_people_weight);
571			if let Err(e) = op_res {
572				log::debug!(target: LOG_TARGET, "failed to onboard people: {:?}", e);
573			}
574
575			let current_ring = CurrentRingIndex::<T>::get();
576			let should_build_ring_weight = T::WeightInfo::should_build_ring(max_ring_size);
577			let build_ring_weight = T::WeightInfo::build_ring(max_ring_size);
578			for ring_index in (0..=current_ring).rev() {
579				if !weight_meter.can_consume(should_build_ring_weight) {
580					return weight_meter.consumed();
581				}
582
583				let maybe_to_include = Self::should_build_ring(ring_index, max_ring_size);
584				weight_meter.consume(should_build_ring_weight);
585				let Some(to_include) = maybe_to_include else { continue };
586				if !weight_meter.can_consume(build_ring_weight) {
587					return weight_meter.consumed();
588				}
589				let op_res = with_storage_layer::<(), DispatchError, _>(|| {
590					Self::build_ring(ring_index, to_include)
591				});
592				weight_meter.consume(build_ring_weight);
593				if let Err(e) = op_res {
594					log::error!(target: LOG_TARGET, "failed to build ring: {:?}", e);
595				}
596			}
597
598			weight_meter.consumed()
599		}
600	}
601
602	#[pallet::genesis_config]
603	pub struct GenesisConfig<T: Config> {
604		pub encoded_chunks: Vec<u8>,
605		#[serde(skip)]
606		pub _phantom_data: core::marker::PhantomData<T>,
607		pub onboarding_size: u32,
608	}
609
610	impl<T: Config> Default for GenesisConfig<T> {
611		fn default() -> Self {
612			// The default genesis config will put in the chunks that pertain to the ring vrf
613			// implementation in the `verifiable` crate. This default config will not work for other
614			// custom `GenerateVerifiable` implementations.
615			use verifiable::ring_vrf_impl::StaticChunk;
616			let params = verifiable::ring_vrf_impl::ring_verifier_builder_params();
617			let chunks: Vec<StaticChunk> = params.0.iter().map(|c| StaticChunk(*c)).collect();
618			Self {
619				encoded_chunks: chunks.encode(),
620				_phantom_data: PhantomData,
621				onboarding_size: T::MaxRingSize::get(),
622			}
623		}
624	}
625
626	#[pallet::genesis_build]
627	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
628		fn build(&self) {
629			let chunks: Vec<<<T as Config>::Crypto as GenerateVerifiable>::StaticChunk> =
630				Decode::decode(&mut &(self.encoded_chunks.clone())[..])
631					.expect("couldn't decode chunks");
632			assert_eq!(chunks.len(), 1 << 9);
633			let page_size = <T as Config>::ChunkPageSize::get();
634
635			let mut page_idx = 0;
636			let mut chunk_idx = 0;
637			while chunk_idx < chunks.len() {
638				let chunk_idx_end = cmp::min(chunk_idx + page_size as usize, chunks.len());
639				let chunk_page: ChunksOf<T> = chunks[chunk_idx..chunk_idx_end]
640					.to_vec()
641					.try_into()
642					.expect("page size was checked against the array length; qed");
643				Chunks::<T>::insert(page_idx, chunk_page);
644				page_idx += 1;
645				chunk_idx = chunk_idx_end;
646			}
647
648			OnboardingSize::<T>::set(self.onboarding_size);
649		}
650	}
651
652	#[pallet::call(weight = <T as Config>::WeightInfo)]
653	impl<T: Config> Pallet<T> {
654		/// Build a ring root by including registered people.
655		///
656		/// This task is performed automatically by the pallet through the `on_idle` hook whenever
657		/// there is leftover weight in a block. This call is meant to be a backup in case of
658		/// extreme congestion and should be submitted by signed origins.
659		#[pallet::weight(
660			T::WeightInfo::should_build_ring(
661				limit.unwrap_or_else(T::MaxRingSize::get)
662			).saturating_add(T::WeightInfo::build_ring(limit.unwrap_or_else(T::MaxRingSize::get))))]
663		#[pallet::call_index(100)]
664		pub fn build_ring_manual(
665			origin: OriginFor<T>,
666			ring_index: RingIndex,
667			limit: Option<u32>,
668		) -> DispatchResultWithPostInfo {
669			ensure_signed(origin)?;
670
671			// Get the keys for this ring, and make sure that the ring is full before we build it.
672			let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
673			let to_include =
674				Self::should_build_ring(ring_index, limit.unwrap_or_else(T::MaxRingSize::get))
675					.ok_or(Error::<T>::StillFresh)?;
676
677			// Get the current ring, and check it should be rebuilt.
678			// Return the next revision.
679			let (next_revision, mut intermediate) =
680				if let Some(existing_root) = Root::<T>::get(ring_index) {
681					// We should build a new ring. Return the new revision number we should use.
682					(
683						existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
684						existing_root.intermediate,
685					)
686				} else {
687					// No ring has been built at this index, so we start at revision 0.
688					(0, T::Crypto::start_members())
689				};
690
691			// Push the members.
692			T::Crypto::push_members(
693				&mut intermediate,
694				keys.iter()
695					.skip(ring_status.included as usize)
696					.take(to_include as usize)
697					.cloned(),
698				Self::fetch_chunks,
699			)
700			.map_err(|_| Error::<T>::CouldNotPush)?;
701
702			// By the end of the loop, we have included the maximum number of keys in the vector.
703			ring_status.included = ring_status.included.saturating_add(to_include);
704			RingKeysStatus::<T>::insert(ring_index, ring_status);
705
706			// We create the root after pushing all members.
707			let root = T::Crypto::finish_members(intermediate.clone());
708			let ring_root = RingRoot { root, revision: next_revision, intermediate };
709			Root::<T>::insert(ring_index, ring_root);
710
711			Ok(Pays::No.into())
712		}
713
714		/// Onboard people into a ring by taking their keys from the onboarding queue and
715		/// registering them into the ring. This does not compute the root, that is done using
716		/// `build_ring`.
717		///
718		/// This task is performed automatically by the pallet through the `on_idle` hook whenever
719		/// there is leftover weight in a block. This call is meant to be a backup in case of
720		/// extreme congestion and should be submitted by signed origins.
721		#[pallet::weight(T::WeightInfo::onboard_people())]
722		#[pallet::call_index(101)]
723		pub fn onboard_people_manual(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
724			ensure_signed(origin)?;
725
726			// Get the keys for this ring, and make sure that the ring is full before we build it.
727			let (top_ring_index, mut keys) = Self::available_ring();
728			let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
729			defensive_assert!(
730				keys.len() == ring_status.total as usize,
731				"Stored key count doesn't match the actual length"
732			);
733
734			let keys_len = keys.len() as u32;
735			let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
736
737			let (mut head, tail) = QueuePageIndices::<T>::get();
738			let old_head = head;
739			let mut keys_to_include: Vec<MemberOf<T>> =
740				OnboardingQueue::<T>::take(head).into_inner();
741
742			// A `head != tail` condition should mean that there is at least one key in the page
743			// following this one.
744			if keys_to_include.len() < open_slots as usize && head != tail {
745				head = head.checked_add(1).unwrap_or(0);
746				let second_key_page = OnboardingQueue::<T>::take(head);
747				defensive_assert!(!second_key_page.is_empty());
748				keys_to_include.extend(second_key_page.into_iter());
749			}
750
751			let onboarding_size = OnboardingSize::<T>::get();
752
753			let (to_include, ring_filled) = Self::should_onboard_people(
754				top_ring_index,
755				&ring_status,
756				open_slots,
757				keys_to_include.len().saturated_into(),
758				onboarding_size,
759			)
760			.ok_or(Error::<T>::Incomplete)?;
761
762			let mut remaining_keys = keys_to_include.split_off(to_include as usize);
763			for key in keys_to_include.into_iter() {
764				let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
765				let mut record =
766					People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
767				record.position = RingPosition::Included {
768					ring_index: top_ring_index,
769					ring_position: keys.len().saturated_into(),
770					scheduled_for_removal: false,
771				};
772				People::<T>::insert(personal_id, record);
773				keys.try_push(key).map_err(|_| Error::<T>::TooManyMembers)?;
774			}
775			RingKeys::<T>::insert(top_ring_index, keys);
776			ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
777			ring_status.total = ring_status.total.saturating_add(to_include);
778			RingKeysStatus::<T>::insert(top_ring_index, ring_status);
779
780			// Update the top ring index if this onboarding round filled the current ring.
781			if ring_filled {
782				CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
783			}
784
785			if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
786				let split_idx =
787					remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
788				let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
789					remaining_keys
790						.split_off(split_idx)
791						.try_into()
792						.expect("the list shrunk so it must fit; qed");
793				let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
794					remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
795				OnboardingQueue::<T>::insert(old_head, remaining_keys);
796				OnboardingQueue::<T>::insert(head, second_page_keys);
797				QueuePageIndices::<T>::put((old_head, tail));
798			} else if !remaining_keys.is_empty() {
799				let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
800					remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
801				OnboardingQueue::<T>::insert(head, remaining_keys);
802				QueuePageIndices::<T>::put((head, tail));
803			} else {
804				// We have nothing to put back into the queue, so if this isn't the last page, move
805				// the head to the next page of the queue.
806				if head != tail {
807					head = head.checked_add(1).unwrap_or(0);
808				}
809				QueuePageIndices::<T>::put((head, tail));
810			}
811
812			Ok(Pays::No.into())
813		}
814
815		/// Merge the people in two rings into a single, new ring. In order for the rings to be
816		/// eligible for merging, they must be below 1/2 of max capacity, have no pending
817		/// suspensions and not be the top ring used for onboarding.
818		#[pallet::call_index(102)]
819		pub fn merge_rings(
820			origin: OriginFor<T>,
821			base_ring_index: RingIndex,
822			target_ring_index: RingIndex,
823		) -> DispatchResultWithPostInfo {
824			let _ = ensure_signed(origin)?;
825
826			ensure!(RingsState::<T>::get().append_only(), Error::<T>::SuspensionSessionInProgress);
827			// Top ring that onboards new candidates cannot be merged. Identical rings cannot be
828			// merged.
829			let current_ring_index = CurrentRingIndex::<T>::get();
830			ensure!(
831				base_ring_index != target_ring_index &&
832					base_ring_index != current_ring_index &&
833					target_ring_index != current_ring_index,
834				Error::<T>::InvalidRing
835			);
836
837			// Enforce eligibility criteria.
838			let (mut base_keys, mut base_ring_status) = Self::ring_keys_and_info(base_ring_index);
839			ensure!(
840				base_keys.len() < T::MaxRingSize::get() as usize / 2,
841				Error::<T>::RingAboveMergeThreshold
842			);
843			ensure!(
844				PendingSuspensions::<T>::decode_len(base_ring_index).unwrap_or(0) == 0,
845				Error::<T>::SuspensionsPending
846			);
847			let target_keys = RingKeys::<T>::get(target_ring_index);
848			RingKeysStatus::<T>::remove(target_ring_index);
849			ensure!(
850				target_keys.len() < T::MaxRingSize::get() as usize / 2,
851				Error::<T>::RingAboveMergeThreshold
852			);
853			ensure!(
854				PendingSuspensions::<T>::decode_len(target_ring_index).unwrap_or(0) == 0,
855				Error::<T>::SuspensionsPending
856			);
857
858			// Update the status of the ring to reflect the newly added keys.
859			base_ring_status.total =
860				base_ring_status.total.saturating_add(target_keys.len().saturated_into());
861
862			for key in target_keys {
863				let personal_id =
864					Keys::<T>::get(&key).defensive().ok_or(Error::<T>::KeyNotFound)?;
865				let mut record =
866					People::<T>::get(personal_id).defensive().ok_or(Error::<T>::NotPerson)?;
867				record.position = RingPosition::Included {
868					ring_index: base_ring_index,
869					ring_position: base_keys.len().saturated_into(),
870					scheduled_for_removal: false,
871				};
872				base_keys.try_push(key).map_err(|_| Error::<T>::TooManyMembers)?;
873				People::<T>::insert(personal_id, record)
874			}
875
876			// Newly added keys are not yet included.
877			RingKeys::<T>::insert(base_ring_index, base_keys);
878			RingKeysStatus::<T>::insert(base_ring_index, base_ring_status);
879			// Remove the stale ring root of the target ring. The keys in the target ring will be
880			// part of a valid ring root again when the base ring is rebuilt.
881			Root::<T>::remove(target_ring_index);
882			RingKeys::<T>::remove(target_ring_index);
883			RingKeysStatus::<T>::remove(target_ring_index);
884
885			Ok(Pays::No.into())
886		}
887
888		/// Dispatch a call under an alias using the `account <-> alias` mapping.
889		///
890		/// This is a call version of the transaction extension `AsPersonalAliasWithAccount`.
891		/// It is recommended to use the transaction extension instead when suitable.
892		#[pallet::call_index(0)]
893		#[pallet::weight(T::WeightInfo::under_alias().saturating_add(call.get_dispatch_info().call_weight))]
894		pub fn under_alias(
895			origin: OriginFor<T>,
896			call: Box<<T as frame_system::Config>::RuntimeCall>,
897		) -> DispatchResultWithPostInfo {
898			let account = ensure_signed(origin.clone())?;
899			let rev_ca = AccountToAlias::<T>::get(&account).ok_or(Error::<T>::InvalidAccount)?;
900			ensure!(
901				Root::<T>::get(rev_ca.ring).is_some_and(|ring| ring.revision == rev_ca.revision),
902				DispatchError::BadOrigin,
903			);
904
905			let derivation_weight = T::WeightInfo::under_alias();
906			let local_origin = Origin::PersonalAlias(rev_ca);
907			Self::derivative_call(origin, local_origin, *call, derivation_weight)
908		}
909
910		/// This transaction is refunded if successful and no alias was previously set.
911		///
912		/// The call is valid from `call_valid_at` until
913		/// `call_valid_at + account_setup_time_tolerance`.
914		/// `account_setup_time_tolerance` is a constant available in the metadata.
915		///
916		/// Parameters:
917		/// - `account`: The account to set the alias for.
918		/// - `call_valid_at`: The block number when the call becomes valid.
919		#[pallet::call_index(1)]
920		pub fn set_alias_account(
921			origin: OriginFor<T>,
922			account: T::AccountId,
923			call_valid_at: BlockNumberFor<T>,
924		) -> DispatchResultWithPostInfo {
925			let rev_ca = Self::ensure_revised_personal_alias(origin)?;
926			let now = frame_system::Pallet::<T>::block_number();
927			let time_tolerance = Self::account_setup_time_tolerance();
928			ensure!(
929				call_valid_at <= now && now <= call_valid_at.saturating_add(time_tolerance),
930				Error::<T>::TimeOutOfRange
931			);
932			ensure!(T::AccountContexts::contains(&rev_ca.ca.context), Error::<T>::InvalidContext);
933			ensure!(!AccountToPersonalId::<T>::contains_key(&account), Error::<T>::AccountInUse);
934
935			let old_account = AliasToAccount::<T>::get(&rev_ca.ca);
936			let old_rev_ca = old_account.as_ref().and_then(AccountToAlias::<T>::get);
937
938			let needs_revision = old_rev_ca.is_some_and(|old_rev_ca| {
939				old_rev_ca.revision != rev_ca.revision || old_rev_ca.ring != rev_ca.ring
940			});
941
942			// Ensure it changes the account associated, or it needs revision.
943			ensure!(
944				old_account.as_ref() != Some(&account) || needs_revision,
945				Error::<T>::AliasAccountAlreadySet
946			);
947
948			// If the old account is different from the new one:
949			// * decrease the sufficients of the old account
950			// * increase the sufficients of the new account
951			// * check new account is not already in use
952			if old_account.as_ref() != Some(&account) {
953				ensure!(!AccountToAlias::<T>::contains_key(&account), Error::<T>::AccountInUse);
954				if let Some(old_account) = &old_account {
955					frame_system::Pallet::<T>::dec_sufficients(old_account);
956					AccountToAlias::<T>::remove(old_account);
957				}
958				frame_system::Pallet::<T>::inc_sufficients(&account);
959			}
960
961			AccountToAlias::<T>::insert(&account, &rev_ca);
962			AliasToAccount::<T>::insert(&rev_ca.ca, &account);
963
964			if old_account.is_none() || needs_revision {
965				Ok(Pays::No.into())
966			} else {
967				Ok(Pays::Yes.into())
968			}
969		}
970
971		/// Remove the mapping from a particular alias to its registered account.
972		#[pallet::call_index(2)]
973		pub fn unset_alias_account(origin: OriginFor<T>) -> DispatchResult {
974			let alias = Self::ensure_personal_alias(origin)?;
975			let account = AliasToAccount::<T>::take(&alias).ok_or(Error::<T>::InvalidAccount)?;
976			AccountToAlias::<T>::remove(&account);
977			frame_system::Pallet::<T>::dec_sufficients(&account);
978
979			Ok(())
980		}
981
982		/// Recognize a set of people without any additional checks.
983		///
984		/// The people are identified by the provided list of keys and will each be assigned, in
985		/// order, the next available personal ID.
986		///
987		/// The origin for this call must have root privileges.
988		#[pallet::call_index(3)]
989		pub fn force_recognize_personhood(
990			origin: OriginFor<T>,
991			people: Vec<MemberOf<T>>,
992		) -> DispatchResultWithPostInfo {
993			ensure_root(origin)?;
994			for key in people {
995				let personal_id = Self::reserve_new_id();
996				Self::recognize_personhood(personal_id, Some(key))?;
997			}
998			Ok(().into())
999		}
1000
1001		/// Set a personal id account.
1002		///
1003		/// The account can then be used to sign transactions on behalf of the personal id, and
1004		/// provide replay protection with the nonce.
1005		///
1006		/// This transaction is refunded if successful and no account was previously set for the
1007		/// personal id.
1008		///
1009		/// The call is valid from `call_valid_at` until
1010		/// `call_valid_at + account_setup_time_tolerance`.
1011		/// `account_setup_time_tolerance` is a constant available in the metadata.
1012		///
1013		/// Parameters:
1014		/// - `account`: The account to set the alias for.
1015		/// - `call_valid_at`: The block number when the call becomes valid.
1016		#[pallet::call_index(4)]
1017		pub fn set_personal_id_account(
1018			origin: OriginFor<T>,
1019			account: T::AccountId,
1020			call_valid_at: BlockNumberFor<T>,
1021		) -> DispatchResultWithPostInfo {
1022			let id = Self::ensure_personal_identity(origin)?;
1023			let now = frame_system::Pallet::<T>::block_number();
1024			let time_tolerance = Self::account_setup_time_tolerance();
1025			ensure!(
1026				call_valid_at <= now && now <= call_valid_at.saturating_add(time_tolerance),
1027				Error::<T>::TimeOutOfRange
1028			);
1029			ensure!(!AccountToPersonalId::<T>::contains_key(&account), Error::<T>::AccountInUse);
1030			ensure!(!AccountToAlias::<T>::contains_key(&account), Error::<T>::AccountInUse);
1031			let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1032			let pays = if let Some(old_account) = record.account {
1033				frame_system::Pallet::<T>::dec_sufficients(&old_account);
1034				AccountToPersonalId::<T>::remove(&old_account);
1035				Pays::Yes
1036			} else {
1037				Pays::No
1038			};
1039			record.account = Some(account.clone());
1040			frame_system::Pallet::<T>::inc_sufficients(&account);
1041			AccountToPersonalId::<T>::insert(&account, id);
1042			People::<T>::insert(id, &record);
1043
1044			Ok(pays.into())
1045		}
1046
1047		/// Unset the personal id account.
1048		#[pallet::call_index(5)]
1049		pub fn unset_personal_id_account(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
1050			let id = Self::ensure_personal_identity(origin)?;
1051			let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1052			let account = record.account.take().ok_or(Error::<T>::InvalidAccount)?;
1053			AccountToPersonalId::<T>::take(&account).ok_or(Error::<T>::InvalidAccount)?;
1054			frame_system::Pallet::<T>::dec_sufficients(&account);
1055			People::<T>::insert(id, &record);
1056
1057			Ok(Pays::Yes.into())
1058		}
1059
1060		/// Migrate the key for a person who was onboarded and is currently included in a ring. The
1061		/// migration is not instant as the key replacement and subsequent inclusion in a new ring
1062		/// root will happen only after the next mutation session.
1063		#[pallet::call_index(6)]
1064		pub fn migrate_included_key(
1065			origin: OriginFor<T>,
1066			new_key: MemberOf<T>,
1067		) -> DispatchResultWithPostInfo {
1068			let id = Self::ensure_personal_identity(origin)?;
1069			ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1070			let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1071			ensure!(record.key != new_key, Error::<T>::SameKey);
1072			match &record.position {
1073				// If the key is already included in a ring, enqueue it for migration during the
1074				// next mutation session.
1075				RingPosition::Included { ring_index, ring_position, .. } => {
1076					// If the person scheduled another migration before, remove the key we are
1077					// replacing from the key registry.
1078					if let Some(old_migrated_key) = KeyMigrationQueue::<T>::get(id) {
1079						Keys::<T>::remove(old_migrated_key);
1080					}
1081					// Add this new key to the migration queue.
1082					KeyMigrationQueue::<T>::insert(id, &new_key);
1083					// Mark this record as stale.
1084					record.position = RingPosition::Included {
1085						ring_index: *ring_index,
1086						ring_position: *ring_position,
1087						scheduled_for_removal: true,
1088					};
1089					// Update the record.
1090					People::<T>::insert(id, record);
1091				},
1092				// This call accepts migrations only for included keys.
1093				RingPosition::Onboarding { .. } =>
1094					return Err(Error::<T>::InvalidKeyMigration.into()),
1095				// Suspended people shouldn't be able to call this, but protect against this case
1096				// anyway.
1097				RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1098			}
1099			Keys::<T>::insert(new_key, id);
1100
1101			Ok(().into())
1102		}
1103
1104		/// Migrate the key for a person who is currently onboarding. The operation is instant,
1105		/// replacing the old key in the onboarding queue.
1106		#[pallet::call_index(7)]
1107		pub fn migrate_onboarding_key(
1108			origin: OriginFor<T>,
1109			new_key: MemberOf<T>,
1110		) -> DispatchResultWithPostInfo {
1111			let id = Self::ensure_personal_identity(origin)?;
1112			ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1113			let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1114			ensure!(record.key != new_key, Error::<T>::SameKey);
1115			match &record.position {
1116				// If it's still onboarding, just replace the old key in the queue.
1117				RingPosition::Onboarding { queue_page } => {
1118					let mut keys = OnboardingQueue::<T>::get(queue_page);
1119					if let Some(idx) = keys.iter().position(|k| *k == record.key) {
1120						// Remove the key that never made it into a ring.
1121						Keys::<T>::remove(&keys[idx]);
1122						// Update the key in the queue.
1123						keys[idx] = new_key.clone();
1124						OnboardingQueue::<T>::insert(queue_page, keys);
1125						// Replace the key in the record.
1126						record.key = new_key.clone();
1127						// Update the record.
1128						People::<T>::insert(id, record);
1129					} else {
1130						defensive!("No key found at the position in the person record of {}", id);
1131					}
1132				},
1133				// This call accepts migrations only for included keys.
1134				RingPosition::Included { .. } => return Err(Error::<T>::InvalidKeyMigration.into()),
1135				// Suspended people shouldn't be able to call this, but protect against this case
1136				// anyway.
1137				RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1138			}
1139			Keys::<T>::insert(new_key, id);
1140
1141			Ok(().into())
1142		}
1143
1144		/// Force set the onboarding size for new people. This call requires root privileges.
1145		#[pallet::call_index(8)]
1146		pub fn set_onboarding_size(
1147			origin: OriginFor<T>,
1148			onboarding_size: u32,
1149		) -> DispatchResultWithPostInfo {
1150			ensure_root(origin)?;
1151			ensure!(
1152				onboarding_size <= <T as Config>::MaxRingSize::get(),
1153				Error::<T>::InvalidOnboardingSize
1154			);
1155			OnboardingSize::<T>::put(onboarding_size);
1156			Ok(Pays::No.into())
1157		}
1158	}
1159
1160	impl<T: Config> Pallet<T> {
1161		/// If the conditions to build a ring are met, this function returns the number of people to
1162		/// be included in a `build_ring` call. Otherwise, this function returns `None`.
1163		pub(crate) fn should_build_ring(ring_index: RingIndex, limit: u32) -> Option<u32> {
1164			// Ring root cannot be built while there are people to remove.
1165			if !RingsState::<T>::get().append_only() {
1166				return None;
1167			}
1168			// Suspended people should be removed from the ring before building it.
1169			if PendingSuspensions::<T>::contains_key(ring_index) {
1170				return None;
1171			}
1172
1173			let ring_status = RingKeysStatus::<T>::get(ring_index);
1174			let not_included_count = ring_status.total.saturating_sub(ring_status.included);
1175			let to_include = not_included_count.min(limit);
1176			// There must be at least one person waiting to be included to build the ring.
1177			if to_include == 0 {
1178				return None;
1179			}
1180
1181			Some(to_include)
1182		}
1183
1184		/// If the conditions to onboard new people into rings are met, this function returns the
1185		/// number of people to be onboarded from the queue in a `onboard_people` call along with a
1186		/// flag which states whether the call will completely populate the ring. Otherwise, this
1187		/// function returns `None`.
1188		fn should_onboard_people(
1189			ring_index: RingIndex,
1190			ring_status: &RingStatus,
1191			open_slots: u32,
1192			available_for_inclusion: u32,
1193			onboarding_size: u32,
1194		) -> Option<(u32, bool)> {
1195			// People cannot be onboarded while suspensions are ongoing.
1196			if !RingsState::<T>::get().append_only() {
1197				return None;
1198			}
1199
1200			// Suspended people should be removed from the ring before building it.
1201			if PendingSuspensions::<T>::contains_key(ring_index) {
1202				return None;
1203			}
1204
1205			let to_include = available_for_inclusion.min(open_slots);
1206			// If everything is already included, nothing to do.
1207			if to_include == 0 {
1208				return None;
1209			}
1210
1211			// Here we check we have enough items in the queue so that the onboarding group size is
1212			// respected, but also that we can support another queue of at least onboarding size
1213			// in a future call.
1214			let can_onboard_with_cohort = to_include >= onboarding_size &&
1215				ring_status.total.saturating_add(to_include.saturated_into()) <=
1216					T::MaxRingSize::get().saturating_sub(onboarding_size);
1217			// If this call completely fills the ring, no onboarding rule enforcement will be
1218			// necessary.
1219			let ring_filled = open_slots == to_include;
1220
1221			let should_onboard = ring_filled || can_onboard_with_cohort;
1222			if !should_onboard {
1223				return None;
1224			}
1225
1226			Some((to_include, ring_filled))
1227		}
1228
1229		/// Returns whether suspensions are allowed and necessary for a given ring index.
1230		pub(crate) fn should_remove_suspended_keys(
1231			ring_index: RingIndex,
1232			check_rings_state: bool,
1233		) -> bool {
1234			if check_rings_state && !RingsState::<T>::get().append_only() {
1235				return false;
1236			}
1237			let suspended_count = PendingSuspensions::<T>::decode_len(ring_index).unwrap_or(0);
1238			// There must be keys to suspend.
1239			if suspended_count == 0 {
1240				return false;
1241			}
1242
1243			true
1244		}
1245
1246		/// Function that checks if the top two onboarding queue pages can be merged into a single
1247		/// page to defragment the list. This function returns an action to take following the
1248		/// check. In case a merge is needed, the following information is provided, in order:
1249		/// * The initial `head` of the queue - will need to remove the page at this index in case
1250		///   the merge is performed.
1251		/// * The new `head` of the queue.
1252		/// * The keys on the first page of the queue.
1253		/// * The keys on the second page of the queue.
1254		pub(crate) fn should_merge_queue_pages() -> QueueMergeAction<T> {
1255			let (initial_head, tail) = QueuePageIndices::<T>::get();
1256			let first_key_page = OnboardingQueue::<T>::get(initial_head);
1257			// A `head != tail` condition should mean that there is at least one more page
1258			// following this one.
1259			if initial_head == tail {
1260				return QueueMergeAction::NoAction;
1261			}
1262			let new_head = initial_head.checked_add(1).unwrap_or(0);
1263			let second_key_page = OnboardingQueue::<T>::get(new_head);
1264
1265			let page_size = T::OnboardingQueuePageSize::get();
1266			// Make sure the pages can be merged.
1267			if first_key_page.len().saturating_add(second_key_page.len()) > page_size as usize {
1268				return QueueMergeAction::NoAction;
1269			}
1270
1271			QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page }
1272		}
1273
1274		/// Build a ring root by adding all people who were assigned to this ring but not yet
1275		/// included into the root.
1276		pub(crate) fn build_ring(ring_index: RingIndex, to_include: u32) -> DispatchResult {
1277			let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
1278			// Get the current ring, and check it should be rebuilt.
1279			// Return the next revision.
1280			let (next_revision, mut intermediate) =
1281				if let Some(existing_root) = Root::<T>::get(ring_index) {
1282					// We should build a new ring. Return the new revision number we should use.
1283					(
1284						existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
1285						existing_root.intermediate,
1286					)
1287				} else {
1288					// No ring has been built at this index, so we start at revision 0.
1289					(0, T::Crypto::start_members())
1290				};
1291
1292			// Push the members.
1293			T::Crypto::push_members(
1294				&mut intermediate,
1295				keys.iter()
1296					.skip(ring_status.included as usize)
1297					.take(to_include as usize)
1298					.cloned(),
1299				Self::fetch_chunks,
1300			)
1301			.defensive()
1302			.map_err(|_| Error::<T>::CouldNotPush)?;
1303
1304			// By the end of the loop, we have included the maximum number of keys in the vector.
1305			ring_status.included = ring_status.included.saturating_add(to_include);
1306			RingKeysStatus::<T>::insert(ring_index, ring_status);
1307
1308			// We create the root after pushing all members.
1309			let root = T::Crypto::finish_members(intermediate.clone());
1310			let ring_root = RingRoot { root, revision: next_revision, intermediate };
1311			Root::<T>::insert(ring_index, ring_root);
1312			Ok(())
1313		}
1314
1315		/// Onboard as many people as possible into the available ring.
1316		///
1317		/// This function returns an error if there aren't enough people in the onboarding queue to
1318		/// complete the operation, or if the number of remaining open slots in the ring would be
1319		/// below the minimum onboarding size allowed.
1320		#[transactional]
1321		pub(crate) fn onboard_people() -> DispatchResult {
1322			// Get the keys for this ring, and make sure that the ring is full before we build it.
1323			let (top_ring_index, mut keys) = Self::available_ring();
1324			let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
1325			defensive_assert!(
1326				keys.len() == ring_status.total as usize,
1327				"Stored key count doesn't match the actual length"
1328			);
1329
1330			let keys_len = keys.len() as u32;
1331			let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
1332
1333			let (mut head, tail) = QueuePageIndices::<T>::get();
1334			let old_head = head;
1335			let mut keys_to_include: Vec<MemberOf<T>> =
1336				OnboardingQueue::<T>::take(head).into_inner();
1337
1338			// A `head != tail` condition should mean that there is at least one key in the page
1339			// following this one.
1340			if keys_to_include.len() < open_slots as usize && head != tail {
1341				head = head.checked_add(1).unwrap_or(0);
1342				let second_key_page = OnboardingQueue::<T>::take(head);
1343				defensive_assert!(!second_key_page.is_empty());
1344				keys_to_include.extend(second_key_page.into_iter());
1345			}
1346
1347			let onboarding_size = OnboardingSize::<T>::get();
1348
1349			let (to_include, ring_filled) = Self::should_onboard_people(
1350				top_ring_index,
1351				&ring_status,
1352				open_slots,
1353				keys_to_include.len().saturated_into(),
1354				onboarding_size,
1355			)
1356			.ok_or(Error::<T>::Incomplete)?;
1357
1358			let mut remaining_keys = keys_to_include.split_off(to_include as usize);
1359			for key in keys_to_include.into_iter() {
1360				let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
1361				let mut record =
1362					People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1363				record.position = RingPosition::Included {
1364					ring_index: top_ring_index,
1365					ring_position: keys.len().saturated_into(),
1366					scheduled_for_removal: false,
1367				};
1368				People::<T>::insert(personal_id, record);
1369				keys.try_push(key).defensive().map_err(|_| Error::<T>::TooManyMembers)?;
1370			}
1371			RingKeys::<T>::insert(top_ring_index, keys);
1372			ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
1373			ring_status.total = ring_status.total.saturating_add(to_include);
1374			RingKeysStatus::<T>::insert(top_ring_index, ring_status);
1375
1376			// Update the top ring index if this onboarding round filled the current ring.
1377			if ring_filled {
1378				CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
1379			}
1380
1381			if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
1382				let split_idx =
1383					remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
1384				let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1385					remaining_keys
1386						.split_off(split_idx)
1387						.try_into()
1388						.expect("the list shrunk so it must fit; qed");
1389				let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1390					remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1391				OnboardingQueue::<T>::insert(old_head, remaining_keys);
1392				OnboardingQueue::<T>::insert(head, second_page_keys);
1393				QueuePageIndices::<T>::put((old_head, tail));
1394			} else if !remaining_keys.is_empty() {
1395				let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1396					remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1397				OnboardingQueue::<T>::insert(head, remaining_keys);
1398				QueuePageIndices::<T>::put((head, tail));
1399			} else {
1400				// We have nothing to put back into the queue, so if this isn't the last page, move
1401				// the head to the next page of the queue.
1402				if head != tail {
1403					head = head.checked_add(1).unwrap_or(0);
1404				}
1405				QueuePageIndices::<T>::put((head, tail));
1406			}
1407			Ok(())
1408		}
1409
1410		fn derivative_call(
1411			mut origin: OriginFor<T>,
1412			local_origin: Origin,
1413			call: <T as frame_system::Config>::RuntimeCall,
1414			derivation_weight: Weight,
1415		) -> DispatchResultWithPostInfo {
1416			origin.set_caller_from(<T::RuntimeOrigin as OriginTrait>::PalletsOrigin::from(
1417				local_origin,
1418			));
1419			let info = call.get_dispatch_info();
1420			let result = call.dispatch(origin);
1421			let weight = derivation_weight.saturating_add(extract_actual_weight(&result, &info));
1422			result
1423				.map(|p| PostDispatchInfo { actual_weight: Some(weight), pays_fee: p.pays_fee })
1424				.map_err(|mut err| {
1425					err.post_info = Some(weight).into();
1426					err
1427				})
1428		}
1429
1430		/// Ensure that the origin `o` represents a person.
1431		/// Returns `Ok` with the base identity of the person on success.
1432		pub fn ensure_personal_identity(
1433			origin: T::RuntimeOrigin,
1434		) -> Result<PersonalId, DispatchError> {
1435			Ok(ensure_personal_identity(origin.into_caller())?)
1436		}
1437
1438		/// Ensure that the origin `o` represents a person.
1439		/// Returns `Ok` with the alias of the person together with the context in which it can
1440		/// be used on success.
1441		pub fn ensure_personal_alias(
1442			origin: T::RuntimeOrigin,
1443		) -> Result<ContextualAlias, DispatchError> {
1444			Ok(ensure_personal_alias(origin.into_caller())?)
1445		}
1446
1447		/// Ensure that the origin `o` represents a person.
1448		/// On success returns `Ok` with the revised alias of the person together with the context
1449		/// in which it can be used and the revision of the ring the person is in.
1450		pub fn ensure_revised_personal_alias(
1451			origin: T::RuntimeOrigin,
1452		) -> Result<RevisedContextualAlias, DispatchError> {
1453			Ok(ensure_revised_personal_alias(origin.into_caller())?)
1454		}
1455
1456		// This function always returns the ring index and the keys for the ring which is currently
1457		// accepting new members.
1458		pub fn available_ring() -> (RingIndex, BoundedVec<MemberOf<T>, T::MaxRingSize>) {
1459			let mut current_ring_index = CurrentRingIndex::<T>::get();
1460			let mut current_keys = RingKeys::<T>::get(current_ring_index);
1461
1462			defensive_assert!(
1463				!current_keys.is_full(),
1464				"Something bad happened inside the STF, where the current keys are full, but we should have incremented in that case."
1465			);
1466
1467			// This condition shouldn't be reached, but we handle the error just in case.
1468			if current_keys.is_full() {
1469				current_ring_index.saturating_inc();
1470				CurrentRingIndex::<T>::put(current_ring_index);
1471				current_keys = RingKeys::<T>::get(current_ring_index);
1472			}
1473
1474			defensive_assert!(
1475				!current_keys.is_full(),
1476				"Something bad happened inside the STF, where the current key and next key are both full. Nothing we can do here."
1477			);
1478
1479			(current_ring_index, current_keys)
1480		}
1481
1482		// This allows us to associate a key with a person.
1483		pub fn do_insert_key(who: PersonalId, key: MemberOf<T>) -> DispatchResult {
1484			// If the key is already in use by another person then error.
1485			ensure!(!Keys::<T>::contains_key(&key), Error::<T>::KeyAlreadyInUse);
1486			// This is a first time key, so it must be reserved.
1487			ensure!(
1488				ReservedPersonalId::<T>::take(who).is_some(),
1489				Error::<T>::PersonalIdNotReservedOrNotRecognized
1490			);
1491
1492			Self::push_to_onboarding_queue(who, key, None)
1493		}
1494
1495		// Enqueue personhood suspensions. This function can be called multiple times until all
1496		// people are marked as suspended, but it can only happen while there is a mutation session
1497		// in progress.
1498		pub fn queue_personhood_suspensions(suspensions: &[PersonalId]) -> DispatchResult {
1499			ensure!(RingsState::<T>::get().mutating(), Error::<T>::NoMutationSession);
1500			for who in suspensions {
1501				let mut record = People::<T>::get(who).ok_or(Error::<T>::InvalidSuspensions)?;
1502				match record.position {
1503					RingPosition::Included { ring_index, ring_position, .. } => {
1504						let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1505						let Err(insert_idx) = suspended_indices.binary_search(&ring_position)
1506						else {
1507							return Err(Error::<T>::KeyAlreadySuspended.into())
1508						};
1509						suspended_indices
1510							.try_insert(insert_idx, ring_position)
1511							.defensive()
1512							.map_err(|_| Error::<T>::TooManyMembers)?;
1513						PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1514					},
1515					RingPosition::Onboarding { queue_page } => {
1516						let mut keys = OnboardingQueue::<T>::get(queue_page);
1517						let queue_idx = keys.iter().position(|k| *k == record.key);
1518						if let Some(idx) = queue_idx {
1519							// It is expensive to shift the whole vec in the worst case to remove a
1520							// suspended person from onboarding, but the pages will be small and
1521							// suspension of people who are not yet onboarded is supposed to be
1522							// extremely rare if not impossible as the pallet hooks should have
1523							// plenty of time to include someone recognized before the beginning of
1524							// the next suspension round. The only legitimate case when this could
1525							// happen is if someone is sitting in the onboarding queue for a long
1526							// time and cannot be included because not enough people are joining,
1527							// but it should be a rare case.
1528							keys.remove(idx);
1529							OnboardingQueue::<T>::insert(queue_page, keys);
1530						} else {
1531							defensive!(
1532								"No key found at the position in the person record of {}",
1533								who
1534							);
1535						}
1536					},
1537					RingPosition::Suspended => {
1538						defensive!("Suspension queued for person {} while already suspended", who);
1539					},
1540				}
1541
1542				record.position = RingPosition::Suspended;
1543				if let Some(account) = record.account {
1544					AccountToPersonalId::<T>::remove(account);
1545					record.account = None;
1546				}
1547
1548				People::<T>::insert(who, record);
1549			}
1550
1551			Ok(())
1552		}
1553
1554		// Resume someone's personhood. This assumes that their personhood is currently suspended,
1555		// so the person was previously recognized.
1556		pub fn resume_personhood(who: PersonalId) -> DispatchResult {
1557			let record = People::<T>::get(who).ok_or(Error::<T>::NotPerson)?;
1558			ensure!(record.position.suspended(), Error::<T>::NotSuspended);
1559			ensure!(Keys::<T>::get(&record.key) == Some(who), Error::<T>::NoKey);
1560
1561			Self::push_to_onboarding_queue(who, record.key, record.account)
1562		}
1563
1564		fn push_to_onboarding_queue(
1565			who: PersonalId,
1566			key: MemberOf<T>,
1567			account: Option<T::AccountId>,
1568		) -> DispatchResult {
1569			let (head, mut tail) = QueuePageIndices::<T>::get();
1570			let mut keys = OnboardingQueue::<T>::get(tail);
1571			if let Err(k) = keys.try_push(key.clone()) {
1572				tail = tail.checked_add(1).unwrap_or(0);
1573				ensure!(tail != head, Error::<T>::TooManyMembers);
1574				keys = alloc::vec![k].try_into().expect("must be able to hold one key; qed");
1575			};
1576
1577			let record = PersonRecord {
1578				key,
1579				position: RingPosition::Onboarding { queue_page: tail },
1580				account,
1581			};
1582			Keys::<T>::insert(&record.key, who);
1583			People::<T>::insert(who, &record);
1584			Self::deposit_event(Event::<T>::PersonOnboarding { who, key: record.key });
1585
1586			QueuePageIndices::<T>::put((head, tail));
1587			OnboardingQueue::<T>::insert(tail, keys);
1588			Ok(())
1589		}
1590
1591		/// Fetch the keys in a ring along with stored inclusion information.
1592		pub fn ring_keys_and_info(
1593			ring_index: RingIndex,
1594		) -> (BoundedVec<MemberOf<T>, T::MaxRingSize>, RingStatus) {
1595			let keys = RingKeys::<T>::get(ring_index);
1596			let ring_status = RingKeysStatus::<T>::get(ring_index);
1597			defensive_assert!(
1598				keys.len() == ring_status.total as usize,
1599				"Stored key count doesn't match the actual length"
1600			);
1601			(keys, ring_status)
1602		}
1603
1604		// Given a range, returns the list of chunks that maps to the keys at those indices.
1605		pub(crate) fn fetch_chunks(
1606			range: Range<usize>,
1607		) -> Result<Vec<<T::Crypto as GenerateVerifiable>::StaticChunk>, ()> {
1608			let chunk_page_size = T::ChunkPageSize::get();
1609			let expected_len = range.end.saturating_sub(range.start);
1610			let mut page_idx = range.start.checked_div(chunk_page_size as usize).ok_or(())?;
1611			let mut chunks: Vec<_> = Chunks::<T>::get(page_idx.saturated_into::<u32>())
1612				.defensive()
1613				.ok_or(())?
1614				.into_iter()
1615				.skip(range.start % chunk_page_size as usize)
1616				.take(expected_len)
1617				.collect();
1618			while chunks.len() < expected_len {
1619				// Condition to eventually break out of a possible infinite loop in case
1620				// storage is full of empty chunk pages.
1621				page_idx = page_idx.checked_add(1).ok_or(())?;
1622				let page =
1623					Chunks::<T>::get(page_idx.saturated_into::<u32>()).defensive().ok_or(())?;
1624				chunks.extend(
1625					page.into_inner().into_iter().take(expected_len.saturating_sub(chunks.len())),
1626				);
1627			}
1628
1629			Ok(chunks)
1630		}
1631
1632		/// Migrates keys that people intend to replace with other keys, if possible. As this
1633		/// function mutates a fair amount of storage, it comes with a weight meter to limit on the
1634		/// number of keys to migrate in one call.
1635		pub(crate) fn migrate_keys(meter: &mut WeightMeter) {
1636			let mut drain = KeyMigrationQueue::<T>::drain();
1637			loop {
1638				// Ensure we have enough weight to look into `KeyMigrationQueue` and perform a
1639				// removal.
1640				let weight = T::WeightInfo::migrate_keys_single_included_key()
1641					.saturating_add(T::DbWeight::get().reads_writes(1, 1));
1642				if !meter.can_consume(weight) {
1643					return;
1644				}
1645
1646				let op_res = with_storage_layer::<bool, DispatchError, _>(|| match drain.next() {
1647					Some((id, new_key)) =>
1648						Self::migrate_keys_single_included_key(id, new_key).map(|_| false),
1649					None => {
1650						let rings_state = RingsState::<T>::get()
1651							.end_key_migration()
1652							.map_err(|_| Error::<T>::NoMutationSession)?;
1653						RingsState::<T>::put(rings_state);
1654						meter.consume(T::DbWeight::get().reads_writes(1, 1));
1655						Ok(true)
1656					},
1657				});
1658				match op_res {
1659					Ok(false) => meter.consume(weight),
1660					Ok(true) => {
1661						// Read on `KeyMigrationQueue`.
1662						meter.consume(T::DbWeight::get().reads(1));
1663						break
1664					},
1665					Err(e) => {
1666						meter.consume(weight);
1667						log::error!(target: LOG_TARGET, "failed to migrate keys: {:?}", e);
1668						break;
1669					},
1670				}
1671			}
1672		}
1673
1674		/// A single iteration of the key migration process where an included key marked for
1675		/// suspension is being removed from a ring.
1676		pub(crate) fn migrate_keys_single_included_key(
1677			id: PersonalId,
1678			new_key: MemberOf<T>,
1679		) -> DispatchResult {
1680			if let Some(record) = People::<T>::get(id) {
1681				let RingPosition::Included {
1682					ring_index,
1683					ring_position,
1684					scheduled_for_removal: true,
1685				} = record.position
1686				else {
1687					Keys::<T>::remove(new_key);
1688					return Ok(())
1689				};
1690				let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1691				let Err(insert_idx) = suspended_indices.binary_search(&ring_position) else {
1692					log::info!(target: LOG_TARGET, "key migration for person {} skipped as the person's key was already suspended", id);
1693					return Ok(());
1694				};
1695				suspended_indices
1696					.try_insert(insert_idx, ring_position)
1697					.map_err(|_| Error::<T>::TooManyMembers)?;
1698				PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1699				Keys::<T>::remove(&record.key);
1700				Self::push_to_onboarding_queue(id, new_key, record.account)?;
1701			} else {
1702				log::info!(target: LOG_TARGET, "key migration for person {} skipped as no record was found", id);
1703			}
1704			Ok(())
1705		}
1706
1707		/// Removes people's keys marked as suspended or inactive from a ring with a given index.
1708		pub(crate) fn remove_suspended_keys(ring_index: RingIndex) -> Weight {
1709			let keys = RingKeys::<T>::get(ring_index);
1710			let keys_len = keys.len();
1711			let suspended_indices = PendingSuspensions::<T>::get(ring_index);
1712			// Construct the new keys map by skipping the suspended keys. This should prevent
1713			// reallocations in the `Vec` which happens with `remove`.
1714			let mut new_keys: BoundedVec<MemberOf<T>, T::MaxRingSize> = Default::default();
1715			let mut j = 0;
1716			for (i, key) in keys.into_iter().enumerate() {
1717				if j < suspended_indices.len() && i == suspended_indices[j] as usize {
1718					j += 1;
1719				} else if new_keys
1720					.try_push(key)
1721					.defensive_proof("cannot move more ring members than the max ring size; qed")
1722					.is_err()
1723				{
1724					return T::WeightInfo::remove_suspended_people(
1725						keys_len.try_into().unwrap_or(u32::MAX),
1726					);
1727				}
1728			}
1729
1730			let suspended_count = RingKeysStatus::<T>::mutate(ring_index, |ring_status| {
1731				let new_total = new_keys.len().saturated_into();
1732				let suspended_count = ring_status.total.saturating_sub(new_total);
1733				ring_status.total = new_total;
1734				ring_status.included = 0;
1735				suspended_count
1736			});
1737			ActiveMembers::<T>::mutate(|active| *active = active.saturating_sub(suspended_count));
1738			RingKeys::<T>::insert(ring_index, new_keys);
1739			Root::<T>::mutate(ring_index, |maybe_root| {
1740				if let Some(root) = maybe_root {
1741					// The revision will be incremented on the next call of `build_ring`. The
1742					// current root is preserved.
1743					root.intermediate = T::Crypto::start_members();
1744				}
1745			});
1746
1747			// Make sure to remove the entry from the map so that the pallet hooks don't iterate
1748			// over it.
1749			PendingSuspensions::<T>::remove(ring_index);
1750			T::WeightInfo::remove_suspended_people(keys_len.try_into().unwrap_or(u32::MAX))
1751		}
1752
1753		/// Merges the two pages at the front of the onboarding queue. After a round of suspensions,
1754		/// it is possible for the second page of the onboarding queue to be left with few members
1755		/// such that, if the first page also has few members, the total count is below the required
1756		/// onboarding size, thus stalling the queue. This function fixes this by moving the people
1757		/// from the first page to the front of the second page, defragmenting the queue.
1758		///
1759		/// If the operation fails, the storage is rolled back.
1760		pub(crate) fn merge_queue_pages(
1761			initial_head: u32,
1762			new_head: u32,
1763			mut first_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1764			second_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1765		) {
1766			let op_res = with_storage_layer::<(), DispatchError, _>(|| {
1767				// Update the records of the people in the first page.
1768				for key in first_key_page.iter() {
1769					let personal_id =
1770						Keys::<T>::get(key).defensive().ok_or(Error::<T>::NotPerson)?;
1771					let mut record =
1772						People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1773					record.position = RingPosition::Onboarding { queue_page: new_head };
1774					People::<T>::insert(personal_id, record);
1775				}
1776
1777				first_key_page
1778					.try_extend(second_key_page.into_iter())
1779					.defensive()
1780					.map_err(|_| Error::<T>::TooManyMembers)?;
1781				OnboardingQueue::<T>::remove(initial_head);
1782				OnboardingQueue::<T>::insert(new_head, first_key_page);
1783				QueuePageIndices::<T>::mutate(|(h, _)| *h = new_head);
1784				Ok(())
1785			});
1786			if let Err(e) = op_res {
1787				log::error!(target: LOG_TARGET, "failed to merge queue pages: {:?}", e);
1788			}
1789		}
1790	}
1791
1792	impl<T: Config> AddOnlyPeopleTrait for Pallet<T> {
1793		type Member = MemberOf<T>;
1794
1795		fn reserve_new_id() -> PersonalId {
1796			let new_id = NextPersonalId::<T>::mutate(|id| {
1797				let new_id = *id;
1798				id.saturating_inc();
1799				new_id
1800			});
1801			ReservedPersonalId::<T>::insert(new_id, ());
1802			new_id
1803		}
1804
1805		fn cancel_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1806			ReservedPersonalId::<T>::take(personal_id).ok_or(Error::<T>::PersonalIdNotReserved)?;
1807			Ok(())
1808		}
1809
1810		fn renew_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1811			if NextPersonalId::<T>::get() <= personal_id ||
1812				People::<T>::contains_key(personal_id) ||
1813				ReservedPersonalId::<T>::contains_key(personal_id)
1814			{
1815				return Err(Error::<T>::PersonalIdReservationCannotRenew.into());
1816			}
1817			ReservedPersonalId::<T>::insert(personal_id, ());
1818			Ok(())
1819		}
1820
1821		fn recognize_personhood(
1822			who: PersonalId,
1823			maybe_key: Option<MemberOf<T>>,
1824		) -> Result<(), DispatchError> {
1825			match maybe_key {
1826				Some(key) => Self::do_insert_key(who, key),
1827				None => Self::resume_personhood(who),
1828			}
1829		}
1830
1831		#[cfg(feature = "runtime-benchmarks")]
1832		type Secret = <<T as Config>::Crypto as GenerateVerifiable>::Secret;
1833
1834		#[cfg(feature = "runtime-benchmarks")]
1835		fn mock_key(who: PersonalId) -> (Self::Member, Self::Secret) {
1836			let mut buf = [0u8; 32];
1837			buf[..core::mem::size_of::<PersonalId>()].copy_from_slice(&who.to_le_bytes()[..]);
1838			let secret = T::Crypto::new_secret(buf);
1839			(T::Crypto::member_from_secret(&secret), secret)
1840		}
1841	}
1842
1843	impl<T: Config> PeopleTrait for Pallet<T> {
1844		fn suspend_personhood(suspensions: &[PersonalId]) -> DispatchResult {
1845			Self::queue_personhood_suspensions(suspensions)
1846		}
1847		fn start_people_set_mutation_session() -> DispatchResult {
1848			let current_state = RingsState::<T>::get();
1849			RingsState::<T>::put(
1850				current_state
1851					.start_mutation_session()
1852					.map_err(|_| Error::<T>::CouldNotStartMutationSession)?,
1853			);
1854			Ok(())
1855		}
1856		fn end_people_set_mutation_session() -> DispatchResult {
1857			let current_state = RingsState::<T>::get();
1858			RingsState::<T>::put(
1859				current_state
1860					.end_mutation_session()
1861					.map_err(|_| Error::<T>::NoMutationSession)?,
1862			);
1863			Ok(())
1864		}
1865	}
1866
1867	/// Ensure that the origin `o` represents an extrinsic (i.e. transaction) from a personal
1868	/// identity. Returns `Ok` with the personal identity that signed the extrinsic or an `Err`
1869	/// otherwise.
1870	pub fn ensure_personal_identity<OuterOrigin>(o: OuterOrigin) -> Result<PersonalId, BadOrigin>
1871	where
1872		OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1873	{
1874		match o.try_into() {
1875			Ok(Origin::PersonalIdentity(m)) => Ok(m),
1876			_ => Err(BadOrigin),
1877		}
1878	}
1879
1880	/// Ensure that the origin `o` represents an extrinsic (i.e. transaction) from a personal alias.
1881	/// Returns `Ok` with the personal alias that signed the extrinsic or an `Err` otherwise.
1882	pub fn ensure_personal_alias<OuterOrigin>(o: OuterOrigin) -> Result<ContextualAlias, BadOrigin>
1883	where
1884		OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1885	{
1886		match o.try_into() {
1887			Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca.ca),
1888			_ => Err(BadOrigin),
1889		}
1890	}
1891
1892	/// Guard to ensure that the given origin is a person. The underlying identity of the person is
1893	/// provided on success.
1894	pub struct EnsurePersonalIdentity<T>(PhantomData<T>);
1895	impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalIdentity<T> {
1896		type Success = PersonalId;
1897
1898		fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1899			ensure_personal_identity(o.clone().into_caller()).map_err(|_| o)
1900		}
1901
1902		#[cfg(feature = "runtime-benchmarks")]
1903		fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1904			Ok(Origin::PersonalIdentity(0).into())
1905		}
1906	}
1907
1908	frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1909		impl<{ T: Config, A }>
1910			EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalIdentity<T>
1911		{}
1912	}
1913
1914	impl<T: Config> CountedMembers for EnsurePersonalIdentity<T> {
1915		fn active_count(&self) -> u32 {
1916			Keys::<T>::count()
1917		}
1918	}
1919
1920	/// Guard to ensure that the given origin is a person. The contextual alias of the person is
1921	/// provided on success.
1922	pub struct EnsurePersonalAlias<T>(PhantomData<T>);
1923	impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalAlias<T> {
1924		type Success = ContextualAlias;
1925
1926		fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1927			ensure_personal_alias(o.clone().into_caller()).map_err(|_| o)
1928		}
1929
1930		#[cfg(feature = "runtime-benchmarks")]
1931		fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1932			Ok(Origin::PersonalAlias(RevisedContextualAlias {
1933				revision: 0,
1934				ring: 0,
1935				ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
1936			})
1937			.into())
1938		}
1939	}
1940
1941	frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1942		impl<{ T: Config, A }>
1943			EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalAlias<T>
1944		{}
1945	}
1946
1947	impl<T: Config> CountedMembers for EnsurePersonalAlias<T> {
1948		fn active_count(&self) -> u32 {
1949			ActiveMembers::<T>::get()
1950		}
1951	}
1952
1953	/// Guard to ensure that the given origin is a person. The alias of the person within the
1954	/// context provided as an argument is returned on success.
1955	pub struct EnsurePersonalAliasInContext<T>(PhantomData<T>);
1956	impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context> for EnsurePersonalAliasInContext<T> {
1957		type Success = Alias;
1958
1959		fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
1960			match ensure_personal_alias(o.clone().into_caller()) {
1961				Ok(ca) if &ca.context == arg => Ok(ca.alias),
1962				_ => Err(o),
1963			}
1964		}
1965
1966		#[cfg(feature = "runtime-benchmarks")]
1967		fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
1968			Ok(Origin::PersonalAlias(RevisedContextualAlias {
1969				revision: 0,
1970				ring: 0,
1971				ca: ContextualAlias { alias: [1; 32], context: *context },
1972			})
1973			.into())
1974		}
1975	}
1976
1977	impl<T: Config> CountedMembers for EnsurePersonalAliasInContext<T> {
1978		fn active_count(&self) -> u32 {
1979			ActiveMembers::<T>::get()
1980		}
1981	}
1982
1983	/// Ensure that the origin `o` represents an extrinsic (i.e. transaction) from a personal alias
1984	/// with revision information.
1985	///
1986	/// Returns `Ok` with the revised personal alias that signed the extrinsic or an `Err`
1987	/// otherwise.
1988	pub fn ensure_revised_personal_alias<OuterOrigin>(
1989		o: OuterOrigin,
1990	) -> Result<RevisedContextualAlias, BadOrigin>
1991	where
1992		OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1993	{
1994		match o.try_into() {
1995			Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca),
1996			_ => Err(BadOrigin),
1997		}
1998	}
1999
2000	/// Guard to ensure that the given origin is a person.
2001	///
2002	/// The revised contextual alias of the person is provided on success. The revision can be used
2003	/// to tell in the future if an alias may have been suspended. See [`RevisedContextualAlias`].
2004	pub struct EnsureRevisedPersonalAlias<T>(PhantomData<T>);
2005	impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsureRevisedPersonalAlias<T> {
2006		type Success = RevisedContextualAlias;
2007
2008		fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
2009			ensure_revised_personal_alias(o.clone().into_caller()).map_err(|_| o)
2010		}
2011
2012		#[cfg(feature = "runtime-benchmarks")]
2013		fn try_successful_origin() -> Result<OriginFor<T>, ()> {
2014			Ok(Origin::PersonalAlias(RevisedContextualAlias {
2015				revision: 0,
2016				ring: 0,
2017				ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
2018			})
2019			.into())
2020		}
2021	}
2022
2023	frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
2024		impl<{ T: Config, A }>
2025			EnsureOriginWithArg< OriginFor<T>, A> for EnsureRevisedPersonalAlias<T>
2026		{}
2027	}
2028
2029	impl<T: Config> CountedMembers for EnsureRevisedPersonalAlias<T> {
2030		fn active_count(&self) -> u32 {
2031			ActiveMembers::<T>::get()
2032		}
2033	}
2034
2035	/// Guard to ensure that the given origin is a person.
2036	///
2037	/// The revised alias of the person within the context provided as an argument is returned on
2038	/// success. The revision can be used to tell in the future if an alias may have been suspended.
2039	/// See [`RevisedAlias`].
2040	pub struct EnsureRevisedPersonalAliasInContext<T>(PhantomData<T>);
2041	impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context>
2042		for EnsureRevisedPersonalAliasInContext<T>
2043	{
2044		type Success = RevisedAlias;
2045
2046		fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
2047			match ensure_revised_personal_alias(o.clone().into_caller()) {
2048				Ok(ca) if &ca.ca.context == arg =>
2049					Ok(RevisedAlias { revision: ca.revision, ring: ca.ring, alias: ca.ca.alias }),
2050				_ => Err(o),
2051			}
2052		}
2053
2054		#[cfg(feature = "runtime-benchmarks")]
2055		fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
2056			Ok(Origin::PersonalAlias(RevisedContextualAlias {
2057				revision: 0,
2058				ring: 0,
2059				ca: ContextualAlias { alias: [1; 32], context: *context },
2060			})
2061			.into())
2062		}
2063	}
2064
2065	impl<T: Config> CountedMembers for EnsureRevisedPersonalAliasInContext<T> {
2066		fn active_count(&self) -> u32 {
2067			ActiveMembers::<T>::get()
2068		}
2069	}
2070}