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 RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1090 }
1091 Keys::<T>::insert(new_key, id);
1092
1093 Ok(().into())
1094 }
1095
1096 #[pallet::call_index(7)]
1099 pub fn migrate_onboarding_key(
1100 origin: OriginFor<T>,
1101 new_key: MemberOf<T>,
1102 ) -> DispatchResultWithPostInfo {
1103 let id = Self::ensure_personal_identity(origin)?;
1104 ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1105 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1106 ensure!(record.key != new_key, Error::<T>::SameKey);
1107 match &record.position {
1108 RingPosition::Onboarding { queue_page } => {
1110 let mut keys = OnboardingQueue::<T>::get(queue_page);
1111 if let Some(idx) = keys.iter().position(|k| *k == record.key) {
1112 Keys::<T>::remove(&keys[idx]);
1114 keys[idx] = new_key.clone();
1116 OnboardingQueue::<T>::insert(queue_page, keys);
1117 record.key = new_key.clone();
1119 People::<T>::insert(id, record);
1121 } else {
1122 defensive!("No key found at the position in the person record of {}", id);
1123 }
1124 },
1125 RingPosition::Included { .. } => return Err(Error::<T>::InvalidKeyMigration.into()),
1127 RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1130 }
1131 Keys::<T>::insert(new_key, id);
1132
1133 Ok(().into())
1134 }
1135
1136 #[pallet::call_index(8)]
1138 pub fn set_onboarding_size(
1139 origin: OriginFor<T>,
1140 onboarding_size: u32,
1141 ) -> DispatchResultWithPostInfo {
1142 ensure_root(origin)?;
1143 ensure!(
1144 onboarding_size <= <T as Config>::MaxRingSize::get(),
1145 Error::<T>::InvalidOnboardingSize
1146 );
1147 OnboardingSize::<T>::put(onboarding_size);
1148 Ok(Pays::No.into())
1149 }
1150 }
1151
1152 impl<T: Config> Pallet<T> {
1153 pub(crate) fn should_build_ring(ring_index: RingIndex, limit: u32) -> Option<u32> {
1156 if !RingsState::<T>::get().append_only() {
1158 return None;
1159 }
1160 if PendingSuspensions::<T>::contains_key(ring_index) {
1162 return None;
1163 }
1164
1165 let ring_status = RingKeysStatus::<T>::get(ring_index);
1166 let not_included_count = ring_status.total.saturating_sub(ring_status.included);
1167 let to_include = not_included_count.min(limit);
1168 if to_include == 0 {
1170 return None;
1171 }
1172
1173 Some(to_include)
1174 }
1175
1176 fn should_onboard_people(
1181 ring_index: RingIndex,
1182 ring_status: &RingStatus,
1183 open_slots: u32,
1184 available_for_inclusion: u32,
1185 onboarding_size: u32,
1186 ) -> Option<(u32, bool)> {
1187 if !RingsState::<T>::get().append_only() {
1189 return None;
1190 }
1191
1192 if PendingSuspensions::<T>::contains_key(ring_index) {
1194 return None;
1195 }
1196
1197 let to_include = available_for_inclusion.min(open_slots);
1198 if to_include == 0 {
1200 return None;
1201 }
1202
1203 let can_onboard_with_cohort = to_include >= onboarding_size &&
1207 ring_status.total.saturating_add(to_include.saturated_into()) <=
1208 T::MaxRingSize::get().saturating_sub(onboarding_size);
1209 let ring_filled = open_slots == to_include;
1212
1213 let should_onboard = ring_filled || can_onboard_with_cohort;
1214 if !should_onboard {
1215 return None;
1216 }
1217
1218 Some((to_include, ring_filled))
1219 }
1220
1221 pub(crate) fn should_remove_suspended_keys(
1223 ring_index: RingIndex,
1224 check_rings_state: bool,
1225 ) -> bool {
1226 if check_rings_state && !RingsState::<T>::get().append_only() {
1227 return false;
1228 }
1229 let suspended_count = PendingSuspensions::<T>::decode_len(ring_index).unwrap_or(0);
1230 if suspended_count == 0 {
1232 return false;
1233 }
1234
1235 true
1236 }
1237
1238 pub(crate) fn should_merge_queue_pages() -> QueueMergeAction<T> {
1247 let (initial_head, tail) = QueuePageIndices::<T>::get();
1248 let first_key_page = OnboardingQueue::<T>::get(initial_head);
1249 if initial_head == tail {
1252 return QueueMergeAction::NoAction;
1253 }
1254 let new_head = initial_head.checked_add(1).unwrap_or(0);
1255 let second_key_page = OnboardingQueue::<T>::get(new_head);
1256
1257 let page_size = T::OnboardingQueuePageSize::get();
1258 if first_key_page.len().saturating_add(second_key_page.len()) > page_size as usize {
1260 return QueueMergeAction::NoAction;
1261 }
1262
1263 QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page }
1264 }
1265
1266 pub(crate) fn build_ring(ring_index: RingIndex, to_include: u32) -> DispatchResult {
1269 let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
1270 let (next_revision, mut intermediate) =
1273 if let Some(existing_root) = Root::<T>::get(ring_index) {
1274 (
1276 existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
1277 existing_root.intermediate,
1278 )
1279 } else {
1280 (0, T::Crypto::start_members())
1282 };
1283
1284 T::Crypto::push_members(
1286 &mut intermediate,
1287 keys.iter()
1288 .skip(ring_status.included as usize)
1289 .take(to_include as usize)
1290 .cloned(),
1291 Self::fetch_chunks,
1292 )
1293 .defensive()
1294 .map_err(|_| Error::<T>::CouldNotPush)?;
1295
1296 ring_status.included = ring_status.included.saturating_add(to_include);
1298 RingKeysStatus::<T>::insert(ring_index, ring_status);
1299
1300 let root = T::Crypto::finish_members(intermediate.clone());
1302 let ring_root = RingRoot { root, revision: next_revision, intermediate };
1303 Root::<T>::insert(ring_index, ring_root);
1304 Ok(())
1305 }
1306
1307 #[transactional]
1313 pub(crate) fn onboard_people() -> DispatchResult {
1314 let (top_ring_index, mut keys) = Self::available_ring();
1316 let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
1317 defensive_assert!(
1318 keys.len() == ring_status.total as usize,
1319 "Stored key count doesn't match the actual length"
1320 );
1321
1322 let keys_len = keys.len() as u32;
1323 let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
1324
1325 let (mut head, tail) = QueuePageIndices::<T>::get();
1326 let old_head = head;
1327 let mut keys_to_include: Vec<MemberOf<T>> =
1328 OnboardingQueue::<T>::take(head).into_inner();
1329
1330 if keys_to_include.len() < open_slots as usize && head != tail {
1333 head = head.checked_add(1).unwrap_or(0);
1334 let second_key_page = OnboardingQueue::<T>::take(head);
1335 defensive_assert!(!second_key_page.is_empty());
1336 keys_to_include.extend(second_key_page.into_iter());
1337 }
1338
1339 let onboarding_size = OnboardingSize::<T>::get();
1340
1341 let (to_include, ring_filled) = Self::should_onboard_people(
1342 top_ring_index,
1343 &ring_status,
1344 open_slots,
1345 keys_to_include.len().saturated_into(),
1346 onboarding_size,
1347 )
1348 .ok_or(Error::<T>::Incomplete)?;
1349
1350 let mut remaining_keys = keys_to_include.split_off(to_include as usize);
1351 for key in keys_to_include.into_iter() {
1352 let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
1353 let mut record =
1354 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1355 record.position = RingPosition::Included {
1356 ring_index: top_ring_index,
1357 ring_position: keys.len().saturated_into(),
1358 scheduled_for_removal: false,
1359 };
1360 People::<T>::insert(personal_id, record);
1361 keys.try_push(key).defensive().map_err(|_| Error::<T>::TooManyMembers)?;
1362 }
1363 RingKeys::<T>::insert(top_ring_index, keys);
1364 ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
1365 ring_status.total = ring_status.total.saturating_add(to_include);
1366 RingKeysStatus::<T>::insert(top_ring_index, ring_status);
1367
1368 if ring_filled {
1370 CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
1371 }
1372
1373 if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
1374 let split_idx =
1375 remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
1376 let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1377 remaining_keys
1378 .split_off(split_idx)
1379 .try_into()
1380 .expect("the list shrunk so it must fit; qed");
1381 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1382 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1383 OnboardingQueue::<T>::insert(old_head, remaining_keys);
1384 OnboardingQueue::<T>::insert(head, second_page_keys);
1385 QueuePageIndices::<T>::put((old_head, tail));
1386 } else if !remaining_keys.is_empty() {
1387 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1388 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1389 OnboardingQueue::<T>::insert(head, remaining_keys);
1390 QueuePageIndices::<T>::put((head, tail));
1391 } else {
1392 if head != tail {
1395 head = head.checked_add(1).unwrap_or(0);
1396 }
1397 QueuePageIndices::<T>::put((head, tail));
1398 }
1399 Ok(())
1400 }
1401
1402 fn derivative_call(
1403 mut origin: OriginFor<T>,
1404 local_origin: Origin,
1405 call: <T as frame_system::Config>::RuntimeCall,
1406 derivation_weight: Weight,
1407 ) -> DispatchResultWithPostInfo {
1408 origin.set_caller_from(<T::RuntimeOrigin as OriginTrait>::PalletsOrigin::from(
1409 local_origin,
1410 ));
1411 let info = call.get_dispatch_info();
1412 let result = call.dispatch(origin);
1413 let weight = derivation_weight.saturating_add(extract_actual_weight(&result, &info));
1414 result
1415 .map(|p| PostDispatchInfo { actual_weight: Some(weight), pays_fee: p.pays_fee })
1416 .map_err(|mut err| {
1417 err.post_info = Some(weight).into();
1418 err
1419 })
1420 }
1421
1422 pub fn ensure_personal_identity(
1425 origin: T::RuntimeOrigin,
1426 ) -> Result<PersonalId, DispatchError> {
1427 Ok(ensure_personal_identity(origin.into_caller())?)
1428 }
1429
1430 pub fn ensure_personal_alias(
1434 origin: T::RuntimeOrigin,
1435 ) -> Result<ContextualAlias, DispatchError> {
1436 Ok(ensure_personal_alias(origin.into_caller())?)
1437 }
1438
1439 pub fn ensure_revised_personal_alias(
1443 origin: T::RuntimeOrigin,
1444 ) -> Result<RevisedContextualAlias, DispatchError> {
1445 Ok(ensure_revised_personal_alias(origin.into_caller())?)
1446 }
1447
1448 pub fn available_ring() -> (RingIndex, BoundedVec<MemberOf<T>, T::MaxRingSize>) {
1451 let mut current_ring_index = CurrentRingIndex::<T>::get();
1452 let mut current_keys = RingKeys::<T>::get(current_ring_index);
1453
1454 defensive_assert!(
1455 !current_keys.is_full(),
1456 "Something bad happened inside the STF, where the current keys are full, but we should have incremented in that case."
1457 );
1458
1459 if current_keys.is_full() {
1461 current_ring_index.saturating_inc();
1462 CurrentRingIndex::<T>::put(current_ring_index);
1463 current_keys = RingKeys::<T>::get(current_ring_index);
1464 }
1465
1466 defensive_assert!(
1467 !current_keys.is_full(),
1468 "Something bad happened inside the STF, where the current key and next key are both full. Nothing we can do here."
1469 );
1470
1471 (current_ring_index, current_keys)
1472 }
1473
1474 pub fn do_insert_key(who: PersonalId, key: MemberOf<T>) -> DispatchResult {
1476 ensure!(!Keys::<T>::contains_key(&key), Error::<T>::KeyAlreadyInUse);
1478 ensure!(
1480 ReservedPersonalId::<T>::take(who).is_some(),
1481 Error::<T>::PersonalIdNotReservedOrNotRecognized
1482 );
1483
1484 Self::push_to_onboarding_queue(who, key, None)
1485 }
1486
1487 pub fn queue_personhood_suspensions(suspensions: &[PersonalId]) -> DispatchResult {
1491 ensure!(RingsState::<T>::get().mutating(), Error::<T>::NoMutationSession);
1492 for who in suspensions {
1493 let mut record = People::<T>::get(who).ok_or(Error::<T>::InvalidSuspensions)?;
1494 match record.position {
1495 RingPosition::Included { ring_index, ring_position, .. } => {
1496 let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1497 let Err(insert_idx) = suspended_indices.binary_search(&ring_position)
1498 else {
1499 return Err(Error::<T>::KeyAlreadySuspended.into())
1500 };
1501 suspended_indices
1502 .try_insert(insert_idx, ring_position)
1503 .defensive()
1504 .map_err(|_| Error::<T>::TooManyMembers)?;
1505 PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1506 },
1507 RingPosition::Onboarding { queue_page } => {
1508 let mut keys = OnboardingQueue::<T>::get(queue_page);
1509 let queue_idx = keys.iter().position(|k| *k == record.key);
1510 if let Some(idx) = queue_idx {
1511 keys.remove(idx);
1521 OnboardingQueue::<T>::insert(queue_page, keys);
1522 } else {
1523 defensive!(
1524 "No key found at the position in the person record of {}",
1525 who
1526 );
1527 }
1528 },
1529 RingPosition::Suspended => {
1530 defensive!("Suspension queued for person {} while already suspended", who);
1531 },
1532 }
1533
1534 record.position = RingPosition::Suspended;
1535 if let Some(account) = record.account {
1536 AccountToPersonalId::<T>::remove(account);
1537 record.account = None;
1538 }
1539
1540 People::<T>::insert(who, record);
1541 }
1542
1543 Ok(())
1544 }
1545
1546 pub fn resume_personhood(who: PersonalId) -> DispatchResult {
1549 let record = People::<T>::get(who).ok_or(Error::<T>::NotPerson)?;
1550 ensure!(record.position.suspended(), Error::<T>::NotSuspended);
1551 ensure!(Keys::<T>::get(&record.key) == Some(who), Error::<T>::NoKey);
1552
1553 Self::push_to_onboarding_queue(who, record.key, record.account)
1554 }
1555
1556 fn push_to_onboarding_queue(
1557 who: PersonalId,
1558 key: MemberOf<T>,
1559 account: Option<T::AccountId>,
1560 ) -> DispatchResult {
1561 let (head, mut tail) = QueuePageIndices::<T>::get();
1562 let mut keys = OnboardingQueue::<T>::get(tail);
1563 if let Err(k) = keys.try_push(key.clone()) {
1564 tail = tail.checked_add(1).unwrap_or(0);
1565 ensure!(tail != head, Error::<T>::TooManyMembers);
1566 keys = alloc::vec![k].try_into().expect("must be able to hold one key; qed");
1567 };
1568
1569 let record = PersonRecord {
1570 key,
1571 position: RingPosition::Onboarding { queue_page: tail },
1572 account,
1573 };
1574 Keys::<T>::insert(&record.key, who);
1575 People::<T>::insert(who, &record);
1576 Self::deposit_event(Event::<T>::PersonOnboarding { who, key: record.key });
1577
1578 QueuePageIndices::<T>::put((head, tail));
1579 OnboardingQueue::<T>::insert(tail, keys);
1580 Ok(())
1581 }
1582
1583 pub fn ring_keys_and_info(
1585 ring_index: RingIndex,
1586 ) -> (BoundedVec<MemberOf<T>, T::MaxRingSize>, RingStatus) {
1587 let keys = RingKeys::<T>::get(ring_index);
1588 let ring_status = RingKeysStatus::<T>::get(ring_index);
1589 defensive_assert!(
1590 keys.len() == ring_status.total as usize,
1591 "Stored key count doesn't match the actual length"
1592 );
1593 (keys, ring_status)
1594 }
1595
1596 pub(crate) fn fetch_chunks(
1598 range: Range<usize>,
1599 ) -> Result<Vec<<T::Crypto as GenerateVerifiable>::StaticChunk>, ()> {
1600 let chunk_page_size = T::ChunkPageSize::get();
1601 let expected_len = range.end.saturating_sub(range.start);
1602 let mut page_idx = range.start.checked_div(chunk_page_size as usize).ok_or(())?;
1603 let mut chunks: Vec<_> = Chunks::<T>::get(page_idx.saturated_into::<u32>())
1604 .defensive()
1605 .ok_or(())?
1606 .into_iter()
1607 .skip(range.start % chunk_page_size as usize)
1608 .take(expected_len)
1609 .collect();
1610 while chunks.len() < expected_len {
1611 page_idx = page_idx.checked_add(1).ok_or(())?;
1614 let page =
1615 Chunks::<T>::get(page_idx.saturated_into::<u32>()).defensive().ok_or(())?;
1616 chunks.extend(
1617 page.into_inner().into_iter().take(expected_len.saturating_sub(chunks.len())),
1618 );
1619 }
1620
1621 Ok(chunks)
1622 }
1623
1624 pub(crate) fn migrate_keys(meter: &mut WeightMeter) {
1628 let mut drain = KeyMigrationQueue::<T>::drain();
1629 loop {
1630 let weight = T::WeightInfo::migrate_keys_single_included_key()
1633 .saturating_add(T::DbWeight::get().reads_writes(1, 1));
1634 if !meter.can_consume(weight) {
1635 return;
1636 }
1637
1638 let op_res = with_storage_layer::<bool, DispatchError, _>(|| match drain.next() {
1639 Some((id, new_key)) =>
1640 Self::migrate_keys_single_included_key(id, new_key).map(|_| false),
1641 None => {
1642 let rings_state = RingsState::<T>::get()
1643 .end_key_migration()
1644 .map_err(|_| Error::<T>::NoMutationSession)?;
1645 RingsState::<T>::put(rings_state);
1646 meter.consume(T::DbWeight::get().reads_writes(1, 1));
1647 Ok(true)
1648 },
1649 });
1650 match op_res {
1651 Ok(false) => meter.consume(weight),
1652 Ok(true) => {
1653 meter.consume(T::DbWeight::get().reads(1));
1655 break
1656 },
1657 Err(e) => {
1658 meter.consume(weight);
1659 log::error!(target: LOG_TARGET, "failed to migrate keys: {:?}", e);
1660 break;
1661 },
1662 }
1663 }
1664 }
1665
1666 pub(crate) fn migrate_keys_single_included_key(
1669 id: PersonalId,
1670 new_key: MemberOf<T>,
1671 ) -> DispatchResult {
1672 if let Some(record) = People::<T>::get(id) {
1673 let RingPosition::Included {
1674 ring_index,
1675 ring_position,
1676 scheduled_for_removal: true,
1677 } = record.position
1678 else {
1679 Keys::<T>::remove(new_key);
1680 return Ok(())
1681 };
1682 let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1683 let Err(insert_idx) = suspended_indices.binary_search(&ring_position) else {
1684 log::info!(target: LOG_TARGET, "key migration for person {} skipped as the person's key was already suspended", id);
1685 return Ok(());
1686 };
1687 suspended_indices
1688 .try_insert(insert_idx, ring_position)
1689 .map_err(|_| Error::<T>::TooManyMembers)?;
1690 PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1691 Keys::<T>::remove(&record.key);
1692 Self::push_to_onboarding_queue(id, new_key, record.account)?;
1693 } else {
1694 log::info!(target: LOG_TARGET, "key migration for person {} skipped as no record was found", id);
1695 }
1696 Ok(())
1697 }
1698
1699 pub(crate) fn remove_suspended_keys(ring_index: RingIndex) -> Weight {
1701 let keys = RingKeys::<T>::get(ring_index);
1702 let keys_len = keys.len();
1703 let suspended_indices = PendingSuspensions::<T>::get(ring_index);
1704 let mut new_keys: BoundedVec<MemberOf<T>, T::MaxRingSize> = Default::default();
1707 let mut j = 0;
1708 for (i, key) in keys.into_iter().enumerate() {
1709 if j < suspended_indices.len() && i == suspended_indices[j] as usize {
1710 j += 1;
1711 } else if new_keys
1712 .try_push(key)
1713 .defensive_proof("cannot move more ring members than the max ring size; qed")
1714 .is_err()
1715 {
1716 return T::WeightInfo::remove_suspended_people(
1717 keys_len.try_into().unwrap_or(u32::MAX),
1718 );
1719 }
1720 }
1721
1722 let suspended_count = RingKeysStatus::<T>::mutate(ring_index, |ring_status| {
1723 let new_total = new_keys.len().saturated_into();
1724 let suspended_count = ring_status.total.saturating_sub(new_total);
1725 ring_status.total = new_total;
1726 ring_status.included = 0;
1727 suspended_count
1728 });
1729 ActiveMembers::<T>::mutate(|active| *active = active.saturating_sub(suspended_count));
1730 RingKeys::<T>::insert(ring_index, new_keys);
1731 Root::<T>::mutate(ring_index, |maybe_root| {
1732 if let Some(root) = maybe_root {
1733 root.intermediate = T::Crypto::start_members();
1736 }
1737 });
1738
1739 PendingSuspensions::<T>::remove(ring_index);
1742 T::WeightInfo::remove_suspended_people(keys_len.try_into().unwrap_or(u32::MAX))
1743 }
1744
1745 pub(crate) fn merge_queue_pages(
1753 initial_head: u32,
1754 new_head: u32,
1755 mut first_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1756 second_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1757 ) {
1758 let op_res = with_storage_layer::<(), DispatchError, _>(|| {
1759 for key in first_key_page.iter() {
1761 let personal_id =
1762 Keys::<T>::get(key).defensive().ok_or(Error::<T>::NotPerson)?;
1763 let mut record =
1764 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1765 record.position = RingPosition::Onboarding { queue_page: new_head };
1766 People::<T>::insert(personal_id, record);
1767 }
1768
1769 first_key_page
1770 .try_extend(second_key_page.into_iter())
1771 .defensive()
1772 .map_err(|_| Error::<T>::TooManyMembers)?;
1773 OnboardingQueue::<T>::remove(initial_head);
1774 OnboardingQueue::<T>::insert(new_head, first_key_page);
1775 QueuePageIndices::<T>::mutate(|(h, _)| *h = new_head);
1776 Ok(())
1777 });
1778 if let Err(e) = op_res {
1779 log::error!(target: LOG_TARGET, "failed to merge queue pages: {:?}", e);
1780 }
1781 }
1782 }
1783
1784 impl<T: Config> AddOnlyPeopleTrait for Pallet<T> {
1785 type Member = MemberOf<T>;
1786
1787 fn reserve_new_id() -> PersonalId {
1788 let new_id = NextPersonalId::<T>::mutate(|id| {
1789 let new_id = *id;
1790 id.saturating_inc();
1791 new_id
1792 });
1793 ReservedPersonalId::<T>::insert(new_id, ());
1794 new_id
1795 }
1796
1797 fn cancel_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1798 ReservedPersonalId::<T>::take(personal_id).ok_or(Error::<T>::PersonalIdNotReserved)?;
1799 Ok(())
1800 }
1801
1802 fn renew_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1803 if NextPersonalId::<T>::get() <= personal_id ||
1804 People::<T>::contains_key(personal_id) ||
1805 ReservedPersonalId::<T>::contains_key(personal_id)
1806 {
1807 return Err(Error::<T>::PersonalIdReservationCannotRenew.into());
1808 }
1809 ReservedPersonalId::<T>::insert(personal_id, ());
1810 Ok(())
1811 }
1812
1813 fn recognize_personhood(
1814 who: PersonalId,
1815 maybe_key: Option<MemberOf<T>>,
1816 ) -> Result<(), DispatchError> {
1817 match maybe_key {
1818 Some(key) => Self::do_insert_key(who, key),
1819 None => Self::resume_personhood(who),
1820 }
1821 }
1822
1823 #[cfg(feature = "runtime-benchmarks")]
1824 type Secret = <<T as Config>::Crypto as GenerateVerifiable>::Secret;
1825
1826 #[cfg(feature = "runtime-benchmarks")]
1827 fn mock_key(who: PersonalId) -> (Self::Member, Self::Secret) {
1828 let mut buf = [0u8; 32];
1829 buf[..core::mem::size_of::<PersonalId>()].copy_from_slice(&who.to_le_bytes()[..]);
1830 let secret = T::Crypto::new_secret(buf);
1831 (T::Crypto::member_from_secret(&secret), secret)
1832 }
1833 }
1834
1835 impl<T: Config> PeopleTrait for Pallet<T> {
1836 fn suspend_personhood(suspensions: &[PersonalId]) -> DispatchResult {
1837 Self::queue_personhood_suspensions(suspensions)
1838 }
1839 fn start_people_set_mutation_session() -> DispatchResult {
1840 let current_state = RingsState::<T>::get();
1841 RingsState::<T>::put(
1842 current_state
1843 .start_mutation_session()
1844 .map_err(|_| Error::<T>::CouldNotStartMutationSession)?,
1845 );
1846 Ok(())
1847 }
1848 fn end_people_set_mutation_session() -> DispatchResult {
1849 let current_state = RingsState::<T>::get();
1850 RingsState::<T>::put(
1851 current_state
1852 .end_mutation_session()
1853 .map_err(|_| Error::<T>::NoMutationSession)?,
1854 );
1855 Ok(())
1856 }
1857 }
1858
1859 pub fn ensure_personal_identity<OuterOrigin>(o: OuterOrigin) -> Result<PersonalId, BadOrigin>
1863 where
1864 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1865 {
1866 match o.try_into() {
1867 Ok(Origin::PersonalIdentity(m)) => Ok(m),
1868 _ => Err(BadOrigin),
1869 }
1870 }
1871
1872 pub fn ensure_personal_alias<OuterOrigin>(o: OuterOrigin) -> Result<ContextualAlias, BadOrigin>
1875 where
1876 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1877 {
1878 match o.try_into() {
1879 Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca.ca),
1880 _ => Err(BadOrigin),
1881 }
1882 }
1883
1884 pub struct EnsurePersonalIdentity<T>(PhantomData<T>);
1887 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalIdentity<T> {
1888 type Success = PersonalId;
1889
1890 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1891 ensure_personal_identity(o.clone().into_caller()).map_err(|_| o)
1892 }
1893
1894 #[cfg(feature = "runtime-benchmarks")]
1895 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1896 Ok(Origin::PersonalIdentity(0).into())
1897 }
1898 }
1899
1900 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1901 impl<{ T: Config, A }>
1902 EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalIdentity<T>
1903 {}
1904 }
1905
1906 impl<T: Config> CountedMembers for EnsurePersonalIdentity<T> {
1907 fn active_count(&self) -> u32 {
1908 Keys::<T>::count()
1909 }
1910 }
1911
1912 pub struct EnsurePersonalAlias<T>(PhantomData<T>);
1915 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalAlias<T> {
1916 type Success = ContextualAlias;
1917
1918 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1919 ensure_personal_alias(o.clone().into_caller()).map_err(|_| o)
1920 }
1921
1922 #[cfg(feature = "runtime-benchmarks")]
1923 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1924 Ok(Origin::PersonalAlias(RevisedContextualAlias {
1925 revision: 0,
1926 ring: 0,
1927 ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
1928 })
1929 .into())
1930 }
1931 }
1932
1933 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1934 impl<{ T: Config, A }>
1935 EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalAlias<T>
1936 {}
1937 }
1938
1939 impl<T: Config> CountedMembers for EnsurePersonalAlias<T> {
1940 fn active_count(&self) -> u32 {
1941 ActiveMembers::<T>::get()
1942 }
1943 }
1944
1945 pub struct EnsurePersonalAliasInContext<T>(PhantomData<T>);
1948 impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context> for EnsurePersonalAliasInContext<T> {
1949 type Success = Alias;
1950
1951 fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
1952 match ensure_personal_alias(o.clone().into_caller()) {
1953 Ok(ca) if &ca.context == arg => Ok(ca.alias),
1954 _ => Err(o),
1955 }
1956 }
1957
1958 #[cfg(feature = "runtime-benchmarks")]
1959 fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
1960 Ok(Origin::PersonalAlias(RevisedContextualAlias {
1961 revision: 0,
1962 ring: 0,
1963 ca: ContextualAlias { alias: [1; 32], context: *context },
1964 })
1965 .into())
1966 }
1967 }
1968
1969 impl<T: Config> CountedMembers for EnsurePersonalAliasInContext<T> {
1970 fn active_count(&self) -> u32 {
1971 ActiveMembers::<T>::get()
1972 }
1973 }
1974
1975 pub fn ensure_revised_personal_alias<OuterOrigin>(
1981 o: OuterOrigin,
1982 ) -> Result<RevisedContextualAlias, BadOrigin>
1983 where
1984 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1985 {
1986 match o.try_into() {
1987 Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca),
1988 _ => Err(BadOrigin),
1989 }
1990 }
1991
1992 pub struct EnsureRevisedPersonalAlias<T>(PhantomData<T>);
1997 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsureRevisedPersonalAlias<T> {
1998 type Success = RevisedContextualAlias;
1999
2000 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
2001 ensure_revised_personal_alias(o.clone().into_caller()).map_err(|_| o)
2002 }
2003
2004 #[cfg(feature = "runtime-benchmarks")]
2005 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
2006 Ok(Origin::PersonalAlias(RevisedContextualAlias {
2007 revision: 0,
2008 ring: 0,
2009 ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
2010 })
2011 .into())
2012 }
2013 }
2014
2015 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
2016 impl<{ T: Config, A }>
2017 EnsureOriginWithArg< OriginFor<T>, A> for EnsureRevisedPersonalAlias<T>
2018 {}
2019 }
2020
2021 impl<T: Config> CountedMembers for EnsureRevisedPersonalAlias<T> {
2022 fn active_count(&self) -> u32 {
2023 ActiveMembers::<T>::get()
2024 }
2025 }
2026
2027 pub struct EnsureRevisedPersonalAliasInContext<T>(PhantomData<T>);
2033 impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context>
2034 for EnsureRevisedPersonalAliasInContext<T>
2035 {
2036 type Success = RevisedAlias;
2037
2038 fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
2039 match ensure_revised_personal_alias(o.clone().into_caller()) {
2040 Ok(ca) if &ca.ca.context == arg =>
2041 Ok(RevisedAlias { revision: ca.revision, ring: ca.ring, alias: ca.ca.alias }),
2042 _ => Err(o),
2043 }
2044 }
2045
2046 #[cfg(feature = "runtime-benchmarks")]
2047 fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
2048 Ok(Origin::PersonalAlias(RevisedContextualAlias {
2049 revision: 0,
2050 ring: 0,
2051 ca: ContextualAlias { alias: [1; 32], context: *context },
2052 })
2053 .into())
2054 }
2055 }
2056
2057 impl<T: Config> CountedMembers for EnsureRevisedPersonalAliasInContext<T> {
2058 fn active_count(&self) -> u32 {
2059 ActiveMembers::<T>::get()
2060 }
2061 }
2062}