Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frame/identity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sp-runtime = { version = "3.0.0", default-features = false, path = "../../primit
frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true }
frame-support = { version = "3.0.0", default-features = false, path = "../support" }
frame-system = { version = "3.0.0", default-features = false, path = "../system" }
max-encoded-len = { version = "3.0.0", default-features = false, path = "../../max-encoded-len", features = ["derive"] }

[dev-dependencies]
sp-core = { version = "3.0.0", path = "../../primitives/core" }
Expand All @@ -36,6 +37,7 @@ std = [
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"max-encoded-len/std",
]
runtime-benchmarks = ["frame-benchmarking"]
try-runtime = ["frame-support/try-runtime"]
295 changes: 19 additions & 276 deletions frame/identity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,285 +74,22 @@

#[cfg(test)]
mod tests;
mod types;
mod benchmarking;
pub mod weights;

use sp_std::prelude::*;
use sp_std::{fmt::Debug, ops::Add, iter::once};
use enumflags2::BitFlags;
use codec::{Encode, Decode};
use sp_runtime::RuntimeDebug;
use sp_std::convert::TryInto;
use sp_runtime::traits::{StaticLookup, Zero, AppendZerosInput, Saturating};
use frame_support::traits::{Currency, ReservableCurrency, OnUnbalanced, BalanceStatus};
use frame_support::traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency};
pub use weights::WeightInfo;

pub use pallet::*;
use types::*;

type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;

/// 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.
///
/// Can also be `None`.
#[derive(Clone, Eq, PartialEq, RuntimeDebug)]
pub enum Data {
/// No data here.
None,
/// The data is stored directly.
Raw(Vec<u8>),
/// Only the Blake2 hash of the data is stored. The preimage of the hash may be retrieved
/// through some hash-lookup service.
BlakeTwo256([u8; 32]),
/// Only the SHA2-256 hash of the data is stored. The preimage of the hash may be retrieved
/// through some hash-lookup service.
Sha256([u8; 32]),
/// Only the Keccak-256 hash of the data is stored. The preimage of the hash may be retrieved
/// through some hash-lookup service.
Keccak256([u8; 32]),
/// Only the SHA3-256 hash of the data is stored. The preimage of the hash may be retrieved
/// through some hash-lookup service.
ShaThree256([u8; 32]),
}

impl Decode for Data {
fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
let b = input.read_byte()?;
Ok(match b {
0 => Data::None,
n @ 1 ..= 33 => {
let mut r = vec![0u8; n as usize - 1];
input.read(&mut r[..])?;
Data::Raw(r)
}
34 => Data::BlakeTwo256(<[u8; 32]>::decode(input)?),
35 => Data::Sha256(<[u8; 32]>::decode(input)?),
36 => Data::Keccak256(<[u8; 32]>::decode(input)?),
37 => Data::ShaThree256(<[u8; 32]>::decode(input)?),
_ => return Err(codec::Error::from("invalid leading byte")),
})
}
}

impl Encode for Data {
fn encode(&self) -> Vec<u8> {
match self {
Data::None => vec![0u8; 1],
Data::Raw(ref x) => {
let l = x.len().min(32);
let mut r = vec![l as u8 + 1; l + 1];
&mut r[1..].copy_from_slice(&x[..l as usize]);
r
}
Data::BlakeTwo256(ref h) => once(34u8).chain(h.iter().cloned()).collect(),
Data::Sha256(ref h) => once(35u8).chain(h.iter().cloned()).collect(),
Data::Keccak256(ref h) => once(36u8).chain(h.iter().cloned()).collect(),
Data::ShaThree256(ref h) => once(37u8).chain(h.iter().cloned()).collect(),
}
}
}
impl codec::EncodeLike for Data {}

impl Default for Data {
fn default() -> Self {
Self::None
}
}

/// 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
/// which fields their attestation is relevant for by off-chain means.
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)]
pub enum Judgement<
Balance: Encode + Decode + Copy + Clone + Debug + Eq + PartialEq
> {
/// The default value; no opinion is held.
Unknown,
/// No judgement is yet in place, but a deposit is reserved as payment for providing one.
FeePaid(Balance),
/// The data appears to be reasonably acceptable in terms of its accuracy, however no in depth
/// checks (such as in-person meetings or formal KYC) have been conducted.
Reasonable,
/// The target is known directly by the registrar and the registrar can fully attest to the
/// the data's accuracy.
KnownGood,
/// The data was once good but is currently out of date. There is no malicious intent in the
/// inaccuracy. This judgement can be removed through updating the data.
OutOfDate,
/// The data is imprecise or of sufficiently low-quality to be problematic. It is not
/// indicative of malicious intent. This judgement can be removed through updating the data.
LowQuality,
/// The data is erroneous. This may be indicative of malicious intent. This cannot be removed
/// except by the registrar.
Erroneous,
}

impl<
Balance: Encode + Decode + Copy + Clone + Debug + Eq + PartialEq
> Judgement<Balance> {
/// Returns `true` if this judgement is indicative of a deposit being currently held. This means
/// it should not be cleared or replaced except by an operation which utilizes the deposit.
fn has_deposit(&self) -> bool {
match self {
Judgement::FeePaid(_) => true,
_ => false,
}
}

/// Returns `true` if this judgement is one that should not be generally be replaced outside
/// of specialized handlers. Examples include "malicious" judgements and deposit-holding
/// judgements.
fn is_sticky(&self) -> bool {
match self {
Judgement::FeePaid(_) | Judgement::Erroneous => true,
_ => false,
}
}
}

