// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! A simple interface to interact with a Proof-of-Personhood system. #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "128"] use codec::{Decode, Encode, MaxEncodedLen}; use pezframe_support::traits::reality::{AddOnlyPeopleTrait, PeopleTrait, PersonalId}; use scale_info::TypeInfo; #[cfg(test)] mod mock; #[cfg(test)] mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; pub mod weights; pub use pezpallet::*; pub use weights::WeightInfo; type MemberOf = <::People as AddOnlyPeopleTrait>::Member; #[pezframe_support::pezpallet] pub mod pezpallet { use super::*; use pezframe_support::{pezpallet_prelude::*, Twox64Concat}; use pezframe_system::pezpallet_prelude::*; #[pezpallet::pezpallet] pub struct Pezpallet(_); /// The configuration of the pezpallet dummy DIM. #[pezpallet::config] pub trait Config: pezframe_system::Config { /// Weight information for extrinsics in this pezpallet. type WeightInfo: WeightInfo; /// The runtime event type. #[allow(deprecated)] type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The origin which may command personhood updates through this pezpallet. Root can always /// do this. type UpdateOrigin: EnsureOrigin; /// The maximum number of people supported in a single operation. type MaxPersonBatchSize: Get; /// Who to tell when we recognise personhood. type People: PeopleTrait; } /// The record of recognized people. #[derive( Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo, DecodeWithMemTracking, )] pub struct Record { /// The key of the person. pub key: Key, /// Flag describing the suspension status. pub suspended: bool, } /// The personal IDs that are reserved by unproven people. #[pezpallet::storage] pub type ReservedIds = StorageMap<_, Blake2_128Concat, PersonalId, (), OptionQuery>; /// The people we track along with their records. #[pezpallet::storage] pub type People = StorageMap<_, Twox64Concat, PersonalId, Record>, OptionQuery>; #[pezpallet::event] #[pezpallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// A number of IDs was reserved. IdsReserved { count: u32 }, /// An ID was renewed. IdRenewed { id: PersonalId }, /// A reserved ID was removed. IdUnreserved { id: PersonalId }, /// Register multiple people. PeopleRegistered { count: u32 }, /// Suspend a number of people. PeopleSuspended { count: u32 }, /// Someone's personhood was resumed. PersonhoodResumed { id: PersonalId }, /// The pezpallet enabled suspensions. SuspensionsStarted, /// The pezpallet disabled suspensions. SuspensionsEnded, } #[pezpallet::error] pub enum Error { /// The personal ID does not belong to a recognized person. NotPerson, /// The personal ID does not belong to a suspended person. NotSuspended, /// The personal ID is not reserved and awaiting recognition. NotReserved, /// The operation does not support this many people. TooManyPeople, } #[pezpallet::call(weight = ::WeightInfo)] impl Pezpallet { /// Reserve a number of personal IDs. #[pezpallet::weight(T::WeightInfo::reserve_ids(T::MaxPersonBatchSize::get()))] #[pezpallet::call_index(0)] pub fn reserve_ids(origin: OriginFor, count: u32) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; ensure!(count <= T::MaxPersonBatchSize::get(), Error::::TooManyPeople); for _ in 0..count { let id = T::People::reserve_new_id(); ReservedIds::::insert(id, ()); } Self::deposit_event(Event::IdsReserved { count }); Ok(().into()) } /// Renew a personal ID. The ID must not be in use. #[pezpallet::call_index(1)] pub fn renew_id_reservation( origin: OriginFor, id: PersonalId, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; T::People::renew_id_reservation(id)?; ReservedIds::::insert(id, ()); Self::deposit_event(Event::IdRenewed { id }); Ok(().into()) } /// Cancel a personal ID reservation. #[pezpallet::call_index(2)] pub fn cancel_id_reservation( origin: OriginFor, id: PersonalId, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; T::People::cancel_id_reservation(id)?; ReservedIds::::remove(id); Self::deposit_event(Event::IdUnreserved { id }); Ok(().into()) } /// Grant personhood for a list of candidates that have reserved personal IDs. #[pezpallet::weight(T::WeightInfo::recognize_personhood(T::MaxPersonBatchSize::get()))] #[pezpallet::call_index(3)] pub fn recognize_personhood( origin: OriginFor, ids_and_keys: BoundedVec<(PersonalId, MemberOf), T::MaxPersonBatchSize>, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; let count = ids_and_keys.len() as u32; for (id, key) in ids_and_keys.into_iter() { ReservedIds::::take(id).ok_or(Error::::NotReserved)?; People::::insert(id, Record { key: key.clone(), suspended: false }); T::People::recognize_personhood(id, Some(key))?; } Self::deposit_event(Event::PeopleRegistered { count }); Ok(().into()) } /// Suspend the personhood of a list of recognized people. The people must not currently be /// suspended. #[pezpallet::weight(T::WeightInfo::suspend_personhood(T::MaxPersonBatchSize::get()))] #[pezpallet::call_index(4)] pub fn suspend_personhood( origin: OriginFor, ids: BoundedVec, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; T::People::suspend_personhood(&ids[..])?; let count = ids.len() as u32; for id in ids.into_iter() { let mut record = People::::get(id).ok_or(Error::::NotPerson)?; record.suspended = true; People::::insert(id, record); } Self::deposit_event(Event::PeopleSuspended { count }); Ok(().into()) } /// Resume someone's personhood. The person must currently be suspended. #[pezpallet::call_index(5)] pub fn resume_personhood( origin: OriginFor, id: PersonalId, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; let mut record = People::::get(id).ok_or(Error::::NotPerson)?; ensure!(record.suspended, Error::::NotSuspended); T::People::recognize_personhood(id, None)?; record.suspended = false; People::::insert(id, record); Self::deposit_event(Event::PersonhoodResumed { id }); Ok(().into()) } /// Start a mutation session in the underlying `People` interface. This call does not check /// whether a mutation session is already ongoing and can start new sessions. #[pezpallet::call_index(6)] pub fn start_mutation_session(origin: OriginFor) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; T::People::start_people_set_mutation_session()?; Self::deposit_event(Event::SuspensionsStarted); Ok(().into()) } /// End a mutation session in the underlying `People` interface. This call can end multiple /// mutation sessions, even ones not started by this pezpallet. /// /// This call will fail if no mutation session is ongoing. #[pezpallet::call_index(7)] pub fn end_mutation_session(origin: OriginFor) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin_or_root(origin)?; T::People::end_people_set_mutation_session()?; Self::deposit_event(Event::SuspensionsEnded); Ok(().into()) } } }