From 91851951856b8effe627fb1d151fe336a51eef2d Mon Sep 17 00:00:00 2001
From: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com>
Date: Tue, 24 Oct 2023 13:47:11 +0200
Subject: [PATCH] Make `IdentityInfo` generic in `pallet-identity` (#1661)

Fixes #179

# Description

This PR makes the structure containing identity information used in
`pallet-identity` generic through the pallet `Config`. Additionally, the
old structure is now available in a separate module called `simple`
(pending rename) and is compatible with the new interface.

Another change in this PR is that while the `additional` field in
`IdentityInfo` stays for backwards compatibility reasons, the associated
costs are stil present in the pallet through the `additional` function
in the `IdentityInformationProvider` interface. This function is marked
as deprecated as it is only a temporary solution to the backwards
compatibility problem we had. In short, we could have removed the
additional fields in the struct and done a migration, but we chose to
wait and do it off-chain through the genesis of the system parachain.
After we move the identity pallet to the parachain, additional fields
will be migrated into the existing fields and the `additional` key-value
store will be removed. Until that happens, this interface will provide
the necessary information to properly account for the associated costs.

Additionally, this PR fixes an unrelated issue; the `IdentityField` enum
used to represent the fields as bitflags couldn't store more than 8
fields, even though it was marked as `#[repr(u64)]`. This was because of
the `derive` implementation of `TypeInfo`, which assumed `u8` semantics.
The custom implementation of this trait in
https://github.com/paritytech/polkadot-sdk/commit/0105cc0396b7a53d0b290f48b1225847f6d17321
fixes the issue.

---------

Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Co-authored-by: Sam Johnson <sam@durosoft.com>
Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
---
 polkadot/runtime/rococo/src/lib.rs           |   2 +
 polkadot/runtime/westend/src/lib.rs          |   2 +
 substrate/bin/node/runtime/src/lib.rs        |   2 +
 substrate/frame/alliance/src/lib.rs          |   2 +-
 substrate/frame/alliance/src/mock.rs         |   3 +-
 substrate/frame/identity/src/benchmarking.rs |  60 ++---
 substrate/frame/identity/src/lib.rs          | 163 ++++++--------
 substrate/frame/identity/src/simple.rs       | 185 +++++++++++++++
 substrate/frame/identity/src/tests.rs        |  55 ++++-
 substrate/frame/identity/src/types.rs        | 224 +++++++------------
 10 files changed, 404 insertions(+), 294 deletions(-)
 create mode 100644 substrate/frame/identity/src/simple.rs

diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index cc12920ff82..e6ad061ce06 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -76,6 +76,7 @@ use frame_support::{
 };
 use frame_system::EnsureRoot;
 use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId};
+use pallet_identity::simple::IdentityInfo;
 use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_session::historical as session_historical;
 use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo};
@@ -610,6 +611,7 @@ impl pallet_identity::Config for Runtime {
 	type SubAccountDeposit = SubAccountDeposit;
 	type MaxSubAccounts = MaxSubAccounts;
 	type MaxAdditionalFields = MaxAdditionalFields;
+	type IdentityInformation = IdentityInfo<MaxAdditionalFields>;
 	type MaxRegistrars = MaxRegistrars;
 	type Slashed = Treasury;
 	type ForceOrigin = EitherOf<EnsureRoot<Self::AccountId>, GeneralAdmin>;
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index d166c8a3326..9ee4f3cf23e 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -40,6 +40,7 @@ use frame_support::{
 };
 use frame_system::EnsureRoot;
 use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId};
+use pallet_identity::simple::IdentityInfo;
 use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_session::historical as session_historical;
 use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo};
@@ -876,6 +877,7 @@ impl pallet_identity::Config for Runtime {
 	type SubAccountDeposit = SubAccountDeposit;
 	type MaxSubAccounts = MaxSubAccounts;
 	type MaxAdditionalFields = MaxAdditionalFields;
+	type IdentityInformation = IdentityInfo<MaxAdditionalFields>;
 	type MaxRegistrars = MaxRegistrars;
 	type ForceOrigin = EitherOf<EnsureRoot<Self::AccountId>, GeneralAdmin>;
 	type RegistrarOrigin = EitherOf<EnsureRoot<Self::AccountId>, GeneralAdmin>;
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 36ad4d985cf..f4c8a5940a3 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -60,6 +60,7 @@ use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce};
 use pallet_asset_conversion::{NativeOrAssetId, NativeOrAssetIdConverter};
 use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600};
 use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf};
+use pallet_identity::simple::IdentityInfo;
 use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_nfts::PalletFeatures;
 use pallet_nis::WithMaximumOf;
@@ -1474,6 +1475,7 @@ impl pallet_identity::Config for Runtime {
 	type SubAccountDeposit = SubAccountDeposit;
 	type MaxSubAccounts = MaxSubAccounts;
 	type MaxAdditionalFields = MaxAdditionalFields;
+	type IdentityInformation = IdentityInfo<MaxAdditionalFields>;
 	type MaxRegistrars = MaxRegistrars;
 	type Slashed = Treasury;
 	type ForceOrigin = EnsureRootOrHalfCouncil;
diff --git a/substrate/frame/alliance/src/lib.rs b/substrate/frame/alliance/src/lib.rs
index 627399f805b..f3ff03780f5 100644
--- a/substrate/frame/alliance/src/lib.rs
+++ b/substrate/frame/alliance/src/lib.rs
@@ -112,7 +112,7 @@ use frame_support::{
 	},
 	weights::Weight,
 };
-use pallet_identity::IdentityField;
+use pallet_identity::simple::IdentityField;
 use scale_info::TypeInfo;
 
 pub use pallet::*;
diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs
index eac8411699e..a5970bc7af6 100644
--- a/substrate/frame/alliance/src/mock.rs
+++ b/substrate/frame/alliance/src/mock.rs
@@ -31,7 +31,7 @@ pub use frame_support::{
 	BoundedVec,
 };
 use frame_system::{EnsureRoot, EnsureSignedBy};
-use pallet_identity::{Data, IdentityInfo, Judgement};
+use pallet_identity::{simple::IdentityInfo, Data, Judgement};
 
 pub use crate as pallet_alliance;
 
