1#![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, Debug, 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 type WeightInfo: WeightInfo;
200
201 #[allow(deprecated)]
203 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
204
205 type Crypto: GenerateVerifiable<
208 Proof: Send + Sync + DecodeWithMemTracking,
209 Signature: Send + Sync + DecodeWithMemTracking,
210 Member: DecodeWithMemTracking,
211 >;
212
213 type AccountContexts: Contains<Context>;
215
216 #[pallet::constant]
218 type ChunkPageSize: Get<u32>;
219
220 #[pallet::constant]
222 type MaxRingSize: Get<u32>;
223
224 #[pallet::constant]
227 type OnboardingQueuePageSize: Get<u32>;
228
229 #[cfg(feature = "runtime-benchmarks")]
231 type BenchmarkHelper: BenchmarkHelper<<Self::Crypto as GenerateVerifiable>::StaticChunk>;
232 }
233
234 #[pallet::storage]
236 pub type Root<T> = StorageMap<_, Blake2_128Concat, RingIndex, RingRoot<T>>;
237
238 #[pallet::storage]
240 pub type CurrentRingIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
241
242 #[pallet::storage]
244 pub type OnboardingSize<T: Config> = StorageValue<_, u32, ValueQuery>;
245
246 #[pallet::storage]
249 pub type RingBuildingPeopleLimit<T: Config> = StorageValue<_, u32, OptionQuery>;
250
251 #[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 #[pallet::storage]
265 pub type RingKeysStatus<T: Config> =
266 StorageMap<_, Blake2_128Concat, RingIndex, RingStatus, ValueQuery>;
267
268 #[pallet::storage]
271 pub type PendingSuspensions<T: Config> =
272 StorageMap<_, Twox64Concat, RingIndex, BoundedVec<u32, T::MaxRingSize>, ValueQuery>;
273
274 #[pallet::storage]
276 pub type ActiveMembers<T: Config> = StorageValue<_, u32, ValueQuery>;
277
278 #[pallet::storage]
284 pub type Keys<T> = CountedStorageMap<_, Blake2_128Concat, MemberOf<T>, PersonalId>;
285
286 #[pallet::storage]
289 pub type KeyMigrationQueue<T: Config> =
290 StorageMap<_, Blake2_128Concat, PersonalId, MemberOf<T>>;
291
292 #[pallet::storage]
296 pub type People<T: Config> =
297 StorageMap<_, Blake2_128Concat, PersonalId, PersonRecord<MemberOf<T>, T::AccountId>>;
298
299 #[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 #[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 #[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 #[pallet::storage]
334 pub type Chunks<T> = StorageMap<_, Twox64Concat, PageIndex, ChunksOf<T>, OptionQuery>;
335
336 #[pallet::storage]
338 pub type NextPersonalId<T> = StorageValue<_, PersonalId, ValueQuery>;
339
340 #[pallet::storage]
343 pub type RingsState<T> = StorageValue<_, RingMembersState, ValueQuery>;
344
345 #[pallet::storage]
347 pub type ReservedPersonalId<T: Config> =
348 StorageMap<_, Twox64Concat, PersonalId, (), OptionQuery>;
349
350 #[pallet::storage]
352 pub type QueuePageIndices<T: Config> = StorageValue<_, (PageIndex, PageIndex), ValueQuery>;
353
354 #[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 PersonhoodRecognized { who: PersonalId, key: MemberOf<T> },
369 PersonOnboarding { who: PersonalId, key: MemberOf<T> },
371 }
372
373 #[pallet::extra_constants]
374 impl<T: Config> Pallet<T> {
375 pub fn account_setup_time_tolerance() -> BlockNumberFor<T> {
381 600u32.into()
382 }
383 }
384
385 #[pallet::error]
386 pub enum Error<T> {
387 NotPerson,
389 NoKey,
391 InvalidContext,
393 InvalidAccount,
395 AccountInUse,
397 InvalidProof,
399 InvalidSignature,
401 NoMembers,
403 Incomplete,
405 StillFresh,
407 TooManyMembers,
409 KeyAlreadyInUse,
411 KeyNotFound,
413 CouldNotPush,
415 SameKey,
417 PersonalIdNotReserved,
419 PersonalIdReservationCannotRenew,
421 PersonalIdNotReservedOrNotRecognized,
423 InvalidRing,
425 SuspensionsPending,
427 RingAboveMergeThreshold,
429 InvalidSuspensions,
431 NoMutationSession,
433 CouldNotStartMutationSession,
435 SuspensionSessionInProgress,
437 TimeOutOfRange,
439 AliasAccountAlreadySet,
441 NotSuspended,
443 Suspended,
445 InvalidKeyMigration,
447 KeyAlreadySuspended,
450 InvalidOnboardingSize,
452 }
453
454 #[pallet::origin]
455 #[derive(
456 Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking,
457 )]
458 pub enum Origin {
459 PersonalIdentity(PersonalId),
460 PersonalAlias(RevisedContextualAlias),
461 }
462
463 #[pallet::hooks]
464 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
465 fn integrity_test() {
466 assert!(
467 <T as Config>::ChunkPageSize::get() > 0,
468 "chunk page size must hold at least one element"
469 );
470 assert!(<T as Config>::MaxRingSize::get() > 0, "rings must hold at least one person");
471 assert!(
472 <T as Config>::MaxRingSize::get() <= <T as Config>::OnboardingQueuePageSize::get(),
473 "onboarding queue page size must greater than or equal to max ring size"
474 );
475 }
476
477 fn on_poll(_: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
478 if weight_meter.try_consume(T::WeightInfo::on_poll_base()).is_err() {
480 return;
481 }
482 if RingsState::<T>::get().key_migration() {
483 Self::migrate_keys(weight_meter);
484 }
485
486 if let Some(ring_index) = PendingSuspensions::<T>::iter_keys().next() {
488 if Self::should_remove_suspended_keys(ring_index, true) &&
489 weight_meter.can_consume(T::WeightInfo::remove_suspended_people(
490 T::MaxRingSize::get(),
491 )) {
492 let actual = Self::remove_suspended_keys(ring_index);
493 weight_meter.consume(actual)
494 }
495 }
496
497 let merge_weight = T::WeightInfo::merge_queue_pages();
498 if !weight_meter.can_consume(merge_weight) {
499 return;
500 }
501 let merge_action = Self::should_merge_queue_pages();
502 if let QueueMergeAction::Merge {
503 initial_head,
504 new_head,
505 first_key_page,
506 second_key_page,
507 } = merge_action
508 {
509 Self::merge_queue_pages(initial_head, new_head, first_key_page, second_key_page);
510 weight_meter.consume(merge_weight);
511 }
512 }
513
514 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
515 let mut weight_meter = WeightMeter::with_limit(limit.saturating_div(2));
516 let on_idle_weight = T::WeightInfo::on_idle_base();
517 if !weight_meter.can_consume(on_idle_weight) {
518 return weight_meter.consumed();
519 }
520 weight_meter.consume(on_idle_weight);
521
522 let max_ring_size = T::MaxRingSize::get();
523 let remove_people_weight = T::WeightInfo::remove_suspended_people(max_ring_size);
524 let rings_state = RingsState::<T>::get();
525
526 if !rings_state.append_only() {
529 return weight_meter.consumed();
530 }
531 let suspension_step_weight = T::WeightInfo::pending_suspensions_iteration();
533 if !weight_meter.can_consume(suspension_step_weight) {
534 return weight_meter.consumed();
535 }
536 while let Some(ring_index) = PendingSuspensions::<T>::iter_keys().next() {
539 weight_meter.consume(suspension_step_weight);
540 if !weight_meter.can_consume(remove_people_weight) {
542 return weight_meter.consumed();
543 }
544 if Self::should_remove_suspended_keys(ring_index, false) {
545 let actual = Self::remove_suspended_keys(ring_index);
546 weight_meter.consume(actual)
547 }
548 if !weight_meter.can_consume(suspension_step_weight) {
550 return weight_meter.consumed();
551 }
552 }
553
554 let onboard_people_weight = T::WeightInfo::onboard_people();
558 if !weight_meter.can_consume(onboard_people_weight) {
559 return weight_meter.consumed();
560 }
561 let op_res = with_storage_layer::<(), DispatchError, _>(|| Self::onboard_people());
562 weight_meter.consume(onboard_people_weight);
563 if let Err(e) = op_res {
564 log::debug!(target: LOG_TARGET, "failed to onboard people: {:?}", e);
565 }
566
567 let current_ring = CurrentRingIndex::<T>::get();
568 let should_build_ring_weight = T::WeightInfo::should_build_ring(max_ring_size);
569 let build_ring_weight = T::WeightInfo::build_ring(max_ring_size);
570 for ring_index in (0..=current_ring).rev() {
571 if !weight_meter.can_consume(should_build_ring_weight) {
572 return weight_meter.consumed();
573 }
574
575 let maybe_to_include = Self::should_build_ring(ring_index, max_ring_size);
576 weight_meter.consume(should_build_ring_weight);
577 let Some(to_include) = maybe_to_include else { continue };
578 if !weight_meter.can_consume(build_ring_weight) {
579 return weight_meter.consumed();
580 }
581 let op_res = with_storage_layer::<(), DispatchError, _>(|| {
582 Self::build_ring(ring_index, to_include)
583 });
584 weight_meter.consume(build_ring_weight);
585 if let Err(e) = op_res {
586 log::error!(target: LOG_TARGET, "failed to build ring: {:?}", e);
587 }
588 }
589
590 weight_meter.consumed()
591 }
592 }
593
594 #[pallet::genesis_config]
595 pub struct GenesisConfig<T: Config> {
596 pub encoded_chunks: Vec<u8>,
597 #[serde(skip)]
598 pub _phantom_data: core::marker::PhantomData<T>,
599 pub onboarding_size: u32,
600 }
601
602 impl<T: Config> Default for GenesisConfig<T> {
603 fn default() -> Self {
604 use verifiable::ring_vrf_impl::StaticChunk;
608 let params = verifiable::ring_vrf_impl::ring_verifier_builder_params();
609 let chunks: Vec<StaticChunk> = params.0.iter().map(|c| StaticChunk(*c)).collect();
610 Self {
611 encoded_chunks: chunks.encode(),
612 _phantom_data: PhantomData,
613 onboarding_size: T::MaxRingSize::get(),
614 }
615 }
616 }
617
618 #[pallet::genesis_build]
619 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
620 fn build(&self) {
621 let chunks: Vec<<<T as Config>::Crypto as GenerateVerifiable>::StaticChunk> =
622 Decode::decode(&mut &(self.encoded_chunks.clone())[..])
623 .expect("couldn't decode chunks");
624 assert_eq!(chunks.len(), 1 << 9);
625 let page_size = <T as Config>::ChunkPageSize::get();
626
627 let mut page_idx = 0;
628 let mut chunk_idx = 0;
629 while chunk_idx < chunks.len() {
630 let chunk_idx_end = cmp::min(chunk_idx + page_size as usize, chunks.len());
631 let chunk_page: ChunksOf<T> = chunks[chunk_idx..chunk_idx_end]
632 .to_vec()
633 .try_into()
634 .expect("page size was checked against the array length; qed");
635 Chunks::<T>::insert(page_idx, chunk_page);
636 page_idx += 1;
637 chunk_idx = chunk_idx_end;
638 }
639
640 OnboardingSize::<T>::set(self.onboarding_size);
641 }
642 }
643
644 #[pallet::call(weight = <T as Config>::WeightInfo)]
645 impl<T: Config> Pallet<T> {
646 #[pallet::weight(
652 T::WeightInfo::should_build_ring(
653 limit.unwrap_or_else(T::MaxRingSize::get)
654 ).saturating_add(T::WeightInfo::build_ring(limit.unwrap_or_else(T::MaxRingSize::get))))]
655 #[pallet::call_index(100)]
656 pub fn build_ring_manual(
657 origin: OriginFor<T>,
658 ring_index: RingIndex,
659 limit: Option<u32>,
660 ) -> DispatchResultWithPostInfo {
661 ensure_signed(origin)?;
662
663 let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
665 let to_include =
666 Self::should_build_ring(ring_index, limit.unwrap_or_else(T::MaxRingSize::get))
667 .ok_or(Error::<T>::StillFresh)?;
668
669 let (next_revision, mut intermediate) =
672 if let Some(existing_root) = Root::<T>::get(ring_index) {
673 (
675 existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
676 existing_root.intermediate,
677 )
678 } else {
679 (0, T::Crypto::start_members())
681 };
682
683 T::Crypto::push_members(
685 &mut intermediate,
686 keys.iter()
687 .skip(ring_status.included as usize)
688 .take(to_include as usize)
689 .cloned(),
690 Self::fetch_chunks,
691 )
692 .map_err(|_| Error::<T>::CouldNotPush)?;
693
694 ring_status.included = ring_status.included.saturating_add(to_include);
696 RingKeysStatus::<T>::insert(ring_index, ring_status);
697
698 let root = T::Crypto::finish_members(intermediate.clone());
700 let ring_root = RingRoot { root, revision: next_revision, intermediate };
701 Root::<T>::insert(ring_index, ring_root);
702
703 Ok(Pays::No.into())
704 }
705
706 #[pallet::weight(T::WeightInfo::onboard_people())]
714 #[pallet::call_index(101)]
715 pub fn onboard_people_manual(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
716 ensure_signed(origin)?;
717
718 let (top_ring_index, mut keys) = Self::available_ring();
720 let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
721 defensive_assert!(
722 keys.len() == ring_status.total as usize,
723 "Stored key count doesn't match the actual length"
724 );
725
726 let keys_len = keys.len() as u32;
727 let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
728
729 let (mut head, tail) = QueuePageIndices::<T>::get();
730 let old_head = head;
731 let mut keys_to_include: Vec<MemberOf<T>> =
732 OnboardingQueue::<T>::take(head).into_inner();
733
734 if keys_to_include.len() < open_slots as usize && head != tail {
737 head = head.checked_add(1).unwrap_or(0);
738 let second_key_page = OnboardingQueue::<T>::take(head);
739 defensive_assert!(!second_key_page.is_empty());
740 keys_to_include.extend(second_key_page.into_iter());
741 }
742
743 let onboarding_size = OnboardingSize::<T>::get();
744
745 let (to_include, ring_filled) = Self::should_onboard_people(
746 top_ring_index,
747 &ring_status,
748 open_slots,
749 keys_to_include.len().saturated_into(),
750 onboarding_size,
751 )
752 .ok_or(Error::<T>::Incomplete)?;
753
754 let mut remaining_keys = keys_to_include.split_off(to_include as usize);
755 for key in keys_to_include.into_iter() {
756 let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
757 let mut record =
758 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
759 record.position = RingPosition::Included {
760 ring_index: top_ring_index,
761 ring_position: keys.len().saturated_into(),
762 scheduled_for_removal: false,
763 };
764 People::<T>::insert(personal_id, record);
765 keys.try_push(key).map_err(|_| Error::<T>::TooManyMembers)?;
766 }
767 RingKeys::<T>::insert(top_ring_index, keys);
768 ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
769 ring_status.total = ring_status.total.saturating_add(to_include);
770 RingKeysStatus::<T>::insert(top_ring_index, ring_status);
771
772 if ring_filled {
774 CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
775 }
776
777 if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
778 let split_idx =
779 remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
780 let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
781 remaining_keys
782 .split_off(split_idx)
783 .try_into()
784 .expect("the list shrunk so it must fit; qed");
785 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
786 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
787 OnboardingQueue::<T>::insert(old_head, remaining_keys);
788 OnboardingQueue::<T>::insert(head, second_page_keys);
789 QueuePageIndices::<T>::put((old_head, tail));
790 } else if !remaining_keys.is_empty() {
791 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
792 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
793 OnboardingQueue::<T>::insert(head, remaining_keys);
794 QueuePageIndices::<T>::put((head, tail));
795 } else {
796 if head != tail {
799 head = head.checked_add(1).unwrap_or(0);
800 }
801 QueuePageIndices::<T>::put((head, tail));
802 }
803
804 Ok(Pays::No.into())
805 }
806
807 #[pallet::call_index(102)]
811 pub fn merge_rings(
812 origin: OriginFor<T>,
813 base_ring_index: RingIndex,
814 target_ring_index: RingIndex,
815 ) -> DispatchResultWithPostInfo {
816 let _ = ensure_signed(origin)?;
817
818 ensure!(RingsState::<T>::get().append_only(), Error::<T>::SuspensionSessionInProgress);
819 let current_ring_index = CurrentRingIndex::<T>::get();
822 ensure!(
823 base_ring_index != target_ring_index &&
824 base_ring_index != current_ring_index &&
825 target_ring_index != current_ring_index,
826 Error::<T>::InvalidRing
827 );
828
829 let (mut base_keys, mut base_ring_status) = Self::ring_keys_and_info(base_ring_index);
831 ensure!(
832 base_keys.len() < T::MaxRingSize::get() as usize / 2,
833 Error::<T>::RingAboveMergeThreshold
834 );
835 ensure!(
836 PendingSuspensions::<T>::decode_len(base_ring_index).unwrap_or(0) == 0,
837 Error::<T>::SuspensionsPending
838 );
839 let target_keys = RingKeys::<T>::get(target_ring_index);
840 RingKeysStatus::<T>::remove(target_ring_index);
841 ensure!(
842 target_keys.len() < T::MaxRingSize::get() as usize / 2,
843 Error::<T>::RingAboveMergeThreshold
844 );
845 ensure!(
846 PendingSuspensions::<T>::decode_len(target_ring_index).unwrap_or(0) == 0,
847 Error::<T>::SuspensionsPending
848 );
849
850 base_ring_status.total =
852 base_ring_status.total.saturating_add(target_keys.len().saturated_into());
853
854 for key in target_keys {
855 let personal_id =
856 Keys::<T>::get(&key).defensive().ok_or(Error::<T>::KeyNotFound)?;
857 let mut record =
858 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::NotPerson)?;
859 record.position = RingPosition::Included {
860 ring_index: base_ring_index,
861 ring_position: base_keys.len().saturated_into(),
862 scheduled_for_removal: false,
863 };
864 base_keys.try_push(key).map_err(|_| Error::<T>::TooManyMembers)?;
865 People::<T>::insert(personal_id, record)
866 }
867
868 RingKeys::<T>::insert(base_ring_index, base_keys);
870 RingKeysStatus::<T>::insert(base_ring_index, base_ring_status);
871 Root::<T>::remove(target_ring_index);
874 RingKeys::<T>::remove(target_ring_index);
875 RingKeysStatus::<T>::remove(target_ring_index);
876
877 Ok(Pays::No.into())
878 }
879
880 #[pallet::call_index(0)]
885 #[pallet::weight(T::WeightInfo::under_alias().saturating_add(call.get_dispatch_info().call_weight))]
886 pub fn under_alias(
887 origin: OriginFor<T>,
888 call: Box<<T as frame_system::Config>::RuntimeCall>,
889 ) -> DispatchResultWithPostInfo {
890 let account = ensure_signed(origin.clone())?;
891 let rev_ca = AccountToAlias::<T>::get(&account).ok_or(Error::<T>::InvalidAccount)?;
892 ensure!(
893 Root::<T>::get(rev_ca.ring).is_some_and(|ring| ring.revision == rev_ca.revision),
894 DispatchError::BadOrigin,
895 );
896
897 let derivation_weight = T::WeightInfo::under_alias();
898 let local_origin = Origin::PersonalAlias(rev_ca);
899 Self::derivative_call(origin, local_origin, *call, derivation_weight)
900 }
901
902 #[pallet::call_index(1)]
912 pub fn set_alias_account(
913 origin: OriginFor<T>,
914 account: T::AccountId,
915 call_valid_at: BlockNumberFor<T>,
916 ) -> DispatchResultWithPostInfo {
917 let rev_ca = Self::ensure_revised_personal_alias(origin)?;
918 let now = frame_system::Pallet::<T>::block_number();
919 let time_tolerance = Self::account_setup_time_tolerance();
920 ensure!(
921 call_valid_at <= now && now <= call_valid_at.saturating_add(time_tolerance),
922 Error::<T>::TimeOutOfRange
923 );
924 ensure!(T::AccountContexts::contains(&rev_ca.ca.context), Error::<T>::InvalidContext);
925 ensure!(!AccountToPersonalId::<T>::contains_key(&account), Error::<T>::AccountInUse);
926
927 let old_account = AliasToAccount::<T>::get(&rev_ca.ca);
928 let old_rev_ca = old_account.as_ref().and_then(AccountToAlias::<T>::get);
929
930 let needs_revision = old_rev_ca.is_some_and(|old_rev_ca| {
931 old_rev_ca.revision != rev_ca.revision || old_rev_ca.ring != rev_ca.ring
932 });
933
934 ensure!(
936 old_account.as_ref() != Some(&account) || needs_revision,
937 Error::<T>::AliasAccountAlreadySet
938 );
939
940 if old_account.as_ref() != Some(&account) {
945 ensure!(!AccountToAlias::<T>::contains_key(&account), Error::<T>::AccountInUse);
946 if let Some(old_account) = &old_account {
947 frame_system::Pallet::<T>::dec_sufficients(old_account);
948 AccountToAlias::<T>::remove(old_account);
949 }
950 frame_system::Pallet::<T>::inc_sufficients(&account);
951 }
952
953 AccountToAlias::<T>::insert(&account, &rev_ca);
954 AliasToAccount::<T>::insert(&rev_ca.ca, &account);
955
956 if old_account.is_none() || needs_revision {
957 Ok(Pays::No.into())
958 } else {
959 Ok(Pays::Yes.into())
960 }
961 }
962
963 #[pallet::call_index(2)]
965 pub fn unset_alias_account(origin: OriginFor<T>) -> DispatchResult {
966 let alias = Self::ensure_personal_alias(origin)?;
967 let account = AliasToAccount::<T>::take(&alias).ok_or(Error::<T>::InvalidAccount)?;
968 AccountToAlias::<T>::remove(&account);
969 frame_system::Pallet::<T>::dec_sufficients(&account);
970
971 Ok(())
972 }
973
974 #[pallet::call_index(3)]
981 pub fn force_recognize_personhood(
982 origin: OriginFor<T>,
983 people: Vec<MemberOf<T>>,
984 ) -> DispatchResultWithPostInfo {
985 ensure_root(origin)?;
986 for key in people {
987 let personal_id = Self::reserve_new_id();
988 Self::recognize_personhood(personal_id, Some(key))?;
989 }
990 Ok(().into())
991 }
992
993 #[pallet::call_index(4)]
1009 pub fn set_personal_id_account(
1010 origin: OriginFor<T>,
1011 account: T::AccountId,
1012 call_valid_at: BlockNumberFor<T>,
1013 ) -> DispatchResultWithPostInfo {
1014 let id = Self::ensure_personal_identity(origin)?;
1015 let now = frame_system::Pallet::<T>::block_number();
1016 let time_tolerance = Self::account_setup_time_tolerance();
1017 ensure!(
1018 call_valid_at <= now && now <= call_valid_at.saturating_add(time_tolerance),
1019 Error::<T>::TimeOutOfRange
1020 );
1021 ensure!(!AccountToPersonalId::<T>::contains_key(&account), Error::<T>::AccountInUse);
1022 ensure!(!AccountToAlias::<T>::contains_key(&account), Error::<T>::AccountInUse);
1023 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1024 let pays = if let Some(old_account) = record.account {
1025 frame_system::Pallet::<T>::dec_sufficients(&old_account);
1026 AccountToPersonalId::<T>::remove(&old_account);
1027 Pays::Yes
1028 } else {
1029 Pays::No
1030 };
1031 record.account = Some(account.clone());
1032 frame_system::Pallet::<T>::inc_sufficients(&account);
1033 AccountToPersonalId::<T>::insert(&account, id);
1034 People::<T>::insert(id, &record);
1035
1036 Ok(pays.into())
1037 }
1038
1039 #[pallet::call_index(5)]
1041 pub fn unset_personal_id_account(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
1042 let id = Self::ensure_personal_identity(origin)?;
1043 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1044 let account = record.account.take().ok_or(Error::<T>::InvalidAccount)?;
1045 AccountToPersonalId::<T>::take(&account).ok_or(Error::<T>::InvalidAccount)?;
1046 frame_system::Pallet::<T>::dec_sufficients(&account);
1047 People::<T>::insert(id, &record);
1048
1049 Ok(Pays::Yes.into())
1050 }
1051
1052 #[pallet::call_index(6)]
1056 pub fn migrate_included_key(
1057 origin: OriginFor<T>,
1058 new_key: MemberOf<T>,
1059 ) -> DispatchResultWithPostInfo {
1060 let id = Self::ensure_personal_identity(origin)?;
1061 ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1062 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1063 ensure!(record.key != new_key, Error::<T>::SameKey);
1064 match &record.position {
1065 RingPosition::Included { ring_index, ring_position, .. } => {
1068 if let Some(old_migrated_key) = KeyMigrationQueue::<T>::get(id) {
1071 Keys::<T>::remove(old_migrated_key);
1072 }
1073 KeyMigrationQueue::<T>::insert(id, &new_key);
1075 record.position = RingPosition::Included {
1077 ring_index: *ring_index,
1078 ring_position: *ring_position,
1079 scheduled_for_removal: true,
1080 };
1081 People::<T>::insert(id, record);
1083 },
1084 RingPosition::Onboarding { .. } => {
1086 return Err(Error::<T>::InvalidKeyMigration.into())
1087 },
1088 RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1091 }
1092 Keys::<T>::insert(new_key, id);
1093
1094 Ok(().into())
1095 }
1096
1097 #[pallet::call_index(7)]
1100 pub fn migrate_onboarding_key(
1101 origin: OriginFor<T>,
1102 new_key: MemberOf<T>,
1103 ) -> DispatchResultWithPostInfo {
1104 let id = Self::ensure_personal_identity(origin)?;
1105 ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1106 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1107 ensure!(record.key != new_key, Error::<T>::SameKey);
1108 match &record.position {
1109 RingPosition::Onboarding { queue_page } => {
1111 let mut keys = OnboardingQueue::<T>::get(queue_page);
1112 if let Some(idx) = keys.iter().position(|k| *k == record.key) {
1113 Keys::<T>::remove(&keys[idx]);
1115 keys[idx] = new_key.clone();
1117 OnboardingQueue::<T>::insert(queue_page, keys);
1118 record.key = new_key.clone();
1120 People::<T>::insert(id, record);
1122 } else {
1123 defensive!("No key found at the position in the person record of {}", id);
1124 }
1125 },
1126 RingPosition::Included { .. } => return Err(Error::<T>::InvalidKeyMigration.into()),
1128 RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1131 }
1132 Keys::<T>::insert(new_key, id);
1133
1134 Ok(().into())
1135 }
1136
1137 #[pallet::call_index(8)]
1139 pub fn set_onboarding_size(
1140 origin: OriginFor<T>,
1141 onboarding_size: u32,
1142 ) -> DispatchResultWithPostInfo {
1143 ensure_root(origin)?;
1144 ensure!(
1145 onboarding_size <= <T as Config>::MaxRingSize::get(),
1146 Error::<T>::InvalidOnboardingSize
1147 );
1148 OnboardingSize::<T>::put(onboarding_size);
1149 Ok(Pays::No.into())
1150 }
1151 }
1152
1153 impl<T: Config> Pallet<T> {
1154 pub(crate) fn should_build_ring(ring_index: RingIndex, limit: u32) -> Option<u32> {
1157 if !RingsState::<T>::get().append_only() {
1159 return None;
1160 }
1161 if PendingSuspensions::<T>::contains_key(ring_index) {
1163 return None;
1164 }
1165
1166 let ring_status = RingKeysStatus::<T>::get(ring_index);
1167 let not_included_count = ring_status.total.saturating_sub(ring_status.included);
1168 let to_include = not_included_count.min(limit);
1169 if to_include == 0 {
1171 return None;
1172 }
1173
1174 Some(to_include)
1175 }
1176
1177 fn should_onboard_people(
1182 ring_index: RingIndex,
1183 ring_status: &RingStatus,
1184 open_slots: u32,
1185 available_for_inclusion: u32,
1186 onboarding_size: u32,
1187 ) -> Option<(u32, bool)> {
1188 if !RingsState::<T>::get().append_only() {
1190 return None;
1191 }
1192
1193 if PendingSuspensions::<T>::contains_key(ring_index) {
1195 return None;
1196 }
1197
1198 let to_include = available_for_inclusion.min(open_slots);
1199 if to_include == 0 {
1201 return None;
1202 }
1203
1204 let can_onboard_with_cohort = to_include >= onboarding_size &&
1208 ring_status.total.saturating_add(to_include.saturated_into()) <=
1209 T::MaxRingSize::get().saturating_sub(onboarding_size);
1210 let ring_filled = open_slots == to_include;
1213
1214 let should_onboard = ring_filled || can_onboard_with_cohort;
1215 if !should_onboard {
1216 return None;
1217 }
1218
1219 Some((to_include, ring_filled))
1220 }
1221
1222 pub(crate) fn should_remove_suspended_keys(
1224 ring_index: RingIndex,
1225 check_rings_state: bool,
1226 ) -> bool {
1227 if check_rings_state && !RingsState::<T>::get().append_only() {
1228 return false;
1229 }
1230 let suspended_count = PendingSuspensions::<T>::decode_len(ring_index).unwrap_or(0);
1231 if suspended_count == 0 {
1233 return false;
1234 }
1235
1236 true
1237 }
1238
1239 pub(crate) fn should_merge_queue_pages() -> QueueMergeAction<T> {
1248 let (initial_head, tail) = QueuePageIndices::<T>::get();
1249 let first_key_page = OnboardingQueue::<T>::get(initial_head);
1250 if initial_head == tail {
1253 return QueueMergeAction::NoAction;
1254 }
1255 let new_head = initial_head.checked_add(1).unwrap_or(0);
1256 let second_key_page = OnboardingQueue::<T>::get(new_head);
1257
1258 let page_size = T::OnboardingQueuePageSize::get();
1259 if first_key_page.len().saturating_add(second_key_page.len()) > page_size as usize {
1261 return QueueMergeAction::NoAction;
1262 }
1263
1264 QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page }
1265 }
1266
1267 pub(crate) fn build_ring(ring_index: RingIndex, to_include: u32) -> DispatchResult {
1270 let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
1271 let (next_revision, mut intermediate) =
1274 if let Some(existing_root) = Root::<T>::get(ring_index) {
1275 (
1277 existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
1278 existing_root.intermediate,
1279 )
1280 } else {
1281 (0, T::Crypto::start_members())
1283 };
1284
1285 T::Crypto::push_members(
1287 &mut intermediate,
1288 keys.iter()
1289 .skip(ring_status.included as usize)
1290 .take(to_include as usize)
1291 .cloned(),
1292 Self::fetch_chunks,
1293 )
1294 .defensive()
1295 .map_err(|_| Error::<T>::CouldNotPush)?;
1296
1297 ring_status.included = ring_status.included.saturating_add(to_include);
1299 RingKeysStatus::<T>::insert(ring_index, ring_status);
1300
1301 let root = T::Crypto::finish_members(intermediate.clone());
1303 let ring_root = RingRoot { root, revision: next_revision, intermediate };
1304 Root::<T>::insert(ring_index, ring_root);
1305 Ok(())
1306 }
1307
1308 #[transactional]
1314 pub(crate) fn onboard_people() -> DispatchResult {
1315 let (top_ring_index, mut keys) = Self::available_ring();
1317 let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
1318 defensive_assert!(
1319 keys.len() == ring_status.total as usize,
1320 "Stored key count doesn't match the actual length"
1321 );
1322
1323 let keys_len = keys.len() as u32;
1324 let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
1325
1326 let (mut head, tail) = QueuePageIndices::<T>::get();
1327 let old_head = head;
1328 let mut keys_to_include: Vec<MemberOf<T>> =
1329 OnboardingQueue::<T>::take(head).into_inner();
1330
1331 if keys_to_include.len() < open_slots as usize && head != tail {
1334 head = head.checked_add(1).unwrap_or(0);
1335 let second_key_page = OnboardingQueue::<T>::take(head);
1336 defensive_assert!(!second_key_page.is_empty());
1337 keys_to_include.extend(second_key_page.into_iter());
1338 }
1339
1340 let onboarding_size = OnboardingSize::<T>::get();
1341
1342 let (to_include, ring_filled) = Self::should_onboard_people(
1343 top_ring_index,
1344 &ring_status,
1345 open_slots,
1346 keys_to_include.len().saturated_into(),
1347 onboarding_size,
1348 )
1349 .ok_or(Error::<T>::Incomplete)?;
1350
1351 let mut remaining_keys = keys_to_include.split_off(to_include as usize);
1352 for key in keys_to_include.into_iter() {
1353 let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
1354 let mut record =
1355 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1356 record.position = RingPosition::Included {
1357 ring_index: top_ring_index,
1358 ring_position: keys.len().saturated_into(),
1359 scheduled_for_removal: false,
1360 };
1361 People::<T>::insert(personal_id, record);
1362 keys.try_push(key).defensive().map_err(|_| Error::<T>::TooManyMembers)?;
1363 }
1364 RingKeys::<T>::insert(top_ring_index, keys);
1365 ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
1366 ring_status.total = ring_status.total.saturating_add(to_include);
1367 RingKeysStatus::<T>::insert(top_ring_index, ring_status);
1368
1369 if ring_filled {
1371 CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
1372 }
1373
1374 if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
1375 let split_idx =
1376 remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
1377 let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1378 remaining_keys
1379 .split_off(split_idx)
1380 .try_into()
1381 .expect("the list shrunk so it must fit; qed");
1382 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1383 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1384 OnboardingQueue::<T>::insert(old_head, remaining_keys);
1385 OnboardingQueue::<T>::insert(head, second_page_keys);
1386 QueuePageIndices::<T>::put((old_head, tail));
1387 } else if !remaining_keys.is_empty() {
1388 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1389 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1390 OnboardingQueue::<T>::insert(head, remaining_keys);
1391 QueuePageIndices::<T>::put((head, tail));
1392 } else {
1393 if head != tail {
1396 head = head.checked_add(1).unwrap_or(0);
1397 }
1398 QueuePageIndices::<T>::put((head, tail));
1399 }
1400 Ok(())
1401 }
1402
1403 fn derivative_call(
1404 mut origin: OriginFor<T>,
1405 local_origin: Origin,
1406 call: <T as frame_system::Config>::RuntimeCall,
1407 derivation_weight: Weight,
1408 ) -> DispatchResultWithPostInfo {
1409 origin.set_caller_from(<T::RuntimeOrigin as OriginTrait>::PalletsOrigin::from(
1410 local_origin,
1411 ));
1412 let info = call.get_dispatch_info();
1413 let result = call.dispatch(origin);
1414 let weight = derivation_weight.saturating_add(extract_actual_weight(&result, &info));
1415 result
1416 .map(|p| PostDispatchInfo { actual_weight: Some(weight), pays_fee: p.pays_fee })
1417 .map_err(|mut err| {
1418 err.post_info = Some(weight).into();
1419 err
1420 })
1421 }
1422
1423 pub fn ensure_personal_identity(
1426 origin: T::RuntimeOrigin,
1427 ) -> Result<PersonalId, DispatchError> {
1428 Ok(ensure_personal_identity(origin.into_caller())?)
1429 }
1430
1431 pub fn ensure_personal_alias(
1435 origin: T::RuntimeOrigin,
1436 ) -> Result<ContextualAlias, DispatchError> {
1437 Ok(ensure_personal_alias(origin.into_caller())?)
1438 }
1439
1440 pub fn ensure_revised_personal_alias(
1444 origin: T::RuntimeOrigin,
1445 ) -> Result<RevisedContextualAlias, DispatchError> {
1446 Ok(ensure_revised_personal_alias(origin.into_caller())?)
1447 }
1448
1449 pub fn available_ring() -> (RingIndex, BoundedVec<MemberOf<T>, T::MaxRingSize>) {
1452 let mut current_ring_index = CurrentRingIndex::<T>::get();
1453 let mut current_keys = RingKeys::<T>::get(current_ring_index);
1454
1455 defensive_assert!(
1456 !current_keys.is_full(),
1457 "Something bad happened inside the STF, where the current keys are full, but we should have incremented in that case."
1458 );
1459
1460 if current_keys.is_full() {
1462 current_ring_index.saturating_inc();
1463 CurrentRingIndex::<T>::put(current_ring_index);
1464 current_keys = RingKeys::<T>::get(current_ring_index);
1465 }
1466
1467 defensive_assert!(
1468 !current_keys.is_full(),
1469 "Something bad happened inside the STF, where the current key and next key are both full. Nothing we can do here."
1470 );
1471
1472 (current_ring_index, current_keys)
1473 }
1474
1475 pub fn do_insert_key(who: PersonalId, key: MemberOf<T>) -> DispatchResult {
1477 ensure!(!Keys::<T>::contains_key(&key), Error::<T>::KeyAlreadyInUse);
1479 ensure!(
1481 ReservedPersonalId::<T>::take(who).is_some(),
1482 Error::<T>::PersonalIdNotReservedOrNotRecognized
1483 );
1484
1485 Self::push_to_onboarding_queue(who, key, None)
1486 }
1487
1488 pub fn queue_personhood_suspensions(suspensions: &[PersonalId]) -> DispatchResult {
1492 ensure!(RingsState::<T>::get().mutating(), Error::<T>::NoMutationSession);
1493 for who in suspensions {
1494 let mut record = People::<T>::get(who).ok_or(Error::<T>::InvalidSuspensions)?;
1495 match record.position {
1496 RingPosition::Included { ring_index, ring_position, .. } => {
1497 let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1498 let Err(insert_idx) = suspended_indices.binary_search(&ring_position)
1499 else {
1500 return Err(Error::<T>::KeyAlreadySuspended.into());
1501 };
1502 suspended_indices
1503 .try_insert(insert_idx, ring_position)
1504 .defensive()
1505 .map_err(|_| Error::<T>::TooManyMembers)?;
1506 PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1507 },
1508 RingPosition::Onboarding { queue_page } => {
1509 let mut keys = OnboardingQueue::<T>::get(queue_page);
1510 let queue_idx = keys.iter().position(|k| *k == record.key);
1511 if let Some(idx) = queue_idx {
1512 keys.remove(idx);
1522 OnboardingQueue::<T>::insert(queue_page, keys);
1523 } else {
1524 defensive!(
1525 "No key found at the position in the person record of {}",
1526 who
1527 );
1528 }
1529 },
1530 RingPosition::Suspended => {
1531 defensive!("Suspension queued for person {} while already suspended", who);
1532 },
1533 }
1534
1535 record.position = RingPosition::Suspended;
1536 if let Some(account) = record.account {
1537 AccountToPersonalId::<T>::remove(account);
1538 record.account = None;
1539 }
1540
1541 People::<T>::insert(who, record);
1542 }
1543
1544 Ok(())
1545 }
1546
1547 pub fn resume_personhood(who: PersonalId) -> DispatchResult {
1550 let record = People::<T>::get(who).ok_or(Error::<T>::NotPerson)?;
1551 ensure!(record.position.suspended(), Error::<T>::NotSuspended);
1552 ensure!(Keys::<T>::get(&record.key) == Some(who), Error::<T>::NoKey);
1553
1554 Self::push_to_onboarding_queue(who, record.key, record.account)
1555 }
1556
1557 fn push_to_onboarding_queue(
1558 who: PersonalId,
1559 key: MemberOf<T>,
1560 account: Option<T::AccountId>,
1561 ) -> DispatchResult {
1562 let (head, mut tail) = QueuePageIndices::<T>::get();
1563 let mut keys = OnboardingQueue::<T>::get(tail);
1564 if let Err(k) = keys.try_push(key.clone()) {
1565 tail = tail.checked_add(1).unwrap_or(0);
1566 ensure!(tail != head, Error::<T>::TooManyMembers);
1567 keys = alloc::vec![k].try_into().expect("must be able to hold one key; qed");
1568 };
1569
1570 let record = PersonRecord {
1571 key,
1572 position: RingPosition::Onboarding { queue_page: tail },
1573 account,
1574 };
1575 Keys::<T>::insert(&record.key, who);
1576 People::<T>::insert(who, &record);
1577 Self::deposit_event(Event::<T>::PersonOnboarding { who, key: record.key });
1578
1579 QueuePageIndices::<T>::put((head, tail));
1580 OnboardingQueue::<T>::insert(tail, keys);
1581 Ok(())
1582 }
1583
1584 pub fn ring_keys_and_info(
1586 ring_index: RingIndex,
1587 ) -> (BoundedVec<MemberOf<T>, T::MaxRingSize>, RingStatus) {
1588 let keys = RingKeys::<T>::get(ring_index);
1589 let ring_status = RingKeysStatus::<T>::get(ring_index);
1590 defensive_assert!(
1591 keys.len() == ring_status.total as usize,
1592 "Stored key count doesn't match the actual length"
1593 );
1594 (keys, ring_status)
1595 }
1596
1597 pub(crate) fn fetch_chunks(
1599 range: Range<usize>,
1600 ) -> Result<Vec<<T::Crypto as GenerateVerifiable>::StaticChunk>, ()> {
1601 let chunk_page_size = T::ChunkPageSize::get();
1602 let expected_len = range.end.saturating_sub(range.start);
1603 let mut page_idx = range.start.checked_div(chunk_page_size as usize).ok_or(())?;
1604 let mut chunks: Vec<_> = Chunks::<T>::get(page_idx.saturated_into::<u32>())
1605 .defensive()
1606 .ok_or(())?
1607 .into_iter()
1608 .skip(range.start % chunk_page_size as usize)
1609 .take(expected_len)
1610 .collect();
1611 while chunks.len() < expected_len {
1612 page_idx = page_idx.checked_add(1).ok_or(())?;
1615 let page =
1616 Chunks::<T>::get(page_idx.saturated_into::<u32>()).defensive().ok_or(())?;
1617 chunks.extend(
1618 page.into_inner().into_iter().take(expected_len.saturating_sub(chunks.len())),
1619 );
1620 }
1621
1622 Ok(chunks)
1623 }
1624
1625 pub(crate) fn migrate_keys(meter: &mut WeightMeter) {
1629 let mut drain = KeyMigrationQueue::<T>::drain();
1630 loop {
1631 let weight = T::WeightInfo::migrate_keys_single_included_key()
1634 .saturating_add(T::DbWeight::get().reads_writes(1, 1));
1635 if !meter.can_consume(weight) {
1636 return;
1637 }
1638
1639 let op_res = with_storage_layer::<bool, DispatchError, _>(|| match drain.next() {
1640 Some((id, new_key)) => {
1641 Self::migrate_keys_single_included_key(id, new_key).map(|_| false)
1642 },
1643 None => {
1644 let rings_state = RingsState::<T>::get()
1645 .end_key_migration()
1646 .map_err(|_| Error::<T>::NoMutationSession)?;
1647 RingsState::<T>::put(rings_state);
1648 meter.consume(T::DbWeight::get().reads_writes(1, 1));
1649 Ok(true)
1650 },
1651 });
1652 match op_res {
1653 Ok(false) => meter.consume(weight),
1654 Ok(true) => {
1655 meter.consume(T::DbWeight::get().reads(1));
1657 break;
1658 },
1659 Err(e) => {
1660 meter.consume(weight);
1661 log::error!(target: LOG_TARGET, "failed to migrate keys: {:?}", e);
1662 break;
1663 },
1664 }
1665 }
1666 }
1667
1668 pub(crate) fn migrate_keys_single_included_key(
1671 id: PersonalId,
1672 new_key: MemberOf<T>,
1673 ) -> DispatchResult {
1674 if let Some(record) = People::<T>::get(id) {
1675 let RingPosition::Included {
1676 ring_index,
1677 ring_position,
1678 scheduled_for_removal: true,
1679 } = record.position
1680 else {
1681 Keys::<T>::remove(new_key);
1682 return Ok(());
1683 };
1684 let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1685 let Err(insert_idx) = suspended_indices.binary_search(&ring_position) else {
1686 log::info!(target: LOG_TARGET, "key migration for person {} skipped as the person's key was already suspended", id);
1687 return Ok(());
1688 };
1689 suspended_indices
1690 .try_insert(insert_idx, ring_position)
1691 .map_err(|_| Error::<T>::TooManyMembers)?;
1692 PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1693 Keys::<T>::remove(&record.key);
1694 Self::push_to_onboarding_queue(id, new_key, record.account)?;
1695 } else {
1696 log::info!(target: LOG_TARGET, "key migration for person {} skipped as no record was found", id);
1697 }
1698 Ok(())
1699 }
1700
1701 pub(crate) fn remove_suspended_keys(ring_index: RingIndex) -> Weight {
1703 let keys = RingKeys::<T>::get(ring_index);
1704 let keys_len = keys.len();
1705 let suspended_indices = PendingSuspensions::<T>::get(ring_index);
1706 let mut new_keys: BoundedVec<MemberOf<T>, T::MaxRingSize> = Default::default();
1709 let mut j = 0;
1710 for (i, key) in keys.into_iter().enumerate() {
1711 if j < suspended_indices.len() && i == suspended_indices[j] as usize {
1712 j += 1;
1713 } else if new_keys
1714 .try_push(key)
1715 .defensive_proof("cannot move more ring members than the max ring size; qed")
1716 .is_err()
1717 {
1718 return T::WeightInfo::remove_suspended_people(
1719 keys_len.try_into().unwrap_or(u32::MAX),
1720 );
1721 }
1722 }
1723
1724 let suspended_count = RingKeysStatus::<T>::mutate(ring_index, |ring_status| {
1725 let new_total = new_keys.len().saturated_into();
1726 let suspended_count = ring_status.total.saturating_sub(new_total);
1727 ring_status.total = new_total;
1728 ring_status.included = 0;
1729 suspended_count
1730 });
1731 ActiveMembers::<T>::mutate(|active| *active = active.saturating_sub(suspended_count));
1732 RingKeys::<T>::insert(ring_index, new_keys);
1733 Root::<T>::mutate(ring_index, |maybe_root| {
1734 if let Some(root) = maybe_root {
1735 root.intermediate = T::Crypto::start_members();
1738 }
1739 });
1740
1741 PendingSuspensions::<T>::remove(ring_index);
1744 T::WeightInfo::remove_suspended_people(keys_len.try_into().unwrap_or(u32::MAX))
1745 }
1746
1747 pub(crate) fn merge_queue_pages(
1755 initial_head: u32,
1756 new_head: u32,
1757 mut first_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1758 second_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1759 ) {
1760 let op_res = with_storage_layer::<(), DispatchError, _>(|| {
1761 for key in first_key_page.iter() {
1763 let personal_id =
1764 Keys::<T>::get(key).defensive().ok_or(Error::<T>::NotPerson)?;
1765 let mut record =
1766 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1767 record.position = RingPosition::Onboarding { queue_page: new_head };
1768 People::<T>::insert(personal_id, record);
1769 }
1770
1771 first_key_page
1772 .try_extend(second_key_page.into_iter())
1773 .defensive()
1774 .map_err(|_| Error::<T>::TooManyMembers)?;
1775 OnboardingQueue::<T>::remove(initial_head);
1776 OnboardingQueue::<T>::insert(new_head, first_key_page);
1777 QueuePageIndices::<T>::mutate(|(h, _)| *h = new_head);
1778 Ok(())
1779 });
1780 if let Err(e) = op_res {
1781 log::error!(target: LOG_TARGET, "failed to merge queue pages: {:?}", e);
1782 }
1783 }
1784 }
1785
1786 impl<T: Config> AddOnlyPeopleTrait for Pallet<T> {
1787 type Member = MemberOf<T>;
1788
1789 fn reserve_new_id() -> PersonalId {
1790 let new_id = NextPersonalId::<T>::mutate(|id| {
1791 let new_id = *id;
1792 id.saturating_inc();
1793 new_id
1794 });
1795 ReservedPersonalId::<T>::insert(new_id, ());
1796 new_id
1797 }
1798
1799 fn cancel_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1800 ReservedPersonalId::<T>::take(personal_id).ok_or(Error::<T>::PersonalIdNotReserved)?;
1801 Ok(())
1802 }
1803
1804 fn renew_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1805 if NextPersonalId::<T>::get() <= personal_id ||
1806 People::<T>::contains_key(personal_id) ||
1807 ReservedPersonalId::<T>::contains_key(personal_id)
1808 {
1809 return Err(Error::<T>::PersonalIdReservationCannotRenew.into());
1810 }
1811 ReservedPersonalId::<T>::insert(personal_id, ());
1812 Ok(())
1813 }
1814
1815 fn recognize_personhood(
1816 who: PersonalId,
1817 maybe_key: Option<MemberOf<T>>,
1818 ) -> Result<(), DispatchError> {
1819 match maybe_key {
1820 Some(key) => Self::do_insert_key(who, key),
1821 None => Self::resume_personhood(who),
1822 }
1823 }
1824
1825 #[cfg(feature = "runtime-benchmarks")]
1826 type Secret = <<T as Config>::Crypto as GenerateVerifiable>::Secret;
1827
1828 #[cfg(feature = "runtime-benchmarks")]
1829 fn mock_key(who: PersonalId) -> (Self::Member, Self::Secret) {
1830 let mut buf = [0u8; 32];
1831 buf[..core::mem::size_of::<PersonalId>()].copy_from_slice(&who.to_le_bytes()[..]);
1832 let secret = T::Crypto::new_secret(buf);
1833 (T::Crypto::member_from_secret(&secret), secret)
1834 }
1835 }
1836
1837 impl<T: Config> PeopleTrait for Pallet<T> {
1838 fn suspend_personhood(suspensions: &[PersonalId]) -> DispatchResult {
1839 Self::queue_personhood_suspensions(suspensions)
1840 }
1841 fn start_people_set_mutation_session() -> DispatchResult {
1842 let current_state = RingsState::<T>::get();
1843 RingsState::<T>::put(
1844 current_state
1845 .start_mutation_session()
1846 .map_err(|_| Error::<T>::CouldNotStartMutationSession)?,
1847 );
1848 Ok(())
1849 }
1850 fn end_people_set_mutation_session() -> DispatchResult {
1851 let current_state = RingsState::<T>::get();
1852 RingsState::<T>::put(
1853 current_state
1854 .end_mutation_session()
1855 .map_err(|_| Error::<T>::NoMutationSession)?,
1856 );
1857 Ok(())
1858 }
1859 }
1860
1861 pub fn ensure_personal_identity<OuterOrigin>(o: OuterOrigin) -> Result<PersonalId, BadOrigin>
1865 where
1866 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1867 {
1868 match o.try_into() {
1869 Ok(Origin::PersonalIdentity(m)) => Ok(m),
1870 _ => Err(BadOrigin),
1871 }
1872 }
1873
1874 pub fn ensure_personal_alias<OuterOrigin>(o: OuterOrigin) -> Result<ContextualAlias, BadOrigin>
1877 where
1878 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1879 {
1880 match o.try_into() {
1881 Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca.ca),
1882 _ => Err(BadOrigin),
1883 }
1884 }
1885
1886 pub struct EnsurePersonalIdentity<T>(PhantomData<T>);
1889 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalIdentity<T> {
1890 type Success = PersonalId;
1891
1892 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1893 ensure_personal_identity(o.clone().into_caller()).map_err(|_| o)
1894 }
1895
1896 #[cfg(feature = "runtime-benchmarks")]
1897 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1898 Ok(Origin::PersonalIdentity(0).into())
1899 }
1900 }
1901
1902 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1903 impl<{ T: Config, A }>
1904 EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalIdentity<T>
1905 {}
1906 }
1907
1908 impl<T: Config> CountedMembers for EnsurePersonalIdentity<T> {
1909 fn active_count(&self) -> u32 {
1910 Keys::<T>::count()
1911 }
1912 }
1913
1914 pub struct EnsurePersonalAlias<T>(PhantomData<T>);
1917 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalAlias<T> {
1918 type Success = ContextualAlias;
1919
1920 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1921 ensure_personal_alias(o.clone().into_caller()).map_err(|_| o)
1922 }
1923
1924 #[cfg(feature = "runtime-benchmarks")]
1925 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1926 Ok(Origin::PersonalAlias(RevisedContextualAlias {
1927 revision: 0,
1928 ring: 0,
1929 ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
1930 })
1931 .into())
1932 }
1933 }
1934
1935 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1936 impl<{ T: Config, A }>
1937 EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalAlias<T>
1938 {}
1939 }
1940
1941 impl<T: Config> CountedMembers for EnsurePersonalAlias<T> {
1942 fn active_count(&self) -> u32 {
1943 ActiveMembers::<T>::get()
1944 }
1945 }
1946
1947 pub struct EnsurePersonalAliasInContext<T>(PhantomData<T>);
1950 impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context> for EnsurePersonalAliasInContext<T> {
1951 type Success = Alias;
1952
1953 fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
1954 match ensure_personal_alias(o.clone().into_caller()) {
1955 Ok(ca) if &ca.context == arg => Ok(ca.alias),
1956 _ => Err(o),
1957 }
1958 }
1959
1960 #[cfg(feature = "runtime-benchmarks")]
1961 fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
1962 Ok(Origin::PersonalAlias(RevisedContextualAlias {
1963 revision: 0,
1964 ring: 0,
1965 ca: ContextualAlias { alias: [1; 32], context: *context },
1966 })
1967 .into())
1968 }
1969 }
1970
1971 impl<T: Config> CountedMembers for EnsurePersonalAliasInContext<T> {
1972 fn active_count(&self) -> u32 {
1973 ActiveMembers::<T>::get()
1974 }
1975 }
1976
1977 pub fn ensure_revised_personal_alias<OuterOrigin>(
1983 o: OuterOrigin,
1984 ) -> Result<RevisedContextualAlias, BadOrigin>
1985 where
1986 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1987 {
1988 match o.try_into() {
1989 Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca),
1990 _ => Err(BadOrigin),
1991 }
1992 }
1993
1994 pub struct EnsureRevisedPersonalAlias<T>(PhantomData<T>);
1999 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsureRevisedPersonalAlias<T> {
2000 type Success = RevisedContextualAlias;
2001
2002 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
2003 ensure_revised_personal_alias(o.clone().into_caller()).map_err(|_| o)
2004 }
2005
2006 #[cfg(feature = "runtime-benchmarks")]
2007 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
2008 Ok(Origin::PersonalAlias(RevisedContextualAlias {
2009 revision: 0,
2010 ring: 0,
2011 ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
2012 })
2013 .into())
2014 }
2015 }
2016
2017 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
2018 impl<{ T: Config, A }>
2019 EnsureOriginWithArg< OriginFor<T>, A> for EnsureRevisedPersonalAlias<T>
2020 {}
2021 }
2022
2023 impl<T: Config> CountedMembers for EnsureRevisedPersonalAlias<T> {
2024 fn active_count(&self) -> u32 {
2025 ActiveMembers::<T>::get()
2026 }
2027 }
2028
2029 pub struct EnsureRevisedPersonalAliasInContext<T>(PhantomData<T>);
2035 impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context>
2036 for EnsureRevisedPersonalAliasInContext<T>
2037 {
2038 type Success = RevisedAlias;
2039
2040 fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
2041 match ensure_revised_personal_alias(o.clone().into_caller()) {
2042 Ok(ca) if &ca.ca.context == arg => {
2043 Ok(RevisedAlias { revision: ca.revision, ring: ca.ring, alias: ca.ca.alias })
2044 },
2045 _ => Err(o),
2046 }
2047 }
2048
2049 #[cfg(feature = "runtime-benchmarks")]
2050 fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
2051 Ok(Origin::PersonalAlias(RevisedContextualAlias {
2052 revision: 0,
2053 ring: 0,
2054 ca: ContextualAlias { alias: [1; 32], context: *context },
2055 })
2056 .into())
2057 }
2058 }
2059
2060 impl<T: Config> CountedMembers for EnsureRevisedPersonalAliasInContext<T> {
2061 fn active_count(&self) -> u32 {
2062 ActiveMembers::<T>::get()
2063 }
2064 }
2065}