git.delta.rocks / unique-network / refs/commits / bce2cc1e9ab8

difftreelog

source

pallets/identity/src/lib.rs39.8 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original license:18// This file is part of Substrate.1920// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.21// SPDX-License-Identifier: Apache-2.02223// Licensed under the Apache License, Version 2.0 (the "License");24// you may not use this file except in compliance with the License.25// You may obtain a copy of the License at26//27// 	http://www.apache.org/licenses/LICENSE-2.028//29// Unless required by applicable law or agreed to in writing, software30// distributed under the License is distributed on an "AS IS" BASIS,31// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.32// See the License for the specific language governing permissions and33// limitations under the License.3435//! # Identity Pallet36//!37//! - [`Config`]38//! - [`Call`]39//!40//! ## Overview41//!42//! A federated naming system, allowing for multiple registrars to be added from a specified origin.43//! Registrars can set a fee to provide identity-verification service. Anyone can put forth a44//! proposed identity for a fixed deposit and ask for review by any number of registrars (paying45//! each of their fees). Registrar judgements are given as an `enum`, allowing for sophisticated,46//! multi-tier opinions.47//!48//! Some judgements are identified as *sticky*, which means they cannot be removed except by49//! complete removal of the identity, or by the registrar. Judgements are allowed to represent a50//! portion of funds that have been reserved for the registrar.51//!52//! A super-user can remove accounts and in doing so, slash the deposit.53//!54//! All accounts may also have a limited number of sub-accounts which may be specified by the owner;55//! by definition, these have equivalent ownership and each has an individual name.56//!57//! The number of registrars should be limited, and the deposit made sufficiently large, to ensure58//! no state-bloat attack is viable.59//!60//! ## Interface61//!62//! ### Dispatchable Functions63//!64//! #### For general users65//! * `set_identity` - Set the associated identity of an account; a small deposit is reserved if not66//!   already taken.67//! * `clear_identity` - Remove an account's associated identity; the deposit is returned.68//! * `request_judgement` - Request a judgement from a registrar, paying a fee.69//! * `cancel_request` - Cancel the previous request for a judgement.70//!71//! #### For general users with sub-identities72//! * `set_subs` - Set the sub-accounts of an identity.73//! * `add_sub` - Add a sub-identity to an identity.74//! * `remove_sub` - Remove a sub-identity of an identity.75//! * `rename_sub` - Rename a sub-identity of an identity.76//! * `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity).77//!78//! #### For registrars79//! * `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar.80//! * `set_fields` - Set the fields that a registrar cares about in their judgements.81//! * `provide_judgement` - Provide a judgement to an identity.82//!83//! #### For super-users84//! * `add_registrar` - Add a new registrar to the system.85//! * `kill_identity` - Forcibly remove the associated identity; the deposit is lost.86//!87//! [`Call`]: ./enum.Call.html88//! [`Config`]: ./trait.Config.html8990#![cfg_attr(not(feature = "std"), no_std)]9192mod benchmarking;93#[cfg(test)]94mod tests;95mod types;96pub mod weights;9798use frame_support::{99	traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency},100};101use sp_runtime::{102	BoundedVec,103	traits::{AppendZerosInput, Hash, Saturating, StaticLookup, Zero},104};105use sp_std::prelude::*;106pub use weights::WeightInfo;107108pub use pallet::*;109pub use types::{110	Data, IdentityField, IdentityFields, IdentityInfo, Judgement, RegistrarIndex, RegistrarInfo,111	Registration,112};113114pub type BalanceOf<T> =115	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;116type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<117	<T as frame_system::Config>::AccountId,118>>::NegativeImbalance;119type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;120type RegistrarInfoOf<T> = RegistrarInfo<BalanceOf<T>, <T as frame_system::Config>::AccountId>;121type RegistrationOf<T> =122	Registration<BalanceOf<T>, <T as Config>::MaxRegistrars, <T as Config>::MaxAdditionalFields>;123type SubAccounts<T> =124	sp_runtime::BoundedVec<<T as frame_system::Config>::AccountId, <T as Config>::MaxSubAccounts>;125type SubAccountsByAccountId<T> = (126	<T as frame_system::Config>::AccountId,127	(128		BalanceOf<T>,129		BoundedVec<(<T as frame_system::Config>::AccountId, Data), <T as Config>::MaxSubAccounts>,130	),131);132133#[frame_support::pallet]134pub mod pallet {135	use super::*;136	use frame_support::pallet_prelude::*;137	use frame_system::pallet_prelude::*;138139	#[pallet::config]140	pub trait Config: frame_system::Config {141		/// The overarching event type.142		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;143144		/// The currency trait.145		type Currency: ReservableCurrency<Self::AccountId>;146147		/// The amount held on deposit for a registered identity148		#[pallet::constant]149		type BasicDeposit: Get<BalanceOf<Self>>;150151		/// The amount held on deposit per additional field for a registered identity.152		#[pallet::constant]153		type FieldDeposit: Get<BalanceOf<Self>>;154155		/// The amount held on deposit for a registered subaccount. This should account for the fact156		/// that one storage item's value will increase by the size of an account ID, and there will157		/// be another trie item whose value is the size of an account ID plus 32 bytes.158		#[pallet::constant]159		type SubAccountDeposit: Get<BalanceOf<Self>>;160161		/// The maximum number of sub-accounts allowed per identified account.162		#[pallet::constant]163		type MaxSubAccounts: Get<u32>;164165		/// Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O166		/// required to access an identity, but can be pretty high.167		#[pallet::constant]168		type MaxAdditionalFields: Get<u32>;169170		/// Maxmimum number of registrars allowed in the system. Needed to bound the complexity171		/// of, e.g., updating judgements.172		#[pallet::constant]173		type MaxRegistrars: Get<u32>;174175		/// What to do with slashed funds.176		type Slashed: OnUnbalanced<NegativeImbalanceOf<Self>>;177178		/// The origin which may forcibly set or remove a name. Root can always do this.179		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;180181		/// The origin which may add or remove registrars. Root can always do this.182		type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;183184		/// Weight information for extrinsics in this pallet.185		type WeightInfo: WeightInfo;186	}187188	#[pallet::pallet]189	pub struct Pallet<T>(_);190191	/// Information that is pertinent to identify the entity behind an account.192	///193	/// TWOX-NOTE: OK ― `AccountId` is a secure hash.194	#[pallet::storage]195	#[pallet::getter(fn identity)]196	pub(super) type IdentityOf<T: Config> = StorageMap<197		_,198		Twox64Concat,199		T::AccountId,200		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,201		OptionQuery,202	>;203204	/// The super-identity of an alternative "sub" identity together with its name, within that205	/// context. If the account is not some other account's sub-identity, then just `None`.206	#[pallet::storage]207	#[pallet::getter(fn super_of)]208	pub(super) type SuperOf<T: Config> =209		StorageMap<_, Blake2_128Concat, T::AccountId, (T::AccountId, Data), OptionQuery>;210211	/// Alternative "sub" identities of this account.212	///213	/// The first item is the deposit, the second is a vector of the accounts.214	///215	/// TWOX-NOTE: OK ― `AccountId` is a secure hash.216	#[pallet::storage]217	#[pallet::getter(fn subs_of)]218	pub(super) type SubsOf<T: Config> =219		StorageMap<_, Twox64Concat, T::AccountId, (BalanceOf<T>, SubAccounts<T>), ValueQuery>;220221	/// The set of registrars. Not expected to get very big as can only be added through a222	/// special origin (likely a council motion).223	///224	/// The index into this can be cast to `RegistrarIndex` to get a valid value.225	#[pallet::storage]226	#[pallet::getter(fn registrars)]227	pub(super) type Registrars<T: Config> =228		StorageValue<_, BoundedVec<Option<RegistrarInfoOf<T>>, T::MaxRegistrars>, ValueQuery>;229230	#[pallet::error]231	pub enum Error<T> {232		/// Too many subs-accounts.233		TooManySubAccounts,234		/// Account isn't found.235		NotFound,236		/// Account isn't named.237		NotNamed,238		/// Empty index.239		EmptyIndex,240		/// Fee is changed.241		FeeChanged,242		/// No identity found.243		NoIdentity,244		/// Sticky judgement.245		StickyJudgement,246		/// Judgement given.247		JudgementGiven,248		/// Invalid judgement.249		InvalidJudgement,250		/// The index is invalid.251		InvalidIndex,252		/// The target is invalid.253		InvalidTarget,254		/// Too many additional fields.255		TooManyFields,256		/// Maximum amount of registrars reached. Cannot add any more.257		TooManyRegistrars,258		/// Account ID is already named.259		AlreadyClaimed,260		/// Sender is not a sub-account.261		NotSub,262		/// Sub-account isn't owned by sender.263		NotOwned,264		/// The provided judgement was for a different identity.265		JudgementForDifferentIdentity,266		/// Error that occurs when there is an issue paying for judgement.267		JudgementPaymentFailed,268	}269270	#[pallet::event]271	#[pallet::generate_deposit(pub(super) fn deposit_event)]272	pub enum Event<T: Config> {273		/// A name was set or reset (which will remove all judgements).274		IdentitySet { who: T::AccountId },275		/// A name was cleared, and the given balance returned.276		IdentityCleared {277			who: T::AccountId,278			deposit: BalanceOf<T>,279		},280		/// A name was removed and the given balance slashed.281		IdentityKilled {282			who: T::AccountId,283			deposit: BalanceOf<T>,284		},285		/// A number of identities and associated info were forcibly inserted.286		IdentitiesInserted { amount: u32 },287		/// A number of identities and all associated info were forcibly removed.288		IdentitiesRemoved { amount: u32 },289		/// A judgement was asked from a registrar.290		JudgementRequested {291			who: T::AccountId,292			registrar_index: RegistrarIndex,293		},294		/// A judgement request was retracted.295		JudgementUnrequested {296			who: T::AccountId,297			registrar_index: RegistrarIndex,298		},299		/// A judgement was given by a registrar.300		JudgementGiven {301			target: T::AccountId,302			registrar_index: RegistrarIndex,303		},304		/// A registrar was added.305		RegistrarAdded { registrar_index: RegistrarIndex },306		/// A sub-identity was added to an identity and the deposit paid.307		SubIdentityAdded {308			sub: T::AccountId,309			main: T::AccountId,310			deposit: BalanceOf<T>,311		},312		/// A sub-identity was removed from an identity and the deposit freed.313		SubIdentityRemoved {314			sub: T::AccountId,315			main: T::AccountId,316			deposit: BalanceOf<T>,317		},318		/// A sub-identity was cleared, and the given deposit repatriated from the319		/// main identity account to the sub-identity account.320		SubIdentityRevoked {321			sub: T::AccountId,322			main: T::AccountId,323			deposit: BalanceOf<T>,324		},325		/// A number of identities were forcibly updated with new sub-identities.326		SubIdentitiesInserted { amount: u32 },327	}328329	#[pallet::call]330	/// Identity pallet declaration.331	impl<T: Config> Pallet<T> {332		/// Add a registrar to the system.333		///334		/// The dispatch origin for this call must be `T::RegistrarOrigin`.335		///336		/// - `account`: the account of the registrar.337		///338		/// Emits `RegistrarAdded` if successful.339		///340		/// # <weight>341		/// - `O(R)` where `R` registrar-count (governance-bounded and code-bounded).342		/// - One storage mutation (codec `O(R)`).343		/// - One event.344		/// # </weight>345		#[pallet::call_index(0)]346		#[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))]347		pub fn add_registrar(348			origin: OriginFor<T>,349			account: AccountIdLookupOf<T>,350		) -> DispatchResultWithPostInfo {351			T::RegistrarOrigin::ensure_origin(origin)?;352			let account = T::Lookup::lookup(account)?;353354			let (i, registrar_count) = <Registrars<T>>::try_mutate(355				|registrars| -> Result<(RegistrarIndex, usize), DispatchError> {356					registrars357						.try_push(Some(RegistrarInfo {358							account,359							fee: Zero::zero(),360							fields: Default::default(),361						}))362						.map_err(|_| Error::<T>::TooManyRegistrars)?;363					Ok(((registrars.len() - 1) as RegistrarIndex, registrars.len()))364				},365			)?;366367			Self::deposit_event(Event::RegistrarAdded { registrar_index: i });368369			Ok(Some(T::WeightInfo::add_registrar(registrar_count as u32)).into())370		}371372		/// Set an account's identity information and reserve the appropriate deposit.373		///374		/// If the account already has identity information, the deposit is taken as part payment375		/// for the new deposit.376		///377		/// The dispatch origin for this call must be _Signed_.378		///379		/// - `info`: The identity information.380		///381		/// Emits `IdentitySet` if successful.382		///383		/// # <weight>384		/// - `O(X + X' + R)`385		///   - where `X` additional-field-count (deposit-bounded and code-bounded)386		///   - where `R` judgements-count (registrar-count-bounded)387		/// - One balance reserve operation.388		/// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`).389		/// - One event.390		/// # </weight>391		#[pallet::call_index(1)]392		#[pallet::weight( T::WeightInfo::set_identity(393			T::MaxRegistrars::get(), // R394			T::MaxAdditionalFields::get(), // X395		))]396		pub fn set_identity(397			origin: OriginFor<T>,398			info: Box<IdentityInfo<T::MaxAdditionalFields>>,399		) -> DispatchResultWithPostInfo {400			let sender = ensure_signed(origin)?;401			let extra_fields = info.additional.len() as u32;402			ensure!(403				extra_fields <= T::MaxAdditionalFields::get(),404				Error::<T>::TooManyFields405			);406			let fd = <BalanceOf<T>>::from(extra_fields) * T::FieldDeposit::get();407408			let mut id = match <IdentityOf<T>>::get(&sender) {409				Some(mut id) => {410					// Only keep non-positive judgements.411					id.judgements.retain(|j| j.1.is_sticky());412					id.info = *info;413					id414				}415				None => Registration {416					info: *info,417					judgements: BoundedVec::default(),418					deposit: Zero::zero(),419				},420			};421422			let old_deposit = id.deposit;423			id.deposit = T::BasicDeposit::get() + fd;424			if id.deposit > old_deposit {425				T::Currency::reserve(&sender, id.deposit - old_deposit)?;426			}427			if old_deposit > id.deposit {428				let err_amount = T::Currency::unreserve(&sender, old_deposit - id.deposit);429				debug_assert!(err_amount.is_zero());430			}431432			let judgements = id.judgements.len();433			<IdentityOf<T>>::insert(&sender, id);434			Self::deposit_event(Event::IdentitySet { who: sender });435436			Ok(Some(T::WeightInfo::set_identity(437				judgements as u32, // R438				extra_fields,      // X439			))440			.into())441		}442443		/// Set the sub-accounts of the sender.444		///445		/// Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned446		/// and an amount `SubAccountDeposit` will be reserved for each item in `subs`.447		///448		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered449		/// identity.450		///451		/// - `subs`: The identity's (new) sub-accounts.452		///453		/// # <weight>454		/// - `O(P + S)`455		///   - where `P` old-subs-count (hard- and deposit-bounded).456		///   - where `S` subs-count (hard- and deposit-bounded).457		/// - At most one balance operations.458		/// - DB:459		///   - `P + S` storage mutations (codec complexity `O(1)`)460		///   - One storage read (codec complexity `O(P)`).461		///   - One storage write (codec complexity `O(S)`).462		///   - One storage-exists (`IdentityOf::contains_key`).463		/// # </weight>464		// TODO: This whole extrinsic screams "not optimized". For example we could465		// filter any overlap between new and old subs, and avoid reading/writing466		// to those values... We could also ideally avoid needing to write to467		// N storage items for N sub accounts. Right now the weight on this function468		// is a large overestimate due to the fact that it could potentially write469		// to 2 x T::MaxSubAccounts::get().470		#[pallet::call_index(2)]471		#[pallet::weight(T::WeightInfo::set_subs_old(T::MaxSubAccounts::get()) // P: Assume max sub accounts removed.472			.saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32)) // S: Assume all subs are new.473		)]474		pub fn set_subs(475			origin: OriginFor<T>,476			subs: Vec<(T::AccountId, Data)>,477		) -> DispatchResultWithPostInfo {478			let sender = ensure_signed(origin)?;479			ensure!(<IdentityOf<T>>::contains_key(&sender), Error::<T>::NotFound);480			ensure!(481				subs.len() <= T::MaxSubAccounts::get() as usize,482				Error::<T>::TooManySubAccounts483			);484485			let (old_deposit, old_ids) = <SubsOf<T>>::get(&sender);486			let new_deposit = T::SubAccountDeposit::get() * <BalanceOf<T>>::from(subs.len() as u32);487488			let not_other_sub = subs489				.iter()490				.filter_map(|i| SuperOf::<T>::get(&i.0))491				.all(|i| i.0 == sender);492			ensure!(not_other_sub, Error::<T>::AlreadyClaimed);493494			match old_deposit.cmp(&new_deposit) {495				core::cmp::Ordering::Less => {496					T::Currency::reserve(&sender, new_deposit - old_deposit)?497				}498				core::cmp::Ordering::Equal => { /* do nothing if they're equal. */ }499				core::cmp::Ordering::Greater => {500					let err_amount = T::Currency::unreserve(&sender, old_deposit - new_deposit);501					debug_assert!(err_amount.is_zero());502				}503			}504505			for s in old_ids.iter() {506				<SuperOf<T>>::remove(s);507			}508			let mut ids = <SubAccounts<T>>::default();509			for (id, name) in subs {510				<SuperOf<T>>::insert(&id, (sender.clone(), name));511				ids.try_push(id)512					.expect("subs length is less than T::MaxSubAccounts; qed");513			}514			let new_subs = ids.len();515516			if ids.is_empty() {517				<SubsOf<T>>::remove(&sender);518			} else {519				<SubsOf<T>>::insert(&sender, (new_deposit, ids));520			}521522			Ok(Some(523				T::WeightInfo::set_subs_old(old_ids.len() as u32) // P: Real number of old accounts removed.524					// S: New subs added525					.saturating_add(T::WeightInfo::set_subs_new(new_subs as u32)),526			)527			.into())528		}529530		/// Clear an account's identity info and all sub-accounts and return all deposits.531		///532		/// Payment: All reserved balances on the account are returned.533		///534		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered535		/// identity.536		///537		/// Emits `IdentityCleared` if successful.538		///539		/// # <weight>540		/// - `O(R + S + X)`541		///   - where `R` registrar-count (governance-bounded).542		///   - where `S` subs-count (hard- and deposit-bounded).543		///   - where `X` additional-field-count (deposit-bounded and code-bounded).544		/// - One balance-unreserve operation.545		/// - `2` storage reads and `S + 2` storage deletions.546		/// - One event.547		/// # </weight>548		#[pallet::call_index(3)]549		#[pallet::weight(T::WeightInfo::clear_identity(550			T::MaxRegistrars::get(), // R551			T::MaxSubAccounts::get(), // S552			T::MaxAdditionalFields::get(), // X553		))]554		pub fn clear_identity(origin: OriginFor<T>) -> DispatchResultWithPostInfo {555			let sender = ensure_signed(origin)?;556557			let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&sender);558			let id = <IdentityOf<T>>::take(&sender).ok_or(Error::<T>::NotNamed)?;559			let deposit = id.total_deposit() + subs_deposit;560			for sub in sub_ids.iter() {561				<SuperOf<T>>::remove(sub);562			}563564			let err_amount = T::Currency::unreserve(&sender, deposit);565			debug_assert!(err_amount.is_zero());566567			Self::deposit_event(Event::IdentityCleared {568				who: sender,569				deposit,570			});571572			Ok(Some(T::WeightInfo::clear_identity(573				id.judgements.len() as u32,      // R574				sub_ids.len() as u32,            // S575				id.info.additional.len() as u32, // X576			))577			.into())578		}579580		/// Request a judgement from a registrar.581		///582		/// Payment: At most `max_fee` will be reserved for payment to the registrar if judgement583		/// given.584		///585		/// The dispatch origin for this call must be _Signed_ and the sender must have a586		/// registered identity.587		///588		/// - `reg_index`: The index of the registrar whose judgement is requested.589		/// - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as:590		///591		/// ```nocompile592		/// Self::registrars().get(reg_index).unwrap().fee593		/// ```594		///595		/// Emits `JudgementRequested` if successful.596		///597		/// # <weight>598		/// - `O(R + X)`.599		/// - One balance-reserve operation.600		/// - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`.601		/// - One event.602		/// # </weight>603		#[pallet::call_index(4)]604		#[pallet::weight(T::WeightInfo::request_judgement(605			T::MaxRegistrars::get(), // R606			T::MaxAdditionalFields::get(), // X607		))]608		pub fn request_judgement(609			origin: OriginFor<T>,610			#[pallet::compact] reg_index: RegistrarIndex,611			#[pallet::compact] max_fee: BalanceOf<T>,612		) -> DispatchResultWithPostInfo {613			let sender = ensure_signed(origin)?;614			let registrars = <Registrars<T>>::get();615			let registrar = registrars616				.get(reg_index as usize)617				.and_then(Option::as_ref)618				.ok_or(Error::<T>::EmptyIndex)?;619			ensure!(max_fee >= registrar.fee, Error::<T>::FeeChanged);620			let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;621622			let item = (reg_index, Judgement::FeePaid(registrar.fee));623			match id.judgements.binary_search_by_key(&reg_index, |x| x.0) {624				Ok(i) => {625					if id.judgements[i].1.is_sticky() {626						return Err(Error::<T>::StickyJudgement.into());627					} else {628						id.judgements[i] = item629					}630				}631				Err(i) => id632					.judgements633					.try_insert(i, item)634					.map_err(|_| Error::<T>::TooManyRegistrars)?,635			}636637			T::Currency::reserve(&sender, registrar.fee)?;638639			let judgements = id.judgements.len();640			let extra_fields = id.info.additional.len();641			<IdentityOf<T>>::insert(&sender, id);642643			Self::deposit_event(Event::JudgementRequested {644				who: sender,645				registrar_index: reg_index,646			});647648			Ok(Some(T::WeightInfo::request_judgement(649				judgements as u32,650				extra_fields as u32,651			))652			.into())653		}654655		/// Cancel a previous request.656		///657		/// Payment: A previously reserved deposit is returned on success.658		///659		/// The dispatch origin for this call must be _Signed_ and the sender must have a660		/// registered identity.661		///662		/// - `reg_index`: The index of the registrar whose judgement is no longer requested.663		///664		/// Emits `JudgementUnrequested` if successful.665		///666		/// # <weight>667		/// - `O(R + X)`.668		/// - One balance-reserve operation.669		/// - One storage mutation `O(R + X)`.670		/// - One event671		/// # </weight>672		#[pallet::call_index(5)]673		#[pallet::weight(T::WeightInfo::cancel_request(674			T::MaxRegistrars::get(), // R675			T::MaxAdditionalFields::get(), // X676		))]677		pub fn cancel_request(678			origin: OriginFor<T>,679			reg_index: RegistrarIndex,680		) -> DispatchResultWithPostInfo {681			let sender = ensure_signed(origin)?;682			let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;683684			let pos = id685				.judgements686				.binary_search_by_key(&reg_index, |x| x.0)687				.map_err(|_| Error::<T>::NotFound)?;688			let fee = if let Judgement::FeePaid(fee) = id.judgements.remove(pos).1 {689				fee690			} else {691				return Err(Error::<T>::JudgementGiven.into());692			};693694			let err_amount = T::Currency::unreserve(&sender, fee);695			debug_assert!(err_amount.is_zero());696			let judgements = id.judgements.len();697			let extra_fields = id.info.additional.len();698			<IdentityOf<T>>::insert(&sender, id);699700			Self::deposit_event(Event::JudgementUnrequested {701				who: sender,702				registrar_index: reg_index,703			});704705			Ok(Some(T::WeightInfo::cancel_request(706				judgements as u32,707				extra_fields as u32,708			))709			.into())710		}711712		/// Set the fee required for a judgement to be requested from a registrar.713		///714		/// The dispatch origin for this call must be _Signed_ and the sender must be the account715		/// of the registrar whose index is `index`.716		///717		/// - `index`: the index of the registrar whose fee is to be set.718		/// - `fee`: the new fee.719		///720		/// # <weight>721		/// - `O(R)`.722		/// - One storage mutation `O(R)`.723		/// - Benchmark: 7.315 + R * 0.329 µs (min squares analysis)724		/// # </weight>725		#[pallet::call_index(6)]726		#[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] // R727		pub fn set_fee(728			origin: OriginFor<T>,729			#[pallet::compact] index: RegistrarIndex,730			#[pallet::compact] fee: BalanceOf<T>,731		) -> DispatchResultWithPostInfo {732			let who = ensure_signed(origin)?;733734			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {735				rs.get_mut(index as usize)736					.and_then(|x| x.as_mut())737					.and_then(|r| {738						if r.account == who {739							r.fee = fee;740							Some(())741						} else {742							None743						}744					})745					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;746				Ok(rs.len())747			})?;748			Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) // R749		}750751		/// Change the account associated with a registrar.752		///753		/// The dispatch origin for this call must be _Signed_ and the sender must be the account754		/// of the registrar whose index is `index`.755		///756		/// - `index`: the index of the registrar whose fee is to be set.757		/// - `new`: the new account ID.758		///759		/// # <weight>760		/// - `O(R)`.761		/// - One storage mutation `O(R)`.762		/// - Benchmark: 8.823 + R * 0.32 µs (min squares analysis)763		/// # </weight>764		#[pallet::call_index(7)]765		#[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] // R766		pub fn set_account_id(767			origin: OriginFor<T>,768			#[pallet::compact] index: RegistrarIndex,769			new: AccountIdLookupOf<T>,770		) -> DispatchResultWithPostInfo {771			let who = ensure_signed(origin)?;772			let new = T::Lookup::lookup(new)?;773774			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {775				rs.get_mut(index as usize)776					.and_then(|x| x.as_mut())777					.and_then(|r| {778						if r.account == who {779							r.account = new;780							Some(())781						} else {782							None783						}784					})785					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;786				Ok(rs.len())787			})?;788			Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) // R789		}790791		/// Set the field information for a registrar.792		///793		/// The dispatch origin for this call must be _Signed_ and the sender must be the account794		/// of the registrar whose index is `index`.795		///796		/// - `index`: the index of the registrar whose fee is to be set.797		/// - `fields`: the fields that the registrar concerns themselves with.798		///799		/// # <weight>800		/// - `O(R)`.801		/// - One storage mutation `O(R)`.802		/// - Benchmark: 7.464 + R * 0.325 µs (min squares analysis)803		/// # </weight>804		#[pallet::call_index(8)]805		#[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] // R806		pub fn set_fields(807			origin: OriginFor<T>,808			#[pallet::compact] index: RegistrarIndex,809			fields: IdentityFields,810		) -> DispatchResultWithPostInfo {811			let who = ensure_signed(origin)?;812813			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {814				rs.get_mut(index as usize)815					.and_then(|x| x.as_mut())816					.and_then(|r| {817						if r.account == who {818							r.fields = fields;819							Some(())820						} else {821							None822						}823					})824					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;825				Ok(rs.len())826			})?;827			Ok(Some(T::WeightInfo::set_fields(828				registrars as u32, // R829			))830			.into())831		}832833		/// Provide a judgement for an account's identity.834		///835		/// The dispatch origin for this call must be _Signed_ and the sender must be the account836		/// of the registrar whose index is `reg_index`.837		///838		/// - `reg_index`: the index of the registrar whose judgement is being made.839		/// - `target`: the account whose identity the judgement is upon. This must be an account840		///   with a registered identity.841		/// - `judgement`: the judgement of the registrar of index `reg_index` about `target`.842		/// - `identity`: The hash of the [`IdentityInfo`] for that the judgement is provided.843		///844		/// Emits `JudgementGiven` if successful.845		///846		/// # <weight>847		/// - `O(R + X)`.848		/// - One balance-transfer operation.849		/// - Up to one account-lookup operation.850		/// - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`.851		/// - One event.852		/// # </weight>853		#[pallet::call_index(9)]854		#[pallet::weight(T::WeightInfo::provide_judgement(855			T::MaxRegistrars::get(), // R856			T::MaxAdditionalFields::get(), // X857		))]858		pub fn provide_judgement(859			origin: OriginFor<T>,860			#[pallet::compact] reg_index: RegistrarIndex,861			target: AccountIdLookupOf<T>,862			judgement: Judgement<BalanceOf<T>>,863			identity: T::Hash,864		) -> DispatchResultWithPostInfo {865			let sender = ensure_signed(origin)?;866			let target = T::Lookup::lookup(target)?;867			ensure!(!judgement.has_deposit(), Error::<T>::InvalidJudgement);868			<Registrars<T>>::get()869				.get(reg_index as usize)870				.and_then(Option::as_ref)871				.filter(|r| r.account == sender)872				.ok_or(Error::<T>::InvalidIndex)?;873			let mut id = <IdentityOf<T>>::get(&target).ok_or(Error::<T>::InvalidTarget)?;874875			if T::Hashing::hash_of(&id.info) != identity {876				return Err(Error::<T>::JudgementForDifferentIdentity.into());877			}878879			let item = (reg_index, judgement);880			match id.judgements.binary_search_by_key(&reg_index, |x| x.0) {881				Ok(position) => {882					if let Judgement::FeePaid(fee) = id.judgements[position].1 {883						T::Currency::repatriate_reserved(884							&target,885							&sender,886							fee,887							BalanceStatus::Free,888						)889						.map_err(|_| Error::<T>::JudgementPaymentFailed)?;890					}891					id.judgements[position] = item892				}893				Err(position) => id894					.judgements895					.try_insert(position, item)896					.map_err(|_| Error::<T>::TooManyRegistrars)?,897			}898899			let judgements = id.judgements.len();900			let extra_fields = id.info.additional.len();901			<IdentityOf<T>>::insert(&target, id);902			Self::deposit_event(Event::JudgementGiven {903				target,904				registrar_index: reg_index,905			});906907			Ok(Some(T::WeightInfo::provide_judgement(908				judgements as u32,909				extra_fields as u32,910			))911			.into())912		}913914		/// Remove an account's identity and sub-account information and slash the deposits.915		///916		/// Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by917		/// `Slash`. Verification request deposits are not returned; they should be cancelled918		/// manually using `cancel_request`.919		///920		/// The dispatch origin for this call must match `T::ForceOrigin`.921		///922		/// - `target`: the account whose identity the judgement is upon. This must be an account923		///   with a registered identity.924		///925		/// Emits `IdentityKilled` if successful.926		///927		/// # <weight>928		/// - `O(R + S + X)`.929		/// - One balance-reserve operation.930		/// - `S + 2` storage mutations.931		/// - One event.932		/// # </weight>933		#[pallet::call_index(10)]934		#[pallet::weight(T::WeightInfo::kill_identity(935			T::MaxRegistrars::get(), // R936			T::MaxSubAccounts::get(), // S937			T::MaxAdditionalFields::get(), // X938		))]939		pub fn kill_identity(940			origin: OriginFor<T>,941			target: AccountIdLookupOf<T>,942		) -> DispatchResultWithPostInfo {943			T::ForceOrigin::ensure_origin(origin)?;944945			// Figure out who we're meant to be clearing.946			let target = T::Lookup::lookup(target)?;947			// Grab their deposit (and check that they have one).948			let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&target);949			let id = <IdentityOf<T>>::take(&target).ok_or(Error::<T>::NotNamed)?;950			let deposit = id.total_deposit() + subs_deposit;951			for sub in sub_ids.iter() {952				<SuperOf<T>>::remove(sub);953			}954			// Slash their deposit from them.955			T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0);956957			Self::deposit_event(Event::IdentityKilled {958				who: target,959				deposit,960			});961962			Ok(Some(T::WeightInfo::kill_identity(963				id.judgements.len() as u32,      // R964				sub_ids.len() as u32,            // S965				id.info.additional.len() as u32, // X966			))967			.into())968		}969970		/// Add the given account to the sender's subs.971		///972		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated973		/// to the sender.974		///975		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered976		/// sub identity of `sub`.977		#[pallet::call_index(11)]978		#[pallet::weight(T::WeightInfo::add_sub(T::MaxSubAccounts::get()))]979		pub fn add_sub(980			origin: OriginFor<T>,981			sub: AccountIdLookupOf<T>,982			data: Data,983		) -> DispatchResult {984			let sender = ensure_signed(origin)?;985			let sub = T::Lookup::lookup(sub)?;986			ensure!(987				IdentityOf::<T>::contains_key(&sender),988				Error::<T>::NoIdentity989			);990991			// Check if it's already claimed as sub-identity.992			ensure!(993				!SuperOf::<T>::contains_key(&sub),994				Error::<T>::AlreadyClaimed995			);996997			SubsOf::<T>::try_mutate(&sender, |(ref mut subs_deposit, ref mut sub_ids)| {998				// Ensure there is space and that the deposit is paid.999				ensure!(1000					sub_ids.len() < T::MaxSubAccounts::get() as usize,1001					Error::<T>::TooManySubAccounts1002				);1003				let deposit = T::SubAccountDeposit::get();1004				T::Currency::reserve(&sender, deposit)?;10051006				SuperOf::<T>::insert(&sub, (sender.clone(), data));1007				sub_ids1008					.try_push(sub.clone())1009					.expect("sub ids length checked above; qed");1010				*subs_deposit = subs_deposit.saturating_add(deposit);10111012				Self::deposit_event(Event::SubIdentityAdded {1013					sub,1014					main: sender.clone(),1015					deposit,1016				});1017				Ok(())1018			})1019		}10201021		/// Alter the associated name of the given sub-account.1022		///1023		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered1024		/// sub identity of `sub`.1025		#[pallet::call_index(12)]1026		#[pallet::weight(T::WeightInfo::rename_sub(T::MaxSubAccounts::get()))]1027		pub fn rename_sub(1028			origin: OriginFor<T>,1029			sub: AccountIdLookupOf<T>,1030			data: Data,1031		) -> DispatchResult {1032			let sender = ensure_signed(origin)?;1033			let sub = T::Lookup::lookup(sub)?;1034			ensure!(1035				IdentityOf::<T>::contains_key(&sender),1036				Error::<T>::NoIdentity1037			);1038			ensure!(1039				SuperOf::<T>::get(&sub).map_or(false, |x| x.0 == sender),1040				Error::<T>::NotOwned1041			);1042			SuperOf::<T>::insert(&sub, (sender, data));1043			Ok(())1044		}10451046		/// Remove the given account from the sender's subs.1047		///1048		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated1049		/// to the sender.1050		///1051		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered1052		/// sub identity of `sub`.1053		#[pallet::call_index(13)]1054		#[pallet::weight(T::WeightInfo::remove_sub(T::MaxSubAccounts::get()))]1055		pub fn remove_sub(origin: OriginFor<T>, sub: AccountIdLookupOf<T>) -> DispatchResult {1056			let sender = ensure_signed(origin)?;1057			ensure!(1058				IdentityOf::<T>::contains_key(&sender),1059				Error::<T>::NoIdentity1060			);1061			let sub = T::Lookup::lookup(sub)?;1062			let (sup, _) = SuperOf::<T>::get(&sub).ok_or(Error::<T>::NotSub)?;1063			ensure!(sup == sender, Error::<T>::NotOwned);1064			SuperOf::<T>::remove(&sub);1065			SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {1066				sub_ids.retain(|x| x != &sub);1067				let deposit = T::SubAccountDeposit::get().min(*subs_deposit);1068				*subs_deposit -= deposit;1069				let err_amount = T::Currency::unreserve(&sender, deposit);1070				debug_assert!(err_amount.is_zero());1071				Self::deposit_event(Event::SubIdentityRemoved {1072					sub,1073					main: sender,1074					deposit,1075				});1076			});1077			Ok(())1078		}10791080		/// Remove the sender as a sub-account.1081		///1082		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated1083		/// to the sender (*not* the original depositor).1084		///1085		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered1086		/// super-identity.1087		///1088		/// NOTE: This should not normally be used, but is provided in the case that the non-1089		/// controller of an account is maliciously registered as a sub-account.1090		#[pallet::call_index(14)]1091		#[pallet::weight(T::WeightInfo::quit_sub(T::MaxSubAccounts::get()))]1092		pub fn quit_sub(origin: OriginFor<T>) -> DispatchResult {1093			let sender = ensure_signed(origin)?;1094			let (sup, _) = SuperOf::<T>::take(&sender).ok_or(Error::<T>::NotSub)?;1095			SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {1096				sub_ids.retain(|x| x != &sender);1097				let deposit = T::SubAccountDeposit::get().min(*subs_deposit);1098				*subs_deposit -= deposit;1099				let _ =1100					T::Currency::repatriate_reserved(&sup, &sender, deposit, BalanceStatus::Free);1101				Self::deposit_event(Event::SubIdentityRevoked {1102					sub: sender,1103					main: sup.clone(),1104					deposit,1105				});1106			});1107			Ok(())1108		}11091110		/// Set identities to be associated with the provided accounts as force origin.1111		///1112		/// This is not meant to operate in tandem with the identity pallet as is,1113		/// and be instead used to keep identities made and verified externally,1114		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.1115		#[pallet::call_index(15)]1116		#[pallet::weight(T::WeightInfo::force_insert_identities(1117			T::MaxAdditionalFields::get(), // X1118			identities.len() as u32, // N1119		))]1120		pub fn force_insert_identities(1121			origin: OriginFor<T>,1122			identities: Vec<(T::AccountId, RegistrationOf<T>)>,1123		) -> DispatchResult {1124			T::ForceOrigin::ensure_origin(origin)?;1125			for identity in identities.clone() {1126				IdentityOf::<T>::insert(identity.0, identity.1);1127			}1128			Self::deposit_event(Event::IdentitiesInserted {1129				amount: identities.len() as u32,1130			});1131			Ok(())1132		}11331134		/// Remove identities associated with the provided accounts as force origin.1135		///1136		/// This is not meant to operate in tandem with the identity pallet as is,1137		/// and be instead used to keep identities made and verified externally,1138		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.1139		#[pallet::call_index(16)]1140		#[pallet::weight(T::WeightInfo::force_remove_identities(1141			T::MaxAdditionalFields::get(), // X1142			identities.len() as u32, // N1143		))]1144		pub fn force_remove_identities(1145			origin: OriginFor<T>,1146			identities: Vec<T::AccountId>,1147		) -> DispatchResult {1148			T::ForceOrigin::ensure_origin(origin)?;1149			for identity in identities.clone() {1150				let (_, sub_ids) = <SubsOf<T>>::take(&identity);1151				<IdentityOf<T>>::remove(&identity);1152				for sub in sub_ids.iter() {1153					<SuperOf<T>>::remove(sub);1154				}1155			}1156			Self::deposit_event(Event::IdentitiesRemoved {1157				amount: identities.len() as u32,1158			});1159			Ok(())1160		}11611162		/// Set sub-identities to be associated with the provided accounts as force origin.1163		///1164		/// This is not meant to operate in tandem with the identity pallet as is,1165		/// and be instead used to keep identities made and verified externally,1166		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.1167		#[pallet::call_index(17)]1168		#[pallet::weight(T::WeightInfo::force_set_subs(1169			T::MaxSubAccounts::get(), // S1170			subs.len() as u32, // N1171		))]1172		pub fn force_set_subs(1173			origin: OriginFor<T>,1174			subs: Vec<SubAccountsByAccountId<T>>,1175		) -> DispatchResult {1176			T::ForceOrigin::ensure_origin(origin)?;1177			for identity in subs.clone() {1178				let account = identity.0;1179				let (_, old_subs) = <SubsOf<T>>::get(&account);1180				for old_sub in old_subs {1181					<SuperOf<T>>::remove(old_sub);1182				}11831184				let mut ids = <SubAccounts<T>>::default();1185				for (id, name) in identity.1 .1 {1186					<SuperOf<T>>::insert(&id, (account.clone(), name));1187					ids.try_push(id)1188						.expect("subs length is less than T::MaxSubAccounts; qed");1189				}11901191				if ids.is_empty() {1192					<SubsOf<T>>::remove(&account);1193				} else {1194					<SubsOf<T>>::insert(account, (identity.1 .0, ids));1195				}1196			}1197			Self::deposit_event(Event::SubIdentitiesInserted {1198				amount: subs.len() as u32,1199			});1200			Ok(())1201		}1202	}1203}12041205impl<T: Config> Pallet<T> {1206	/// Get the subs of an account.1207	pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> {1208		SubsOf::<T>::get(who)1209			.11210			.into_iter()1211			.filter_map(|a| SuperOf::<T>::get(&a).map(|x| (a, x.1)))1212			.collect()1213	}12141215	/// Check if the account has corresponding identity information by the identity field.1216	pub fn has_identity(who: &T::AccountId, fields: u64) -> bool {1217		IdentityOf::<T>::get(who).map_or(false, |registration| {1218			(registration.info.fields().0.bits() & fields) == fields1219		})1220	}1221}