@@ -121,6 +121,7 @@ impl pallet_identity::Config for Test {
 	type SubAccountDeposit = SubAccountDeposit;
 	type MaxSubAccounts = MaxSubAccounts;
 	type MaxAdditionalFields = MaxAdditionalFields;
+	type IdentityInformation = IdentityInfo<MaxAdditionalFields>;
 	type MaxRegistrars = MaxRegistrars;
 	type Slashed = ();
 	type RegistrarOrigin = EnsureOneOrRoot;
diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs
index 059de204bbf..16ce4d8246e 100644
--- a/substrate/frame/identity/src/benchmarking.rs
+++ b/substrate/frame/identity/src/benchmarking.rs
@@ -22,6 +22,7 @@
 use super::*;
 
 use crate::Pallet as Identity;
+use enumflags2::BitFlag;
 use frame_benchmarking::{
 	account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError,
 };
@@ -48,14 +49,9 @@ fn add_registrars<T: Config>(r: u32) -> Result<(), &'static str> {
 			.expect("RegistrarOrigin has no successful origin required for the benchmark");
 		Identity::<T>::add_registrar(registrar_origin, registrar_lookup)?;
 		Identity::<T>::set_fee(RawOrigin::Signed(registrar.clone()).into(), i, 10u32.into())?;
-		let fields =
-			IdentityFields(
-				IdentityField::Display |
-					IdentityField::Legal | IdentityField::Web |
-					IdentityField::Riot | IdentityField::Email |
-					IdentityField::PgpFingerprint |
-					IdentityField::Image | IdentityField::Twitter,
-			);
+		let fields = IdentityFields(
+			<T::IdentityInformation as IdentityInformationProvider>::IdentityField::all(),
+		);
 		Identity::<T>::set_fields(RawOrigin::Signed(registrar.clone()).into(), i, fields)?;
 	}
 
