git.delta.rocks / unique-network / refs/commits / 02607563eb3d

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::traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency};99pub use pallet::*;100use sp_runtime::{101	traits::{AppendZerosInput, Hash, Saturating, StaticLookup, Zero},102	BoundedVec,103};104use sp_std::prelude::*;105pub use types::{106	Data, IdentityField, IdentityFields, IdentityInfo, Judgement, RegistrarIndex, RegistrarInfo,107	Registration,108};109pub use weights::WeightInfo;110111pub type BalanceOf<T> =112	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;113type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<114	<T as frame_system::Config>::AccountId,115>>::NegativeImbalance;116type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;117type RegistrarInfoOf<T> = RegistrarInfo<BalanceOf<T>, <T as frame_system::Config>::AccountId>;118type RegistrationOf<T> =119	Registration<BalanceOf<T>, <T as Config>::MaxRegistrars, <T as Config>::MaxAdditionalFields>;120type SubAccounts<T> =121	sp_runtime::BoundedVec<<T as frame_system::Config>::AccountId, <T as Config>::MaxSubAccounts>;122type SubAccountsByAccountId<T> = (123	<T as frame_system::Config>::AccountId,124	(125		BalanceOf<T>,126		BoundedVec<(<T as frame_system::Config>::AccountId, Data), <T as Config>::MaxSubAccounts>,127	),128);129130#[frame_support::pallet]131pub mod pallet {132	use frame_support::pallet_prelude::*;133	use frame_system::pallet_prelude::*;134135	use super::*;136137	#[pallet::config]138	pub trait Config: frame_system::Config {139		/// The overarching event type.140		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;141142		/// The currency trait.143		type Currency: ReservableCurrency<Self::AccountId>;144145		/// The amount held on deposit for a registered identity146		#[pallet::constant]147		type BasicDeposit: Get<BalanceOf<Self>>;148149		/// The amount held on deposit per additional field for a registered identity.150		#[pallet::constant]151		type FieldDeposit: Get<BalanceOf<Self>>;152153		/// The amount held on deposit for a registered subaccount. This should account for the fact154		/// that one storage item's value will increase by the size of an account ID, and there will155		/// be another trie item whose value is the size of an account ID plus 32 bytes.156		#[pallet::constant]157		type SubAccountDeposit: Get<BalanceOf<Self>>;158159		/// The maximum number of sub-accounts allowed per identified account.160		#[pallet::constant]161		type MaxSubAccounts: Get<u32>;162163		/// Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O164		/// required to access an identity, but can be pretty high.165		#[pallet::constant]166		type MaxAdditionalFields: Get<u32>;167168		/// Maxmimum number of registrars allowed in the system. Needed to bound the complexity169		/// of, e.g., updating judgements.170		#[pallet::constant]171		type MaxRegistrars: Get<u32>;172173		/// What to do with slashed funds.174		type Slashed: OnUnbalanced<NegativeImbalanceOf<Self>>;175176		/// The origin which may forcibly set or remove a name. Root can always do this.177		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;178179		/// The origin which may add or remove registrars. Root can always do this.180		type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;181182		/// Weight information for extrinsics in this pallet.183		type WeightInfo: WeightInfo;184	}185186	#[pallet::pallet]187	pub struct Pallet<T>(_);188189	/// Information that is pertinent to identify the entity behind an account.190	///191	/// TWOX-NOTE: OK ― `AccountId` is a secure hash.192	#[pallet::storage]193	#[pallet::getter(fn identity)]194	pub(super) type IdentityOf<T: Config> = StorageMap<195		_,196		Twox64Concat,197		T::AccountId,198		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,199		OptionQuery,200	>;201202	/// The super-identity of an alternative "sub" identity together with its name, within that203	/// context. If the account is not some other account's sub-identity, then just `None`.204	#[pallet::storage]205	#[pallet::getter(fn super_of)]206	pub(super) type SuperOf<T: Config> =207		StorageMap<_, Blake2_128Concat, T::AccountId, (T::AccountId, Data), OptionQuery>;208209	/// Alternative "sub" identities of this account.210	///211	/// The first item is the deposit, the second is a vector of the accounts.212	///213	/// TWOX-NOTE: OK ― `AccountId` is a secure hash.214	#[pallet::storage]215	#[pallet::getter(fn subs_of)]216	pub(super) type SubsOf<T: Config> =217		StorageMap<_, Twox64Concat, T::AccountId, (BalanceOf<T>, SubAccounts<T>), ValueQuery>;218219	/// The set of registrars. Not expected to get very big as can only be added through a220	/// special origin (likely a council motion).221	///222	/// The index into this can be cast to `RegistrarIndex` to get a valid value.223	#[pallet::storage]224	#[pallet::getter(fn registrars)]225	pub(super) type Registrars<T: Config> =226		StorageValue<_, BoundedVec<Option<RegistrarInfoOf<T>>, T::MaxRegistrars>, ValueQuery>;227228	#[pallet::error]229	pub enum Error<T> {230		/// Too many subs-accounts.231		TooManySubAccounts,232		/// Account isn't found.233		NotFound,234		/// Account isn't named.235		NotNamed,236		/// Empty index.237		EmptyIndex,238		/// Fee is changed.239		FeeChanged,240		/// No identity found.241		NoIdentity,242		/// Sticky judgement.243		StickyJudgement,244		/// Judgement given.245		JudgementGiven,246		/// Invalid judgement.247		InvalidJudgement,248		/// The index is invalid.249		InvalidIndex,250		/// The target is invalid.251		InvalidTarget,252		/// Too many additional fields.253		TooManyFields,254		/// Maximum amount of registrars reached. Cannot add any more.255		TooManyRegistrars,256		/// Account ID is already named.257		AlreadyClaimed,258		/// Sender is not a sub-account.259		NotSub,260		/// Sub-account isn't owned by sender.261		NotOwned,262		/// The provided judgement was for a different identity.263		JudgementForDifferentIdentity,264		/// Error that occurs when there is an issue paying for judgement.265		JudgementPaymentFailed,266	}267268	#[pallet::event]269	#[pallet::generate_deposit(pub(super) fn deposit_event)]270	pub enum Event<T: Config> {271		/// A name was set or reset (which will remove all judgements).272		IdentitySet { who: T::AccountId },273		/// A name was cleared, and the given balance returned.274		IdentityCleared {275			who: T::AccountId,276			deposit: BalanceOf<T>,277		},278		/// A name was removed and the given balance slashed.279		IdentityKilled {280			who: T::AccountId,281			deposit: BalanceOf<T>,282		},283		/// A number of identities and associated info were forcibly inserted.284		IdentitiesInserted { amount: u32 },285		/// A number of identities and all associated info were forcibly removed.286		IdentitiesRemoved { amount: u32 },287		/// A judgement was asked from a registrar.288		JudgementRequested {289			who: T::AccountId,290			registrar_index: RegistrarIndex,291		},292		/// A judgement request was retracted.293		JudgementUnrequested {294			who: T::AccountId,295			registrar_index: RegistrarIndex,296		},297		/// A judgement was given by a registrar.298		JudgementGiven {299			target: T::AccountId,300			registrar_index: RegistrarIndex,301		},302		/// A registrar was added.303		RegistrarAdded { registrar_index: RegistrarIndex },304		/// A sub-identity was added to an identity and the deposit paid.305		SubIdentityAdded {306			sub: T::AccountId,307			main: T::AccountId,308			deposit: BalanceOf<T>,309		},310		/// A sub-identity was removed from an identity and the deposit freed.311		SubIdentityRemoved {312			sub: T::AccountId,313			main: T::AccountId,314			deposit: BalanceOf<T>,315		},316		/// A sub-identity was cleared, and the given deposit repatriated from the317		/// main identity account to the sub-identity account.318		SubIdentityRevoked {319			sub: T::AccountId,320			main: T::AccountId,321			deposit: BalanceOf<T>,322		},323		/// A number of identities were forcibly updated with new sub-identities.324		SubIdentitiesInserted { amount: u32 },325	}326327	#[pallet::call]328	/// Identity pallet declaration.329	impl<T: Config> Pallet<T> {330		/// Add a registrar to the system.331		///332		/// The dispatch origin for this call must be `T::RegistrarOrigin`.333		///334		/// - `account`: the account of the registrar.335		///336		/// Emits `RegistrarAdded` if successful.337		///338		/// # <weight>339		/// - `O(R)` where `R` registrar-count (governance-bounded and code-bounded).340		/// - One storage mutation (codec `O(R)`).341		/// - One event.342		/// # </weight>343		#[pallet::call_index(0)]344		#[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))]345		pub fn add_registrar(346			origin: OriginFor<T>,347			account: AccountIdLookupOf<T>,348		) -> DispatchResultWithPostInfo {349			T::RegistrarOrigin::ensure_origin(origin)?;350			let account = T::Lookup::lookup(account)?;351352			let (i, registrar_count) = <Registrars<T>>::try_mutate(353				|registrars| -> Result<(RegistrarIndex, usize), DispatchError> {354					registrars355						.try_push(Some(RegistrarInfo {356							account,357							fee: Zero::zero(),358							fields: Default::default(),359						}))360						.map_err(|_| Error::<T>::TooManyRegistrars)?;361					Ok(((registrars.len() - 1) as RegistrarIndex, registrars.len()))362				},363			)?;364365			Self::deposit_event(Event::RegistrarAdded { registrar_index: i });366367			Ok(Some(T::WeightInfo::add_registrar(registrar_count as u32)).into())368		}369370		/// Set an account's identity information and reserve the appropriate deposit.371		///372		/// If the account already has identity information, the deposit is taken as part payment373		/// for the new deposit.374		///375		/// The dispatch origin for this call must be _Signed_.376		///377		/// - `info`: The identity information.378		///379		/// Emits `IdentitySet` if successful.380		///381		/// # <weight>382		/// - `O(X + X' + R)`383		///   - where `X` additional-field-count (deposit-bounded and code-bounded)384		///   - where `R` judgements-count (registrar-count-bounded)385		/// - One balance reserve operation.386		/// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`).387		/// - One event.388		/// # </weight>389		#[pallet::call_index(1)]390		#[pallet::weight( T::WeightInfo::set_identity(391			T::MaxRegistrars::get(), // R392			T::MaxAdditionalFields::get(), // X393		))]394		pub fn set_identity(395			origin: OriginFor<T>,396			info: Box<IdentityInfo<T::MaxAdditionalFields>>,397		) -> DispatchResultWithPostInfo {398			let sender = ensure_signed(origin)?;399			let extra_fields = info.additional.len() as u32;400			ensure!(401				extra_fields <= T::MaxAdditionalFields::get(),402				Error::<T>::TooManyFields403			);404			let fd = <BalanceOf<T>>::from(extra_fields) * T::FieldDeposit::get();405406			let mut id = match <IdentityOf<T>>::get(&sender) {407				Some(mut id) => {408					// Only keep non-positive judgements.409					id.judgements.retain(|j| j.1.is_sticky());410					id.info = *info;411					id412				}413				None => Registration {414					info: *info,415					judgements: BoundedVec::default(),416					deposit: Zero::zero(),417				},418			};419420			let old_deposit = id.deposit;421			id.deposit = T::BasicDeposit::get() + fd;422			if id.deposit > old_deposit {423				T::Currency::reserve(&sender, id.deposit - old_deposit)?;424			}425			if old_deposit > id.deposit {426				let err_amount = T::Currency::unreserve(&sender, old_deposit - id.deposit);427				debug_assert!(err_amount.is_zero());428			}429430			let judgements = id.judgements.len();431			<IdentityOf<T>>::insert(&sender, id);432			Self::deposit_event(Event::IdentitySet { who: sender });433434			Ok(Some(T::WeightInfo::set_identity(435				judgements as u32, // R436				extra_fields,      // X437			))438			.into())439		}440441		/// Set the sub-accounts of the sender.442		///443		/// Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned444		/// and an amount `SubAccountDeposit` will be reserved for each item in `subs`.445		///446		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered447		/// identity.448		///449		/// - `subs`: The identity's (new) sub-accounts.450		///451		/// # <weight>452		/// - `O(P + S)`453		///   - where `P` old-subs-count (hard- and deposit-bounded).454		///   - where `S` subs-count (hard- and deposit-bounded).455		/// - At most one balance operations.456		/// - DB:457		///   - `P + S` storage mutations (codec complexity `O(1)`)458		///   - One storage read (codec complexity `O(P)`).459		///   - One storage write (codec complexity `O(S)`).460		///   - One storage-exists (`IdentityOf::contains_key`).461		/// # </weight>462		// TODO: This whole extrinsic screams "not optimized". For example we could463		// filter any overlap between new and old subs, and avoid reading/writing464		// to those values... We could also ideally avoid needing to write to465		// N storage items for N sub accounts. Right now the weight on this function466		// is a large overestimate due to the fact that it could potentially write467		// to 2 x T::MaxSubAccounts::get().468		#[pallet::call_index(2)]469		#[pallet::weight(T::WeightInfo::set_subs_old(T::MaxSubAccounts::get()) // P: Assume max sub accounts removed.470			.saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32)) // S: Assume all subs are new.471		)]472		pub fn set_subs(473			origin: OriginFor<T>,474			subs: Vec<(T::AccountId, Data)>,475		) -> DispatchResultWithPostInfo {476			let sender = ensure_signed(origin)?;477			ensure!(<IdentityOf<T>>::contains_key(&sender), Error::<T>::NotFound);478			ensure!(479				subs.len() <= T::MaxSubAccounts::get() as usize,480				Error::<T>::TooManySubAccounts481			);482483			let (old_deposit, old_ids) = <SubsOf<T>>::get(&sender);484			let new_deposit = T::SubAccountDeposit::get() * <BalanceOf<T>>::from(subs.len() as u32);485486			let not_other_sub = subs487				.iter()488				.filter_map(|i| SuperOf::<T>::get(&i.0))489				.all(|i| i.0 == sender);490			ensure!(not_other_sub, Error::<T>::AlreadyClaimed);491492			match old_deposit.cmp(&new_deposit) {493				core::cmp::Ordering::Less => {494					T::Currency::reserve(&sender, new_deposit - old_deposit)?495				}496				core::cmp::Ordering::Equal => { /* do nothing if they're equal. */ }497				core::cmp::Ordering::Greater => {498					let err_amount = T::Currency::unreserve(&sender, old_deposit - new_deposit);499					debug_assert!(err_amount.is_zero());500				}501			}502503			for s in old_ids.iter() {504				<SuperOf<T>>::remove(s);505			}506			let mut ids = <SubAccounts<T>>::default();507			for (id, name) in subs {508				<SuperOf<T>>::insert(&id, (sender.clone(), name));509				ids.try_push(id)510					.expect("subs length is less than T::MaxSubAccounts; qed");511			}512			let new_subs = ids.len();513514			if ids.is_empty() {515				<SubsOf<T>>::remove(&sender);516			} else {517				<SubsOf<T>>::insert(&sender, (new_deposit, ids));518			}519520			Ok(Some(521				T::WeightInfo::set_subs_old(old_ids.len() as u32) // P: Real number of old accounts removed.522					// S: New subs added523					.saturating_add(T::WeightInfo::set_subs_new(new_subs as u32)),524			)525			.into())526		}527528		/// Clear an account's identity info and all sub-accounts and return all deposits.529		///530		/// Payment: All reserved balances on the account are returned.531		///532		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered533		/// identity.534		///535		/// Emits `IdentityCleared` if successful.536		///537		/// # <weight>538		/// - `O(R + S + X)`539		///   - where `R` registrar-count (governance-bounded).540		///   - where `S` subs-count (hard- and deposit-bounded).541		///   - where `X` additional-field-count (deposit-bounded and code-bounded).542		/// - One balance-unreserve operation.543		/// - `2` storage reads and `S + 2` storage deletions.544		/// - One event.545		/// # </weight>546		#[pallet::call_index(3)]547		#[pallet::weight(T::WeightInfo::clear_identity(548			T::MaxRegistrars::get(), // R549			T::MaxSubAccounts::get(), // S550			T::MaxAdditionalFields::get(), // X551		))]552		pub fn clear_identity(origin: OriginFor<T>) -> DispatchResultWithPostInfo {553			let sender = ensure_signed(origin)?;554555			let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&sender);556			let id = <IdentityOf<T>>::take(&sender).ok_or(Error::<T>::NotNamed)?;557			let deposit = id.total_deposit() + subs_deposit;558			for sub in sub_ids.iter() {559				<SuperOf<T>>::remove(sub);560			}561562			let err_amount = T::Currency::unreserve(&sender, deposit);563			debug_assert!(err_amount.is_zero());564565			Self::deposit_event(Event::IdentityCleared {566				who: sender,567				deposit,568			});569570			Ok(Some(T::WeightInfo::clear_identity(571				id.judgements.len() as u32,      // R572				sub_ids.len() as u32,            // S573				id.info.additional.len() as u32, // X574			))575			.into())576		}577578		/// Request a judgement from a registrar.579		///580		/// Payment: At most `max_fee` will be reserved for payment to the registrar if judgement581		/// given.582		///583		/// The dispatch origin for this call must be _Signed_ and the sender must have a584		/// registered identity.585		///586		/// - `reg_index`: The index of the registrar whose judgement is requested.587		/// - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as:588		///589		/// ```nocompile590		/// Self::registrars().get(reg_index).unwrap().fee591		/// ```592		///593		/// Emits `JudgementRequested` if successful.594		///595		/// # <weight>596		/// - `O(R + X)`.597		/// - One balance-reserve operation.598		/// - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`.599		/// - One event.600		/// # </weight>601		#[pallet::call_index(4)]602		#[pallet::weight(T::WeightInfo::request_judgement(603			T::MaxRegistrars::get(), // R604			T::MaxAdditionalFields::get(), // X605		))]606		pub fn request_judgement(607			origin: OriginFor<T>,608			#[pallet::compact] reg_index: RegistrarIndex,609			#[pallet::compact] max_fee: BalanceOf<T>,610		) -> DispatchResultWithPostInfo {611			let sender = ensure_signed(origin)?;612			let registrars = <Registrars<T>>::get();613			let registrar = registrars614				.get(reg_index as usize)615				.and_then(Option::as_ref)616				.ok_or(Error::<T>::EmptyIndex)?;617			ensure!(max_fee >= registrar.fee, Error::<T>::FeeChanged);618			let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;619620			let item = (reg_index, Judgement::FeePaid(registrar.fee));621			match id.judgements.binary_search_by_key(&reg_index, |x| x.0) {622				Ok(i) => {623					if id.judgements[i].1.is_sticky() {624						return Err(Error::<T>::StickyJudgement.into());625					} else {626						id.judgements[i] = item627					}628				}629				Err(i) => id630					.judgements631					.try_insert(i, item)632					.map_err(|_| Error::<T>::TooManyRegistrars)?,633			}634635			T::Currency::reserve(&sender, registrar.fee)?;636637			let judgements = id.judgements.len();638			let extra_fields = id.info.additional.len();639			<IdentityOf<T>>::insert(&sender, id);640641			Self::deposit_event(Event::JudgementRequested {642				who: sender,643				registrar_index: reg_index,644			});645646			Ok(Some(T::WeightInfo::request_judgement(647				judgements as u32,648				extra_fields as u32,649			))650			.into())651		}652653		/// Cancel a previous request.654		///655		/// Payment: A previously reserved deposit is returned on success.656		///657		/// The dispatch origin for this call must be _Signed_ and the sender must have a658		/// registered identity.659		///660		/// - `reg_index`: The index of the registrar whose judgement is no longer requested.661		///662		/// Emits `JudgementUnrequested` if successful.663		///664		/// # <weight>665		/// - `O(R + X)`.666		/// - One balance-reserve operation.667		/// - One storage mutation `O(R + X)`.668		/// - One event669		/// # </weight>670		#[pallet::call_index(5)]671		#[pallet::weight(T::WeightInfo::cancel_request(672			T::MaxRegistrars::get(), // R673			T::MaxAdditionalFields::get(), // X674		))]675		pub fn cancel_request(676			origin: OriginFor<T>,677			reg_index: RegistrarIndex,678		) -> DispatchResultWithPostInfo {679			let sender = ensure_signed(origin)?;680			let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;681682			let pos = id683				.judgements684				.binary_search_by_key(&reg_index, |x| x.0)685				.map_err(|_| Error::<T>::NotFound)?;686			let fee = if let Judgement::FeePaid(fee) = id.judgements.remove(pos).1 {687				fee688			} else {689				return Err(Error::<T>::JudgementGiven.into());690			};691692			let err_amount = T::Currency::unreserve(&sender, fee);693			debug_assert!(err_amount.is_zero());694			let judgements = id.judgements.len();695			let extra_fields = id.info.additional.len();696			<IdentityOf<T>>::insert(&sender, id);697698			Self::deposit_event(Event::JudgementUnrequested {699				who: sender,700				registrar_index: reg_index,701			});702703			Ok(Some(T::WeightInfo::cancel_request(704				judgements as u32,705				extra_fields as u32,706			))707			.into())708		}709710		/// Set the fee required for a judgement to be requested from a registrar.711		///712		/// The dispatch origin for this call must be _Signed_ and the sender must be the account713		/// of the registrar whose index is `index`.714		///715		/// - `index`: the index of the registrar whose fee is to be set.716		/// - `fee`: the new fee.717		///718		/// # <weight>719		/// - `O(R)`.720		/// - One storage mutation `O(R)`.721		/// - Benchmark: 7.315 + R * 0.329 µs (min squares analysis)722		/// # </weight>723		#[pallet::call_index(6)]724		#[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] // R725		pub fn set_fee(726			origin: OriginFor<T>,727			#[pallet::compact] index: RegistrarIndex,728			#[pallet::compact] fee: BalanceOf<T>,729		) -> DispatchResultWithPostInfo {730			let who = ensure_signed(origin)?;731732			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {733				rs.get_mut(index as usize)734					.and_then(|x| x.as_mut())735					.and_then(|r| {736						if r.account == who {737							r.fee = fee;738							Some(())739						} else {740							None741						}742					})743					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;744				Ok(rs.len())745			})?;746			Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) // R747		}748749		/// Change the account associated with a registrar.750		///751		/// The dispatch origin for this call must be _Signed_ and the sender must be the account752		/// of the registrar whose index is `index`.753		///754		/// - `index`: the index of the registrar whose fee is to be set.755		/// - `new`: the new account ID.756		///757		/// # <weight>758		/// - `O(R)`.759		/// - One storage mutation `O(R)`.760		/// - Benchmark: 8.823 + R * 0.32 µs (min squares analysis)761		/// # </weight>762		#[pallet::call_index(7)]763		#[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] // R764		pub fn set_account_id(765			origin: OriginFor<T>,766			#[pallet::compact] index: RegistrarIndex,767			new: AccountIdLookupOf<T>,768		) -> DispatchResultWithPostInfo {769			let who = ensure_signed(origin)?;770			let new = T::Lookup::lookup(new)?;771772			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {773				rs.get_mut(index as usize)774					.and_then(|x| x.as_mut())775					.and_then(|r| {776						if r.account == who {777							r.account = new;778							Some(())779						} else {780							None781						}782					})783					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;784				Ok(rs.len())785			})?;786			Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) // R787		}788789		/// Set the field information for a registrar.790		///791		/// The dispatch origin for this call must be _Signed_ and the sender must be the account792		/// of the registrar whose index is `index`.793		///794		/// - `index`: the index of the registrar whose fee is to be set.795		/// - `fields`: the fields that the registrar concerns themselves with.796		///797		/// # <weight>798		/// - `O(R)`.799		/// - One storage mutation `O(R)`.800		/// - Benchmark: 7.464 + R * 0.325 µs (min squares analysis)801		/// # </weight>802		#[pallet::call_index(8)]803		#[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] // R804		pub fn set_fields(805			origin: OriginFor<T>,806			#[pallet::compact] index: RegistrarIndex,807			fields: IdentityFields,808		) -> DispatchResultWithPostInfo {809			let who = ensure_signed(origin)?;810811			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {812				rs.get_mut(index as usize)813					.and_then(|x| x.as_mut())814					.and_then(|r| {815						if r.account == who {816							r.fields = fields;817							Some(())818						} else {819							None820						}821					})822					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;823				Ok(rs.len())824			})?;825			Ok(Some(T::WeightInfo::set_fields(826				registrars as u32, // R827			))828			.into())829		}830831		/// Provide a judgement for an account's identity.832		///833		/// The dispatch origin for this call must be _Signed_ and the sender must be the account834		/// of the registrar whose index is `reg_index`.835		///836		/// - `reg_index`: the index of the registrar whose judgement is being made.837		/// - `target`: the account whose identity the judgement is upon. This must be an account838		///   with a registered identity.839		/// - `judgement`: the judgement of the registrar of index `reg_index` about `target`.840		/// - `identity`: The hash of the [`IdentityInfo`] for that the judgement is provided.841		///842		/// Emits `JudgementGiven` if successful.843		///844		/// # <weight>845		/// - `O(R + X)`.846		/// - One balance-transfer operation.847		/// - Up to one account-lookup operation.848		/// - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`.849		/// - One event.850		/// # </weight>851		#[pallet::call_index(9)]852		#[pallet::weight(T::WeightInfo::provide_judgement(853			T::MaxRegistrars::get(), // R854			T::MaxAdditionalFields::get(), // X855		))]856		pub fn provide_judgement(857			origin: OriginFor<T>,858			#[pallet::compact] reg_index: RegistrarIndex,859			target: AccountIdLookupOf<T>,860			judgement: Judgement<BalanceOf<T>>,861			identity: T::Hash,862		) -> DispatchResultWithPostInfo {863			let sender = ensure_signed(origin)?;864			let target = T::Lookup::lookup(target)?;865			ensure!(!judgement.has_deposit(), Error::<T>::InvalidJudgement);866			<Registrars<T>>::get()867				.get(reg_index as usize)868				.and_then(Option::as_ref)869				.filter(|r| r.account == sender)870				.ok_or(Error::<T>::InvalidIndex)?;871			let mut id = <IdentityOf<T>>::get(&target).ok_or(Error::<T>::InvalidTarget)?;872873			if T::Hashing::hash_of(&id.info) != identity {874				return Err(Error::<T>::JudgementForDifferentIdentity.into());875			}876877			let item = (reg_index, judgement);878			match id.judgements.binary_search_by_key(&reg_index, |x| x.0) {879				Ok(position) => {880					if let Judgement::FeePaid(fee) = id.judgements[position].1 {881						T::Currency::repatriate_reserved(882							&target,883							&sender,884							fee,885							BalanceStatus::Free,886						)887						.map_err(|_| Error::<T>::JudgementPaymentFailed)?;888					}889					id.judgements[position] = item890				}891				Err(position) => id892					.judgements893					.try_insert(position, item)894					.map_err(|_| Error::<T>::TooManyRegistrars)?,895			}896897			let judgements = id.judgements.len();898			let extra_fields = id.info.additional.len();899			<IdentityOf<T>>::insert(&target, id);900			Self::deposit_event(Event::JudgementGiven {901				target,902				registrar_index: reg_index,903			});904905			Ok(Some(T::WeightInfo::provide_judgement(906				judgements as u32,907				extra_fields as u32,908			))909			.into())910		}911912		/// Remove an account's identity and sub-account information and slash the deposits.913		///914		/// Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by915		/// `Slash`. Verification request deposits are not returned; they should be cancelled916		/// manually using `cancel_request`.917		///918		/// The dispatch origin for this call must match `T::ForceOrigin`.919		///920		/// - `target`: the account whose identity the judgement is upon. This must be an account921		///   with a registered identity.922		///923		/// Emits `IdentityKilled` if successful.924		///925		/// # <weight>926		/// - `O(R + S + X)`.927		/// - One balance-reserve operation.928		/// - `S + 2` storage mutations.929		/// - One event.930		/// # </weight>931		#[pallet::call_index(10)]932		#[pallet::weight(T::WeightInfo::kill_identity(933			T::MaxRegistrars::get(), // R934			T::MaxSubAccounts::get(), // S935			T::MaxAdditionalFields::get(), // X936		))]937		pub fn kill_identity(938			origin: OriginFor<T>,939			target: AccountIdLookupOf<T>,940		) -> DispatchResultWithPostInfo {941			T::ForceOrigin::ensure_origin(origin)?;942943			// Figure out who we're meant to be clearing.944			let target = T::Lookup::lookup(target)?;945			// Grab their deposit (and check that they have one).946			let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&target);947			let id = <IdentityOf<T>>::take(&target).ok_or(Error::<T>::NotNamed)?;948			let deposit = id.total_deposit() + subs_deposit;949			for sub in sub_ids.iter() {950				<SuperOf<T>>::remove(sub);951			}952			// Slash their deposit from them.953			T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0);954955			Self::deposit_event(Event::IdentityKilled {956				who: target,957				deposit,958			});959960			Ok(Some(T::WeightInfo::kill_identity(961				id.judgements.len() as u32,      // R962				sub_ids.len() as u32,            // S963				id.info.additional.len() as u32, // X964			))965			.into())966		}967968		/// Add the given account to the sender's subs.969		///970		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated971		/// to the sender.972		///973		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered974		/// sub identity of `sub`.975		#[pallet::call_index(11)]976		#[pallet::weight(T::WeightInfo::add_sub(T::MaxSubAccounts::get()))]977		pub fn add_sub(978			origin: OriginFor<T>,979			sub: AccountIdLookupOf<T>,980			data: Data,981		) -> DispatchResult {982			let sender = ensure_signed(origin)?;983			let sub = T::Lookup::lookup(sub)?;984			ensure!(985				IdentityOf::<T>::contains_key(&sender),986				Error::<T>::NoIdentity987			);988989			// Check if it's already claimed as sub-identity.990			ensure!(991				!SuperOf::<T>::contains_key(&sub),992				Error::<T>::AlreadyClaimed993			);994995			SubsOf::<T>::try_mutate(&sender, |(ref mut subs_deposit, ref mut sub_ids)| {996				// Ensure there is space and that the deposit is paid.997				ensure!(998					sub_ids.len() < T::MaxSubAccounts::get() as usize,999					Error::<T>::TooManySubAccounts1000				);1001				let deposit = T::SubAccountDeposit::get();1002				T::Currency::reserve(&sender, deposit)?;10031004				SuperOf::<T>::insert(&sub, (sender.clone(), data));1005				sub_ids1006					.try_push(sub.clone())1007					.expect("sub ids length checked above; qed");1008				*subs_deposit = subs_deposit.saturating_add(deposit);10091010				Self::deposit_event(Event::SubIdentityAdded {1011					sub,1012					main: sender.clone(),1013					deposit,1014				});1015				Ok(())1016			})1017		}10181019		/// Alter the associated name of the given sub-account.1020		///1021		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered1022		/// sub identity of `sub`.1023		#[pallet::call_index(12)]1024		#[pallet::weight(T::WeightInfo::rename_sub(T::MaxSubAccounts::get()))]1025		pub fn rename_sub(1026			origin: OriginFor<T>,1027			sub: AccountIdLookupOf<T>,1028			data: Data,1029		) -> DispatchResult {1030			let sender = ensure_signed(origin)?;1031			let sub = T::Lookup::lookup(sub)?;1032			ensure!(1033				IdentityOf::<T>::contains_key(&sender),1034				Error::<T>::NoIdentity1035			);1036			ensure!(1037				SuperOf::<T>::get(&sub).map_or(false, |x| x.0 == sender),1038				Error::<T>::NotOwned1039			);1040			SuperOf::<T>::insert(&sub, (sender, data));1041			Ok(())1042		}10431044		/// Remove the given account from the sender's subs.1045		///1046		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated1047		/// to the sender.1048		///1049		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered1050		/// sub identity of `sub`.1051		#[pallet::call_index(13)]1052		#[pallet::weight(T::WeightInfo::remove_sub(T::MaxSubAccounts::get()))]1053		pub fn remove_sub(origin: OriginFor<T>, sub: AccountIdLookupOf<T>) -> DispatchResult {1054			let sender = ensure_signed(origin)?;1055			ensure!(1056				IdentityOf::<T>::contains_key(&sender),1057				Error::<T>::NoIdentity1058			);1059			let sub = T::Lookup::lookup(sub)?;1060			let (sup, _) = SuperOf::<T>::get(&sub).ok_or(Error::<T>::NotSub)?;1061			ensure!(sup == sender, Error::<T>::NotOwned);1062			SuperOf::<T>::remove(&sub);1063			SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {1064				sub_ids.retain(|x| x != &sub);1065				let deposit = T::SubAccountDeposit::get().min(*subs_deposit);1066				*subs_deposit -= deposit;1067				let err_amount = T::Currency::unreserve(&sender, deposit);1068				debug_assert!(err_amount.is_zero());1069				Self::deposit_event(Event::SubIdentityRemoved {1070					sub,1071					main: sender,1072					deposit,1073				});1074			});1075			Ok(())1076		}10771078		/// Remove the sender as a sub-account.1079		///1080		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated1081		/// to the sender (*not* the original depositor).1082		///1083		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered1084		/// super-identity.1085		///1086		/// NOTE: This should not normally be used, but is provided in the case that the non-1087		/// controller of an account is maliciously registered as a sub-account.1088		#[pallet::call_index(14)]1089		#[pallet::weight(T::WeightInfo::quit_sub(T::MaxSubAccounts::get()))]1090		pub fn quit_sub(origin: OriginFor<T>) -> DispatchResult {1091			let sender = ensure_signed(origin)?;1092			let (sup, _) = SuperOf::<T>::take(&sender).ok_or(Error::<T>::NotSub)?;1093			SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {1094				sub_ids.retain(|x| x != &sender);1095				let deposit = T::SubAccountDeposit::get().min(*subs_deposit);1096				*subs_deposit -= deposit;1097				let _ =1098					T::Currency::repatriate_reserved(&sup, &sender, deposit, BalanceStatus::Free);1099				Self::deposit_event(Event::SubIdentityRevoked {1100					sub: sender,1101					main: sup.clone(),1102					deposit,1103				});1104			});1105			Ok(())1106		}11071108		/// Set identities to be associated with the provided accounts as force origin.1109		///1110		/// This is not meant to operate in tandem with the identity pallet as is,1111		/// and be instead used to keep identities made and verified externally,1112		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.1113		#[pallet::call_index(15)]1114		#[pallet::weight(T::WeightInfo::force_insert_identities(1115			T::MaxAdditionalFields::get(), // X1116			identities.len() as u32, // N1117		))]1118		pub fn force_insert_identities(1119			origin: OriginFor<T>,1120			identities: Vec<(T::AccountId, RegistrationOf<T>)>,1121		) -> DispatchResult {1122			T::ForceOrigin::ensure_origin(origin)?;1123			for identity in identities.clone() {1124				IdentityOf::<T>::insert(identity.0, identity.1);1125			}1126			Self::deposit_event(Event::IdentitiesInserted {1127				amount: identities.len() as u32,1128			});1129			Ok(())1130		}11311132		/// Remove identities associated with the provided accounts as force origin.1133		///1134		/// This is not meant to operate in tandem with the identity pallet as is,1135		/// and be instead used to keep identities made and verified externally,1136		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.1137		#[pallet::call_index(16)]1138		#[pallet::weight(T::WeightInfo::force_remove_identities(1139			T::MaxAdditionalFields::get(), // X1140			identities.len() as u32, // N1141		))]1142		pub fn force_remove_identities(1143			origin: OriginFor<T>,1144			identities: Vec<T::AccountId>,1145		) -> DispatchResult {1146			T::ForceOrigin::ensure_origin(origin)?;1147			for identity in identities.clone() {1148				let (_, sub_ids) = <SubsOf<T>>::take(&identity);1149				<IdentityOf<T>>::remove(&identity);1150				for sub in sub_ids.iter() {1151					<SuperOf<T>>::remove(sub);1152				}1153			}1154			Self::deposit_event(Event::IdentitiesRemoved {1155				amount: identities.len() as u32,1156			});1157			Ok(())1158		}11591160		/// Set sub-identities to be associated with the provided accounts as force origin.1161		///1162		/// This is not meant to operate in tandem with the identity pallet as is,1163		/// and be instead used to keep identities made and verified externally,1164		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.1165		#[pallet::call_index(17)]1166		#[pallet::weight(T::WeightInfo::force_set_subs(1167			T::MaxSubAccounts::get(), // S1168			subs.len() as u32, // N1169		))]1170		pub fn force_set_subs(1171			origin: OriginFor<T>,1172			subs: Vec<SubAccountsByAccountId<T>>,1173		) -> DispatchResult {1174			T::ForceOrigin::ensure_origin(origin)?;1175			for identity in subs.clone() {1176				let account = identity.0;1177				let (_, old_subs) = <SubsOf<T>>::get(&account);1178				for old_sub in old_subs {1179					<SuperOf<T>>::remove(old_sub);1180				}11811182				let mut ids = <SubAccounts<T>>::default();1183				for (id, name) in identity.1 .1 {1184					<SuperOf<T>>::insert(&id, (account.clone(), name));1185					ids.try_push(id)1186						.expect("subs length is less than T::MaxSubAccounts; qed");1187				}11881189				if ids.is_empty() {1190					<SubsOf<T>>::remove(&account);1191				} else {1192					<SubsOf<T>>::insert(account, (identity.1 .0, ids));1193				}1194			}1195			Self::deposit_event(Event::SubIdentitiesInserted {1196				amount: subs.len() as u32,1197			});1198			Ok(())1199		}1200	}1201}12021203impl<T: Config> Pallet<T> {1204	/// Get the subs of an account.1205	pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> {1206		SubsOf::<T>::get(who)1207			.11208			.into_iter()1209			.filter_map(|a| SuperOf::<T>::get(&a).map(|x| (a, x.1)))1210			.collect()1211	}12121213	/// Check if the account has corresponding identity information by the identity field.1214	pub fn has_identity(who: &T::AccountId, fields: u64) -> bool {1215		IdentityOf::<T>::get(who).map_or(false, |registration| {1216			(registration.info.fields().0.bits() & fields) == fields1217		})1218	}1219}