/// The fields that we use to identify the owner of an account with. Each corresponds to a field
/// in the `IdentityInfo` struct.
#[repr(u64)]
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, BitFlags, RuntimeDebug)]
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(BitFlags<IdentityField>);

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")?))
}
}

/// 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(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)]
#[cfg_attr(test, derive(Default))]
pub struct IdentityInfo {
/// Additional fields of the identity that are not catered for with the struct's explicit
/// fields.
pub additional: Vec<(Data, Data)>,

/// 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,
}

/// Information concerning the identity of the controller of an account.
///
/// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a
/// backwards compatible way through a specialized `Decode` impl.
#[derive(Clone, Encode, Eq, PartialEq, RuntimeDebug)]
pub struct Registration<
Balance: Encode + Decode + Copy + Clone + Debug + Eq + PartialEq
> {
/// Judgements from the registrars on this identity. Stored ordered by `RegistrarIndex`. There
/// may be only a single judgement from each registrar.
pub judgements: Vec<(RegistrarIndex, Judgement<Balance>)>,

/// Amount held on deposit for this information.
pub deposit: Balance,

/// Information on the identity.
pub info: IdentityInfo,
}

impl <
Balance: Encode + Decode + Copy + Clone + Debug + Eq + PartialEq + Zero + Add,
> Registration<Balance> {
fn total_deposit(&self) -> Balance {
self.deposit + self.judgements.iter()
.map(|(_, ref j)| if let Judgement::FeePaid(fee) = j { *fee } else { Zero::zero() })
.fold(Zero::zero(), |a, i| a + i)
}
}

impl<
Balance: Encode + Decode + Copy + Clone + Debug + Eq + PartialEq,
> Decode for Registration<Balance> {
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))?;
Ok(Self { judgements, deposit, info })
}
}

/// Information concerning a registrar.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)]
pub struct RegistrarInfo<
Balance: Encode + Decode + Clone + Debug + Eq + PartialEq,
AccountId: Encode + Decode + Clone + Debug + Eq + PartialEq
> {
/// The account of the registrar.
pub account: AccountId,

/// Amount required to be given to the registrar for them to provide judgement.
pub fee: Balance,

/// Relevant fields for this registrar. Registrar judgements are limited to attestations on
/// these fields.
pub fields: IdentityFields,
}

#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
Expand Down Expand Up @@ -422,7 +159,7 @@ pub mod pallet {
_,
Twox64Concat,
T::AccountId,
Registration<BalanceOf<T>>,
Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,
OptionQuery,
>;

Expand Down Expand Up @@ -461,7 +198,7 @@ pub mod pallet {
#[pallet::getter(fn registrars)]
pub(super) type Registrars<T: Config> = StorageValue<
_,
Vec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>>,
BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>,
ValueQuery,
>;

Expand Down Expand Up @@ -554,10 +291,10 @@ pub mod pallet {

let (i, registrar_count) = <Registrars<T>>::try_mutate(
|registrars| -> Result<(RegistrarIndex, usize), DispatchError> {
ensure!(registrars.len() < T::MaxRegistrars::get() as usize, Error::<T>::TooManyRegistrars);
registrars.push(Some(RegistrarInfo {
registrars.try_push(Some(RegistrarInfo {
account, fee: Zero::zero(), fields: Default::default()
}));
}))
.map_err(|_| Error::<T>::TooManyRegistrars)?;
Ok(((registrars.len() - 1) as RegistrarIndex, registrars.len()))
}
)?;
Expand Down Expand Up @@ -590,7 +327,7 @@ pub mod pallet {
T::MaxRegistrars::get().into(), // R
T::MaxAdditionalFields::get().into(), // X
))]
pub fn set_identity(origin: OriginFor<T>, info: IdentityInfo) -> DispatchResultWithPostInfo {
pub fn set_identity(origin: OriginFor<T>, info: IdentityInfo<T::MaxAdditionalFields>) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
let extra_fields = info.additional.len() as u32;
ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::<T>::TooManyFields);
Expand All @@ -603,7 +340,7 @@ pub mod pallet {
id.info = info;
id
}
None => Registration { info, judgements: Vec::new(), deposit: Zero::zero() },
None => Registration { info, judgements: BoundedVec::default(), deposit: Zero::zero() },
};

let old_deposit = id.deposit;
Expand Down Expand Up @@ -786,7 +523,10 @@ pub mod pallet {
} else {
id.judgements[i] = item
},
Err(i) => id.judgements.insert(i, item),
Err(i) => id
.judgements
.try_insert(i, item)
.map_err(|_| Error::<T>::TooManyRegistrars)?,
}

T::Currency::reserve(&sender, registrar.fee)?;
Expand Down Expand Up @@ -988,7 +728,10 @@ pub mod pallet {
}
id.judgements[position] = item
}
Err(position) => id.judgements.insert(position, item),
Err(position) => id
.judgements
.try_insert(position, item)
.map_err(|_| Error::<T>::TooManyRegistrars)?,
}

let judgements = id.judgements.len();
Expand Down
Loading