@@ -81,7 +77,7 @@ fn create_sub_accounts<T: Config>(
 	// Set identity so `set_subs` does not fail.
 	if IdentityOf::<T>::get(who).is_none() {
 		let _ = T::Currency::make_free_balance_be(who, BalanceOf::<T>::max_value() / 2u32.into());
-		let info = create_identity_info::<T>(1);
+		let info = T::IdentityInformation::create_identity_info(1);
 		Identity::<T>::set_identity(who_origin.into(), Box::new(info))?;
 	}
 
@@ -102,24 +98,6 @@ fn add_sub_accounts<T: Config>(
 	Ok(subs)
 }
 
-// This creates an `IdentityInfo` object with `num_fields` extra fields.
-// All data is pre-populated with some arbitrary bytes.
-fn create_identity_info<T: Config>(num_fields: u32) -> IdentityInfo<T::MaxAdditionalFields> {
-	let data = Data::Raw(vec![0; 32].try_into().unwrap());
-
-	IdentityInfo {
-		additional: vec![(data.clone(), data.clone()); num_fields as usize].try_into().unwrap(),
-		display: data.clone(),
-		legal: data.clone(),
-		web: data.clone(),
-		riot: data.clone(),
-		email: data.clone(),
-		pgp_fingerprint: Some([0; 20]),
-		image: data.clone(),
-		twitter: data,
-	}
-}
-
 #[benchmarks]
 mod benchmarks {
 	use super::*;
@@ -153,7 +131,7 @@ mod benchmarks {
 		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
 
 		// Add an initial identity
-		let initial_info = create_identity_info::<T>(1);
+		let initial_info = T::IdentityInformation::create_identity_info(1);
 		Identity::<T>::set_identity(caller_origin.clone(), Box::new(initial_info.clone()))?;
 
 		// User requests judgement from all the registrars, and they approve
@@ -174,7 +152,10 @@ mod benchmarks {
 		}
 
 		#[extrinsic_call]
-		_(RawOrigin::Signed(caller.clone()), Box::new(create_identity_info::<T>(x)));
+		_(
+			RawOrigin::Signed(caller.clone()),
+			Box::new(T::IdentityInformation::create_identity_info(x)),
+		);
 
 		assert_last_event::<T>(Event::<T>::IdentitySet { who: caller }.into());
 		Ok(())
@@ -235,7 +216,7 @@ mod benchmarks {
 		let _ = add_sub_accounts::<T>(&caller, s)?;
 
 		// Create their main identity with x additional fields
-		let info = create_identity_info::<T>(x);
+		let info = T::IdentityInformation::create_identity_info(x);
 		Identity::<T>::set_identity(caller_origin.clone(), Box::new(info.clone()))?;
 
 		// User requests judgement from all the registrars, and they approve
@@ -275,7 +256,7 @@ mod benchmarks {
 		add_registrars::<T>(r)?;
 
 		// Create their main identity with x additional fields
-		let info = create_identity_info::<T>(x);
+		let info = T::IdentityInformation::create_identity_info(x);
 		let caller_origin =
 			<T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller.clone()));
 		Identity::<T>::set_identity(caller_origin.clone(), Box::new(info))?;
@@ -302,7 +283,7 @@ mod benchmarks {
 		add_registrars::<T>(r)?;
 
 		// Create their main identity with x additional fields
-		let info = create_identity_info::<T>(x);
+		let info = T::IdentityInformation::create_identity_info(x);
 		let caller_origin =
 			<T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller.clone()));
 		Identity::<T>::set_identity(caller_origin.clone(), Box::new(info))?;
@@ -386,14 +367,9 @@ mod benchmarks {
 			.expect("RegistrarOrigin has no successful origin required for the benchmark");
 		Identity::<T>::add_registrar(registrar_origin, caller_lookup)?;
 
-		let fields =
-			IdentityFields(
-				IdentityField::Display |
-					IdentityField::Legal | IdentityField::Web |
-					IdentityField::Riot | IdentityField::Email |
-					IdentityField::PgpFingerprint |
-					IdentityField::Image | IdentityField::Twitter,
-			);
+		let fields = IdentityFields(
+			<T::IdentityInformation as IdentityInformationProvider>::IdentityField::all(),
+		);
 
 		let registrars = Registrars::<T>::get();
 		ensure!(
@@ -431,7 +407,7 @@ mod benchmarks {
 
 		add_registrars::<T>(r)?;
 
-		let info = create_identity_info::<T>(x);
+		let info = T::IdentityInformation::create_identity_info(x);
 		let info_hash = T::Hashing::hash_of(&info);
 		Identity::<T>::set_identity(user_origin.clone(), Box::new(info))?;
 
@@ -464,7 +440,7 @@ mod benchmarks {
 		let target_lookup = T::Lookup::unlookup(target.clone());
 		let _ = T::Currency::make_free_balance_be(&target, BalanceOf::<T>::max_value());
 
-		let info = create_identity_info::<T>(x);
+		let info = T::IdentityInformation::create_identity_info(x);
 		Identity::<T>::set_identity(target_origin.clone(), Box::new(info.clone()))?;
 		let _ = add_sub_accounts::<T>(&target, s)?;
 
diff --git a/substrate/frame/identity/src/lib.rs b/substrate/frame/identity/src/lib.rs
index f192ee2b461..a341cc6bb9b 100644
--- a/substrate/frame/identity/src/lib.rs
+++ b/substrate/frame/identity/src/lib.rs
@@ -73,6 +73,7 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 mod benchmarking;
+pub mod simple;
 #[cfg(test)]
 mod tests;
 mod types;
@@ -85,7 +86,7 @@ pub use weights::WeightInfo;
 
 pub use pallet::*;
 pub use types::{
-	Data, IdentityField, IdentityFields, IdentityInfo, Judgement, RegistrarIndex, RegistrarInfo,
+	Data, IdentityFields, IdentityInformationProvider, Judgement, RegistrarIndex, RegistrarInfo,
 	Registration,
 };
 
@@ -133,6 +134,9 @@ pub mod pallet {
 		#[pallet::constant]
 		type MaxAdditionalFields: Get<u32>;
 
+		/// Structure holding information about an identity.
+		type IdentityInformation: IdentityInformationProvider;
+
 		/// Maxmimum number of registrars allowed in the system. Needed to bound the complexity
 		/// of, e.g., updating judgements.
 		#[pallet::constant]
@@ -163,7 +167,7 @@ pub mod pallet {
 		_,
 		Twox64Concat,
 		T::AccountId,
-		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,
+		Registration<BalanceOf<T>, T::MaxRegistrars, T::IdentityInformation>,
 		OptionQuery,
 	>;
 
@@ -197,7 +201,16 @@ pub mod pallet {
 	#[pallet::getter(fn registrars)]
 	pub(super) type Registrars<T: Config> = StorageValue<
 		_,
-		BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>,
+		BoundedVec<
+			Option<
+				RegistrarInfo<
+					BalanceOf<T>,
+					T::AccountId,
+					<T::IdentityInformation as IdentityInformationProvider>::IdentityField,
+				>,
+			>,
+			T::MaxRegistrars,
+		>,
 		ValueQuery,
 	>;
 
@@ -277,9 +290,6 @@ pub mod pallet {
 		/// - `account`: the account of the registrar.
 		///
 		/// Emits `RegistrarAdded` if successful.
-		///
-		/// ## Complexity
-		/// - `O(R)` where `R` registrar-count (governance-bounded and code-bounded).
 		#[pallet::call_index(0)]
 		#[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))]
 		pub fn add_registrar(
@@ -317,22 +327,18 @@ pub mod pallet {
 		/// - `info`: The identity information.
 		///
 		/// Emits `IdentitySet` if successful.
-		///
-		/// ## Complexity
-		/// - `O(X + X' + R)`
-		///   - where `X` additional-field-count (deposit-bounded and code-bounded)
-		///   - where `R` judgements-count (registrar-count-bounded)
 		#[pallet::call_index(1)]
-		#[pallet::weight( T::WeightInfo::set_identity(
-			T::MaxRegistrars::get(), // R
-			T::MaxAdditionalFields::get(), // X
+		#[pallet::weight(T::WeightInfo::set_identity(
+			T::MaxRegistrars::get(),
+			T::MaxAdditionalFields::get(),
 		))]
 		pub fn set_identity(
 			origin: OriginFor<T>,
-			info: Box<IdentityInfo<T::MaxAdditionalFields>>,
+			info: Box<T::IdentityInformation>,
 		) -> DispatchResultWithPostInfo {
 			let sender = ensure_signed(origin)?;
-			let extra_fields = info.additional.len() as u32;
+			#[allow(deprecated)]
+			let extra_fields = info.additional() as u32;
 			ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::<T>::TooManyFields);
 			let fd = <BalanceOf<T>>::from(extra_fields) * T::FieldDeposit::get();
 
@@ -364,11 +370,7 @@ pub mod pallet {
 			<IdentityOf<T>>::insert(&sender, id);
 			Self::deposit_event(Event::IdentitySet { who: sender });
 
-			Ok(Some(T::WeightInfo::set_identity(
-				judgements as u32, // R
-				extra_fields,      // X
-			))
-			.into())
+			Ok(Some(T::WeightInfo::set_identity(judgements as u32, extra_fields)).into())
 		}
 
 		/// Set the sub-accounts of the sender.
@@ -380,11 +382,6 @@ pub mod pallet {
 		/// identity.
 		///
 		/// - `subs`: The identity's (new) sub-accounts.
-		///
-		/// ## Complexity
-		/// - `O(P + S)`
-		///   - where `P` old-subs-count (hard- and deposit-bounded).
-		///   - where `S` subs-count (hard- and deposit-bounded).
 		// TODO: This whole extrinsic screams "not optimized". For example we could
 		// filter any overlap between new and old subs, and avoid reading/writing
 		// to those values... We could also ideally avoid needing to write to
@@ -392,8 +389,8 @@ pub mod pallet {
 		// is a large overestimate due to the fact that it could potentially write
 		// to 2 x T::MaxSubAccounts::get().
 		#[pallet::call_index(2)]
-		#[pallet::weight(T::WeightInfo::set_subs_old(T::MaxSubAccounts::get()) // P: Assume max sub accounts removed.
-			.saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32)) // S: Assume all subs are new.
+		#[pallet::weight(T::WeightInfo::set_subs_old(T::MaxSubAccounts::get())
+			.saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32))
 		)]
 		pub fn set_subs(
 			origin: OriginFor<T>,
@@ -453,17 +450,11 @@ pub mod pallet {
 		/// identity.
 		///
 		/// Emits `IdentityCleared` if successful.
-		///
-		/// ## Complexity
-		/// - `O(R + S + X)`
-		///   - where `R` registrar-count (governance-bounded).
-		///   - where `S` subs-count (hard- and deposit-bounded).
-		///   - where `X` additional-field-count (deposit-bounded and code-bounded).
 		#[pallet::call_index(3)]
 		#[pallet::weight(T::WeightInfo::clear_identity(
-			T::MaxRegistrars::get(), // R
-			T::MaxSubAccounts::get(), // S
-			T::MaxAdditionalFields::get(), // X
+			T::MaxRegistrars::get(),
+			T::MaxSubAccounts::get(),
+			T::MaxAdditionalFields::get(),
 		))]
 		pub fn clear_identity(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
 			let sender = ensure_signed(origin)?;
@@ -480,10 +471,11 @@ pub mod pallet {
 
 			Self::deposit_event(Event::IdentityCleared { who: sender, deposit });
 
+			#[allow(deprecated)]
 			Ok(Some(T::WeightInfo::clear_identity(
-				id.judgements.len() as u32,      // R
-				sub_ids.len() as u32,            // S
-				id.info.additional.len() as u32, // X
+				id.judgements.len() as u32,
+				sub_ids.len() as u32,
+				id.info.additional() as u32,
 			))
 			.into())
 		}
@@ -504,15 +496,10 @@ pub mod pallet {
 		/// ```
 		///
 		/// Emits `JudgementRequested` if successful.
-		///
-		/// ## Complexity
-		/// - `O(R + X)`.
-		///   - where `R` registrar-count (governance-bounded).
-		///   - where `X` additional-field-count (deposit-bounded and code-bounded).
 		#[pallet::call_index(4)]
 		#[pallet::weight(T::WeightInfo::request_judgement(
-			T::MaxRegistrars::get(), // R
-			T::MaxAdditionalFields::get(), // X
+			T::MaxRegistrars::get(),
+			T::MaxAdditionalFields::get(),
 		))]
 		pub fn request_judgement(
 			origin: OriginFor<T>,
@@ -543,7 +530,8 @@ pub mod pallet {
 			T::Currency::reserve(&sender, registrar.fee)?;
 
 			let judgements = id.judgements.len();
-			let extra_fields = id.info.additional.len();
+			#[allow(deprecated)]
+			let extra_fields = id.info.additional();
 			<IdentityOf<T>>::insert(&sender, id);
 
 			Self::deposit_event(Event::JudgementRequested {
@@ -565,15 +553,10 @@ pub mod pallet {
 		/// - `reg_index`: The index of the registrar whose judgement is no longer requested.
 		///
 		/// Emits `JudgementUnrequested` if successful.
-		///
-		/// ## Complexity
-		/// - `O(R + X)`.
-		///   - where `R` registrar-count (governance-bounded).
-		///   - where `X` additional-field-count (deposit-bounded and code-bounded).
 		#[pallet::call_index(5)]
 		#[pallet::weight(T::WeightInfo::cancel_request(
-			T::MaxRegistrars::get(), // R
-			T::MaxAdditionalFields::get(), // X
+			T::MaxRegistrars::get(),
+			T::MaxAdditionalFields::get(),
 		))]
 		pub fn cancel_request(
 			origin: OriginFor<T>,
@@ -595,7 +578,8 @@ pub mod pallet {
 			let err_amount = T::Currency::unreserve(&sender, fee);
 			debug_assert!(err_amount.is_zero());
 			let judgements = id.judgements.len();
-			let extra_fields = id.info.additional.len();
+			#[allow(deprecated)]
+			let extra_fields = id.info.additional();
 			<IdentityOf<T>>::insert(&sender, id);
 
 			Self::deposit_event(Event::JudgementUnrequested {
@@ -613,12 +597,8 @@ pub mod pallet {
 		///
 		/// - `index`: the index of the registrar whose fee is to be set.
 		/// - `fee`: the new fee.
-		///
-		/// ## Complexity
-		/// - `O(R)`.
-		///   - where `R` registrar-count (governance-bounded).
 		#[pallet::call_index(6)]
-		#[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] // R
+		#[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))]
 		pub fn set_fee(
 			origin: OriginFor<T>,
 			#[pallet::compact] index: RegistrarIndex,
@@ -640,7 +620,7 @@ pub mod pallet {
 					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;
 				Ok(rs.len())
 			})?;
-			Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) // R
+			Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into())
 		}
 
 		/// Change the account associated with a registrar.
@@ -650,12 +630,8 @@ pub mod pallet {
 		///
 		/// - `index`: the index of the registrar whose fee is to be set.
 		/// - `new`: the new account ID.
-		///
-		/// ## Complexity
-		/// - `O(R)`.
-		///   - where `R` registrar-count (governance-bounded).
 		#[pallet::call_index(7)]
-		#[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] // R
+		#[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))]
 		pub fn set_account_id(
 			origin: OriginFor<T>,
 			#[pallet::compact] index: RegistrarIndex,
@@ -678,7 +654,7 @@ pub mod pallet {
 					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;
 				Ok(rs.len())
 			})?;
-			Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) // R
+			Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into())
 		}
 
 		/// Set the field information for a registrar.
@@ -688,16 +664,14 @@ pub mod pallet {
 		///
 		/// - `index`: the index of the registrar whose fee is to be set.
 		/// - `fields`: the fields that the registrar concerns themselves with.
-		///
-		/// ## Complexity
-		/// - `O(R)`.
-		///   - where `R` registrar-count (governance-bounded).
 		#[pallet::call_index(8)]
-		#[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] // R
+		#[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))]
 		pub fn set_fields(
 			origin: OriginFor<T>,
 			#[pallet::compact] index: RegistrarIndex,
-			fields: IdentityFields,
+			fields: IdentityFields<
+				<T::IdentityInformation as IdentityInformationProvider>::IdentityField,
+			>,
 		) -> DispatchResultWithPostInfo {
 			let who = ensure_signed(origin)?;
 
@@ -715,10 +689,7 @@ pub mod pallet {
 					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;
 				Ok(rs.len())
 			})?;
-			Ok(Some(T::WeightInfo::set_fields(
-				registrars as u32, // R
-			))
-			.into())
+			Ok(Some(T::WeightInfo::set_fields(registrars as u32)).into())
 		}
 
 		/// Provide a judgement for an account's identity.
@@ -730,18 +701,14 @@ pub mod pallet {
 		/// - `target`: the account whose identity the judgement is upon. This must be an account
 		///   with a registered identity.
 		/// - `judgement`: the judgement of the registrar of index `reg_index` about `target`.
-		/// - `identity`: The hash of the [`IdentityInfo`] for that the judgement is provided.
+		/// - `identity`: The hash of the [`IdentityInformationProvider`] for that the judgement is
+		///   provided.
 		///
 		/// Emits `JudgementGiven` if successful.
-		///
-		/// ## Complexity
-		/// - `O(R + X)`.
-		///   - where `R` registrar-count (governance-bounded).
-		///   - where `X` additional-field-count (deposit-bounded and code-bounded).
 		#[pallet::call_index(9)]
 		#[pallet::weight(T::WeightInfo::provide_judgement(
-			T::MaxRegistrars::get(), // R
-			T::MaxAdditionalFields::get(), // X
+			T::MaxRegistrars::get(),
+			T::MaxAdditionalFields::get(),
 		))]
 		pub fn provide_judgement(
 			origin: OriginFor<T>,
@@ -785,7 +752,8 @@ pub mod pallet {
 			}
 
 			let judgements = id.judgements.len();
-			let extra_fields = id.info.additional.len();
+			#[allow(deprecated)]
+			let extra_fields = id.info.additional();
 			<IdentityOf<T>>::insert(&target, id);
 			Self::deposit_event(Event::JudgementGiven { target, registrar_index: reg_index });
 
@@ -805,17 +773,11 @@ pub mod pallet {
 		///   with a registered identity.
 		///
 		/// Emits `IdentityKilled` if successful.
-		///
-		/// ## Complexity
-		/// - `O(R + S + X)`
-		///   - where `R` registrar-count (governance-bounded).
-		///   - where `S` subs-count (hard- and deposit-bounded).
-		///   - where `X` additional-field-count (deposit-bounded and code-bounded).
 		#[pallet::call_index(10)]
 		#[pallet::weight(T::WeightInfo::kill_identity(
-			T::MaxRegistrars::get(), // R
-			T::MaxSubAccounts::get(), // S
-			T::MaxAdditionalFields::get(), // X
+			T::MaxRegistrars::get(),
+			T::MaxSubAccounts::get(),
+			T::MaxAdditionalFields::get(),
 		))]
 		pub fn kill_identity(
 			origin: OriginFor<T>,
@@ -837,10 +799,11 @@ pub mod pallet {
 
 			Self::deposit_event(Event::IdentityKilled { who: target, deposit });
 
+			#[allow(deprecated)]
 			Ok(Some(T::WeightInfo::kill_identity(
-				id.judgements.len() as u32,      // R
-				sub_ids.len() as u32,            // S
-				id.info.additional.len() as u32, // X
+				id.judgements.len() as u32,
+				sub_ids.len() as u32,
+				id.info.additional() as u32,
 			))
 			.into())
 		}
@@ -975,6 +938,6 @@ impl<T: Config> Pallet<T> {
 	/// Check if the account has corresponding identity information by the identity field.
 	pub fn has_identity(who: &T::AccountId, fields: u64) -> bool {
 		IdentityOf::<T>::get(who)
-			.map_or(false, |registration| (registration.info.fields().0.bits() & fields) == fields)
+			.map_or(false, |registration| (registration.info.has_identity(fields)))
 	}
 }
diff --git a/substrate/frame/identity/src/simple.rs b/substrate/frame/identity/src/simple.rs
new file mode 100644
index 00000000000..db5ecf3b1c9
--- /dev/null
+++ b/substrate/frame/identity/src/simple.rs
@@ -0,0 +1,185 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// 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.
+
+use codec::{Decode, Encode, MaxEncodedLen};
+use enumflags2::{bitflags, BitFlags};
+use frame_support::{traits::Get, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
+use scale_info::{build::Variants, Path, Type, TypeInfo};
+use sp_runtime::{BoundedVec, RuntimeDebug};
+use sp_std::prelude::*;
+
+use crate::types::{Data, IdentityFields, IdentityInformationProvider, U64BitFlag};
+
+/// The fields that we use to identify the owner of an account with. Each corresponds to a field
+/// in the `IdentityInfo` struct.
+#[bitflags]
+#[repr(u64)]
+#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)]
+pub enum IdentityField {
+	Display,
+	Legal,
+	Web,
+	Riot,
+	Email,
+	PgpFingerprint,
+	Image,
+	Twitter,
+}
+
+impl TypeInfo for IdentityField {
+	type Identity = Self;
+
+	fn type_info() -> scale_info::Type {
+		Type::builder().path(Path::new("IdentityField", module_path!())).variant(
+			Variants::new()
+				.variant("Display", |v| v.index(0))
+				.variant("Legal", |v| v.index(1))
+				.variant("Web", |v| v.index(2))
+				.variant("Riot", |v| v.index(3))
+				.variant("Email", |v| v.index(4))
+				.variant("PgpFingerprint", |v| v.index(5))
+				.variant("Image", |v| v.index(6))
+				.variant("Twitter", |v| v.index(7)),
+		)
+	}
+}
+
+impl U64BitFlag for IdentityField {}
+
+/// Information concerning the identity of the controller of an account.
+///
+/// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra
+/// fields in a backwards compatible way through a specialized `Decode` impl.
+#[derive(
+	CloneNoBound,
+	Encode,
+	Decode,
+	EqNoBound,
+	MaxEncodedLen,
+	PartialEqNoBound,
+	RuntimeDebugNoBound,
+	TypeInfo,
+)]
+#[codec(mel_bound())]
+#[cfg_attr(test, derive(frame_support::DefaultNoBound))]
+#[scale_info(skip_type_params(FieldLimit))]
+pub struct IdentityInfo<FieldLimit: Get<u32>> {
+	/// Additional fields of the identity that are not catered for with the struct's explicit
+	/// fields.
+	pub additional: BoundedVec<(Data, Data), FieldLimit>,
+
+	/// A reasonable display name for the controller of the account. This should be whatever it is
+	/// that it is typically known as and should not be confusable with other entities, given
+	/// reasonable context.
+	///
+	/// Stored as UTF-8.
+	pub display: Data,
+
+	/// The full legal name in the local jurisdiction of the entity. This might be a bit
+	/// long-winded.
+	///
+	/// Stored as UTF-8.
+	pub legal: Data,
+
+	/// A representative website held by the controller of the account.
+	///
+	/// NOTE: `https://` is automatically prepended.
+	///
+	/// Stored as UTF-8.
+	pub web: Data,
+
+	/// The Riot/Matrix handle held by the controller of the account.
+	///
+	/// Stored as UTF-8.
+	pub riot: Data,
+
+	/// The email address of the controller of the account.
+	///
+	/// Stored as UTF-8.
+	pub email: Data,
+
+	/// The PGP/GPG public key of the controller of the account.
+	pub pgp_fingerprint: Option<[u8; 20]>,
+
+	/// A graphic image representing the controller of the account. Should be a company,
+	/// organization or project logo or a headshot in the case of a human.
+	pub image: Data,
+
+	/// The Twitter identity. The leading `@` character may be elided.
+	pub twitter: Data,
+}
+
+impl<FieldLimit: Get<u32> + 'static> IdentityInformationProvider for IdentityInfo<FieldLimit> {
+	type IdentityField = IdentityField;
+
+	fn has_identity(&self, fields: u64) -> bool {
+		self.fields().0.bits() & fields == fields
+	}
+
+	fn additional(&self) -> usize {
+		self.additional.len()
+	}
+
+	#[cfg(feature = "runtime-benchmarks")]
+	fn create_identity_info(num_fields: u32) -> Self {
+		let data = Data::Raw(vec![0; 32].try_into().unwrap());
+
+		IdentityInfo {
+			additional: vec![(data.clone(), data.clone()); num_fields as usize].try_into().unwrap(),
+			display: data.clone(),
+			legal: data.clone(),
+			web: data.clone(),
+			riot: data.clone(),
+			email: data.clone(),
+			pgp_fingerprint: Some([0; 20]),
+			image: data.clone(),
+			twitter: data,
+		}
+	}
+}
+
+impl<FieldLimit: Get<u32>> IdentityInfo<FieldLimit> {
+	#[allow(unused)]
+	pub(crate) fn fields(&self) -> IdentityFields<IdentityField> {
+		let mut res = <BitFlags<IdentityField>>::empty();
+		if !self.display.is_none() {
+			res.insert(IdentityField::Display);
+		}
+		if !self.legal.is_none() {
+			res.insert(IdentityField::Legal);
+		}
+		if !self.web.is_none() {
+			res.insert(IdentityField::Web);
+		}
+		if !self.riot.is_none() {
+			res.insert(IdentityField::Riot);
+		}
+		if !self.email.is_none() {
+			res.insert(IdentityField::Email);
+		}
+		if self.pgp_fingerprint.is_some() {
+			res.insert(IdentityField::PgpFingerprint);
+		}
+		if !self.image.is_none() {
+			res.insert(IdentityField::Image);
+		}
+		if !self.twitter.is_none() {
+			res.insert(IdentityField::Twitter);
+		}
+		IdentityFields(res)
+	}
+}
diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs
index 6bfd15b4aae..f0980e9c7cc 100644
--- a/substrate/frame/identity/src/tests.rs
+++ b/substrate/frame/identity/src/tests.rs
@@ -18,7 +18,10 @@
 // Tests for Identity Pallet
 
 use super::*;
-use crate as pallet_identity;
+use crate::{
+	self as pallet_identity,
+	simple::{IdentityField as SimpleIdentityField, IdentityInfo},
+};
 
 use codec::{Decode, Encode};
 use frame_support::{
@@ -107,6 +110,7 @@ impl pallet_identity::Config for Test {
 	type SubAccountDeposit = ConstU64<10>;
 	type MaxSubAccounts = ConstU32<2>;
 	type MaxAdditionalFields = MaxAdditionalFields;
+	type IdentityInformation = IdentityInfo<MaxAdditionalFields>;
 	type MaxRegistrars = MaxRegistrars;
 	type RegistrarOrigin = EnsureOneOrRoot;
 	type ForceOrigin = EnsureTwoOrRoot;
@@ -139,6 +143,43 @@ fn twenty() -> IdentityInfo<MaxAdditionalFields> {
 	}
 }
 
+#[test]
+fn identity_fields_repr_works() {
+	// `SimpleIdentityField` sanity checks.
+	assert_eq!(SimpleIdentityField::Display as u64, 1 << 0);
+	assert_eq!(SimpleIdentityField::Legal as u64, 1 << 1);
+	assert_eq!(SimpleIdentityField::Web as u64, 1 << 2);
+	assert_eq!(SimpleIdentityField::Riot as u64, 1 << 3);
+	assert_eq!(SimpleIdentityField::Email as u64, 1 << 4);
+	assert_eq!(SimpleIdentityField::PgpFingerprint as u64, 1 << 5);
+	assert_eq!(SimpleIdentityField::Image as u64, 1 << 6);
+	assert_eq!(SimpleIdentityField::Twitter as u64, 1 << 7);
+
+	let fields = IdentityFields(
+		SimpleIdentityField::Legal |
+			SimpleIdentityField::Web |
+			SimpleIdentityField::Riot |
+			SimpleIdentityField::PgpFingerprint |
+			SimpleIdentityField::Twitter,
+	);
+
+	assert!(!fields.0.contains(SimpleIdentityField::Display));
+	assert!(fields.0.contains(SimpleIdentityField::Legal));
+	assert!(fields.0.contains(SimpleIdentityField::Web));
+	assert!(fields.0.contains(SimpleIdentityField::Riot));
+	assert!(!fields.0.contains(SimpleIdentityField::Email));
+	assert!(fields.0.contains(SimpleIdentityField::PgpFingerprint));
+	assert!(!fields.0.contains(SimpleIdentityField::Image));
+	assert!(fields.0.contains(SimpleIdentityField::Twitter));
+
+	// The `IdentityFields` inner `BitFlags::bits` is used for `Encode`/`Decode`, so we ensure that
+	// the `u64` representation matches what we expect during encode/decode operations.
+	assert_eq!(
+		fields.0.bits(),
+		0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_10101110
+	);
+}
+
 #[test]
 fn editing_subaccounts_should_work() {
 	new_test_ext().execute_with(|| {
@@ -233,7 +274,7 @@ fn adding_registrar_should_work() {
 	new_test_ext().execute_with(|| {
 		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
 		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
-		let fields = IdentityFields(IdentityField::Display | IdentityField::Legal);
+		let fields = IdentityFields(SimpleIdentityField::Display | SimpleIdentityField::Legal);
 		assert_ok!(Identity::set_fields(RuntimeOrigin::signed(3), 0, fields));
 		assert_eq!(
 			Identity::registrars(),
@@ -608,15 +649,17 @@ fn setting_account_id_should_work() {
 fn test_has_identity() {
 	new_test_ext().execute_with(|| {
 		assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten())));
-		assert!(Identity::has_identity(&10, IdentityField::Display as u64));
-		assert!(Identity::has_identity(&10, IdentityField::Legal as u64));
+		assert!(Identity::has_identity(&10, SimpleIdentityField::Display as u64));
+		assert!(Identity::has_identity(&10, SimpleIdentityField::Legal as u64));
 		assert!(Identity::has_identity(
 			&10,
-			IdentityField::Display as u64 | IdentityField::Legal as u64
+			SimpleIdentityField::Display as u64 | SimpleIdentityField::Legal as u64
 		));
 		assert!(!Identity::has_identity(
 			&10,
-			IdentityField::Display as u64 | IdentityField::Legal as u64 | IdentityField::Web as u64
+			SimpleIdentityField::Display as u64 |
+				SimpleIdentityField::Legal as u64 |
+				SimpleIdentityField::Web as u64
 		));
 	});
 }
diff --git a/substrate/frame/identity/src/types.rs b/substrate/frame/identity/src/types.rs
index 1837b30b027..7055f6d80cf 100644
--- a/substrate/frame/identity/src/types.rs
+++ b/substrate/frame/identity/src/types.rs
@@ -17,7 +17,7 @@
 
 use super::*;
 use codec::{Decode, Encode, MaxEncodedLen};
-use enumflags2::{bitflags, BitFlags};
+use enumflags2::{BitFlag, BitFlags, _internal::RawBitFlags};
 use frame_support::{
 	traits::{ConstU32, Get},
 	BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound,
@@ -29,6 +29,11 @@ use scale_info::{
 use sp_runtime::{traits::Zero, RuntimeDebug};
 use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*};
 
+/// An identifier for a single name registrar/identity verification service.
+pub type RegistrarIndex = u32;
+
+pub trait U64BitFlag: BitFlag + RawBitFlags<Numeric = u64> {}
+
 /// Either underlying data blob if it is at most 32 bytes, or a hash of it. If the data is greater
 /// than 32-bytes then it will be truncated when encoding.
 ///
@@ -180,9 +185,6 @@ impl Default for Data {
 	}
 }
 
-/// An identifier for a single name registrar/identity verification service.
-pub type RegistrarIndex = u32;
-
 /// An attestation of a registrar over how accurate some `IdentityInfo` is in describing an account.
 ///
 /// NOTE: Registrars may pay little attention to some fields. Registrars may want to make clear
@@ -228,143 +230,31 @@ impl<Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + Part
 	}
 }
 
-/// The fields that we use to identify the owner of an account with. Each corresponds to a field
-/// in the `IdentityInfo` struct.
-#[bitflags]
-#[repr(u64)]
-#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
-pub enum IdentityField {
-	Display = 0b0000000000000000000000000000000000000000000000000000000000000001,
-	Legal = 0b0000000000000000000000000000000000000000000000000000000000000010,
-	Web = 0b0000000000000000000000000000000000000000000000000000000000000100,
-	Riot = 0b0000000000000000000000000000000000000000000000000000000000001000,
-	Email = 0b0000000000000000000000000000000000000000000000000000000000010000,
-	PgpFingerprint = 0b0000000000000000000000000000000000000000000000000000000000100000,
-	Image = 0b0000000000000000000000000000000000000000000000000000000001000000,
-	Twitter = 0b0000000000000000000000000000000000000000000000000000000010000000,
-}
-
-/// Wrapper type for `BitFlags<IdentityField>` that implements `Codec`.
-#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)]
-pub struct IdentityFields(pub BitFlags<IdentityField>);
-
-impl MaxEncodedLen for IdentityFields {
-	fn max_encoded_len() -> usize {
-		u64::max_encoded_len()
-	}
-}
-
-impl Eq for IdentityFields {}
-impl Encode for IdentityFields {
-	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
-		self.0.bits().using_encoded(f)
-	}
-}
-impl Decode for IdentityFields {
-	fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
-		let field = u64::decode(input)?;
-		Ok(Self(<BitFlags<IdentityField>>::from_bits(field as u64).map_err(|_| "invalid value")?))
-	}
-}
-impl TypeInfo for IdentityFields {
-	type Identity = Self;
-
-	fn type_info() -> Type {
-		Type::builder()
-			.path(Path::new("BitFlags", module_path!()))
-			.type_params(vec![TypeParameter::new("T", Some(meta_type::<IdentityField>()))])
-			.composite(Fields::unnamed().field(|f| f.ty::<u64>().type_name("IdentityField")))
-	}
-}
-
 /// Information concerning the identity of the controller of an account.
-///
-/// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra
-/// fields in a backwards compatible way through a specialized `Decode` impl.
-#[derive(
-	CloneNoBound, Encode, Decode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
-)]
-#[codec(mel_bound())]
-#[cfg_attr(test, derive(frame_support::DefaultNoBound))]
-#[scale_info(skip_type_params(FieldLimit))]
-pub struct IdentityInfo<FieldLimit: Get<u32>> {
-	/// Additional fields of the identity that are not catered for with the struct's explicit
-	/// fields.
-	pub additional: BoundedVec<(Data, Data), FieldLimit>,
-
-	/// A reasonable display name for the controller of the account. This should be whatever it is
-	/// that it is typically known as and should not be confusable with other entities, given
-	/// reasonable context.
-	///
-	/// Stored as UTF-8.
-	pub display: Data,
-
-	/// The full legal name in the local jurisdiction of the entity. This might be a bit
-	/// long-winded.
-	///
-	/// Stored as UTF-8.
-	pub legal: Data,
-
-	/// A representative website held by the controller of the account.
-	///
-	/// NOTE: `https://` is automatically prepended.
-	///
-	/// Stored as UTF-8.
-	pub web: Data,
-
-	/// The Riot/Matrix handle held by the controller of the account.
-	///
-	/// Stored as UTF-8.
-	pub riot: Data,
-
-	/// The email address of the controller of the account.
-	///
-	/// Stored as UTF-8.
-	pub email: Data,
-
-	/// The PGP/GPG public key of the controller of the account.
-	pub pgp_fingerprint: Option<[u8; 20]>,
-
-	/// A graphic image representing the controller of the account. Should be a company,
-	/// organization or project logo or a headshot in the case of a human.
-	pub image: Data,
-
-	/// The Twitter identity. The leading `@` character may be elided.
-	pub twitter: Data,
-}
-
-impl<FieldLimit: Get<u32>> IdentityInfo<FieldLimit> {
-	pub(crate) fn fields(&self) -> IdentityFields {
-		let mut res = <BitFlags<IdentityField>>::empty();
-		if !self.display.is_none() {
-			res.insert(IdentityField::Display);
-		}
-		if !self.legal.is_none() {
-			res.insert(IdentityField::Legal);
-		}
-		if !self.web.is_none() {
-			res.insert(IdentityField::Web);
-		}
-		if !self.riot.is_none() {
-			res.insert(IdentityField::Riot);
-		}
-		if !self.email.is_none() {
-			res.insert(IdentityField::Email);
-		}
-		if self.pgp_fingerprint.is_some() {
-			res.insert(IdentityField::PgpFingerprint);
-		}
-		if !self.image.is_none() {
-			res.insert(IdentityField::Image);
-		}
-		if !self.twitter.is_none() {
-			res.insert(IdentityField::Twitter);
-		}
-		IdentityFields(res)
+pub trait IdentityInformationProvider:
+	Encode + Decode + MaxEncodedLen + Clone + Debug + Eq + PartialEq + TypeInfo
+{
+	/// Type capable of representing all of the fields present in the identity information as bit
+	/// flags in `u64` format.
+	type IdentityField: Clone + Debug + Eq + PartialEq + TypeInfo + U64BitFlag;
+
+	/// Check if an identity registered information for some given `fields`.
+	fn has_identity(&self, fields: u64) -> bool;
+
+	/// Interface for providing the number of additional fields this identity information provider
+	/// holds, used to charge for additional storage and weight. This interface is present for
+	/// backwards compatibility reasons only and will be removed as soon as the reference identity
+	/// provider removes additional fields.
+	#[deprecated]
+	fn additional(&self) -> usize {
+		0
 	}
+
+	#[cfg(feature = "runtime-benchmarks")]
+	fn create_identity_info(num_fields: u32) -> Self;
 }
 
-/// Information concerning the identity of the controller of an account.
+/// Information on an identity along with judgements from registrars.
 ///
 /// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a
 /// backwards compatible way through a specialized `Decode` impl.
@@ -376,7 +266,7 @@ impl<FieldLimit: Get<u32>> IdentityInfo<FieldLimit> {
 pub struct Registration<
 	Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq,
 	MaxJudgements: Get<u32>,
-	MaxAdditionalFields: Get<u32>,
+	IdentityInfo: IdentityInformationProvider,
 > {
 	/// Judgements from the registrars on this identity. Stored ordered by `RegistrarIndex`. There
 	/// may be only a single judgement from each registrar.
@@ -386,14 +276,14 @@ pub struct Registration<
 	pub deposit: Balance,
 
 	/// Information on the identity.
-	pub info: IdentityInfo<MaxAdditionalFields>,
+	pub info: IdentityInfo,
 }
 
 impl<
 		Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq + Zero + Add,
 		MaxJudgements: Get<u32>,
-		MaxAdditionalFields: Get<u32>,
-	> Registration<Balance, MaxJudgements, MaxAdditionalFields>
+		IdentityInfo: IdentityInformationProvider,
+	> Registration<Balance, MaxJudgements, IdentityInfo>
 {
 	pub(crate) fn total_deposit(&self) -> Balance {
 		self.deposit +
@@ -407,8 +297,8 @@ impl<
 impl<
 		Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq,
 		MaxJudgements: Get<u32>,
-		MaxAdditionalFields: Get<u32>,
-	> Decode for Registration<Balance, MaxJudgements, MaxAdditionalFields>
+		IdentityInfo: IdentityInformationProvider,
+	> Decode for Registration<Balance, MaxJudgements, IdentityInfo>
 {
 	fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
 		let (judgements, deposit, info) = Decode::decode(&mut AppendZerosInput::new(input))?;
@@ -421,6 +311,7 @@ impl<
 pub struct RegistrarInfo<
 	Balance: Encode + Decode + Clone + Debug + Eq + PartialEq,
 	AccountId: Encode + Decode + Clone + Debug + Eq + PartialEq,
+	IdField: Clone + Debug + Eq + PartialEq + TypeInfo + U64BitFlag,
 > {
 	/// The account of the registrar.
 	pub account: AccountId,
@@ -430,7 +321,52 @@ pub struct RegistrarInfo<
 
 	/// Relevant fields for this registrar. Registrar judgements are limited to attestations on
 	/// these fields.
-	pub fields: IdentityFields,
+	pub fields: IdentityFields<IdField>,
+}
+
+/// Wrapper type for `BitFlags<IdentityField>` that implements `Codec`.
+#[derive(Clone, Copy, PartialEq, RuntimeDebug)]
+pub struct IdentityFields<IdField: BitFlag>(pub BitFlags<IdField>);
+
+impl<IdField: U64BitFlag> Default for IdentityFields<IdField> {
+	fn default() -> Self {
+		Self(Default::default())
+	}
+}
+
+impl<IdField: U64BitFlag> MaxEncodedLen for IdentityFields<IdField>
+where
+	IdentityFields<IdField>: Encode,
+{
+	fn max_encoded_len() -> usize {
+		u64::max_encoded_len()
+	}
+}
+
+impl<IdField: U64BitFlag + PartialEq> Eq for IdentityFields<IdField> {}
+impl<IdField: U64BitFlag> Encode for IdentityFields<IdField> {
+	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
+		let bits: u64 = self.0.bits();
+		bits.using_encoded(f)
+	}
+}
+impl<IdField: U64BitFlag> Decode for IdentityFields<IdField> {
+	fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
+		let field = u64::decode(input)?;
+		Ok(Self(<BitFlags<IdField>>::from_bits(field).map_err(|_| "invalid value")?))
+	}
+}
+impl<IdField: Clone + Debug + Eq + PartialEq + TypeInfo + U64BitFlag> TypeInfo
+	for IdentityFields<IdField>
+{
+	type Identity = Self;
+
+	fn type_info() -> Type {
+		Type::builder()
+			.path(Path::new("BitFlags", module_path!()))
+			.type_params(vec![TypeParameter::new("T", Some(meta_type::<IdField>()))])
+			.composite(Fields::unnamed().field(|f| f.ty::<u64>().type_name("IdentityField")))
+	}
 }
 
 #[cfg(test)]
-- 
GitLab