referrerpolicy=no-referrer-when-downgrade

pallet_dummy_dim/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! A simple interface to interact with a Proof-of-Personhood system.
19
20#![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	/// The configuration of the pallet dummy DIM.
51	#[pallet::config]
52	pub trait Config: frame_system::Config {
53		/// Weight information for extrinsics in this pallet.
54		type WeightInfo: WeightInfo;
55
56		/// The runtime event type.
57		#[allow(deprecated)]
58		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
59
60		/// The origin which may command personhood updates through this pallet. Root can always do
61		/// this.
62		type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
63
64		/// The maximum number of people supported in a single operation.
65		type MaxPersonBatchSize: Get<u32>;
66
67		/// Who to tell when we recognise personhood.
68		type People: PeopleTrait;
69	}
70
71	/// The record of recognized people.
72	#[derive(
73		Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking,
74	)]
75	pub struct Record<Key> {
76		/// The key of the person.
77		pub key: Key,
78		/// Flag describing the suspension status.
79		pub suspended: bool,
80	}
81
82	/// The personal IDs that are reserved by unproven people.
83	#[pallet::storage]
84	pub type ReservedIds<T: Config> = StorageMap<_, Blake2_128Concat, PersonalId, (), OptionQuery>;
85
86	/// The people we track along with their records.
87	#[pallet::storage]
88	pub type People<T: Config> =
89		StorageMap<_, Twox64Concat, PersonalId, Record<MemberOf<T>>, OptionQuery>;
90
91	#[pallet::event]
92	#[pallet::generate_deposit(pub(super) fn deposit_event)]
93	pub enum Event<T: Config> {
94		/// A number of IDs was reserved.
95		IdsReserved { count: u32 },
96		/// An ID was renewed.
97		IdRenewed { id: PersonalId },
98		/// A reserved ID was removed.
99		IdUnreserved { id: PersonalId },
100		/// Register multiple people.
101		PeopleRegistered { count: u32 },
102		/// Suspend a number of people.
103		PeopleSuspended { count: u32 },
104		/// Someone's personhood was resumed.
105		PersonhoodResumed { id: PersonalId },
106		/// The pallet enabled suspensions.
107		SuspensionsStarted,
108		/// The pallet disabled suspensions.
109		SuspensionsEnded,
110	}
111
112	#[pallet::error]
113	pub enum Error<T> {
114		/// The personal ID does not belong to a recognized person.
115		NotPerson,
116		/// The personal ID does not belong to a suspended person.
117		NotSuspended,
118		/// The personal ID is not reserved and awaiting recognition.
119		NotReserved,
120		/// The operation does not support this many people.
121		TooManyPeople,
122	}
123
124	#[pallet::call(weight = <T as Config>::WeightInfo)]
125	impl<T: Config> Pallet<T> {
126		/// Reserve a number of personal IDs.
127		#[pallet::weight(T::WeightInfo::reserve_ids(T::MaxPersonBatchSize::get()))]
128		#[pallet::call_index(0)]
129		pub fn reserve_ids(origin: OriginFor<T>, count: u32) -> DispatchResultWithPostInfo {
130			T::UpdateOrigin::ensure_origin_or_root(origin)?;
131			ensure!(count <= T::MaxPersonBatchSize::get(), Error::<T>::TooManyPeople);
132			for _ in 0..count {
133				let id = T::People::reserve_new_id();
134				ReservedIds::<T>::insert(id, ());
135			}
136			Self::deposit_event(Event::IdsReserved { count });
137			Ok(().into())
138		}
139
140		/// Renew a personal ID. The ID must not be in use.
141		#[pallet::call_index(1)]
142		pub fn renew_id_reservation(
143			origin: OriginFor<T>,
144			id: PersonalId,
145		) -> DispatchResultWithPostInfo {
146			T::UpdateOrigin::ensure_origin_or_root(origin)?;
147			T::People::renew_id_reservation(id)?;
148			ReservedIds::<T>::insert(id, ());
149
150			Self::deposit_event(Event::IdRenewed { id });
151			Ok(().into())
152		}
153
154		/// Cancel a personal ID reservation.
155		#[pallet::call_index(2)]
156		pub fn cancel_id_reservation(
157			origin: OriginFor<T>,
158			id: PersonalId,
159		) -> DispatchResultWithPostInfo {
160			T::UpdateOrigin::ensure_origin_or_root(origin)?;
161			T::People::cancel_id_reservation(id)?;
162			ReservedIds::<T>::remove(id);
163
164			Self::deposit_event(Event::IdUnreserved { id });
165			Ok(().into())
166		}
167
168		/// Grant personhood for a list of candidates that have reserved personal IDs.
169		#[pallet::weight(T::WeightInfo::recognize_personhood(T::MaxPersonBatchSize::get()))]
170		#[pallet::call_index(3)]
171		pub fn recognize_personhood(
172			origin: OriginFor<T>,
173			ids_and_keys: BoundedVec<(PersonalId, MemberOf<T>), T::MaxPersonBatchSize>,
174		) -> DispatchResultWithPostInfo {
175			T::UpdateOrigin::ensure_origin_or_root(origin)?;
176			let count = ids_and_keys.len() as u32;
177			for (id, key) in ids_and_keys.into_iter() {
178				ReservedIds::<T>::take(id).ok_or(Error::<T>::NotReserved)?;
179				People::<T>::insert(id, Record { key: key.clone(), suspended: false });
180				T::People::recognize_personhood(id, Some(key))?;
181			}
182
183			Self::deposit_event(Event::PeopleRegistered { count });
184			Ok(().into())
185		}
186
187		/// Suspend the personhood of a list of recognized people. The people must not currently be
188		/// suspended.
189		#[pallet::weight(T::WeightInfo::suspend_personhood(T::MaxPersonBatchSize::get()))]
190		#[pallet::call_index(4)]
191		pub fn suspend_personhood(
192			origin: OriginFor<T>,
193			ids: BoundedVec<PersonalId, T::MaxPersonBatchSize>,
194		) -> DispatchResultWithPostInfo {
195			T::UpdateOrigin::ensure_origin_or_root(origin)?;
196			T::People::suspend_personhood(&ids[..])?;
197			let count = ids.len() as u32;
198			for id in ids.into_iter() {
199				let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
200				record.suspended = true;
201				People::<T>::insert(id, record);
202			}
203
204			Self::deposit_event(Event::PeopleSuspended { count });
205			Ok(().into())
206		}
207
208		/// Resume someone's personhood. The person must currently be suspended.
209		#[pallet::call_index(5)]
210		pub fn resume_personhood(
211			origin: OriginFor<T>,
212			id: PersonalId,
213		) -> DispatchResultWithPostInfo {
214			T::UpdateOrigin::ensure_origin_or_root(origin)?;
215			let mut record = People::<T>::get(id).ok_or(Error::<T>::NotPerson)?;
216			ensure!(record.suspended, Error::<T>::NotSuspended);
217			T::People::recognize_personhood(id, None)?;
218			record.suspended = false;
219			People::<T>::insert(id, record);
220
221			Self::deposit_event(Event::PersonhoodResumed { id });
222			Ok(().into())
223		}
224
225		/// Start a mutation session in the underlying `People` interface. This call does not check
226		/// whether a mutation session is already ongoing and can start new sessions.
227		#[pallet::call_index(6)]
228		pub fn start_mutation_session(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
229			T::UpdateOrigin::ensure_origin_or_root(origin)?;
230			T::People::start_people_set_mutation_session()?;
231			Self::deposit_event(Event::SuspensionsStarted);
232			Ok(().into())
233		}
234
235		/// End a mutation session in the underlying `People` interface. This call can end multiple
236		/// mutation sessions, even ones not started by this pallet.
237		///
238		/// This call will fail if no mutation session is ongoing.
239		#[pallet::call_index(7)]
240		pub fn end_mutation_session(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
241			T::UpdateOrigin::ensure_origin_or_root(origin)?;
242			T::People::end_people_set_mutation_session()?;
243			Self::deposit_event(Event::SuspensionsEnded);
244			Ok(().into())
245		}
246	}
247}