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, RuntimeDebug, SaturatedConversion, Saturating,
159};
160use verifiable::{Alias, GenerateVerifiable};
161
162#[cfg(feature = "runtime-benchmarks")]
163pub use benchmarking::BenchmarkHelper;
164
165#[frame_support::pallet]
166pub mod pallet {
167 use super::*;
168 use frame_support::{pallet_prelude::*, traits::Contains};
169 use frame_system::pallet_prelude::{BlockNumberFor, *};
170
171 const LOG_TARGET: &str = "runtime::people";
172
173 #[pallet::pallet]
174 pub struct Pallet<T>(_);
175
176 #[pallet::config]
177 pub trait Config:
178 frame_system::Config<
179 RuntimeOrigin: From<Origin>
180 + From<<Self::RuntimeOrigin as OriginTrait>::PalletsOrigin>
181 + OriginTrait<
182 PalletsOrigin: From<Origin>
183 + TryInto<
184 Origin,
185 Error = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin,
186 >,
187 >,
188 RuntimeCall: Parameter
189 + GetDispatchInfo
190 + IsSubType<Call<Self>>
191 + Dispatchable<
192 RuntimeOrigin = Self::RuntimeOrigin,
193 Info = DispatchInfo,
194 PostInfo = PostDispatchInfo,
195 >,
196 >
197 {
198 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,
457 PartialEq,
458 Eq,
459 RuntimeDebug,
460 Encode,
461 Decode,
462 MaxEncodedLen,
463 TypeInfo,
464 DecodeWithMemTracking,
465 )]
466 pub enum Origin {
467 PersonalIdentity(PersonalId),
468 PersonalAlias(RevisedContextualAlias),
469 }
470
471 #[pallet::hooks]
472 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
473 fn integrity_test() {
474 assert!(
475 <T as Config>::ChunkPageSize::get() > 0,
476 "chunk page size must hold at least one element"
477 );
478 assert!(<T as Config>::MaxRingSize::get() > 0, "rings must hold at least one person");
479 assert!(
480 <T as Config>::MaxRingSize::get() <= <T as Config>::OnboardingQueuePageSize::get(),
481 "onboarding queue page size must greater than or equal to max ring size"
482 );
483 }
484
485 fn on_poll(_: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
486 if weight_meter.try_consume(T::WeightInfo::on_poll_base()).is_err() {
488 return;
489 }
490 if RingsState::<T>::get().key_migration() {
491 Self::migrate_keys(weight_meter);
492 }
493
494 if let Some(ring_index) = PendingSuspensions::<T>::iter_keys().next() {
496 if Self::should_remove_suspended_keys(ring_index, true) &&
497 weight_meter.can_consume(T::WeightInfo::remove_suspended_people(
498 T::MaxRingSize::get(),
499 )) {
500 let actual = Self::remove_suspended_keys(ring_index);
501 weight_meter.consume(actual)
502 }
503 }
504
505 let merge_weight = T::WeightInfo::merge_queue_pages();
506 if !weight_meter.can_consume(merge_weight) {
507 return;
508 }
509 let merge_action = Self::should_merge_queue_pages();
510 if let QueueMergeAction::Merge {
511 initial_head,
512 new_head,
513 first_key_page,
514 second_key_page,
515 } = merge_action
516 {
517 Self::merge_queue_pages(initial_head, new_head, first_key_page, second_key_page);
518 weight_meter.consume(merge_weight);
519 }
520 }
521
522 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
523 let mut weight_meter = WeightMeter::with_limit(limit.saturating_div(2));
524 let on_idle_weight = T::WeightInfo::on_idle_base();
525 if !weight_meter.can_consume(on_idle_weight) {
526 return weight_meter.consumed();
527 }
528 weight_meter.consume(on_idle_weight);
529
530 let max_ring_size = T::MaxRingSize::get();
531 let remove_people_weight = T::WeightInfo::remove_suspended_people(max_ring_size);
532 let rings_state = RingsState::<T>::get();
533
534 if !rings_state.append_only() {
537 return weight_meter.consumed();
538 }
539 let suspension_step_weight = T::WeightInfo::pending_suspensions_iteration();
541 if !weight_meter.can_consume(suspension_step_weight) {
542 return weight_meter.consumed();
543 }
544 while let Some(ring_index) = PendingSuspensions::<T>::iter_keys().next() {
547 weight_meter.consume(suspension_step_weight);
548 if !weight_meter.can_consume(remove_people_weight) {
550 return weight_meter.consumed();
551 }
552 if Self::should_remove_suspended_keys(ring_index, false) {
553 let actual = Self::remove_suspended_keys(ring_index);
554 weight_meter.consume(actual)
555 }
556 if !weight_meter.can_consume(suspension_step_weight) {
558 return weight_meter.consumed();
559 }
560 }
561
562 let onboard_people_weight = T::WeightInfo::onboard_people();
566 if !weight_meter.can_consume(onboard_people_weight) {
567 return weight_meter.consumed();
568 }
569 let op_res = with_storage_layer::<(), DispatchError, _>(|| Self::onboard_people());
570 weight_meter.consume(onboard_people_weight);
571 if let Err(e) = op_res {
572 log::debug!(target: LOG_TARGET, "failed to onboard people: {:?}", e);
573 }
574
575 let current_ring = CurrentRingIndex::<T>::get();
576 let should_build_ring_weight = T::WeightInfo::should_build_ring(max_ring_size);
577 let build_ring_weight = T::WeightInfo::build_ring(max_ring_size);
578 for ring_index in (0..=current_ring).rev() {
579 if !weight_meter.can_consume(should_build_ring_weight) {
580 return weight_meter.consumed();
581 }
582
583 let maybe_to_include = Self::should_build_ring(ring_index, max_ring_size);
584 weight_meter.consume(should_build_ring_weight);
585 let Some(to_include) = maybe_to_include else { continue };
586 if !weight_meter.can_consume(build_ring_weight) {
587 return weight_meter.consumed();
588 }
589 let op_res = with_storage_layer::<(), DispatchError, _>(|| {
590 Self::build_ring(ring_index, to_include)
591 });
592 weight_meter.consume(build_ring_weight);
593 if let Err(e) = op_res {
594 log::error!(target: LOG_TARGET, "failed to build ring: {:?}", e);
595 }
596 }
597
598 weight_meter.consumed()
599 }
600 }
601
602 #[pallet::genesis_config]
603 pub struct GenesisConfig<T: Config> {
604 pub encoded_chunks: Vec<u8>,
605 #[serde(skip)]
606 pub _phantom_data: core::marker::PhantomData<T>,
607 pub onboarding_size: u32,
608 }
609
610 impl<T: Config> Default for GenesisConfig<T> {
611 fn default() -> Self {
612 use verifiable::ring_vrf_impl::StaticChunk;
616 let params = verifiable::ring_vrf_impl::ring_verifier_builder_params();
617 let chunks: Vec<StaticChunk> = params.0.iter().map(|c| StaticChunk(*c)).collect();
618 Self {
619 encoded_chunks: chunks.encode(),
620 _phantom_data: PhantomData,
621 onboarding_size: T::MaxRingSize::get(),
622 }
623 }
624 }
625
626 #[pallet::genesis_build]
627 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
628 fn build(&self) {
629 let chunks: Vec<<<T as Config>::Crypto as GenerateVerifiable>::StaticChunk> =
630 Decode::decode(&mut &(self.encoded_chunks.clone())[..])
631 .expect("couldn't decode chunks");
632 assert_eq!(chunks.len(), 1 << 9);
633 let page_size = <T as Config>::ChunkPageSize::get();
634
635 let mut page_idx = 0;
636 let mut chunk_idx = 0;
637 while chunk_idx < chunks.len() {
638 let chunk_idx_end = cmp::min(chunk_idx + page_size as usize, chunks.len());
639 let chunk_page: ChunksOf<T> = chunks[chunk_idx..chunk_idx_end]
640 .to_vec()
641 .try_into()
642 .expect("page size was checked against the array length; qed");
643 Chunks::<T>::insert(page_idx, chunk_page);
644 page_idx += 1;
645 chunk_idx = chunk_idx_end;
646 }
647
648 OnboardingSize::<T>::set(self.onboarding_size);
649 }
650 }
651
652 #[pallet::call(weight = <T as Config>::WeightInfo)]
653 impl<T: Config> Pallet<T> {
654 #[pallet::weight(
660 T::WeightInfo::should_build_ring(
661 limit.unwrap_or_else(T::MaxRingSize::get)
662 ).saturating_add(T::WeightInfo::build_ring(limit.unwrap_or_else(T::MaxRingSize::get))))]
663 #[pallet::call_index(100)]
664 pub fn build_ring_manual(
665 origin: OriginFor<T>,
666 ring_index: RingIndex,
667 limit: Option<u32>,
668 ) -> DispatchResultWithPostInfo {
669 ensure_signed(origin)?;
670
671 let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
673 let to_include =
674 Self::should_build_ring(ring_index, limit.unwrap_or_else(T::MaxRingSize::get))
675 .ok_or(Error::<T>::StillFresh)?;
676
677 let (next_revision, mut intermediate) =
680 if let Some(existing_root) = Root::<T>::get(ring_index) {
681 (
683 existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
684 existing_root.intermediate,
685 )
686 } else {
687 (0, T::Crypto::start_members())
689 };
690
691 T::Crypto::push_members(
693 &mut intermediate,
694 keys.iter()
695 .skip(ring_status.included as usize)
696 .take(to_include as usize)
697 .cloned(),
698 Self::fetch_chunks,
699 )
700 .map_err(|_| Error::<T>::CouldNotPush)?;
701
702 ring_status.included = ring_status.included.saturating_add(to_include);
704 RingKeysStatus::<T>::insert(ring_index, ring_status);
705
706 let root = T::Crypto::finish_members(intermediate.clone());
708 let ring_root = RingRoot { root, revision: next_revision, intermediate };
709 Root::<T>::insert(ring_index, ring_root);
710
711 Ok(Pays::No.into())
712 }
713
714 #[pallet::weight(T::WeightInfo::onboard_people())]
722 #[pallet::call_index(101)]
723 pub fn onboard_people_manual(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
724 ensure_signed(origin)?;
725
726 let (top_ring_index, mut keys) = Self::available_ring();
728 let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
729 defensive_assert!(
730 keys.len() == ring_status.total as usize,
731 "Stored key count doesn't match the actual length"
732 );
733
734 let keys_len = keys.len() as u32;
735 let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
736
737 let (mut head, tail) = QueuePageIndices::<T>::get();
738 let old_head = head;
739 let mut keys_to_include: Vec<MemberOf<T>> =
740 OnboardingQueue::<T>::take(head).into_inner();
741
742 if keys_to_include.len() < open_slots as usize && head != tail {
745 head = head.checked_add(1).unwrap_or(0);
746 let second_key_page = OnboardingQueue::<T>::take(head);
747 defensive_assert!(!second_key_page.is_empty());
748 keys_to_include.extend(second_key_page.into_iter());
749 }
750
751 let onboarding_size = OnboardingSize::<T>::get();
752
753 let (to_include, ring_filled) = Self::should_onboard_people(
754 top_ring_index,
755 &ring_status,
756 open_slots,
757 keys_to_include.len().saturated_into(),
758 onboarding_size,
759 )
760 .ok_or(Error::<T>::Incomplete)?;
761
762 let mut remaining_keys = keys_to_include.split_off(to_include as usize);
763 for key in keys_to_include.into_iter() {
764 let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
765 let mut record =
766 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
767 record.position = RingPosition::Included {
768 ring_index: top_ring_index,
769 ring_position: keys.len().saturated_into(),
770 scheduled_for_removal: false,
771 };
772 People::<T>::insert(personal_id, record);
773 keys.try_push(key).map_err(|_| Error::<T>::TooManyMembers)?;
774 }
775 RingKeys::<T>::insert(top_ring_index, keys);
776 ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
777 ring_status.total = ring_status.total.saturating_add(to_include);
778 RingKeysStatus::<T>::insert(top_ring_index, ring_status);
779
780 if ring_filled {
782 CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
783 }
784
785 if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
786 let split_idx =
787 remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
788 let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
789 remaining_keys
790 .split_off(split_idx)
791 .try_into()
792 .expect("the list shrunk so it must fit; qed");
793 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
794 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
795 OnboardingQueue::<T>::insert(old_head, remaining_keys);
796 OnboardingQueue::<T>::insert(head, second_page_keys);
797 QueuePageIndices::<T>::put((old_head, tail));
798 } else if !remaining_keys.is_empty() {
799 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
800 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
801 OnboardingQueue::<T>::insert(head, remaining_keys);
802 QueuePageIndices::<T>::put((head, tail));
803 } else {
804 if head != tail {
807 head = head.checked_add(1).unwrap_or(0);
808 }
809 QueuePageIndices::<T>::put((head, tail));
810 }
811
812 Ok(Pays::No.into())
813 }
814
815 #[pallet::call_index(102)]
819 pub fn merge_rings(
820 origin: OriginFor<T>,
821 base_ring_index: RingIndex,
822 target_ring_index: RingIndex,
823 ) -> DispatchResultWithPostInfo {
824 let _ = ensure_signed(origin)?;
825
826 ensure!(RingsState::<T>::get().append_only(), Error::<T>::SuspensionSessionInProgress);
827 let current_ring_index = CurrentRingIndex::<T>::get();
830 ensure!(
831 base_ring_index != target_ring_index &&
832 base_ring_index != current_ring_index &&
833 target_ring_index != current_ring_index,
834 Error::<T>::InvalidRing
835 );
836
837 let (mut base_keys, mut base_ring_status) = Self::ring_keys_and_info(base_ring_index);
839 ensure!(
840 base_keys.len() < T::MaxRingSize::get() as usize / 2,
841 Error::<T>::RingAboveMergeThreshold
842 );
843 ensure!(
844 PendingSuspensions::<T>::decode_len(base_ring_index).unwrap_or(0) == 0,
845 Error::<T>::SuspensionsPending
846 );
847 let target_keys = RingKeys::<T>::get(target_ring_index);
848 RingKeysStatus::<T>::remove(target_ring_index);
849 ensure!(
850 target_keys.len() < T::MaxRingSize::get() as usize / 2,
851 Error::<T>::RingAboveMergeThreshold
852 );
853 ensure!(
854 PendingSuspensions::<T>::decode_len(target_ring_index).unwrap_or(0) == 0,
855 Error::<T>::SuspensionsPending
856 );
857
858 base_ring_status.total =
860 base_ring_status.total.saturating_add(target_keys.len().saturated_into());
861
862 for key in target_keys {
863 let personal_id =
864 Keys::<T>::get(&key).defensive().ok_or(Error::<T>::KeyNotFound)?;
865 let mut record =
866 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::NotPerson)?;
867 record.position = RingPosition::Included {
868 ring_index: base_ring_index,
869 ring_position: base_keys.len().saturated_into(),
870 scheduled_for_removal: false,
871 };
872 base_keys.try_push(key).map_err(|_| Error::<T>::TooManyMembers)?;
873 People::<T>::insert(personal_id, record)
874 }
875
876 RingKeys::<T>::insert(base_ring_index, base_keys);
878 RingKeysStatus::<T>::insert(base_ring_index, base_ring_status);
879 Root::<T>::remove(target_ring_index);
882 RingKeys::<T>::remove(target_ring_index);
883 RingKeysStatus::<T>::remove(target_ring_index);
884
885 Ok(Pays::No.into())
886 }
887
888 #[pallet::call_index(0)]
893 #[pallet::weight(T::WeightInfo::under_alias().saturating_add(call.get_dispatch_info().call_weight))]
894 pub fn under_alias(
895 origin: OriginFor<T>,
896 call: Box<<T as frame_system::Config>::RuntimeCall>,
897 ) -> DispatchResultWithPostInfo {
898 let account = ensure_signed(origin.clone())?;
899 let rev_ca = AccountToAlias::<T>::get(&account).ok_or(Error::<T>::InvalidAccount)?;
900 ensure!(
901 Root::<T>::get(rev_ca.ring).is_some_and(|ring| ring.revision == rev_ca.revision),
902 DispatchError::BadOrigin,
903 );
904
905 let derivation_weight = T::WeightInfo::under_alias();
906 let local_origin = Origin::PersonalAlias(rev_ca);
907 Self::derivative_call(origin, local_origin, *call, derivation_weight)
908 }
909
910 #[pallet::call_index(1)]
920 pub fn set_alias_account(
921 origin: OriginFor<T>,
922 account: T::AccountId,
923 call_valid_at: BlockNumberFor<T>,
924 ) -> DispatchResultWithPostInfo {
925 let rev_ca = Self::ensure_revised_personal_alias(origin)?;
926 let now = frame_system::Pallet::<T>::block_number();
927 let time_tolerance = Self::account_setup_time_tolerance();
928 ensure!(
929 call_valid_at <= now && now <= call_valid_at.saturating_add(time_tolerance),
930 Error::<T>::TimeOutOfRange
931 );
932 ensure!(T::AccountContexts::contains(&rev_ca.ca.context), Error::<T>::InvalidContext);
933 ensure!(!AccountToPersonalId::<T>::contains_key(&account), Error::<T>::AccountInUse);
934
935 let old_account = AliasToAccount::<T>::get(&rev_ca.ca);
936 let old_rev_ca = old_account.as_ref().and_then(AccountToAlias::<T>::get);
937
938 let needs_revision = old_rev_ca.is_some_and(|old_rev_ca| {
939 old_rev_ca.revision != rev_ca.revision || old_rev_ca.ring != rev_ca.ring
940 });
941
942 ensure!(
944 old_account.as_ref() != Some(&account) || needs_revision,
945 Error::<T>::AliasAccountAlreadySet
946 );
947
948 if old_account.as_ref() != Some(&account) {
953 ensure!(!AccountToAlias::<T>::contains_key(&account), Error::<T>::AccountInUse);
954 if let Some(old_account) = &old_account {
955 frame_system::Pallet::<T>::dec_sufficients(old_account);
956 AccountToAlias::<T>::remove(old_account);
957 }
958 frame_system::Pallet::<T>::inc_sufficients(&account);
959 }
960
961 AccountToAlias::<T>::insert(&account, &rev_ca);
962 AliasToAccount::<T>::insert(&rev_ca.ca, &account);
963
964 if old_account.is_none() || needs_revision {
965 Ok(Pays::No.into())
966 } else {
967 Ok(Pays::Yes.into())
968 }
969 }
970
971 #[pallet::call_index(2)]
973 pub fn unset_alias_account(origin: OriginFor<T>) -> DispatchResult {
974 let alias = Self::ensure_personal_alias(origin)?;
975 let account = AliasToAccount::<T>::take(&alias).ok_or(Error::<T>::InvalidAccount)?;
976 AccountToAlias::<T>::remove(&account);
977 frame_system::Pallet::<T>::dec_sufficients(&account);
978
979 Ok(())
980 }
981
982 #[pallet::call_index(3)]
989 pub fn force_recognize_personhood(
990 origin: OriginFor<T>,
991 people: Vec<MemberOf<T>>,
992 ) -> DispatchResultWithPostInfo {
993 ensure_root(origin)?;
994 for key in people {
995 let personal_id = Self::reserve_new_id();
996 Self::recognize_personhood(personal_id, Some(key))?;
997 }
998 Ok(().into())
999 }
1000
1001 #[pallet::call_index(4)]
1017 pub fn set_personal_id_account(
1018 origin: OriginFor<T>,
1019 account: T::AccountId,
1020 call_valid_at: BlockNumberFor<T>,
1021 ) -> DispatchResultWithPostInfo {
1022 let id = Self::ensure_personal_identity(origin)?;
1023 let now = frame_system::Pallet::<T>::block_number();
1024 let time_tolerance = Self::account_setup_time_tolerance();
1025 ensure!(
1026 call_valid_at <= now && now <= call_valid_at.saturating_add(time_tolerance),
1027 Error::<T>::TimeOutOfRange
1028 );
1029 ensure!(!AccountToPersonalId::<T>::contains_key(&account), Error::<T>::AccountInUse);
1030 ensure!(!AccountToAlias::<T>::contains_key(&account), Error::<T>::AccountInUse);
1031 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1032 let pays = if let Some(old_account) = record.account {
1033 frame_system::Pallet::<T>::dec_sufficients(&old_account);
1034 AccountToPersonalId::<T>::remove(&old_account);
1035 Pays::Yes
1036 } else {
1037 Pays::No
1038 };
1039 record.account = Some(account.clone());
1040 frame_system::Pallet::<T>::inc_sufficients(&account);
1041 AccountToPersonalId::<T>::insert(&account, id);
1042 People::<T>::insert(id, &record);
1043
1044 Ok(pays.into())
1045 }
1046
1047 #[pallet::call_index(5)]
1049 pub fn unset_personal_id_account(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
1050 let id = Self::ensure_personal_identity(origin)?;
1051 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1052 let account = record.account.take().ok_or(Error::<T>::InvalidAccount)?;
1053 AccountToPersonalId::<T>::take(&account).ok_or(Error::<T>::InvalidAccount)?;
1054 frame_system::Pallet::<T>::dec_sufficients(&account);
1055 People::<T>::insert(id, &record);
1056
1057 Ok(Pays::Yes.into())
1058 }
1059
1060 #[pallet::call_index(6)]
1064 pub fn migrate_included_key(
1065 origin: OriginFor<T>,
1066 new_key: MemberOf<T>,
1067 ) -> DispatchResultWithPostInfo {
1068 let id = Self::ensure_personal_identity(origin)?;
1069 ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1070 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1071 ensure!(record.key != new_key, Error::<T>::SameKey);
1072 match &record.position {
1073 RingPosition::Included { ring_index, ring_position, .. } => {
1076 if let Some(old_migrated_key) = KeyMigrationQueue::<T>::get(id) {
1079 Keys::<T>::remove(old_migrated_key);
1080 }
1081 KeyMigrationQueue::<T>::insert(id, &new_key);
1083 record.position = RingPosition::Included {
1085 ring_index: *ring_index,
1086 ring_position: *ring_position,
1087 scheduled_for_removal: true,
1088 };
1089 People::<T>::insert(id, record);
1091 },
1092 RingPosition::Onboarding { .. } =>
1094 return Err(Error::<T>::InvalidKeyMigration.into()),
1095 RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1098 }
1099 Keys::<T>::insert(new_key, id);
1100
1101 Ok(().into())
1102 }
1103
1104 #[pallet::call_index(7)]
1107 pub fn migrate_onboarding_key(
1108 origin: OriginFor<T>,
1109 new_key: MemberOf<T>,
1110 ) -> DispatchResultWithPostInfo {
1111 let id = Self::ensure_personal_identity(origin)?;
1112 ensure!(!Keys::<T>::contains_key(&new_key), Error::<T>::KeyAlreadyInUse);
1113 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
1114 ensure!(record.key != new_key, Error::<T>::SameKey);
1115 match &record.position {
1116 RingPosition::Onboarding { queue_page } => {
1118 let mut keys = OnboardingQueue::<T>::get(queue_page);
1119 if let Some(idx) = keys.iter().position(|k| *k == record.key) {
1120 Keys::<T>::remove(&keys[idx]);
1122 keys[idx] = new_key.clone();
1124 OnboardingQueue::<T>::insert(queue_page, keys);
1125 record.key = new_key.clone();
1127 People::<T>::insert(id, record);
1129 } else {
1130 defensive!("No key found at the position in the person record of {}", id);
1131 }
1132 },
1133 RingPosition::Included { .. } => return Err(Error::<T>::InvalidKeyMigration.into()),
1135 RingPosition::Suspended => return Err(Error::<T>::Suspended.into()),
1138 }
1139 Keys::<T>::insert(new_key, id);
1140
1141 Ok(().into())
1142 }
1143
1144 #[pallet::call_index(8)]
1146 pub fn set_onboarding_size(
1147 origin: OriginFor<T>,
1148 onboarding_size: u32,
1149 ) -> DispatchResultWithPostInfo {
1150 ensure_root(origin)?;
1151 ensure!(
1152 onboarding_size <= <T as Config>::MaxRingSize::get(),
1153 Error::<T>::InvalidOnboardingSize
1154 );
1155 OnboardingSize::<T>::put(onboarding_size);
1156 Ok(Pays::No.into())
1157 }
1158 }
1159
1160 impl<T: Config> Pallet<T> {
1161 pub(crate) fn should_build_ring(ring_index: RingIndex, limit: u32) -> Option<u32> {
1164 if !RingsState::<T>::get().append_only() {
1166 return None;
1167 }
1168 if PendingSuspensions::<T>::contains_key(ring_index) {
1170 return None;
1171 }
1172
1173 let ring_status = RingKeysStatus::<T>::get(ring_index);
1174 let not_included_count = ring_status.total.saturating_sub(ring_status.included);
1175 let to_include = not_included_count.min(limit);
1176 if to_include == 0 {
1178 return None;
1179 }
1180
1181 Some(to_include)
1182 }
1183
1184 fn should_onboard_people(
1189 ring_index: RingIndex,
1190 ring_status: &RingStatus,
1191 open_slots: u32,
1192 available_for_inclusion: u32,
1193 onboarding_size: u32,
1194 ) -> Option<(u32, bool)> {
1195 if !RingsState::<T>::get().append_only() {
1197 return None;
1198 }
1199
1200 if PendingSuspensions::<T>::contains_key(ring_index) {
1202 return None;
1203 }
1204
1205 let to_include = available_for_inclusion.min(open_slots);
1206 if to_include == 0 {
1208 return None;
1209 }
1210
1211 let can_onboard_with_cohort = to_include >= onboarding_size &&
1215 ring_status.total.saturating_add(to_include.saturated_into()) <=
1216 T::MaxRingSize::get().saturating_sub(onboarding_size);
1217 let ring_filled = open_slots == to_include;
1220
1221 let should_onboard = ring_filled || can_onboard_with_cohort;
1222 if !should_onboard {
1223 return None;
1224 }
1225
1226 Some((to_include, ring_filled))
1227 }
1228
1229 pub(crate) fn should_remove_suspended_keys(
1231 ring_index: RingIndex,
1232 check_rings_state: bool,
1233 ) -> bool {
1234 if check_rings_state && !RingsState::<T>::get().append_only() {
1235 return false;
1236 }
1237 let suspended_count = PendingSuspensions::<T>::decode_len(ring_index).unwrap_or(0);
1238 if suspended_count == 0 {
1240 return false;
1241 }
1242
1243 true
1244 }
1245
1246 pub(crate) fn should_merge_queue_pages() -> QueueMergeAction<T> {
1255 let (initial_head, tail) = QueuePageIndices::<T>::get();
1256 let first_key_page = OnboardingQueue::<T>::get(initial_head);
1257 if initial_head == tail {
1260 return QueueMergeAction::NoAction;
1261 }
1262 let new_head = initial_head.checked_add(1).unwrap_or(0);
1263 let second_key_page = OnboardingQueue::<T>::get(new_head);
1264
1265 let page_size = T::OnboardingQueuePageSize::get();
1266 if first_key_page.len().saturating_add(second_key_page.len()) > page_size as usize {
1268 return QueueMergeAction::NoAction;
1269 }
1270
1271 QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page }
1272 }
1273
1274 pub(crate) fn build_ring(ring_index: RingIndex, to_include: u32) -> DispatchResult {
1277 let (keys, mut ring_status) = Self::ring_keys_and_info(ring_index);
1278 let (next_revision, mut intermediate) =
1281 if let Some(existing_root) = Root::<T>::get(ring_index) {
1282 (
1284 existing_root.revision.checked_add(1).ok_or(ArithmeticError::Overflow)?,
1285 existing_root.intermediate,
1286 )
1287 } else {
1288 (0, T::Crypto::start_members())
1290 };
1291
1292 T::Crypto::push_members(
1294 &mut intermediate,
1295 keys.iter()
1296 .skip(ring_status.included as usize)
1297 .take(to_include as usize)
1298 .cloned(),
1299 Self::fetch_chunks,
1300 )
1301 .defensive()
1302 .map_err(|_| Error::<T>::CouldNotPush)?;
1303
1304 ring_status.included = ring_status.included.saturating_add(to_include);
1306 RingKeysStatus::<T>::insert(ring_index, ring_status);
1307
1308 let root = T::Crypto::finish_members(intermediate.clone());
1310 let ring_root = RingRoot { root, revision: next_revision, intermediate };
1311 Root::<T>::insert(ring_index, ring_root);
1312 Ok(())
1313 }
1314
1315 #[transactional]
1321 pub(crate) fn onboard_people() -> DispatchResult {
1322 let (top_ring_index, mut keys) = Self::available_ring();
1324 let mut ring_status = RingKeysStatus::<T>::get(top_ring_index);
1325 defensive_assert!(
1326 keys.len() == ring_status.total as usize,
1327 "Stored key count doesn't match the actual length"
1328 );
1329
1330 let keys_len = keys.len() as u32;
1331 let open_slots = T::MaxRingSize::get().saturating_sub(keys_len);
1332
1333 let (mut head, tail) = QueuePageIndices::<T>::get();
1334 let old_head = head;
1335 let mut keys_to_include: Vec<MemberOf<T>> =
1336 OnboardingQueue::<T>::take(head).into_inner();
1337
1338 if keys_to_include.len() < open_slots as usize && head != tail {
1341 head = head.checked_add(1).unwrap_or(0);
1342 let second_key_page = OnboardingQueue::<T>::take(head);
1343 defensive_assert!(!second_key_page.is_empty());
1344 keys_to_include.extend(second_key_page.into_iter());
1345 }
1346
1347 let onboarding_size = OnboardingSize::<T>::get();
1348
1349 let (to_include, ring_filled) = Self::should_onboard_people(
1350 top_ring_index,
1351 &ring_status,
1352 open_slots,
1353 keys_to_include.len().saturated_into(),
1354 onboarding_size,
1355 )
1356 .ok_or(Error::<T>::Incomplete)?;
1357
1358 let mut remaining_keys = keys_to_include.split_off(to_include as usize);
1359 for key in keys_to_include.into_iter() {
1360 let personal_id = Keys::<T>::get(&key).defensive().ok_or(Error::<T>::NotPerson)?;
1361 let mut record =
1362 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1363 record.position = RingPosition::Included {
1364 ring_index: top_ring_index,
1365 ring_position: keys.len().saturated_into(),
1366 scheduled_for_removal: false,
1367 };
1368 People::<T>::insert(personal_id, record);
1369 keys.try_push(key).defensive().map_err(|_| Error::<T>::TooManyMembers)?;
1370 }
1371 RingKeys::<T>::insert(top_ring_index, keys);
1372 ActiveMembers::<T>::mutate(|active| *active = active.saturating_add(to_include));
1373 ring_status.total = ring_status.total.saturating_add(to_include);
1374 RingKeysStatus::<T>::insert(top_ring_index, ring_status);
1375
1376 if ring_filled {
1378 CurrentRingIndex::<T>::mutate(|i| i.saturating_inc());
1379 }
1380
1381 if remaining_keys.len() > T::OnboardingQueuePageSize::get() as usize {
1382 let split_idx =
1383 remaining_keys.len().saturating_sub(T::OnboardingQueuePageSize::get() as usize);
1384 let second_page_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1385 remaining_keys
1386 .split_off(split_idx)
1387 .try_into()
1388 .expect("the list shrunk so it must fit; qed");
1389 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1390 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1391 OnboardingQueue::<T>::insert(old_head, remaining_keys);
1392 OnboardingQueue::<T>::insert(head, second_page_keys);
1393 QueuePageIndices::<T>::put((old_head, tail));
1394 } else if !remaining_keys.is_empty() {
1395 let remaining_keys: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize> =
1396 remaining_keys.try_into().expect("the list shrunk so it must fit; qed");
1397 OnboardingQueue::<T>::insert(head, remaining_keys);
1398 QueuePageIndices::<T>::put((head, tail));
1399 } else {
1400 if head != tail {
1403 head = head.checked_add(1).unwrap_or(0);
1404 }
1405 QueuePageIndices::<T>::put((head, tail));
1406 }
1407 Ok(())
1408 }
1409
1410 fn derivative_call(
1411 mut origin: OriginFor<T>,
1412 local_origin: Origin,
1413 call: <T as frame_system::Config>::RuntimeCall,
1414 derivation_weight: Weight,
1415 ) -> DispatchResultWithPostInfo {
1416 origin.set_caller_from(<T::RuntimeOrigin as OriginTrait>::PalletsOrigin::from(
1417 local_origin,
1418 ));
1419 let info = call.get_dispatch_info();
1420 let result = call.dispatch(origin);
1421 let weight = derivation_weight.saturating_add(extract_actual_weight(&result, &info));
1422 result
1423 .map(|p| PostDispatchInfo { actual_weight: Some(weight), pays_fee: p.pays_fee })
1424 .map_err(|mut err| {
1425 err.post_info = Some(weight).into();
1426 err
1427 })
1428 }
1429
1430 pub fn ensure_personal_identity(
1433 origin: T::RuntimeOrigin,
1434 ) -> Result<PersonalId, DispatchError> {
1435 Ok(ensure_personal_identity(origin.into_caller())?)
1436 }
1437
1438 pub fn ensure_personal_alias(
1442 origin: T::RuntimeOrigin,
1443 ) -> Result<ContextualAlias, DispatchError> {
1444 Ok(ensure_personal_alias(origin.into_caller())?)
1445 }
1446
1447 pub fn ensure_revised_personal_alias(
1451 origin: T::RuntimeOrigin,
1452 ) -> Result<RevisedContextualAlias, DispatchError> {
1453 Ok(ensure_revised_personal_alias(origin.into_caller())?)
1454 }
1455
1456 pub fn available_ring() -> (RingIndex, BoundedVec<MemberOf<T>, T::MaxRingSize>) {
1459 let mut current_ring_index = CurrentRingIndex::<T>::get();
1460 let mut current_keys = RingKeys::<T>::get(current_ring_index);
1461
1462 defensive_assert!(
1463 !current_keys.is_full(),
1464 "Something bad happened inside the STF, where the current keys are full, but we should have incremented in that case."
1465 );
1466
1467 if current_keys.is_full() {
1469 current_ring_index.saturating_inc();
1470 CurrentRingIndex::<T>::put(current_ring_index);
1471 current_keys = RingKeys::<T>::get(current_ring_index);
1472 }
1473
1474 defensive_assert!(
1475 !current_keys.is_full(),
1476 "Something bad happened inside the STF, where the current key and next key are both full. Nothing we can do here."
1477 );
1478
1479 (current_ring_index, current_keys)
1480 }
1481
1482 pub fn do_insert_key(who: PersonalId, key: MemberOf<T>) -> DispatchResult {
1484 ensure!(!Keys::<T>::contains_key(&key), Error::<T>::KeyAlreadyInUse);
1486 ensure!(
1488 ReservedPersonalId::<T>::take(who).is_some(),
1489 Error::<T>::PersonalIdNotReservedOrNotRecognized
1490 );
1491
1492 Self::push_to_onboarding_queue(who, key, None)
1493 }
1494
1495 pub fn queue_personhood_suspensions(suspensions: &[PersonalId]) -> DispatchResult {
1499 ensure!(RingsState::<T>::get().mutating(), Error::<T>::NoMutationSession);
1500 for who in suspensions {
1501 let mut record = People::<T>::get(who).ok_or(Error::<T>::InvalidSuspensions)?;
1502 match record.position {
1503 RingPosition::Included { ring_index, ring_position, .. } => {
1504 let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1505 let Err(insert_idx) = suspended_indices.binary_search(&ring_position)
1506 else {
1507 return Err(Error::<T>::KeyAlreadySuspended.into())
1508 };
1509 suspended_indices
1510 .try_insert(insert_idx, ring_position)
1511 .defensive()
1512 .map_err(|_| Error::<T>::TooManyMembers)?;
1513 PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1514 },
1515 RingPosition::Onboarding { queue_page } => {
1516 let mut keys = OnboardingQueue::<T>::get(queue_page);
1517 let queue_idx = keys.iter().position(|k| *k == record.key);
1518 if let Some(idx) = queue_idx {
1519 keys.remove(idx);
1529 OnboardingQueue::<T>::insert(queue_page, keys);
1530 } else {
1531 defensive!(
1532 "No key found at the position in the person record of {}",
1533 who
1534 );
1535 }
1536 },
1537 RingPosition::Suspended => {
1538 defensive!("Suspension queued for person {} while already suspended", who);
1539 },
1540 }
1541
1542 record.position = RingPosition::Suspended;
1543 if let Some(account) = record.account {
1544 AccountToPersonalId::<T>::remove(account);
1545 record.account = None;
1546 }
1547
1548 People::<T>::insert(who, record);
1549 }
1550
1551 Ok(())
1552 }
1553
1554 pub fn resume_personhood(who: PersonalId) -> DispatchResult {
1557 let record = People::<T>::get(who).ok_or(Error::<T>::NotPerson)?;
1558 ensure!(record.position.suspended(), Error::<T>::NotSuspended);
1559 ensure!(Keys::<T>::get(&record.key) == Some(who), Error::<T>::NoKey);
1560
1561 Self::push_to_onboarding_queue(who, record.key, record.account)
1562 }
1563
1564 fn push_to_onboarding_queue(
1565 who: PersonalId,
1566 key: MemberOf<T>,
1567 account: Option<T::AccountId>,
1568 ) -> DispatchResult {
1569 let (head, mut tail) = QueuePageIndices::<T>::get();
1570 let mut keys = OnboardingQueue::<T>::get(tail);
1571 if let Err(k) = keys.try_push(key.clone()) {
1572 tail = tail.checked_add(1).unwrap_or(0);
1573 ensure!(tail != head, Error::<T>::TooManyMembers);
1574 keys = alloc::vec![k].try_into().expect("must be able to hold one key; qed");
1575 };
1576
1577 let record = PersonRecord {
1578 key,
1579 position: RingPosition::Onboarding { queue_page: tail },
1580 account,
1581 };
1582 Keys::<T>::insert(&record.key, who);
1583 People::<T>::insert(who, &record);
1584 Self::deposit_event(Event::<T>::PersonOnboarding { who, key: record.key });
1585
1586 QueuePageIndices::<T>::put((head, tail));
1587 OnboardingQueue::<T>::insert(tail, keys);
1588 Ok(())
1589 }
1590
1591 pub fn ring_keys_and_info(
1593 ring_index: RingIndex,
1594 ) -> (BoundedVec<MemberOf<T>, T::MaxRingSize>, RingStatus) {
1595 let keys = RingKeys::<T>::get(ring_index);
1596 let ring_status = RingKeysStatus::<T>::get(ring_index);
1597 defensive_assert!(
1598 keys.len() == ring_status.total as usize,
1599 "Stored key count doesn't match the actual length"
1600 );
1601 (keys, ring_status)
1602 }
1603
1604 pub(crate) fn fetch_chunks(
1606 range: Range<usize>,
1607 ) -> Result<Vec<<T::Crypto as GenerateVerifiable>::StaticChunk>, ()> {
1608 let chunk_page_size = T::ChunkPageSize::get();
1609 let expected_len = range.end.saturating_sub(range.start);
1610 let mut page_idx = range.start.checked_div(chunk_page_size as usize).ok_or(())?;
1611 let mut chunks: Vec<_> = Chunks::<T>::get(page_idx.saturated_into::<u32>())
1612 .defensive()
1613 .ok_or(())?
1614 .into_iter()
1615 .skip(range.start % chunk_page_size as usize)
1616 .take(expected_len)
1617 .collect();
1618 while chunks.len() < expected_len {
1619 page_idx = page_idx.checked_add(1).ok_or(())?;
1622 let page =
1623 Chunks::<T>::get(page_idx.saturated_into::<u32>()).defensive().ok_or(())?;
1624 chunks.extend(
1625 page.into_inner().into_iter().take(expected_len.saturating_sub(chunks.len())),
1626 );
1627 }
1628
1629 Ok(chunks)
1630 }
1631
1632 pub(crate) fn migrate_keys(meter: &mut WeightMeter) {
1636 let mut drain = KeyMigrationQueue::<T>::drain();
1637 loop {
1638 let weight = T::WeightInfo::migrate_keys_single_included_key()
1641 .saturating_add(T::DbWeight::get().reads_writes(1, 1));
1642 if !meter.can_consume(weight) {
1643 return;
1644 }
1645
1646 let op_res = with_storage_layer::<bool, DispatchError, _>(|| match drain.next() {
1647 Some((id, new_key)) =>
1648 Self::migrate_keys_single_included_key(id, new_key).map(|_| false),
1649 None => {
1650 let rings_state = RingsState::<T>::get()
1651 .end_key_migration()
1652 .map_err(|_| Error::<T>::NoMutationSession)?;
1653 RingsState::<T>::put(rings_state);
1654 meter.consume(T::DbWeight::get().reads_writes(1, 1));
1655 Ok(true)
1656 },
1657 });
1658 match op_res {
1659 Ok(false) => meter.consume(weight),
1660 Ok(true) => {
1661 meter.consume(T::DbWeight::get().reads(1));
1663 break
1664 },
1665 Err(e) => {
1666 meter.consume(weight);
1667 log::error!(target: LOG_TARGET, "failed to migrate keys: {:?}", e);
1668 break;
1669 },
1670 }
1671 }
1672 }
1673
1674 pub(crate) fn migrate_keys_single_included_key(
1677 id: PersonalId,
1678 new_key: MemberOf<T>,
1679 ) -> DispatchResult {
1680 if let Some(record) = People::<T>::get(id) {
1681 let RingPosition::Included {
1682 ring_index,
1683 ring_position,
1684 scheduled_for_removal: true,
1685 } = record.position
1686 else {
1687 Keys::<T>::remove(new_key);
1688 return Ok(())
1689 };
1690 let mut suspended_indices = PendingSuspensions::<T>::get(ring_index);
1691 let Err(insert_idx) = suspended_indices.binary_search(&ring_position) else {
1692 log::info!(target: LOG_TARGET, "key migration for person {} skipped as the person's key was already suspended", id);
1693 return Ok(());
1694 };
1695 suspended_indices
1696 .try_insert(insert_idx, ring_position)
1697 .map_err(|_| Error::<T>::TooManyMembers)?;
1698 PendingSuspensions::<T>::insert(ring_index, suspended_indices);
1699 Keys::<T>::remove(&record.key);
1700 Self::push_to_onboarding_queue(id, new_key, record.account)?;
1701 } else {
1702 log::info!(target: LOG_TARGET, "key migration for person {} skipped as no record was found", id);
1703 }
1704 Ok(())
1705 }
1706
1707 pub(crate) fn remove_suspended_keys(ring_index: RingIndex) -> Weight {
1709 let keys = RingKeys::<T>::get(ring_index);
1710 let keys_len = keys.len();
1711 let suspended_indices = PendingSuspensions::<T>::get(ring_index);
1712 let mut new_keys: BoundedVec<MemberOf<T>, T::MaxRingSize> = Default::default();
1715 let mut j = 0;
1716 for (i, key) in keys.into_iter().enumerate() {
1717 if j < suspended_indices.len() && i == suspended_indices[j] as usize {
1718 j += 1;
1719 } else if new_keys
1720 .try_push(key)
1721 .defensive_proof("cannot move more ring members than the max ring size; qed")
1722 .is_err()
1723 {
1724 return T::WeightInfo::remove_suspended_people(
1725 keys_len.try_into().unwrap_or(u32::MAX),
1726 );
1727 }
1728 }
1729
1730 let suspended_count = RingKeysStatus::<T>::mutate(ring_index, |ring_status| {
1731 let new_total = new_keys.len().saturated_into();
1732 let suspended_count = ring_status.total.saturating_sub(new_total);
1733 ring_status.total = new_total;
1734 ring_status.included = 0;
1735 suspended_count
1736 });
1737 ActiveMembers::<T>::mutate(|active| *active = active.saturating_sub(suspended_count));
1738 RingKeys::<T>::insert(ring_index, new_keys);
1739 Root::<T>::mutate(ring_index, |maybe_root| {
1740 if let Some(root) = maybe_root {
1741 root.intermediate = T::Crypto::start_members();
1744 }
1745 });
1746
1747 PendingSuspensions::<T>::remove(ring_index);
1750 T::WeightInfo::remove_suspended_people(keys_len.try_into().unwrap_or(u32::MAX))
1751 }
1752
1753 pub(crate) fn merge_queue_pages(
1761 initial_head: u32,
1762 new_head: u32,
1763 mut first_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1764 second_key_page: BoundedVec<MemberOf<T>, T::OnboardingQueuePageSize>,
1765 ) {
1766 let op_res = with_storage_layer::<(), DispatchError, _>(|| {
1767 for key in first_key_page.iter() {
1769 let personal_id =
1770 Keys::<T>::get(key).defensive().ok_or(Error::<T>::NotPerson)?;
1771 let mut record =
1772 People::<T>::get(personal_id).defensive().ok_or(Error::<T>::KeyNotFound)?;
1773 record.position = RingPosition::Onboarding { queue_page: new_head };
1774 People::<T>::insert(personal_id, record);
1775 }
1776
1777 first_key_page
1778 .try_extend(second_key_page.into_iter())
1779 .defensive()
1780 .map_err(|_| Error::<T>::TooManyMembers)?;
1781 OnboardingQueue::<T>::remove(initial_head);
1782 OnboardingQueue::<T>::insert(new_head, first_key_page);
1783 QueuePageIndices::<T>::mutate(|(h, _)| *h = new_head);
1784 Ok(())
1785 });
1786 if let Err(e) = op_res {
1787 log::error!(target: LOG_TARGET, "failed to merge queue pages: {:?}", e);
1788 }
1789 }
1790 }
1791
1792 impl<T: Config> AddOnlyPeopleTrait for Pallet<T> {
1793 type Member = MemberOf<T>;
1794
1795 fn reserve_new_id() -> PersonalId {
1796 let new_id = NextPersonalId::<T>::mutate(|id| {
1797 let new_id = *id;
1798 id.saturating_inc();
1799 new_id
1800 });
1801 ReservedPersonalId::<T>::insert(new_id, ());
1802 new_id
1803 }
1804
1805 fn cancel_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1806 ReservedPersonalId::<T>::take(personal_id).ok_or(Error::<T>::PersonalIdNotReserved)?;
1807 Ok(())
1808 }
1809
1810 fn renew_id_reservation(personal_id: PersonalId) -> Result<(), DispatchError> {
1811 if NextPersonalId::<T>::get() <= personal_id ||
1812 People::<T>::contains_key(personal_id) ||
1813 ReservedPersonalId::<T>::contains_key(personal_id)
1814 {
1815 return Err(Error::<T>::PersonalIdReservationCannotRenew.into());
1816 }
1817 ReservedPersonalId::<T>::insert(personal_id, ());
1818 Ok(())
1819 }
1820
1821 fn recognize_personhood(
1822 who: PersonalId,
1823 maybe_key: Option<MemberOf<T>>,
1824 ) -> Result<(), DispatchError> {
1825 match maybe_key {
1826 Some(key) => Self::do_insert_key(who, key),
1827 None => Self::resume_personhood(who),
1828 }
1829 }
1830
1831 #[cfg(feature = "runtime-benchmarks")]
1832 type Secret = <<T as Config>::Crypto as GenerateVerifiable>::Secret;
1833
1834 #[cfg(feature = "runtime-benchmarks")]
1835 fn mock_key(who: PersonalId) -> (Self::Member, Self::Secret) {
1836 let mut buf = [0u8; 32];
1837 buf[..core::mem::size_of::<PersonalId>()].copy_from_slice(&who.to_le_bytes()[..]);
1838 let secret = T::Crypto::new_secret(buf);
1839 (T::Crypto::member_from_secret(&secret), secret)
1840 }
1841 }
1842
1843 impl<T: Config> PeopleTrait for Pallet<T> {
1844 fn suspend_personhood(suspensions: &[PersonalId]) -> DispatchResult {
1845 Self::queue_personhood_suspensions(suspensions)
1846 }
1847 fn start_people_set_mutation_session() -> DispatchResult {
1848 let current_state = RingsState::<T>::get();
1849 RingsState::<T>::put(
1850 current_state
1851 .start_mutation_session()
1852 .map_err(|_| Error::<T>::CouldNotStartMutationSession)?,
1853 );
1854 Ok(())
1855 }
1856 fn end_people_set_mutation_session() -> DispatchResult {
1857 let current_state = RingsState::<T>::get();
1858 RingsState::<T>::put(
1859 current_state
1860 .end_mutation_session()
1861 .map_err(|_| Error::<T>::NoMutationSession)?,
1862 );
1863 Ok(())
1864 }
1865 }
1866
1867 pub fn ensure_personal_identity<OuterOrigin>(o: OuterOrigin) -> Result<PersonalId, BadOrigin>
1871 where
1872 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1873 {
1874 match o.try_into() {
1875 Ok(Origin::PersonalIdentity(m)) => Ok(m),
1876 _ => Err(BadOrigin),
1877 }
1878 }
1879
1880 pub fn ensure_personal_alias<OuterOrigin>(o: OuterOrigin) -> Result<ContextualAlias, BadOrigin>
1883 where
1884 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1885 {
1886 match o.try_into() {
1887 Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca.ca),
1888 _ => Err(BadOrigin),
1889 }
1890 }
1891
1892 pub struct EnsurePersonalIdentity<T>(PhantomData<T>);
1895 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalIdentity<T> {
1896 type Success = PersonalId;
1897
1898 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1899 ensure_personal_identity(o.clone().into_caller()).map_err(|_| o)
1900 }
1901
1902 #[cfg(feature = "runtime-benchmarks")]
1903 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1904 Ok(Origin::PersonalIdentity(0).into())
1905 }
1906 }
1907
1908 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1909 impl<{ T: Config, A }>
1910 EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalIdentity<T>
1911 {}
1912 }
1913
1914 impl<T: Config> CountedMembers for EnsurePersonalIdentity<T> {
1915 fn active_count(&self) -> u32 {
1916 Keys::<T>::count()
1917 }
1918 }
1919
1920 pub struct EnsurePersonalAlias<T>(PhantomData<T>);
1923 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsurePersonalAlias<T> {
1924 type Success = ContextualAlias;
1925
1926 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
1927 ensure_personal_alias(o.clone().into_caller()).map_err(|_| o)
1928 }
1929
1930 #[cfg(feature = "runtime-benchmarks")]
1931 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
1932 Ok(Origin::PersonalAlias(RevisedContextualAlias {
1933 revision: 0,
1934 ring: 0,
1935 ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
1936 })
1937 .into())
1938 }
1939 }
1940
1941 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
1942 impl<{ T: Config, A }>
1943 EnsureOriginWithArg< OriginFor<T>, A> for EnsurePersonalAlias<T>
1944 {}
1945 }
1946
1947 impl<T: Config> CountedMembers for EnsurePersonalAlias<T> {
1948 fn active_count(&self) -> u32 {
1949 ActiveMembers::<T>::get()
1950 }
1951 }
1952
1953 pub struct EnsurePersonalAliasInContext<T>(PhantomData<T>);
1956 impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context> for EnsurePersonalAliasInContext<T> {
1957 type Success = Alias;
1958
1959 fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
1960 match ensure_personal_alias(o.clone().into_caller()) {
1961 Ok(ca) if &ca.context == arg => Ok(ca.alias),
1962 _ => Err(o),
1963 }
1964 }
1965
1966 #[cfg(feature = "runtime-benchmarks")]
1967 fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
1968 Ok(Origin::PersonalAlias(RevisedContextualAlias {
1969 revision: 0,
1970 ring: 0,
1971 ca: ContextualAlias { alias: [1; 32], context: *context },
1972 })
1973 .into())
1974 }
1975 }
1976
1977 impl<T: Config> CountedMembers for EnsurePersonalAliasInContext<T> {
1978 fn active_count(&self) -> u32 {
1979 ActiveMembers::<T>::get()
1980 }
1981 }
1982
1983 pub fn ensure_revised_personal_alias<OuterOrigin>(
1989 o: OuterOrigin,
1990 ) -> Result<RevisedContextualAlias, BadOrigin>
1991 where
1992 OuterOrigin: TryInto<Origin, Error = OuterOrigin>,
1993 {
1994 match o.try_into() {
1995 Ok(Origin::PersonalAlias(rev_ca)) => Ok(rev_ca),
1996 _ => Err(BadOrigin),
1997 }
1998 }
1999
2000 pub struct EnsureRevisedPersonalAlias<T>(PhantomData<T>);
2005 impl<T: Config> EnsureOrigin<OriginFor<T>> for EnsureRevisedPersonalAlias<T> {
2006 type Success = RevisedContextualAlias;
2007
2008 fn try_origin(o: OriginFor<T>) -> Result<Self::Success, OriginFor<T>> {
2009 ensure_revised_personal_alias(o.clone().into_caller()).map_err(|_| o)
2010 }
2011
2012 #[cfg(feature = "runtime-benchmarks")]
2013 fn try_successful_origin() -> Result<OriginFor<T>, ()> {
2014 Ok(Origin::PersonalAlias(RevisedContextualAlias {
2015 revision: 0,
2016 ring: 0,
2017 ca: ContextualAlias { alias: [1; 32], context: [0; 32] },
2018 })
2019 .into())
2020 }
2021 }
2022
2023 frame_support::impl_ensure_origin_with_arg_ignoring_arg! {
2024 impl<{ T: Config, A }>
2025 EnsureOriginWithArg< OriginFor<T>, A> for EnsureRevisedPersonalAlias<T>
2026 {}
2027 }
2028
2029 impl<T: Config> CountedMembers for EnsureRevisedPersonalAlias<T> {
2030 fn active_count(&self) -> u32 {
2031 ActiveMembers::<T>::get()
2032 }
2033 }
2034
2035 pub struct EnsureRevisedPersonalAliasInContext<T>(PhantomData<T>);
2041 impl<T: Config> EnsureOriginWithArg<OriginFor<T>, Context>
2042 for EnsureRevisedPersonalAliasInContext<T>
2043 {
2044 type Success = RevisedAlias;
2045
2046 fn try_origin(o: OriginFor<T>, arg: &Context) -> Result<Self::Success, OriginFor<T>> {
2047 match ensure_revised_personal_alias(o.clone().into_caller()) {
2048 Ok(ca) if &ca.ca.context == arg =>
2049 Ok(RevisedAlias { revision: ca.revision, ring: ca.ring, alias: ca.ca.alias }),
2050 _ => Err(o),
2051 }
2052 }
2053
2054 #[cfg(feature = "runtime-benchmarks")]
2055 fn try_successful_origin(context: &Context) -> Result<OriginFor<T>, ()> {
2056 Ok(Origin::PersonalAlias(RevisedContextualAlias {
2057 revision: 0,
2058 ring: 0,
2059 ca: ContextualAlias { alias: [1; 32], context: *context },
2060 })
2061 .into())
2062 }
2063 }
2064
2065 impl<T: Config> CountedMembers for EnsureRevisedPersonalAliasInContext<T> {
2066 fn active_count(&self) -> u32 {
2067 ActiveMembers::<T>::get()
2068 }
2069 }
2070}