// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! # Identity Module
//!
//! - [`identity::Trait`](./trait.Trait.html)
//! - [`Call`](./enum.Call.html)
//!
//! ## Overview
//!
//! A federated naming system, allowing for multiple registrars to be added from a specified origin.
//! Registrars can set a fee to provide identity-verification service. Anyone can put forth a
//! proposed identity for a fixed deposit and ask for review by any number of registrars (paying
//! each of their fees). Registrar judgements are given as an `enum`, allowing for sophisticated,
//! multi-tier opinions.
//!
//! Some judgements are identified as *sticky*, which means they cannot be removed except by
//! complete removal of the identity, or by the registrar. Judgements are allowed to represent a
//! portion of funds that have been reserved for the registrar.
//!
//! A super-user can remove accounts and in doing so, slash the deposit.
//!
//! All accounts may also have a limited number of sub-accounts which may be specified by the owner;
//! by definition, these have equivalent ownership and each has an individual name.
//!
//! The number of registrars should be limited, and the deposit made sufficiently large, to ensure
//! no state-bloat attack is viable.
//!
//! ## Interface
//!
//! ### Dispatchable Functions
//!
//! #### For general users
//! * `set_identity` - Set the associated identity of an account; a small deposit is reserved if not
//! already taken.
//! * `set_subs` - Set the sub-accounts of an identity.
//! * `clear_identity` - Remove an account's associated identity; the deposit is returned.
//! * `request_judgement` - Request a judgement from a registrar, paying a fee.
//! * `cancel_request` - Cancel the previous request for a judgement.
//!
//! #### For registrars
//! * `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar.
//! * `set_fields` - Set the fields that a registrar cares about in their judgements.
//! * `provide_judgement` - Provide a judgement to an identity.
//!
//! #### For super-users
//! * `add_registrar` - Add a new registrar to the system.
//! * `kill_identity` - Forcibly remove the associated identity; the deposit is lost.
//!
//! [`Call`]: ./enum.Call.html
//! [`Trait`]: ./trait.Trait.html
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::prelude::*;
use sp_std::{fmt::Debug, ops::Add, iter::once};
use enumflags2::BitFlags;
use codec::{Encode, Decode};
use sp_runtime::{DispatchResult, RuntimeDebug};
use sp_runtime::traits::{StaticLookup, EnsureOrigin, Zero, AppendZerosInput};
use frame_support::{
decl_module, decl_event, decl_storage, ensure, decl_error,
traits::{Currency, ReservableCurrency, OnUnbalanced, Get},
weights::SimpleDispatchInfo,
};
use frame_system::{self as system, ensure_signed, ensure_root};
type BalanceOf = <::Currency as Currency<::AccountId>>::Balance;
type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance;
pub trait Trait: frame_system::Trait {
/// The overarching event type.
type Event: From> + Into<::Event>;
/// The currency trait.
type Currency: ReservableCurrency;
/// The amount held on deposit for a registered identity.
type BasicDeposit: Get>;
/// The amount held on deposit per additional field for a registered identity.
type FieldDeposit: Get>;
/// The amount held on deposit for a registered subaccount. This should account for the fact
/// that one storage item's value will increase by the size of an account ID, and there will be
/// another trie item whose value is the size of an account ID plus 32 bytes.
type SubAccountDeposit: Get>;
/// The maximum number of sub-accounts allowed per identified account.
type MaxSubAccounts: Get;
/// Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O
/// required to access an identity, but can be pretty high.
type MaxAdditionalFields: Get;
/// What to do with slashed funds.
type Slashed: OnUnbalanced>;
/// The origin which may forcibly set or remove a name. Root can always do this.
type ForceOrigin: EnsureOrigin;
/// The origin which may add or remove registrars. Root can always do this.
type RegistrarOrigin: EnsureOrigin;
}
/// 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),
/// 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(input: &mut I) -> sp_std::result::Result {
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 {
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 {
/// 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` that implements `Codec`.
#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)]
pub struct IdentityFields(BitFlags);
impl Eq for IdentityFields {}
impl Encode for IdentityFields {
fn using_encoded R>(&self, f: F) -> R {
self.0.bits().using_encoded(f)
}
}
impl Decode for IdentityFields {
fn decode(input: &mut I) -> sp_std::result::Result {
let field = u64::decode(input)?;
Ok(Self(>::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)>,
/// 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 {
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 {
fn decode(input: &mut I) -> sp_std::result::Result {
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,
}
decl_storage! {
trait Store for Module as Sudo {
/// Information that is pertinent to identify the entity behind an account.
pub IdentityOf get(fn identity):
map hasher(blake2_256) T::AccountId => Option>>;
/// The super-identity of an alternative "sub" identity together with its name, within that
/// context. If the account is not some other account's sub-identity, then just `None`.
pub SuperOf get(fn super_of):
map hasher(blake2_256) T::AccountId => Option<(T::AccountId, Data)>;
/// Alternative "sub" identities of this account.
///
/// The first item is the deposit, the second is a vector of the accounts.
pub SubsOf get(fn subs):
map hasher(blake2_256) T::AccountId => (BalanceOf, Vec);
/// The set of registrars. Not expected to get very big as can only be added through a
/// special origin (likely a council motion).
///
/// The index into this can be cast to `RegistrarIndex` to get a valid value.
pub Registrars get(fn registrars): Vec