mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 14:11:09 +00:00
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>
This commit is contained in:
@@ -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>;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
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;
|
||||
|
||||
/// 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,
|
||||
/// Check if an identity registered information for some given `fields`.
|
||||
fn has_identity(&self, fields: u64) -> bool;
|
||||
|
||||
/// 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)
|
||||
/// 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)]
|
||||
|
||||
Reference in New Issue
Block a user