mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 19:51:02 +00:00
Unique Usernames in Identity Pallet (#2651)
This PR allows _username authorities_ to issue unique usernames that correspond with an account. It also provides two-way lookup, that is from `AccountId` to a single, "primary" `Username` (alongside `Registration`) and multiple unique `Username`s to an `AccountId`. Key features: - Username Authorities added (and removed) via privileged origin. - Authorities have a `suffix` and an `allocation`. They can grant up to `allocation` usernames. Their `suffix` will be appended to the usernames that they issue. A suffix may be up to 7 characters long. - Users can ask an authority to grant them a username. This will take the form `myusername.suffix`. The entire name (including suffix) must be less than or equal to 32 alphanumeric characters. - Users can approve a username for themselves in one of two ways (that is, authorities cannot grant them arbitrarily): - Pre-sign the entire username (including suffix) with a secret key that corresponds to their `AccountId` (for keyed accounts, obviously); or - Accept the username after it has been granted by an authority (it will be queued until accepted) (for non-keyed accounts like pure proxies or multisigs). - The system does not require any funds or deposits. Users without an identity will be given a default one (presumably all fields set to `None`). If they update this info, they will need to place the normal storage deposit. - If a user does not have any username, their first one will be set as `Primary`, and their `AccountId` will map to that one. If they get subsequent usernames, they can choose which one to be their primary via `set_primary_username`. - There are some state cleanup functions to remove expired usernames that have not been accepted and dangling usernames whose owners have called `clear_identity`. TODO: - [x] Add migration to runtimes - [x] Probably do off-chain migration into People Chain genesis - [x] Address a few TODO questions in code (please review) --------- Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Dónal Murray <donal.murray@parity.io>
This commit is contained in:
@@ -22,9 +22,12 @@ use frame_support::{
|
||||
RuntimeDebugNoBound,
|
||||
};
|
||||
use pallet_identity::{Data, IdentityInformationProvider};
|
||||
use parachains_common::impls::ToParentTreasury;
|
||||
use parachains_common::{impls::ToParentTreasury, DAYS};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
|
||||
use sp_runtime::{
|
||||
traits::{AccountIdConversion, Verify},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
parameter_types! {
|
||||
@@ -51,6 +54,12 @@ impl pallet_identity::Config for Runtime {
|
||||
type Slashed = ToParentTreasury<RelayTreasuryAccount, LocationToAccountId, Runtime>;
|
||||
type ForceOrigin = EnsureRoot<Self::AccountId>;
|
||||
type RegistrarOrigin = EnsureRoot<Self::AccountId>;
|
||||
type OffchainSignature = Signature;
|
||||
type SigningPublicKey = <Signature as Verify>::Signer;
|
||||
type UsernameAuthorityOrigin = EnsureRoot<Self::AccountId>;
|
||||
type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>;
|
||||
type MaxSuffixLength = ConstU32<7>;
|
||||
type MaxUsernameLength = ConstU32<32>;
|
||||
type WeightInfo = weights::pallet_identity::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
@@ -84,7 +93,6 @@ pub enum IdentityField {
|
||||
TypeInfo,
|
||||
)]
|
||||
#[codec(mel_bound())]
|
||||
#[cfg_attr(test, derive(frame_support::DefaultNoBound))]
|
||||
pub struct IdentityInfo {
|
||||
/// A reasonable display name for the controller of the account. This should be whatever the
|
||||
/// account is typically known as and should not be confusable with other entities, given
|
||||
@@ -202,3 +210,21 @@ impl IdentityInfo {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Default` identity. This is given to users who get a username but have not set an identity.
|
||||
impl Default for IdentityInfo {
|
||||
fn default() -> Self {
|
||||
IdentityInfo {
|
||||
display: Data::None,
|
||||
legal: Data::None,
|
||||
web: Data::None,
|
||||
matrix: Data::None,
|
||||
email: Data::None,
|
||||
pgp_fingerprint: None,
|
||||
image: Data::None,
|
||||
twitter: Data::None,
|
||||
github: Data::None,
|
||||
discord: Data::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,4 +312,98 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().reads(3))
|
||||
.saturating_add(T::DbWeight::get().writes(2))
|
||||
}
|
||||
/// Storage: `Identity::UsernameAuthorities` (r:0 w:1)
|
||||
/// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
|
||||
fn add_username_authority() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 13_873_000 picoseconds.
|
||||
Weight::from_parts(13_873_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::UsernameAuthorities` (r:0 w:1)
|
||||
/// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
|
||||
fn remove_username_authority() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 10_653_000 picoseconds.
|
||||
Weight::from_parts(10_653_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::UsernameAuthorities` (r:1 w:1)
|
||||
/// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::AccountOfUsername` (r:1 w:1)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:1)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
fn set_username_for() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `80`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 75_928_000 picoseconds.
|
||||
Weight::from_parts(75_928_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(3))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
/// Storage: `Identity::PendingUsernames` (r:1 w:1)
|
||||
/// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:1)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::AccountOfUsername` (r:0 w:1)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
fn accept_username() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `106`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 38_157_000 picoseconds.
|
||||
Weight::from_parts(38_157_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
/// Storage: `Identity::PendingUsernames` (r:1 w:1)
|
||||
/// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`)
|
||||
fn remove_expired_approval() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `106`
|
||||
// Estimated: `3542`
|
||||
// Minimum execution time: 46_821_000 picoseconds.
|
||||
Weight::from_parts(46_821_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 3542))
|
||||
.saturating_add(T::DbWeight::get().reads(1))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::AccountOfUsername` (r:1 w:0)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:1)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
fn set_primary_username() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `247`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 22_515_000 picoseconds.
|
||||
Weight::from_parts(22_515_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::AccountOfUsername` (r:1 w:1)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:0)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
fn remove_dangling_username() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `126`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 15_997_000 picoseconds.
|
||||
Weight::from_parts(15_997_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ use frame_support::{
|
||||
RuntimeDebugNoBound,
|
||||
};
|
||||
use pallet_identity::{Data, IdentityInformationProvider};
|
||||
use parachains_common::impls::ToParentTreasury;
|
||||
use parachains_common::{impls::ToParentTreasury, DAYS};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
|
||||
use sp_runtime::{
|
||||
traits::{AccountIdConversion, Verify},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
parameter_types! {
|
||||
@@ -51,6 +54,12 @@ impl pallet_identity::Config for Runtime {
|
||||
type Slashed = ToParentTreasury<RelayTreasuryAccount, LocationToAccountId, Runtime>;
|
||||
type ForceOrigin = EnsureRoot<Self::AccountId>;
|
||||
type RegistrarOrigin = EnsureRoot<Self::AccountId>;
|
||||
type OffchainSignature = Signature;
|
||||
type SigningPublicKey = <Signature as Verify>::Signer;
|
||||
type UsernameAuthorityOrigin = EnsureRoot<Self::AccountId>;
|
||||
type PendingUsernameExpiration = ConstU32<{ 7 * DAYS }>;
|
||||
type MaxSuffixLength = ConstU32<7>;
|
||||
type MaxUsernameLength = ConstU32<32>;
|
||||
type WeightInfo = weights::pallet_identity::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
@@ -84,7 +93,6 @@ pub enum IdentityField {
|
||||
TypeInfo,
|
||||
)]
|
||||
#[codec(mel_bound())]
|
||||
#[cfg_attr(test, derive(frame_support::DefaultNoBound))]
|
||||
pub struct IdentityInfo {
|
||||
/// 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
|
||||
@@ -202,3 +210,21 @@ impl IdentityInfo {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Default` identity. This is given to users who get a username but have not set an identity.
|
||||
impl Default for IdentityInfo {
|
||||
fn default() -> Self {
|
||||
IdentityInfo {
|
||||
display: Data::None,
|
||||
legal: Data::None,
|
||||
web: Data::None,
|
||||
matrix: Data::None,
|
||||
email: Data::None,
|
||||
pgp_fingerprint: None,
|
||||
image: Data::None,
|
||||
twitter: Data::None,
|
||||
github: Data::None,
|
||||
discord: Data::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,4 +312,98 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().reads(3))
|
||||
.saturating_add(T::DbWeight::get().writes(2))
|
||||
}
|
||||
/// Storage: `Identity::UsernameAuthorities` (r:0 w:1)
|
||||
/// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
|
||||
fn add_username_authority() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 13_873_000 picoseconds.
|
||||
Weight::from_parts(13_873_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::UsernameAuthorities` (r:0 w:1)
|
||||
/// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
|
||||
fn remove_username_authority() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 10_653_000 picoseconds.
|
||||
Weight::from_parts(10_653_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::UsernameAuthorities` (r:1 w:1)
|
||||
/// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::AccountOfUsername` (r:1 w:1)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:1)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
fn set_username_for() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `80`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 75_928_000 picoseconds.
|
||||
Weight::from_parts(75_928_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(3))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
/// Storage: `Identity::PendingUsernames` (r:1 w:1)
|
||||
/// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:1)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::AccountOfUsername` (r:0 w:1)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
fn accept_username() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `106`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 38_157_000 picoseconds.
|
||||
Weight::from_parts(38_157_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
/// Storage: `Identity::PendingUsernames` (r:1 w:1)
|
||||
/// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`)
|
||||
fn remove_expired_approval() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `106`
|
||||
// Estimated: `3542`
|
||||
// Minimum execution time: 46_821_000 picoseconds.
|
||||
Weight::from_parts(46_821_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 3542))
|
||||
.saturating_add(T::DbWeight::get().reads(1))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::AccountOfUsername` (r:1 w:0)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:1)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
fn set_primary_username() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `247`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 22_515_000 picoseconds.
|
||||
Weight::from_parts(22_515_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `Identity::AccountOfUsername` (r:1 w:1)
|
||||
/// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Identity::IdentityOf` (r:1 w:0)
|
||||
/// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`)
|
||||
fn remove_dangling_username() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `126`
|
||||
// Estimated: `11037`
|
||||
// Minimum execution time: 15_997_000 picoseconds.
|
||||
Weight::from_parts(15_997_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 11037))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user