1#![cfg_attr(not(feature = "std"), no_std)]
21#![recursion_limit = "128"]
22
23use codec::{Decode, Encode, MaxEncodedLen};
24use frame_support::traits::reality::{AddOnlyPeopleTrait, PeopleTrait, PersonalId};
25use scale_info::TypeInfo;
26
27#[cfg(test)]
28mod mock;
29#[cfg(test)]
30mod tests;
31
32#[cfg(feature = "runtime-benchmarks")]
33mod benchmarking;
34pub mod weights;
35
36pub use pallet::*;
37pub use weights::WeightInfo;
38
39type MemberOf<T> = <<T as Config>::People as AddOnlyPeopleTrait>::Member;
40
41#[frame_support::pallet]
42pub mod pallet {
43 use super::*;
44 use frame_support::{pallet_prelude::*, Twox64Concat};
45 use frame_system::pallet_prelude::*;
46
47 #[pallet::pallet]
48 pub struct Pallet<T>(_);
49
50 #[pallet::config]
52 pub trait Config: frame_system::Config {
53 type WeightInfo: WeightInfo;
55
56 #[allow(deprecated)]
58 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
59
60 type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
63
64 type MaxPersonBatchSize: Get<u32>;
66
67 type People: PeopleTrait;
69 }
70
71 #[derive(
73 Clone,
74 PartialEq,
75 Eq,
76 RuntimeDebug,
77 Encode,
78 Decode,
79 MaxEncodedLen,
80 TypeInfo,
81 DecodeWithMemTracking,
82 )]
83 pub struct Record<Key> {
84 pub key: Key,
86 pub suspended: bool,
88 }
89
90 #[pallet::storage]
92 pub type ReservedIds<T: Config> = StorageMap<_, Blake2_128Concat, PersonalId, (), OptionQuery>;
93
94 #[pallet::storage]
96 pub type People<T: Config> =
97 StorageMap<_, Twox64Concat, PersonalId, Record<MemberOf<T>>, OptionQuery>;
98
99 #[pallet::event]
100 #[pallet::generate_deposit(pub(super) fn deposit_event)]
101 pub enum Event<T: Config> {
102 IdsReserved { count: u32 },
104 IdRenewed { id: PersonalId },
106 IdUnreserved { id: PersonalId },
108 PeopleRegistered { count: u32 },
110 PeopleSuspended { count: u32 },
112 PersonhoodResumed { id: PersonalId },
114 SuspensionsStarted,
116 SuspensionsEnded,
118 }
119
120 #[pallet::error]
121 pub enum Error<T> {
122 NotPerson,
124 NotSuspended,
126 NotReserved,
128 TooManyPeople,
130 }
131
132 #[pallet::call(weight = <T as Config>::WeightInfo)]
133 impl<T: Config> Pallet<T> {
134 #[pallet::weight(T::WeightInfo::reserve_ids(T::MaxPersonBatchSize::get()))]
136 #[pallet::call_index(0)]
137 pub fn reserve_ids(origin: OriginFor<T>, count: u32) -> DispatchResultWithPostInfo {
138 T::UpdateOrigin::ensure_origin_or_root(origin)?;
139 ensure!(count <= T::MaxPersonBatchSize::get(), Error::<T>::TooManyPeople);
140 for _ in 0..count {
141 let id = T::People::reserve_new_id();
142 ReservedIds::<T>::insert(id, ());
143 }
144 Self::deposit_event(Event::IdsReserved { count });
145 Ok(().into())
146 }
147
148 #[pallet::call_index(1)]
150 pub fn renew_id_reservation(
151 origin: OriginFor<T>,
152 id: PersonalId,
153 ) -> DispatchResultWithPostInfo {
154 T::UpdateOrigin::ensure_origin_or_root(origin)?;
155 T::People::renew_id_reservation(id)?;
156 ReservedIds::<T>::insert(id, ());
157
158 Self::deposit_event(Event::IdRenewed { id });
159 Ok(().into())
160 }
161
162 #[pallet::call_index(2)]
164 pub fn cancel_id_reservation(
165 origin: OriginFor<T>,
166 id: PersonalId,
167 ) -> DispatchResultWithPostInfo {
168 T::UpdateOrigin::ensure_origin_or_root(origin)?;
169 T::People::cancel_id_reservation(id)?;
170 ReservedIds::<T>::remove(id);
171
172 Self::deposit_event(Event::IdUnreserved { id });
173 Ok(().into())
174 }
175
176 #[pallet::weight(T::WeightInfo::recognize_personhood(T::MaxPersonBatchSize::get()))]
178 #[pallet::call_index(3)]
179 pub fn recognize_personhood(
180 origin: OriginFor<T>,
181 ids_and_keys: BoundedVec<(PersonalId, MemberOf<T>), T::MaxPersonBatchSize>,
182 ) -> DispatchResultWithPostInfo {
183 T::UpdateOrigin::ensure_origin_or_root(origin)?;
184 let count = ids_and_keys.len() as u32;
185 for (id, key) in ids_and_keys.into_iter() {
186 ReservedIds::<T>::take(id).ok_or(Error::<T>::NotReserved)?;
187 People::<T>::insert(id, Record { key: key.clone(), suspended: false });
188 T::People::recognize_personhood(id, Some(key))?;
189 }
190
191 Self::deposit_event(Event::PeopleRegistered { count });
192 Ok(().into())
193 }
194
195 #[pallet::weight(T::WeightInfo::suspend_personhood(T::MaxPersonBatchSize::get()))]
198 #[pallet::call_index(4)]
199 pub fn suspend_personhood(
200 origin: OriginFor<T>,
201 ids: BoundedVec<PersonalId, T::MaxPersonBatchSize>,
202 ) -> DispatchResultWithPostInfo {
203 T::UpdateOrigin::ensure_origin_or_root(origin)?;
204 T::People::suspend_personhood(&ids[..])?;
205 let count = ids.len() as u32;
206 for id in ids.into_iter() {
207 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
208 record.suspended = true;
209 People::<T>::insert(id, record);
210 }
211
212 Self::deposit_event(Event::PeopleSuspended { count });
213 Ok(().into())
214 }
215
216 #[pallet::call_index(5)]
218 pub fn resume_personhood(
219 origin: OriginFor<T>,
220 id: PersonalId,
221 ) -> DispatchResultWithPostInfo {
222 T::UpdateOrigin::ensure_origin_or_root(origin)?;
223 let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
224 ensure!(record.suspended, Error::<T>::NotSuspended);
225 T::People::recognize_personhood(id, None)?;
226 record.suspended = false;
227 People::<T>::insert(id, record);
228
229 Self::deposit_event(Event::PersonhoodResumed { id });
230 Ok(().into())
231 }
232
233 #[pallet::call_index(6)]
236 pub fn start_mutation_session(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
237 T::UpdateOrigin::ensure_origin_or_root(origin)?;
238 T::People::start_people_set_mutation_session()?;
239 Self::deposit_event(Event::SuspensionsStarted);
240 Ok(().into())
241 }
242
243 #[pallet::call_index(7)]
248 pub fn end_mutation_session(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
249 T::UpdateOrigin::ensure_origin_or_root(origin)?;
250 T::People::end_people_set_mutation_session()?;
251 Self::deposit_event(Event::SuspensionsEnded);
252 Ok(().into())
253 }
254 }
255}