From f8930f84e9cdfb404fbb36d337462df0c86a9379 Mon Sep 17 00:00:00 2001 From: Fahrrader Date: Tue, 27 Dec 2022 14:49:43 +0000 Subject: [PATCH] feat(identity): fork + publicize private fields --- --- a/Cargo.lock +++ b/Cargo.lock @@ -4026,7 +4026,7 @@ "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", - "pallet-identity", + "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)", "pallet-im-online", "pallet-indices", "pallet-membership", @@ -5322,7 +5322,7 @@ "pallet-evm-transaction-payment", "pallet-foreign-assets", "pallet-fungible", - "pallet-identity", + "pallet-identity 4.0.0-dev", "pallet-inflation", "pallet-maintenance", "pallet-nonfungible", @@ -5777,7 +5777,7 @@ [[package]] name = "pallet-collator-selection" -version = "3.0.0" +version = "4.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5885,6 +5885,7 @@ "frame-support", "frame-system", "pallet-evm", + "pallet-identity 4.0.0-dev", "parity-scale-codec 3.2.1", "scale-info", "sp-core", @@ -6184,6 +6185,23 @@ [[package]] name = "pallet-identity" version = "4.0.0-dev" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec 3.2.1", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-identity" +version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36#cb4f2491b00af7d7817f3a54209c26b20faa1f51" dependencies = [ "enumflags2", @@ -8137,7 +8155,7 @@ "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", - "pallet-identity", + "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)", "pallet-im-online", "pallet-indices", "pallet-membership", @@ -8912,7 +8930,7 @@ "pallet-evm-transaction-payment", "pallet-foreign-assets", "pallet-fungible", - "pallet-identity", + "pallet-identity 4.0.0-dev", "pallet-inflation", "pallet-maintenance", "pallet-nonfungible", @@ -9353,7 +9371,7 @@ "pallet-democracy", "pallet-elections-phragmen", "pallet-grandpa", - "pallet-identity", + "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)", "pallet-im-online", "pallet-indices", "pallet-membership", @@ -13144,7 +13162,7 @@ "pallet-evm-transaction-payment", "pallet-foreign-assets", "pallet-fungible", - "pallet-identity", + "pallet-identity 4.0.0-dev", "pallet-inflation", "pallet-maintenance", "pallet-nonfungible", @@ -13789,7 +13807,7 @@ "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", - "pallet-identity", + "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)", "pallet-im-online", "pallet-indices", "pallet-membership", --- a/pallets/collator-selection/Cargo.toml +++ b/pallets/collator-selection/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "pallet-collator-selection" -version = "3.0.0" +version = "4.0.0" description = "Simple staking pallet with a fixed stake." -authors = ["Unique Network "] +authors = ["Parity Technologies ", "Unique Network "] edition = "2021" homepage = "https://unique.network" repository = "https://github.com/UniqueNetwork/unique-chain" --- a/pallets/data-management/Cargo.toml +++ b/pallets/data-management/Cargo.toml @@ -16,6 +16,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +pallet-identity = { default-features = false, path = "../identity" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.36" } fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.36" } --- a/pallets/data-management/src/lib.rs +++ b/pallets/data-management/src/lib.rs @@ -34,9 +34,10 @@ use sp_std::vec::Vec; use super::weights::WeightInfo; use pallet_evm::{PrecompileHandle, Pallet as PalletEvm}; + use pallet_identity::Registration; #[pallet::config] - pub trait Config: frame_system::Config + pallet_evm::Config { + pub trait Config: frame_system::Config + pallet_evm::Config + pallet_identity::Config { /// Weights type WeightInfo: WeightInfo; /// The overarching event type. @@ -149,6 +150,23 @@ } Ok(()) } + + /// Insert or remove identities. + #[pallet::call_index(5)] + #[pallet::weight(>::insert_events(identities.len() as u32))] // todo:collator weight + pub fn insert_identities( + origin: OriginFor, + identities: Vec<( + T::AccountId, + Option, T::MaxRegistrars, T::MaxAdditionalFields>>, + )>, + ) -> DispatchResult { + ensure_root(origin)?; + for identity in identities { + >::set(identity.0, identity.1); + } + Ok(()) + } } /// Implements [`pallet_evm::OnMethodCall`], which reserves accounts with pending migration --- /dev/null +++ b/pallets/identity/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "pallet-identity" +version = "4.0.0-dev" +authors = ["Parity Technologies ", "Unique Network "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME identity management pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +enumflags2 = { version = "0.7.4" } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +sp-io = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +sp-runtime = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +sp-std = { version = "5.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } +sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] --- /dev/null +++ b/pallets/identity/README.md @@ -0,0 +1,56 @@ +# Identity Module + +- [`identity::Config`](https://docs.rs/pallet-identity/latest/pallet_identity/trait.Config.html) +- [`Call`](https://docs.rs/pallet-identity/latest/pallet_identity/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. +* `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 general users with sub-identities +* `set_subs` - Set the sub-accounts of an identity. +* `add_sub` - Add a sub-identity to an identity. +* `remove_sub` - Remove a sub-identity of an identity. +* `rename_sub` - Rename a sub-identity of an identity. +* `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity). + +#### 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 +[`Config`]: ./trait.Config.html + +License: Apache-2.0 --- /dev/null +++ b/pallets/identity/src/benchmarking.rs @@ -0,0 +1,462 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network 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. + +// Unique Network 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 Unique Network. If not, see . + +// Original license: +// This file is part of Substrate. + +// Copyright (C) 2020-2022 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. + +//! Identity pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use crate::Pallet as Identity; +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_support::{ + ensure, + traits::{EnsureOrigin, Get}, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::Bounded; + +const SEED: u32 = 0; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +// Adds `r` registrars to the Identity Pallet. These registrars will have set fees and fields. +fn add_registrars(r: u32) -> Result<(), &'static str> { + for i in 0..r { + let registrar: T::AccountId = account("registrar", i, SEED); + let registrar_lookup = T::Lookup::unlookup(registrar.clone()); + let _ = T::Currency::make_free_balance_be(®istrar, BalanceOf::::max_value()); + let registrar_origin = T::RegistrarOrigin::successful_origin(); + Identity::::add_registrar(registrar_origin, registrar_lookup)?; + Identity::::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, + ); + Identity::::set_fields(RawOrigin::Signed(registrar.clone()).into(), i, fields)?; + } + + assert_eq!(Registrars::::get().len(), r as usize); + Ok(()) +} + +// Create `s` sub-accounts for the identity of `who` and return them. +// Each will have 32 bytes of raw data added to it. +fn create_sub_accounts( + who: &T::AccountId, + s: u32, +) -> Result, &'static str> { + let mut subs = Vec::new(); + let who_origin = RawOrigin::Signed(who.clone()); + let data = Data::Raw(vec![0; 32].try_into().unwrap()); + + for i in 0..s { + let sub_account = account("sub", i, SEED); + subs.push((sub_account, data.clone())); + } + + // Set identity so `set_subs` does not fail. + if IdentityOf::::get(who).is_none() { + let _ = T::Currency::make_free_balance_be(who, BalanceOf::::max_value() / 2u32.into()); + let info = create_identity_info::(1); + Identity::::set_identity(who_origin.into(), Box::new(info))?; + } + + Ok(subs) +} + +// Adds `s` sub-accounts to the identity of `who`. Each will have 32 bytes of raw data added to it. +// This additionally returns the vector of sub-accounts so it can be modified if needed. +fn add_sub_accounts( + who: &T::AccountId, + s: u32, +) -> Result, &'static str> { + let who_origin = RawOrigin::Signed(who.clone()); + let subs = create_sub_accounts::(who, s)?; + + Identity::::set_subs(who_origin.into(), subs.clone())?; + + 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(num_fields: u32) -> IdentityInfo { + 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! { + add_registrar { + let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; + ensure!(Registrars::::get().len() as u32 == r, "Registrars not set up correctly."); + let origin = T::RegistrarOrigin::successful_origin(); + let account = T::Lookup::unlookup(account("registrar", r + 1, SEED)); + }: _(origin, account) + verify { + ensure!(Registrars::::get().len() as u32 == r + 1, "Registrars not added."); + } + + set_identity { + let r in 1 .. T::MaxRegistrars::get() => add_registrars::(r)?; + let x in 0 .. T::MaxAdditionalFields::get(); + let caller = { + // The target user + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let caller_origin: ::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Add an initial identity + let initial_info = create_identity_info::(1); + Identity::::set_identity(caller_origin.clone(), Box::new(initial_info.clone()))?; + + // User requests judgement from all the registrars, and they approve + for i in 0..r { + let registrar: T::AccountId = account("registrar", i, SEED); + let registrar_lookup = T::Lookup::unlookup(registrar.clone()); + let balance_to_use = T::Currency::minimum_balance() * 10u32.into(); + let _ = T::Currency::make_free_balance_be(®istrar, balance_to_use); + + Identity::::request_judgement(caller_origin.clone(), i, 10u32.into())?; + Identity::::provide_judgement( + RawOrigin::Signed(registrar).into(), + i, + caller_lookup.clone(), + Judgement::Reasonable, + T::Hashing::hash_of(&initial_info), + )?; + } + caller + }; + }: _(RawOrigin::Signed(caller.clone()), Box::new(create_identity_info::(x))) + verify { + assert_last_event::(Event::::IdentitySet { who: caller }.into()); + } + + // We need to split `set_subs` into two benchmarks to accurately isolate the potential + // writes caused by new or old sub accounts. The actual weight should simply be + // the sum of these two weights. + set_subs_new { + let caller: T::AccountId = whitelisted_caller(); + // Create a new subs vec with s sub accounts + let s in 0 .. T::MaxSubAccounts::get() => (); + let subs = create_sub_accounts::(&caller, s)?; + ensure!(SubsOf::::get(&caller).1.len() == 0, "Caller already has subs"); + }: set_subs(RawOrigin::Signed(caller.clone()), subs) + verify { + ensure!(SubsOf::::get(&caller).1.len() as u32 == s, "Subs not added"); + } + + set_subs_old { + let caller: T::AccountId = whitelisted_caller(); + // Give them p many previous sub accounts. + let p in 0 .. T::MaxSubAccounts::get() => { + let _ = add_sub_accounts::(&caller, p)?; + }; + // Remove all subs. + let subs = create_sub_accounts::(&caller, 0)?; + ensure!( + SubsOf::::get(&caller).1.len() as u32 == p, + "Caller does have subs", + ); + }: set_subs(RawOrigin::Signed(caller.clone()), subs) + verify { + ensure!(SubsOf::::get(&caller).1.len() == 0, "Subs not removed"); + } + + clear_identity { + let caller: T::AccountId = whitelisted_caller(); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller.clone())); + let caller_lookup = ::unlookup(caller.clone()); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let r in 1 .. T::MaxRegistrars::get() => add_registrars::(r)?; + let s in 0 .. T::MaxSubAccounts::get() => { + // Give them s many sub accounts + let caller: T::AccountId = whitelisted_caller(); + let _ = add_sub_accounts::(&caller, s)?; + }; + let x in 0 .. T::MaxAdditionalFields::get(); + + // Create their main identity with x additional fields + let info = create_identity_info::(x); + let caller: T::AccountId = whitelisted_caller(); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller.clone())); + Identity::::set_identity(caller_origin.clone(), Box::new(info.clone()))?; + + // User requests judgement from all the registrars, and they approve + for i in 0..r { + let registrar: T::AccountId = account("registrar", i, SEED); + let balance_to_use = T::Currency::minimum_balance() * 10u32.into(); + let _ = T::Currency::make_free_balance_be(®istrar, balance_to_use); + + Identity::::request_judgement(caller_origin.clone(), i, 10u32.into())?; + Identity::::provide_judgement( + RawOrigin::Signed(registrar).into(), + i, + caller_lookup.clone(), + Judgement::Reasonable, + T::Hashing::hash_of(&info), + )?; + } + ensure!(IdentityOf::::contains_key(&caller), "Identity does not exist."); + }: _(RawOrigin::Signed(caller.clone())) + verify { + ensure!(!IdentityOf::::contains_key(&caller), "Identity not cleared."); + } + + request_judgement { + let caller: T::AccountId = whitelisted_caller(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let r in 1 .. T::MaxRegistrars::get() => add_registrars::(r)?; + let x in 0 .. T::MaxAdditionalFields::get() => { + // Create their main identity with x additional fields + let info = create_identity_info::(x); + let caller: T::AccountId = whitelisted_caller(); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller)); + Identity::::set_identity(caller_origin, Box::new(info))?; + }; + }: _(RawOrigin::Signed(caller.clone()), r - 1, 10u32.into()) + verify { + assert_last_event::(Event::::JudgementRequested { who: caller, registrar_index: r-1 }.into()); + } + + cancel_request { + let caller: T::AccountId = whitelisted_caller(); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller.clone())); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let r in 1 .. T::MaxRegistrars::get() => add_registrars::(r)?; + let x in 0 .. T::MaxAdditionalFields::get() => { + // Create their main identity with x additional fields + let info = create_identity_info::(x); + let caller: T::AccountId = whitelisted_caller(); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller)); + Identity::::set_identity(caller_origin, Box::new(info))?; + }; + + Identity::::request_judgement(caller_origin, r - 1, 10u32.into())?; + }: _(RawOrigin::Signed(caller.clone()), r - 1) + verify { + assert_last_event::(Event::::JudgementUnrequested { who: caller, registrar_index: r-1 }.into()); + } + + set_fee { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + + let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; + + let registrar_origin = T::RegistrarOrigin::successful_origin(); + Identity::::add_registrar(registrar_origin, caller_lookup)?; + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fee == 0u32.into(), "Fee already set."); + }: _(RawOrigin::Signed(caller), r, 100u32.into()) + verify { + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fee == 100u32.into(), "Fee not changed."); + } + + set_account_id { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; + + let registrar_origin = T::RegistrarOrigin::successful_origin(); + Identity::::add_registrar(registrar_origin, caller_lookup)?; + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().account == caller, "id not set."); + let new_account = T::Lookup::unlookup(account("new", 0, SEED)); + }: _(RawOrigin::Signed(caller), r, new_account) + verify { + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().account == account("new", 0, SEED), "id not changed."); + } + + set_fields { + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; + + let registrar_origin = T::RegistrarOrigin::successful_origin(); + Identity::::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 registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fields == Default::default(), "fields already set."); + }: _(RawOrigin::Signed(caller), r, fields) + verify { + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fields != Default::default(), "fields not set."); + } + + provide_judgement { + // The user + let user: T::AccountId = account("user", r, SEED); + let user_origin = ::RuntimeOrigin::from(RawOrigin::Signed(user.clone())); + let user_lookup = ::unlookup(user.clone()); + let _ = T::Currency::make_free_balance_be(&user, BalanceOf::::max_value()); + + let caller: T::AccountId = whitelisted_caller(); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; + let x in 0 .. T::MaxAdditionalFields::get(); + + let info = create_identity_info::(x); + let info_hash = T::Hashing::hash_of(&info); + Identity::::set_identity(user_origin.clone(), Box::new(info))?; + + let registrar_origin = T::RegistrarOrigin::successful_origin(); + Identity::::add_registrar(registrar_origin, caller_lookup)?; + Identity::::request_judgement(user_origin, r, 10u32.into())?; + }: _(RawOrigin::Signed(caller), r, user_lookup, Judgement::Reasonable, info_hash) + verify { + assert_last_event::(Event::::JudgementGiven { target: user, registrar_index: r }.into()) + } + + kill_identity { + let r in 1 .. T::MaxRegistrars::get() => add_registrars::(r)?; + let s in 0 .. T::MaxSubAccounts::get(); + let x in 0 .. T::MaxAdditionalFields::get(); + + let target: T::AccountId = account("target", 0, SEED); + let target_origin: ::RuntimeOrigin = RawOrigin::Signed(target.clone()).into(); + let target_lookup = T::Lookup::unlookup(target.clone()); + let _ = T::Currency::make_free_balance_be(&target, BalanceOf::::max_value()); + + let info = create_identity_info::(x); + Identity::::set_identity(target_origin.clone(), Box::new(info.clone()))?; + let _ = add_sub_accounts::(&target, s)?; + + // User requests judgement from all the registrars, and they approve + for i in 0..r { + let registrar: T::AccountId = account("registrar", i, SEED); + let balance_to_use = T::Currency::minimum_balance() * 10u32.into(); + let _ = T::Currency::make_free_balance_be(®istrar, balance_to_use); + + Identity::::request_judgement(target_origin.clone(), i, 10u32.into())?; + Identity::::provide_judgement( + RawOrigin::Signed(registrar).into(), + i, + target_lookup.clone(), + Judgement::Reasonable, + T::Hashing::hash_of(&info), + )?; + } + ensure!(IdentityOf::::contains_key(&target), "Identity not set"); + let origin = T::ForceOrigin::successful_origin(); + }: _(origin, target_lookup) + verify { + ensure!(!IdentityOf::::contains_key(&target), "Identity not removed"); + } + + add_sub { + let s in 0 .. T::MaxSubAccounts::get() - 1; + + let caller: T::AccountId = whitelisted_caller(); + let _ = add_sub_accounts::(&caller, s)?; + let sub = account("new_sub", 0, SEED); + let data = Data::Raw(vec![0; 32].try_into().unwrap()); + ensure!(SubsOf::::get(&caller).1.len() as u32 == s, "Subs not set."); + }: _(RawOrigin::Signed(caller.clone()), T::Lookup::unlookup(sub), data) + verify { + ensure!(SubsOf::::get(&caller).1.len() as u32 == s + 1, "Subs not added."); + } + + rename_sub { + let s in 1 .. T::MaxSubAccounts::get(); + + let caller: T::AccountId = whitelisted_caller(); + let (sub, _) = add_sub_accounts::(&caller, s)?.remove(0); + let data = Data::Raw(vec![1; 32].try_into().unwrap()); + ensure!(SuperOf::::get(&sub).unwrap().1 != data, "data already set"); + }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub.clone()), data.clone()) + verify { + ensure!(SuperOf::::get(&sub).unwrap().1 == data, "data not set"); + } + + remove_sub { + let s in 1 .. T::MaxSubAccounts::get(); + + let caller: T::AccountId = whitelisted_caller(); + let (sub, _) = add_sub_accounts::(&caller, s)?.remove(0); + ensure!(SuperOf::::contains_key(&sub), "Sub doesn't exists"); + }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub.clone())) + verify { + ensure!(!SuperOf::::contains_key(&sub), "Sub not removed"); + } + + quit_sub { + let s in 0 .. T::MaxSubAccounts::get() - 1; + + let caller: T::AccountId = whitelisted_caller(); + let sup = account("super", 0, SEED); + let _ = add_sub_accounts::(&sup, s)?; + let sup_origin = RawOrigin::Signed(sup).into(); + Identity::::add_sub(sup_origin, T::Lookup::unlookup(caller.clone()), Data::Raw(vec![0; 32].try_into().unwrap()))?; + ensure!(SuperOf::::contains_key(&caller), "Sub doesn't exists"); + }: _(RawOrigin::Signed(caller.clone())) + verify { + ensure!(!SuperOf::::contains_key(&caller), "Sub not removed"); + } + + impl_benchmark_test_suite!(Identity, crate::tests::new_test_ext(), crate::tests::Test); +} --- /dev/null +++ b/pallets/identity/src/lib.rs @@ -0,0 +1,1030 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network 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. + +// Unique Network 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 Unique Network. If not, see . + +// Original license: +// This file is part of Substrate. + +// Copyright (C) 2019-2022 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. + +//! # Identity Pallet +//! +//! - [`Config`] +//! - [`Call`] +//! +//! ## 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. +//! * `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 general users with sub-identities +//! * `set_subs` - Set the sub-accounts of an identity. +//! * `add_sub` - Add a sub-identity to an identity. +//! * `remove_sub` - Remove a sub-identity of an identity. +//! * `rename_sub` - Rename a sub-identity of an identity. +//! * `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity). +//! +//! #### 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 +//! [`Config`]: ./trait.Config.html + +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +#[cfg(test)] +mod tests; +mod types; +pub mod weights; + +use frame_support::traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency}; +use sp_runtime::traits::{AppendZerosInput, Hash, Saturating, StaticLookup, Zero}; +use sp_std::prelude::*; +pub use weights::WeightInfo; + +pub use pallet::*; +pub use types::{ + Data, IdentityField, IdentityFields, IdentityInfo, Judgement, RegistrarIndex, RegistrarInfo, + Registration, +}; + +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::NegativeImbalance; +type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The currency trait. + type Currency: ReservableCurrency; + + /// The amount held on deposit for a registered identity + #[pallet::constant] + type BasicDeposit: Get>; + + /// The amount held on deposit per additional field for a registered identity. + #[pallet::constant] + 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. + #[pallet::constant] + type SubAccountDeposit: Get>; + + /// The maximum number of sub-accounts allowed per identified account. + #[pallet::constant] + 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. + #[pallet::constant] + type MaxAdditionalFields: Get; + + /// Maxmimum number of registrars allowed in the system. Needed to bound the complexity + /// of, e.g., updating judgements. + #[pallet::constant] + type MaxRegistrars: 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; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + /// Information that is pertinent to identify the entity behind an account. + /// + /// TWOX-NOTE: OK ― `AccountId` is a secure hash. + #[pallet::storage] + #[pallet::getter(fn identity)] + pub type IdentityOf = StorageMap< + _, + Twox64Concat, + T::AccountId, + Registration, T::MaxRegistrars, T::MaxAdditionalFields>, + OptionQuery, + >; + + /// 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`. + #[pallet::storage] + #[pallet::getter(fn super_of)] + pub(super) type SuperOf = + StorageMap<_, Blake2_128Concat, T::AccountId, (T::AccountId, Data), OptionQuery>; + + /// Alternative "sub" identities of this account. + /// + /// The first item is the deposit, the second is a vector of the accounts. + /// + /// TWOX-NOTE: OK ― `AccountId` is a secure hash. + #[pallet::storage] + #[pallet::getter(fn subs_of)] + pub(super) type SubsOf = StorageMap< + _, + Twox64Concat, + T::AccountId, + (BalanceOf, BoundedVec), + ValueQuery, + >; + + /// 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. + #[pallet::storage] + #[pallet::getter(fn registrars)] + pub(super) type Registrars = StorageValue< + _, + BoundedVec, T::AccountId>>, T::MaxRegistrars>, + ValueQuery, + >; + + #[pallet::error] + pub enum Error { + /// Too many subs-accounts. + TooManySubAccounts, + /// Account isn't found. + NotFound, + /// Account isn't named. + NotNamed, + /// Empty index. + EmptyIndex, + /// Fee is changed. + FeeChanged, + /// No identity found. + NoIdentity, + /// Sticky judgement. + StickyJudgement, + /// Judgement given. + JudgementGiven, + /// Invalid judgement. + InvalidJudgement, + /// The index is invalid. + InvalidIndex, + /// The target is invalid. + InvalidTarget, + /// Too many additional fields. + TooManyFields, + /// Maximum amount of registrars reached. Cannot add any more. + TooManyRegistrars, + /// Account ID is already named. + AlreadyClaimed, + /// Sender is not a sub-account. + NotSub, + /// Sub-account isn't owned by sender. + NotOwned, + /// The provided judgement was for a different identity. + JudgementForDifferentIdentity, + /// Error that occurs when there is an issue paying for judgement. + JudgementPaymentFailed, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A name was set or reset (which will remove all judgements). + IdentitySet { who: T::AccountId }, + /// A name was cleared, and the given balance returned. + IdentityCleared { who: T::AccountId, deposit: BalanceOf }, + /// A name was removed and the given balance slashed. + IdentityKilled { who: T::AccountId, deposit: BalanceOf }, + /// A judgement was asked from a registrar. + JudgementRequested { who: T::AccountId, registrar_index: RegistrarIndex }, + /// A judgement request was retracted. + JudgementUnrequested { who: T::AccountId, registrar_index: RegistrarIndex }, + /// A judgement was given by a registrar. + JudgementGiven { target: T::AccountId, registrar_index: RegistrarIndex }, + /// A registrar was added. + RegistrarAdded { registrar_index: RegistrarIndex }, + /// A sub-identity was added to an identity and the deposit paid. + SubIdentityAdded { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf }, + /// A sub-identity was removed from an identity and the deposit freed. + SubIdentityRemoved { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf }, + /// A sub-identity was cleared, and the given deposit repatriated from the + /// main identity account to the sub-identity account. + SubIdentityRevoked { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf }, + } + + #[pallet::call] + /// Identity pallet declaration. + impl Pallet { + /// Add a registrar to the system. + /// + /// The dispatch origin for this call must be `T::RegistrarOrigin`. + /// + /// - `account`: the account of the registrar. + /// + /// Emits `RegistrarAdded` if successful. + /// + /// # + /// - `O(R)` where `R` registrar-count (governance-bounded and code-bounded). + /// - One storage mutation (codec `O(R)`). + /// - One event. + /// # + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))] + pub fn add_registrar( + origin: OriginFor, + account: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { + T::RegistrarOrigin::ensure_origin(origin)?; + let account = T::Lookup::lookup(account)?; + + let (i, registrar_count) = >::try_mutate( + |registrars| -> Result<(RegistrarIndex, usize), DispatchError> { + registrars + .try_push(Some(RegistrarInfo { + account, + fee: Zero::zero(), + fields: Default::default(), + })) + .map_err(|_| Error::::TooManyRegistrars)?; + Ok(((registrars.len() - 1) as RegistrarIndex, registrars.len())) + }, + )?; + + Self::deposit_event(Event::RegistrarAdded { registrar_index: i }); + + Ok(Some(T::WeightInfo::add_registrar(registrar_count as u32)).into()) + } + + /// Set an account's identity information and reserve the appropriate deposit. + /// + /// If the account already has identity information, the deposit is taken as part payment + /// for the new deposit. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `info`: The identity information. + /// + /// Emits `IdentitySet` if successful. + /// + /// # + /// - `O(X + X' + R)` + /// - where `X` additional-field-count (deposit-bounded and code-bounded) + /// - where `R` judgements-count (registrar-count-bounded) + /// - One balance reserve operation. + /// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`). + /// - One event. + /// # + #[pallet::call_index(1)] + #[pallet::weight( T::WeightInfo::set_identity( + T::MaxRegistrars::get(), // R + T::MaxAdditionalFields::get(), // X + ))] + pub fn set_identity( + origin: OriginFor, + info: Box>, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + let extra_fields = info.additional.len() as u32; + ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::::TooManyFields); + let fd = >::from(extra_fields) * T::FieldDeposit::get(); + + let mut id = match >::get(&sender) { + Some(mut id) => { + // Only keep non-positive judgements. + id.judgements.retain(|j| j.1.is_sticky()); + id.info = *info; + id + }, + None => Registration { + info: *info, + judgements: BoundedVec::default(), + deposit: Zero::zero(), + }, + }; + + let old_deposit = id.deposit; + id.deposit = T::BasicDeposit::get() + fd; + if id.deposit > old_deposit { + T::Currency::reserve(&sender, id.deposit - old_deposit)?; + } + if old_deposit > id.deposit { + let err_amount = T::Currency::unreserve(&sender, old_deposit - id.deposit); + debug_assert!(err_amount.is_zero()); + } + + let judgements = id.judgements.len(); + >::insert(&sender, id); + Self::deposit_event(Event::IdentitySet { who: sender }); + + Ok(Some(T::WeightInfo::set_identity( + judgements as u32, // R + extra_fields, // X + )) + .into()) + } + + /// Set the sub-accounts of the sender. + /// + /// Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned + /// and an amount `SubAccountDeposit` will be reserved for each item in `subs`. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a registered + /// identity. + /// + /// - `subs`: The identity's (new) sub-accounts. + /// + /// # + /// - `O(P + S)` + /// - where `P` old-subs-count (hard- and deposit-bounded). + /// - where `S` subs-count (hard- and deposit-bounded). + /// - At most one balance operations. + /// - DB: + /// - `P + S` storage mutations (codec complexity `O(1)`) + /// - One storage read (codec complexity `O(P)`). + /// - One storage write (codec complexity `O(S)`). + /// - One storage-exists (`IdentityOf::contains_key`). + /// # + // 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 + // N storage items for N sub accounts. Right now the weight on this function + // 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. + )] + pub fn set_subs( + origin: OriginFor, + subs: Vec<(T::AccountId, Data)>, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(&sender), Error::::NotFound); + ensure!( + subs.len() <= T::MaxSubAccounts::get() as usize, + Error::::TooManySubAccounts + ); + + let (old_deposit, old_ids) = >::get(&sender); + let new_deposit = T::SubAccountDeposit::get() * >::from(subs.len() as u32); + + let not_other_sub = + subs.iter().filter_map(|i| SuperOf::::get(&i.0)).all(|i| i.0 == sender); + ensure!(not_other_sub, Error::::AlreadyClaimed); + + if old_deposit < new_deposit { + T::Currency::reserve(&sender, new_deposit - old_deposit)?; + } else if old_deposit > new_deposit { + let err_amount = T::Currency::unreserve(&sender, old_deposit - new_deposit); + debug_assert!(err_amount.is_zero()); + } + // do nothing if they're equal. + + for s in old_ids.iter() { + >::remove(s); + } + let mut ids = BoundedVec::::default(); + for (id, name) in subs { + >::insert(&id, (sender.clone(), name)); + ids.try_push(id).expect("subs length is less than T::MaxSubAccounts; qed"); + } + let new_subs = ids.len(); + + if ids.is_empty() { + >::remove(&sender); + } else { + >::insert(&sender, (new_deposit, ids)); + } + + Ok(Some( + T::WeightInfo::set_subs_old(old_ids.len() as u32) // P: Real number of old accounts removed. + // S: New subs added + .saturating_add(T::WeightInfo::set_subs_new(new_subs as u32)), + ) + .into()) + } + + /// Clear an account's identity info and all sub-accounts and return all deposits. + /// + /// Payment: All reserved balances on the account are returned. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a registered + /// identity. + /// + /// Emits `IdentityCleared` if successful. + /// + /// # + /// - `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). + /// - One balance-unreserve operation. + /// - `2` storage reads and `S + 2` storage deletions. + /// - One event. + /// # + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::clear_identity( + T::MaxRegistrars::get(), // R + T::MaxSubAccounts::get(), // S + T::MaxAdditionalFields::get(), // X + ))] + pub fn clear_identity(origin: OriginFor) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + + let (subs_deposit, sub_ids) = >::take(&sender); + let id = >::take(&sender).ok_or(Error::::NotNamed)?; + let deposit = id.total_deposit() + subs_deposit; + for sub in sub_ids.iter() { + >::remove(sub); + } + + let err_amount = T::Currency::unreserve(&sender, deposit); + debug_assert!(err_amount.is_zero()); + + Self::deposit_event(Event::IdentityCleared { who: sender, deposit }); + + 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 + )) + .into()) + } + + /// Request a judgement from a registrar. + /// + /// Payment: At most `max_fee` will be reserved for payment to the registrar if judgement + /// given. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a + /// registered identity. + /// + /// - `reg_index`: The index of the registrar whose judgement is requested. + /// - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as: + /// + /// ```nocompile + /// Self::registrars().get(reg_index).unwrap().fee + /// ``` + /// + /// Emits `JudgementRequested` if successful. + /// + /// # + /// - `O(R + X)`. + /// - One balance-reserve operation. + /// - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`. + /// - One event. + /// # + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::request_judgement( + T::MaxRegistrars::get(), // R + T::MaxAdditionalFields::get(), // X + ))] + pub fn request_judgement( + origin: OriginFor, + #[pallet::compact] reg_index: RegistrarIndex, + #[pallet::compact] max_fee: BalanceOf, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + let registrars = >::get(); + let registrar = registrars + .get(reg_index as usize) + .and_then(Option::as_ref) + .ok_or(Error::::EmptyIndex)?; + ensure!(max_fee >= registrar.fee, Error::::FeeChanged); + let mut id = >::get(&sender).ok_or(Error::::NoIdentity)?; + + let item = (reg_index, Judgement::FeePaid(registrar.fee)); + match id.judgements.binary_search_by_key(®_index, |x| x.0) { + Ok(i) => + if id.judgements[i].1.is_sticky() { + return Err(Error::::StickyJudgement.into()) + } else { + id.judgements[i] = item + }, + Err(i) => + id.judgements.try_insert(i, item).map_err(|_| Error::::TooManyRegistrars)?, + } + + T::Currency::reserve(&sender, registrar.fee)?; + + let judgements = id.judgements.len(); + let extra_fields = id.info.additional.len(); + >::insert(&sender, id); + + Self::deposit_event(Event::JudgementRequested { + who: sender, + registrar_index: reg_index, + }); + + Ok(Some(T::WeightInfo::request_judgement(judgements as u32, extra_fields as u32)) + .into()) + } + + /// Cancel a previous request. + /// + /// Payment: A previously reserved deposit is returned on success. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a + /// registered identity. + /// + /// - `reg_index`: The index of the registrar whose judgement is no longer requested. + /// + /// Emits `JudgementUnrequested` if successful. + /// + /// # + /// - `O(R + X)`. + /// - One balance-reserve operation. + /// - One storage mutation `O(R + X)`. + /// - One event + /// # + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::cancel_request( + T::MaxRegistrars::get(), // R + T::MaxAdditionalFields::get(), // X + ))] + pub fn cancel_request( + origin: OriginFor, + reg_index: RegistrarIndex, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + let mut id = >::get(&sender).ok_or(Error::::NoIdentity)?; + + let pos = id + .judgements + .binary_search_by_key(®_index, |x| x.0) + .map_err(|_| Error::::NotFound)?; + let fee = if let Judgement::FeePaid(fee) = id.judgements.remove(pos).1 { + fee + } else { + return Err(Error::::JudgementGiven.into()) + }; + + 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(); + >::insert(&sender, id); + + Self::deposit_event(Event::JudgementUnrequested { + who: sender, + registrar_index: reg_index, + }); + + Ok(Some(T::WeightInfo::cancel_request(judgements as u32, extra_fields as u32)).into()) + } + + /// Set the fee required for a judgement to be requested from a registrar. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must be the account + /// of the registrar whose index is `index`. + /// + /// - `index`: the index of the registrar whose fee is to be set. + /// - `fee`: the new fee. + /// + /// # + /// - `O(R)`. + /// - One storage mutation `O(R)`. + /// - Benchmark: 7.315 + R * 0.329 µs (min squares analysis) + /// # + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] // R + pub fn set_fee( + origin: OriginFor, + #[pallet::compact] index: RegistrarIndex, + #[pallet::compact] fee: BalanceOf, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let registrars = >::mutate(|rs| -> Result { + rs.get_mut(index as usize) + .and_then(|x| x.as_mut()) + .and_then(|r| { + if r.account == who { + r.fee = fee; + Some(()) + } else { + None + } + }) + .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; + Ok(rs.len()) + })?; + Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) // R + } + + /// Change the account associated with a registrar. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must be the account + /// of the registrar whose index is `index`. + /// + /// - `index`: the index of the registrar whose fee is to be set. + /// - `new`: the new account ID. + /// + /// # + /// - `O(R)`. + /// - One storage mutation `O(R)`. + /// - Benchmark: 8.823 + R * 0.32 µs (min squares analysis) + /// # + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] // R + pub fn set_account_id( + origin: OriginFor, + #[pallet::compact] index: RegistrarIndex, + new: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let new = T::Lookup::lookup(new)?; + + let registrars = >::mutate(|rs| -> Result { + rs.get_mut(index as usize) + .and_then(|x| x.as_mut()) + .and_then(|r| { + if r.account == who { + r.account = new; + Some(()) + } else { + None + } + }) + .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; + Ok(rs.len()) + })?; + Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) // R + } + + /// Set the field information for a registrar. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must be the account + /// of the registrar whose index is `index`. + /// + /// - `index`: the index of the registrar whose fee is to be set. + /// - `fields`: the fields that the registrar concerns themselves with. + /// + /// # + /// - `O(R)`. + /// - One storage mutation `O(R)`. + /// - Benchmark: 7.464 + R * 0.325 µs (min squares analysis) + /// # + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] // R + pub fn set_fields( + origin: OriginFor, + #[pallet::compact] index: RegistrarIndex, + fields: IdentityFields, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let registrars = >::mutate(|rs| -> Result { + rs.get_mut(index as usize) + .and_then(|x| x.as_mut()) + .and_then(|r| { + if r.account == who { + r.fields = fields; + Some(()) + } else { + None + } + }) + .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; + Ok(rs.len()) + })?; + Ok(Some(T::WeightInfo::set_fields( + registrars as u32, // R + )) + .into()) + } + + /// Provide a judgement for an account's identity. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must be the account + /// of the registrar whose index is `reg_index`. + /// + /// - `reg_index`: the index of the registrar whose judgement is being made. + /// - `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. + /// + /// Emits `JudgementGiven` if successful. + /// + /// # + /// - `O(R + X)`. + /// - One balance-transfer operation. + /// - Up to one account-lookup operation. + /// - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`. + /// - One event. + /// # + #[pallet::call_index(9)] + #[pallet::weight(T::WeightInfo::provide_judgement( + T::MaxRegistrars::get(), // R + T::MaxAdditionalFields::get(), // X + ))] + pub fn provide_judgement( + origin: OriginFor, + #[pallet::compact] reg_index: RegistrarIndex, + target: AccountIdLookupOf, + judgement: Judgement>, + identity: T::Hash, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + let target = T::Lookup::lookup(target)?; + ensure!(!judgement.has_deposit(), Error::::InvalidJudgement); + >::get() + .get(reg_index as usize) + .and_then(Option::as_ref) + .filter(|r| r.account == sender) + .ok_or(Error::::InvalidIndex)?; + let mut id = >::get(&target).ok_or(Error::::InvalidTarget)?; + + if T::Hashing::hash_of(&id.info) != identity { + return Err(Error::::JudgementForDifferentIdentity.into()) + } + + let item = (reg_index, judgement); + match id.judgements.binary_search_by_key(®_index, |x| x.0) { + Ok(position) => { + if let Judgement::FeePaid(fee) = id.judgements[position].1 { + T::Currency::repatriate_reserved( + &target, + &sender, + fee, + BalanceStatus::Free, + ) + .map_err(|_| Error::::JudgementPaymentFailed)?; + } + id.judgements[position] = item + }, + Err(position) => id + .judgements + .try_insert(position, item) + .map_err(|_| Error::::TooManyRegistrars)?, + } + + let judgements = id.judgements.len(); + let extra_fields = id.info.additional.len(); + >::insert(&target, id); + Self::deposit_event(Event::JudgementGiven { target, registrar_index: reg_index }); + + Ok(Some(T::WeightInfo::provide_judgement(judgements as u32, extra_fields as u32)) + .into()) + } + + /// Remove an account's identity and sub-account information and slash the deposits. + /// + /// Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by + /// `Slash`. Verification request deposits are not returned; they should be cancelled + /// manually using `cancel_request`. + /// + /// The dispatch origin for this call must match `T::ForceOrigin`. + /// + /// - `target`: the account whose identity the judgement is upon. This must be an account + /// with a registered identity. + /// + /// Emits `IdentityKilled` if successful. + /// + /// # + /// - `O(R + S + X)`. + /// - One balance-reserve operation. + /// - `S + 2` storage mutations. + /// - One event. + /// # + #[pallet::call_index(10)] + #[pallet::weight(T::WeightInfo::kill_identity( + T::MaxRegistrars::get(), // R + T::MaxSubAccounts::get(), // S + T::MaxAdditionalFields::get(), // X + ))] + pub fn kill_identity( + origin: OriginFor, + target: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { + T::ForceOrigin::ensure_origin(origin)?; + + // Figure out who we're meant to be clearing. + let target = T::Lookup::lookup(target)?; + // Grab their deposit (and check that they have one). + let (subs_deposit, sub_ids) = >::take(&target); + let id = >::take(&target).ok_or(Error::::NotNamed)?; + let deposit = id.total_deposit() + subs_deposit; + for sub in sub_ids.iter() { + >::remove(sub); + } + // Slash their deposit from them. + T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0); + + Self::deposit_event(Event::IdentityKilled { who: target, deposit }); + + 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 + )) + .into()) + } + + /// Add the given account to the sender's subs. + /// + /// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated + /// to the sender. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a registered + /// sub identity of `sub`. + #[pallet::call_index(11)] + #[pallet::weight(T::WeightInfo::add_sub(T::MaxSubAccounts::get()))] + pub fn add_sub( + origin: OriginFor, + sub: AccountIdLookupOf, + data: Data, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let sub = T::Lookup::lookup(sub)?; + ensure!(IdentityOf::::contains_key(&sender), Error::::NoIdentity); + + // Check if it's already claimed as sub-identity. + ensure!(!SuperOf::::contains_key(&sub), Error::::AlreadyClaimed); + + SubsOf::::try_mutate(&sender, |(ref mut subs_deposit, ref mut sub_ids)| { + // Ensure there is space and that the deposit is paid. + ensure!( + sub_ids.len() < T::MaxSubAccounts::get() as usize, + Error::::TooManySubAccounts + ); + let deposit = T::SubAccountDeposit::get(); + T::Currency::reserve(&sender, deposit)?; + + SuperOf::::insert(&sub, (sender.clone(), data)); + sub_ids.try_push(sub.clone()).expect("sub ids length checked above; qed"); + *subs_deposit = subs_deposit.saturating_add(deposit); + + Self::deposit_event(Event::SubIdentityAdded { sub, main: sender.clone(), deposit }); + Ok(()) + }) + } + + /// Alter the associated name of the given sub-account. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a registered + /// sub identity of `sub`. + #[pallet::call_index(12)] + #[pallet::weight(T::WeightInfo::rename_sub(T::MaxSubAccounts::get()))] + pub fn rename_sub( + origin: OriginFor, + sub: AccountIdLookupOf, + data: Data, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let sub = T::Lookup::lookup(sub)?; + ensure!(IdentityOf::::contains_key(&sender), Error::::NoIdentity); + ensure!(SuperOf::::get(&sub).map_or(false, |x| x.0 == sender), Error::::NotOwned); + SuperOf::::insert(&sub, (sender, data)); + Ok(()) + } + + /// Remove the given account from the sender's subs. + /// + /// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated + /// to the sender. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a registered + /// sub identity of `sub`. + #[pallet::call_index(13)] + #[pallet::weight(T::WeightInfo::remove_sub(T::MaxSubAccounts::get()))] + pub fn remove_sub(origin: OriginFor, sub: AccountIdLookupOf) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(IdentityOf::::contains_key(&sender), Error::::NoIdentity); + let sub = T::Lookup::lookup(sub)?; + let (sup, _) = SuperOf::::get(&sub).ok_or(Error::::NotSub)?; + ensure!(sup == sender, Error::::NotOwned); + SuperOf::::remove(&sub); + SubsOf::::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| { + sub_ids.retain(|x| x != &sub); + let deposit = T::SubAccountDeposit::get().min(*subs_deposit); + *subs_deposit -= deposit; + let err_amount = T::Currency::unreserve(&sender, deposit); + debug_assert!(err_amount.is_zero()); + Self::deposit_event(Event::SubIdentityRemoved { sub, main: sender, deposit }); + }); + Ok(()) + } + + /// Remove the sender as a sub-account. + /// + /// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated + /// to the sender (*not* the original depositor). + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have a registered + /// super-identity. + /// + /// NOTE: This should not normally be used, but is provided in the case that the non- + /// controller of an account is maliciously registered as a sub-account. + #[pallet::call_index(14)] + #[pallet::weight(T::WeightInfo::quit_sub(T::MaxSubAccounts::get()))] + pub fn quit_sub(origin: OriginFor) -> DispatchResult { + let sender = ensure_signed(origin)?; + let (sup, _) = SuperOf::::take(&sender).ok_or(Error::::NotSub)?; + SubsOf::::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| { + sub_ids.retain(|x| x != &sender); + let deposit = T::SubAccountDeposit::get().min(*subs_deposit); + *subs_deposit -= deposit; + let _ = + T::Currency::repatriate_reserved(&sup, &sender, deposit, BalanceStatus::Free); + Self::deposit_event(Event::SubIdentityRevoked { + sub: sender, + main: sup.clone(), + deposit, + }); + }); + Ok(()) + } + } +} + +impl Pallet { + /// Get the subs of an account. + pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> { + SubsOf::::get(who) + .1 + .into_iter() + .filter_map(|a| SuperOf::::get(&a).map(|x| (a, x.1))) + .collect() + } + + /// Check if the account has corresponding identity information by the identity field. + pub fn has_identity(who: &T::AccountId, fields: u64) -> bool { + IdentityOf::::get(who) + .map_or(false, |registration| (registration.info.fields().0.bits() & fields) == fields) + } +} --- /dev/null +++ b/pallets/identity/src/tests.rs @@ -0,0 +1,643 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network 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. + +// Unique Network 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 Unique Network. If not, see . + +// Original license: +// This file is part of Substrate. + +// Copyright (C) 2019-2022 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. + +// Tests for Identity Pallet + +use super::*; +use crate as pallet_identity; + +use codec::{Decode, Encode}; +use frame_support::{ + assert_noop, assert_ok, ord_parameter_types, parameter_types, + traits::{ConstU32, ConstU64, EitherOfDiverse}, + BoundedVec, +}; +use frame_system::{EnsureRoot, EnsureSignedBy}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BadOrigin, BlakeTwo256, IdentityLookup}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Identity: pallet_identity::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(frame_support::weights::Weight::from_ref_time(1024)); +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type RuntimeCall = RuntimeCall; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +parameter_types! { + pub const MaxAdditionalFields: u32 = 2; + pub const MaxRegistrars: u32 = 20; +} + +ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; +} +type EnsureOneOrRoot = EitherOfDiverse, EnsureSignedBy>; +type EnsureTwoOrRoot = EitherOfDiverse, EnsureSignedBy>; +impl pallet_identity::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Slashed = (); + type BasicDeposit = ConstU64<10>; + type FieldDeposit = ConstU64<10>; + type SubAccountDeposit = ConstU64<10>; + type MaxSubAccounts = ConstU32<2>; + type MaxAdditionalFields = MaxAdditionalFields; + type MaxRegistrars = MaxRegistrars; + type RegistrarOrigin = EnsureOneOrRoot; + type ForceOrigin = EnsureTwoOrRoot; + type WeightInfo = (); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(1, 10), (2, 10), (3, 10), (10, 100), (20, 100), (30, 100)], + } + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} + +fn ten() -> IdentityInfo { + IdentityInfo { + display: Data::Raw(b"ten".to_vec().try_into().unwrap()), + legal: Data::Raw(b"The Right Ordinal Ten, Esq.".to_vec().try_into().unwrap()), + ..Default::default() + } +} + +fn twenty() -> IdentityInfo { + IdentityInfo { + display: Data::Raw(b"twenty".to_vec().try_into().unwrap()), + legal: Data::Raw(b"The Right Ordinal Twenty, Esq.".to_vec().try_into().unwrap()), + ..Default::default() + } +} + +#[test] +fn editing_subaccounts_should_work() { + new_test_ext().execute_with(|| { + let data = |x| Data::Raw(vec![x; 1].try_into().unwrap()); + + assert_noop!( + Identity::add_sub(RuntimeOrigin::signed(10), 20, data(1)), + Error::::NoIdentity + ); + + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + + // first sub account + assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 1, data(1))); + assert_eq!(SuperOf::::get(1), Some((10, data(1)))); + assert_eq!(Balances::free_balance(10), 80); + + // second sub account + assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 2, data(2))); + assert_eq!(SuperOf::::get(1), Some((10, data(1)))); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(Balances::free_balance(10), 70); + + // third sub account is too many + assert_noop!( + Identity::add_sub(RuntimeOrigin::signed(10), 3, data(3)), + Error::::TooManySubAccounts + ); + + // rename first sub account + assert_ok!(Identity::rename_sub(RuntimeOrigin::signed(10), 1, data(11))); + assert_eq!(SuperOf::::get(1), Some((10, data(11)))); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(Balances::free_balance(10), 70); + + // remove first sub account + assert_ok!(Identity::remove_sub(RuntimeOrigin::signed(10), 1)); + assert_eq!(SuperOf::::get(1), None); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(Balances::free_balance(10), 80); + + // add third sub account + assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 3, data(3))); + assert_eq!(SuperOf::::get(1), None); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(SuperOf::::get(3), Some((10, data(3)))); + assert_eq!(Balances::free_balance(10), 70); + }); +} + +#[test] +fn resolving_subaccount_ownership_works() { + new_test_ext().execute_with(|| { + let data = |x| Data::Raw(vec![x; 1].try_into().unwrap()); + + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(20), Box::new(twenty()))); + + // 10 claims 1 as a subaccount + assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 1, data(1))); + assert_eq!(Balances::free_balance(1), 10); + assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::reserved_balance(10), 20); + // 20 cannot claim 1 now + assert_noop!( + Identity::add_sub(RuntimeOrigin::signed(20), 1, data(1)), + Error::::AlreadyClaimed + ); + // 1 wants to be with 20 so it quits from 10 + assert_ok!(Identity::quit_sub(RuntimeOrigin::signed(1))); + // 1 gets the 10 that 10 paid. + assert_eq!(Balances::free_balance(1), 20); + assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::reserved_balance(10), 10); + // 20 can claim 1 now + assert_ok!(Identity::add_sub(RuntimeOrigin::signed(20), 1, data(1))); + }); +} + +#[test] +fn trailing_zeros_decodes_into_default_data() { + let encoded = Data::Raw(b"Hello".to_vec().try_into().unwrap()).encode(); + assert!(<(Data, Data)>::decode(&mut &encoded[..]).is_err()); + let input = &mut &encoded[..]; + let (a, b) = <(Data, Data)>::decode(&mut AppendZerosInput::new(input)).unwrap(); + assert_eq!(a, Data::Raw(b"Hello".to_vec().try_into().unwrap())); + assert_eq!(b, Data::None); +} + +#[test] +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); + assert_ok!(Identity::set_fields(RuntimeOrigin::signed(3), 0, fields)); + assert_eq!( + Identity::registrars(), + vec![Some(RegistrarInfo { account: 3, fee: 10, fields })] + ); + }); +} + +#[test] +fn amount_of_registrars_is_limited() { + new_test_ext().execute_with(|| { + for i in 1..MaxRegistrars::get() + 1 { + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), i as u64)); + } + let last_registrar = MaxRegistrars::get() as u64 + 1; + assert_noop!( + Identity::add_registrar(RuntimeOrigin::signed(1), last_registrar), + Error::::TooManyRegistrars + ); + }); +} + +#[test] +fn registration_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 mut three_fields = ten(); + three_fields.additional.try_push(Default::default()).unwrap(); + three_fields.additional.try_push(Default::default()).unwrap(); + assert!(three_fields.additional.try_push(Default::default()).is_err()); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_eq!(Identity::identity(10).unwrap().info, ten()); + assert_eq!(Balances::free_balance(10), 90); + assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10))); + assert_eq!(Balances::free_balance(10), 100); + assert_noop!(Identity::clear_identity(RuntimeOrigin::signed(10)), Error::::NotNamed); + }); +} + +#[test] +fn uninvited_judgement_should_work() { + new_test_ext().execute_with(|| { + assert_noop!( + Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Reasonable, + H256::random() + ), + Error::::InvalidIndex + ); + + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); + assert_noop!( + Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Reasonable, + H256::random() + ), + Error::::InvalidTarget + ); + + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_noop!( + Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Reasonable, + H256::random() + ), + Error::::JudgementForDifferentIdentity + ); + + let identity_hash = BlakeTwo256::hash_of(&ten()); + + assert_noop!( + Identity::provide_judgement( + RuntimeOrigin::signed(10), + 0, + 10, + Judgement::Reasonable, + identity_hash + ), + Error::::InvalidIndex + ); + assert_noop!( + Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::FeePaid(1), + identity_hash + ), + Error::::InvalidJudgement + ); + + assert_ok!(Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Reasonable, + identity_hash + )); + assert_eq!(Identity::identity(10).unwrap().judgements, vec![(0, Judgement::Reasonable)]); + }); +} + +#[test] +fn clearing_judgement_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Reasonable, + BlakeTwo256::hash_of(&ten()) + )); + assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10))); + assert_eq!(Identity::identity(10), None); + }); +} + +#[test] +fn killing_slashing_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_noop!(Identity::kill_identity(RuntimeOrigin::signed(1), 10), BadOrigin); + assert_ok!(Identity::kill_identity(RuntimeOrigin::signed(2), 10)); + assert_eq!(Identity::identity(10), None); + assert_eq!(Balances::free_balance(10), 90); + assert_noop!( + Identity::kill_identity(RuntimeOrigin::signed(2), 10), + Error::::NotNamed + ); + }); +} + +#[test] +fn setting_subaccounts_should_work() { + new_test_ext().execute_with(|| { + let mut subs = vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))]; + assert_noop!( + Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()), + Error::::NotFound + ); + + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone())); + assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Identity::subs_of(10), (10, vec![20].try_into().unwrap())); + assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1].try_into().unwrap())))); + + // push another item and re-set it. + subs.push((30, Data::Raw(vec![50; 1].try_into().unwrap()))); + assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone())); + assert_eq!(Balances::free_balance(10), 70); + assert_eq!(Identity::subs_of(10), (20, vec![20, 30].try_into().unwrap())); + assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1].try_into().unwrap())))); + assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1].try_into().unwrap())))); + + // switch out one of the items and re-set. + subs[0] = (40, Data::Raw(vec![60; 1].try_into().unwrap())); + assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone())); + assert_eq!(Balances::free_balance(10), 70); // no change in the balance + assert_eq!(Identity::subs_of(10), (20, vec![40, 30].try_into().unwrap())); + assert_eq!(Identity::super_of(20), None); + assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1].try_into().unwrap())))); + assert_eq!(Identity::super_of(40), Some((10, Data::Raw(vec![60; 1].try_into().unwrap())))); + + // clear + assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), vec![])); + assert_eq!(Balances::free_balance(10), 90); + assert_eq!(Identity::subs_of(10), (0, BoundedVec::default())); + assert_eq!(Identity::super_of(30), None); + assert_eq!(Identity::super_of(40), None); + + subs.push((20, Data::Raw(vec![40; 1].try_into().unwrap()))); + assert_noop!( + Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()), + Error::::TooManySubAccounts + ); + }); +} + +#[test] +fn clearing_account_should_remove_subaccounts_and_refund() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::set_subs( + RuntimeOrigin::signed(10), + vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))] + )); + assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10))); + assert_eq!(Balances::free_balance(10), 100); + assert!(Identity::super_of(20).is_none()); + }); +} + +#[test] +fn killing_account_should_remove_subaccounts_and_not_refund() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::set_subs( + RuntimeOrigin::signed(10), + vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))] + )); + assert_ok!(Identity::kill_identity(RuntimeOrigin::signed(2), 10)); + assert_eq!(Balances::free_balance(10), 80); + assert!(Identity::super_of(20).is_none()); + }); +} + +#[test] +fn cancelling_requested_judgement_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)); + assert_noop!( + Identity::cancel_request(RuntimeOrigin::signed(10), 0), + Error::::NoIdentity + ); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); + assert_ok!(Identity::cancel_request(RuntimeOrigin::signed(10), 0)); + assert_eq!(Balances::free_balance(10), 90); + assert_noop!( + Identity::cancel_request(RuntimeOrigin::signed(10), 0), + Error::::NotFound + ); + + assert_ok!(Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Reasonable, + BlakeTwo256::hash_of(&ten()) + )); + assert_noop!( + Identity::cancel_request(RuntimeOrigin::signed(10), 0), + Error::::JudgementGiven + ); + }); +} + +#[test] +fn requesting_judgement_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)); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_noop!( + Identity::request_judgement(RuntimeOrigin::signed(10), 0, 9), + Error::::FeeChanged + ); + assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); + // 10 for the judgement request, 10 for the identity. + assert_eq!(Balances::free_balance(10), 80); + + // Re-requesting won't work as we already paid. + assert_noop!( + Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10), + Error::::StickyJudgement + ); + assert_ok!(Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Erroneous, + BlakeTwo256::hash_of(&ten()) + )); + // Registrar got their payment now. + assert_eq!(Balances::free_balance(3), 20); + + // Re-requesting still won't work as it's erroneous. + assert_noop!( + Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10), + Error::::StickyJudgement + ); + + // Requesting from a second registrar still works. + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 4)); + assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 1, 10)); + + // Re-requesting after the judgement has been reduced works. + assert_ok!(Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::OutOfDate, + BlakeTwo256::hash_of(&ten()) + )); + assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); + }); +} + +#[test] +fn provide_judgement_should_return_judgement_payment_failed_error() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); + assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10)); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); + // 10 for the judgement request, 10 for the identity. + assert_eq!(Balances::free_balance(10), 80); + + // This forces judgement payment failed error + Balances::make_free_balance_be(&3, 0); + assert_noop!( + Identity::provide_judgement( + RuntimeOrigin::signed(3), + 0, + 10, + Judgement::Erroneous, + BlakeTwo256::hash_of(&ten()) + ), + Error::::JudgementPaymentFailed + ); + }); +} + +#[test] +fn field_deposit_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)); + assert_ok!(Identity::set_identity( + RuntimeOrigin::signed(10), + Box::new(IdentityInfo { + additional: vec![ + ( + Data::Raw(b"number".to_vec().try_into().unwrap()), + Data::Raw(10u32.encode().try_into().unwrap()) + ), + ( + Data::Raw(b"text".to_vec().try_into().unwrap()), + Data::Raw(b"10".to_vec().try_into().unwrap()) + ), + ] + .try_into() + .unwrap(), + ..Default::default() + }) + )); + assert_eq!(Balances::free_balance(10), 70); + }); +} + +#[test] +fn setting_account_id_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); + // account 4 cannot change the first registrar's identity since it's owned by 3. + assert_noop!( + Identity::set_account_id(RuntimeOrigin::signed(4), 0, 3), + Error::::InvalidIndex + ); + // account 3 can, because that's the registrar's current account. + assert_ok!(Identity::set_account_id(RuntimeOrigin::signed(3), 0, 4)); + // account 4 can now, because that's their new ID. + assert_ok!(Identity::set_account_id(RuntimeOrigin::signed(4), 0, 3)); + }); +} + +#[test] +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, + IdentityField::Display as u64 | IdentityField::Legal as u64 + )); + assert!(!Identity::has_identity( + &10, + IdentityField::Display as u64 | IdentityField::Legal as u64 | IdentityField::Web as u64 + )); + }); +} --- /dev/null +++ b/pallets/identity/src/types.rs @@ -0,0 +1,518 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network 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. + +// Unique Network 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 Unique Network. If not, see . + +// Original license: +// This file is part of Substrate. + +// Copyright (C) 2021-2022 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 super::*; +use codec::{Decode, Encode, MaxEncodedLen}; +use enumflags2::{bitflags, BitFlags}; +use frame_support::{ + traits::{ConstU32, Get}, + BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, +}; +use scale_info::{ + build::{Fields, Variants}, + meta_type, Path, Type, TypeInfo, TypeParameter, +}; +use sp_runtime::{traits::Zero, RuntimeDebug}; +use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*}; + +/// 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, MaxEncodedLen)] +pub enum Data { + /// No data here. + None, + /// The data is stored directly. + Raw(BoundedVec>), + /// 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 Data { + pub fn is_none(&self) -> bool { + self == &Data::None + } +} + +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: BoundedVec<_, _> = vec![0u8; n as usize - 1] + .try_into() + .expect("bound checked in match arm condition; qed"); + 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]; + 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 {} + +/// Add a Raw variant with the given index and a fixed sized byte array +macro_rules! data_raw_variants { + ($variants:ident, $(($index:literal, $size:literal)),* ) => { + $variants + $( + .variant(concat!("Raw", stringify!($size)), |v| v + .index($index) + .fields(Fields::unnamed().field(|f| f.ty::<[u8; $size]>())) + ) + )* + } +} + +impl TypeInfo for Data { + type Identity = Self; + + fn type_info() -> Type { + let variants = Variants::new().variant("None", |v| v.index(0)); + + // create a variant for all sizes of Raw data from 0-32 + let variants = data_raw_variants!( + variants, + (1, 0), + (2, 1), + (3, 2), + (4, 3), + (5, 4), + (6, 5), + (7, 6), + (8, 7), + (9, 8), + (10, 9), + (11, 10), + (12, 11), + (13, 12), + (14, 13), + (15, 14), + (16, 15), + (17, 16), + (18, 17), + (19, 18), + (20, 19), + (21, 20), + (22, 21), + (23, 22), + (24, 23), + (25, 24), + (26, 25), + (27, 26), + (28, 27), + (29, 28), + (30, 29), + (31, 30), + (32, 31), + (33, 32) + ); + + let variants = variants + .variant("BlakeTwo256", |v| { + v.index(34).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }) + .variant("Sha256", |v| { + v.index(35).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }) + .variant("Keccak256", |v| { + v.index(36).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }) + .variant("ShaThree256", |v| { + v.index(37).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }); + + Type::builder().path(Path::new("Data", module_path!())).variant(variants) + } +} + +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, MaxEncodedLen, TypeInfo)] +pub enum Judgement +{ + /// 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 + 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. + pub(crate) fn has_deposit(&self) -> bool { + matches!(self, Judgement::FeePaid(_)) + } + + /// 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. + pub(crate) fn is_sticky(&self) -> bool { + matches!(self, Judgement::FeePaid(_) | Judgement::Erroneous) + } +} + +/// 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` that implements `Codec`. +#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)] +pub struct IdentityFields(pub BitFlags); + +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>(&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")?)) + } +} +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::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().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> { + /// 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> IdentityInfo { + pub(crate) fn fields(&self) -> IdentityFields { + let mut res = >::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) + } +} + +/// 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( + CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, +)] +#[codec(mel_bound())] +#[scale_info(skip_type_params(MaxJudgements, MaxAdditionalFields))] +pub struct Registration< + Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, + MaxJudgements: Get, + MaxAdditionalFields: Get, +> { + /// Judgements from the registrars on this identity. Stored ordered by `RegistrarIndex`. There + /// may be only a single judgement from each registrar. + pub judgements: BoundedVec<(RegistrarIndex, Judgement), MaxJudgements>, + + /// Amount held on deposit for this information. + pub deposit: Balance, + + /// Information on the identity. + pub info: IdentityInfo, +} + +impl< + Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq + Zero + Add, + MaxJudgements: Get, + MaxAdditionalFields: Get, + > Registration +{ + pub(crate) 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 + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, + MaxJudgements: Get, + MaxAdditionalFields: Get, + > 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, MaxEncodedLen, TypeInfo)] +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, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn manual_data_type_info() { + let mut registry = scale_info::Registry::new(); + let type_id = registry.register_type(&scale_info::meta_type::()); + let registry: scale_info::PortableRegistry = registry.into(); + let type_info = registry.resolve(type_id.id()).unwrap(); + + let check_type_info = |data: &Data| { + let variant_name = match data { + Data::None => "None".to_string(), + Data::BlakeTwo256(_) => "BlakeTwo256".to_string(), + Data::Sha256(_) => "Sha256".to_string(), + Data::Keccak256(_) => "Keccak256".to_string(), + Data::ShaThree256(_) => "ShaThree256".to_string(), + Data::Raw(bytes) => format!("Raw{}", bytes.len()), + }; + if let scale_info::TypeDef::Variant(variant) = type_info.type_def() { + let variant = variant + .variants() + .iter() + .find(|v| v.name() == &variant_name) + .expect(&format!("Expected to find variant {}", variant_name)); + + let field_arr_len = variant + .fields() + .first() + .and_then(|f| registry.resolve(f.ty().id())) + .map(|ty| { + if let scale_info::TypeDef::Array(arr) = ty.type_def() { + arr.len() + } else { + panic!("Should be an array type") + } + }) + .unwrap_or(0); + + let encoded = data.encode(); + assert_eq!(encoded[0], variant.index()); + assert_eq!(encoded.len() as u32 - 1, field_arr_len); + } else { + panic!("Should be a variant type") + }; + }; + + let mut data = vec![ + Data::None, + Data::BlakeTwo256(Default::default()), + Data::Sha256(Default::default()), + Data::Keccak256(Default::default()), + Data::ShaThree256(Default::default()), + ]; + + // A Raw instance for all possible sizes of the Raw data + for n in 0..32 { + data.push(Data::Raw(vec![0u8; n as usize].try_into().unwrap())) + } + + for d in data.iter() { + check_type_info(d); + } + } +} --- /dev/null +++ b/pallets/identity/src/weights.rs @@ -0,0 +1,504 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network 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. + +// Unique Network 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 Unique Network. If not, see . + +// Original license: +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +//! Autogenerated weights for pallet_identity +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/substrate +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_identity +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/identity/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_identity. +pub trait WeightInfo { + fn add_registrar(r: u32, ) -> Weight; + fn set_identity(r: u32, x: u32, ) -> Weight; + fn set_subs_new(s: u32, ) -> Weight; + fn set_subs_old(p: u32, ) -> Weight; + fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight; + fn request_judgement(r: u32, x: u32, ) -> Weight; + fn cancel_request(r: u32, x: u32, ) -> Weight; + fn set_fee(r: u32, ) -> Weight; + fn set_account_id(r: u32, ) -> Weight; + fn set_fields(r: u32, ) -> Weight; + fn provide_judgement(r: u32, x: u32, ) -> Weight; + fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight; + fn add_sub(s: u32, ) -> Weight; + fn rename_sub(s: u32, ) -> Weight; + fn remove_sub(s: u32, ) -> Weight; + fn quit_sub(s: u32, ) -> Weight; +} + +/// Weights for pallet_identity using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn add_registrar(r: u32, ) -> Weight { + // Minimum execution time: 20_269 nanoseconds. + Weight::from_ref_time(21_910_543 as u64) + // Standard Error: 4_604 + .saturating_add(Weight::from_ref_time(223_104 as u64).saturating_mul(r as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 20]`. + /// The range of component `x` is `[0, 100]`. + fn set_identity(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 41_872 nanoseconds. + Weight::from_ref_time(40_230_216 as u64) + // Standard Error: 2_342 + .saturating_add(Weight::from_ref_time(145_168 as u64).saturating_mul(r as u64)) + // Standard Error: 457 + .saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity SuperOf (r:2 w:2) + /// The range of component `s` is `[0, 100]`. + fn set_subs_new(s: u32, ) -> Weight { + // Minimum execution time: 12_024 nanoseconds. + Weight::from_ref_time(32_550_819 as u64) + // Standard Error: 5_057 + .saturating_add(Weight::from_ref_time(2_521_245 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64))) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity SuperOf (r:0 w:2) + /// The range of component `p` is `[0, 100]`. + fn set_subs_old(p: u32, ) -> Weight { + // Minimum execution time: 12_232 nanoseconds. + Weight::from_ref_time(34_009_761 as u64) + // Standard Error: 5_047 + .saturating_add(Weight::from_ref_time(1_113_100 as u64).saturating_mul(p as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + } + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity IdentityOf (r:1 w:1) + // Storage: Identity SuperOf (r:0 w:100) + /// The range of component `r` is `[1, 20]`. + /// The range of component `s` is `[0, 100]`. + /// The range of component `x` is `[0, 100]`. + fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + // Minimum execution time: 57_144 nanoseconds. + Weight::from_ref_time(41_559_247 as u64) + // Standard Error: 9_996 + .saturating_add(Weight::from_ref_time(146_770 as u64).saturating_mul(r as u64)) + // Standard Error: 1_952 + .saturating_add(Weight::from_ref_time(1_086_673 as u64).saturating_mul(s as u64)) + // Standard Error: 1_952 + .saturating_add(Weight::from_ref_time(162_481 as u64).saturating_mul(x as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + } + // Storage: Identity Registrars (r:1 w:0) + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 20]`. + /// The range of component `x` is `[0, 100]`. + fn request_judgement(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 44_726 nanoseconds. + Weight::from_ref_time(41_637_308 as u64) + // Standard Error: 1_907 + .saturating_add(Weight::from_ref_time(219_078 as u64).saturating_mul(r as u64)) + // Standard Error: 372 + .saturating_add(Weight::from_ref_time(309_888 as u64).saturating_mul(x as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 20]`. + /// The range of component `x` is `[0, 100]`. + fn cancel_request(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 39_719 nanoseconds. + Weight::from_ref_time(38_008_751 as u64) + // Standard Error: 2_394 + .saturating_add(Weight::from_ref_time(181_870 as u64).saturating_mul(r as u64)) + // Standard Error: 467 + .saturating_add(Weight::from_ref_time(314_990 as u64).saturating_mul(x as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn set_fee(r: u32, ) -> Weight { + // Minimum execution time: 10_634 nanoseconds. + Weight::from_ref_time(11_383_704 as u64) + // Standard Error: 2_250 + .saturating_add(Weight::from_ref_time(193_094 as u64).saturating_mul(r as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn set_account_id(r: u32, ) -> Weight { + // Minimum execution time: 10_840 nanoseconds. + Weight::from_ref_time(11_638_740 as u64) + // Standard Error: 1_985 + .saturating_add(Weight::from_ref_time(193_016 as u64).saturating_mul(r as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn set_fields(r: u32, ) -> Weight { + // Minimum execution time: 10_748 nanoseconds. + Weight::from_ref_time(11_346_901 as u64) + // Standard Error: 2_132 + .saturating_add(Weight::from_ref_time(196_630 as u64).saturating_mul(r as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:0) + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + /// The range of component `x` is `[0, 100]`. + fn provide_judgement(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 33_682 nanoseconds. + Weight::from_ref_time(31_336_603 as u64) + // Standard Error: 3_056 + .saturating_add(Weight::from_ref_time(200_403 as u64).saturating_mul(r as u64)) + // Standard Error: 565 + .saturating_add(Weight::from_ref_time(525_142 as u64).saturating_mul(x as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity IdentityOf (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Identity SuperOf (r:0 w:100) + /// The range of component `r` is `[1, 20]`. + /// The range of component `s` is `[0, 100]`. + /// The range of component `x` is `[0, 100]`. + fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + // Minimum execution time: 68_794 nanoseconds. + Weight::from_ref_time(52_114_486 as u64) + // Standard Error: 4_808 + .saturating_add(Weight::from_ref_time(153_462 as u64).saturating_mul(r as u64)) + // Standard Error: 939 + .saturating_add(Weight::from_ref_time(1_084_612 as u64).saturating_mul(s as u64)) + // Standard Error: 939 + .saturating_add(Weight::from_ref_time(170_112 as u64).saturating_mul(x as u64)) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SuperOf (r:1 w:1) + // Storage: Identity SubsOf (r:1 w:1) + /// The range of component `s` is `[0, 99]`. + fn add_sub(s: u32, ) -> Weight { + // Minimum execution time: 37_914 nanoseconds. + Weight::from_ref_time(43_488_083 as u64) + // Standard Error: 1_631 + .saturating_add(Weight::from_ref_time(118_845 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SuperOf (r:1 w:1) + /// The range of component `s` is `[1, 100]`. + fn rename_sub(s: u32, ) -> Weight { + // Minimum execution time: 16_124 nanoseconds. + Weight::from_ref_time(18_580_462 as u64) + // Standard Error: 688 + .saturating_add(Weight::from_ref_time(67_220 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SuperOf (r:1 w:1) + // Storage: Identity SubsOf (r:1 w:1) + /// The range of component `s` is `[1, 100]`. + fn remove_sub(s: u32, ) -> Weight { + // Minimum execution time: 41_517 nanoseconds. + Weight::from_ref_time(45_123_530 as u64) + // Standard Error: 1_530 + .saturating_add(Weight::from_ref_time(105_429 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: Identity SuperOf (r:1 w:1) + // Storage: Identity SubsOf (r:1 w:1) + /// The range of component `s` is `[0, 99]`. + fn quit_sub(s: u32, ) -> Weight { + // Minimum execution time: 30_171 nanoseconds. + Weight::from_ref_time(33_355_514 as u64) + // Standard Error: 1_286 + .saturating_add(Weight::from_ref_time(114_716 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn add_registrar(r: u32, ) -> Weight { + // Minimum execution time: 20_269 nanoseconds. + Weight::from_ref_time(21_910_543 as u64) + // Standard Error: 4_604 + .saturating_add(Weight::from_ref_time(223_104 as u64).saturating_mul(r as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 20]`. + /// The range of component `x` is `[0, 100]`. + fn set_identity(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 41_872 nanoseconds. + Weight::from_ref_time(40_230_216 as u64) + // Standard Error: 2_342 + .saturating_add(Weight::from_ref_time(145_168 as u64).saturating_mul(r as u64)) + // Standard Error: 457 + .saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity SuperOf (r:2 w:2) + /// The range of component `s` is `[0, 100]`. + fn set_subs_new(s: u32, ) -> Weight { + // Minimum execution time: 12_024 nanoseconds. + Weight::from_ref_time(32_550_819 as u64) + // Standard Error: 5_057 + .saturating_add(Weight::from_ref_time(2_521_245 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64))) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity SuperOf (r:0 w:2) + /// The range of component `p` is `[0, 100]`. + fn set_subs_old(p: u32, ) -> Weight { + // Minimum execution time: 12_232 nanoseconds. + Weight::from_ref_time(34_009_761 as u64) + // Standard Error: 5_047 + .saturating_add(Weight::from_ref_time(1_113_100 as u64).saturating_mul(p as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + } + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity IdentityOf (r:1 w:1) + // Storage: Identity SuperOf (r:0 w:100) + /// The range of component `r` is `[1, 20]`. + /// The range of component `s` is `[0, 100]`. + /// The range of component `x` is `[0, 100]`. + fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + // Minimum execution time: 57_144 nanoseconds. + Weight::from_ref_time(41_559_247 as u64) + // Standard Error: 9_996 + .saturating_add(Weight::from_ref_time(146_770 as u64).saturating_mul(r as u64)) + // Standard Error: 1_952 + .saturating_add(Weight::from_ref_time(1_086_673 as u64).saturating_mul(s as u64)) + // Standard Error: 1_952 + .saturating_add(Weight::from_ref_time(162_481 as u64).saturating_mul(x as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + } + // Storage: Identity Registrars (r:1 w:0) + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 20]`. + /// The range of component `x` is `[0, 100]`. + fn request_judgement(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 44_726 nanoseconds. + Weight::from_ref_time(41_637_308 as u64) + // Standard Error: 1_907 + .saturating_add(Weight::from_ref_time(219_078 as u64).saturating_mul(r as u64)) + // Standard Error: 372 + .saturating_add(Weight::from_ref_time(309_888 as u64).saturating_mul(x as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 20]`. + /// The range of component `x` is `[0, 100]`. + fn cancel_request(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 39_719 nanoseconds. + Weight::from_ref_time(38_008_751 as u64) + // Standard Error: 2_394 + .saturating_add(Weight::from_ref_time(181_870 as u64).saturating_mul(r as u64)) + // Standard Error: 467 + .saturating_add(Weight::from_ref_time(314_990 as u64).saturating_mul(x as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn set_fee(r: u32, ) -> Weight { + // Minimum execution time: 10_634 nanoseconds. + Weight::from_ref_time(11_383_704 as u64) + // Standard Error: 2_250 + .saturating_add(Weight::from_ref_time(193_094 as u64).saturating_mul(r as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn set_account_id(r: u32, ) -> Weight { + // Minimum execution time: 10_840 nanoseconds. + Weight::from_ref_time(11_638_740 as u64) + // Standard Error: 1_985 + .saturating_add(Weight::from_ref_time(193_016 as u64).saturating_mul(r as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + fn set_fields(r: u32, ) -> Weight { + // Minimum execution time: 10_748 nanoseconds. + Weight::from_ref_time(11_346_901 as u64) + // Standard Error: 2_132 + .saturating_add(Weight::from_ref_time(196_630 as u64).saturating_mul(r as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity Registrars (r:1 w:0) + // Storage: Identity IdentityOf (r:1 w:1) + /// The range of component `r` is `[1, 19]`. + /// The range of component `x` is `[0, 100]`. + fn provide_judgement(r: u32, x: u32, ) -> Weight { + // Minimum execution time: 33_682 nanoseconds. + Weight::from_ref_time(31_336_603 as u64) + // Standard Error: 3_056 + .saturating_add(Weight::from_ref_time(200_403 as u64).saturating_mul(r as u64)) + // Standard Error: 565 + .saturating_add(Weight::from_ref_time(525_142 as u64).saturating_mul(x as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity SubsOf (r:1 w:1) + // Storage: Identity IdentityOf (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Identity SuperOf (r:0 w:100) + /// The range of component `r` is `[1, 20]`. + /// The range of component `s` is `[0, 100]`. + /// The range of component `x` is `[0, 100]`. + fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + // Minimum execution time: 68_794 nanoseconds. + Weight::from_ref_time(52_114_486 as u64) + // Standard Error: 4_808 + .saturating_add(Weight::from_ref_time(153_462 as u64).saturating_mul(r as u64)) + // Standard Error: 939 + .saturating_add(Weight::from_ref_time(1_084_612 as u64).saturating_mul(s as u64)) + // Standard Error: 939 + .saturating_add(Weight::from_ref_time(170_112 as u64).saturating_mul(x as u64)) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SuperOf (r:1 w:1) + // Storage: Identity SubsOf (r:1 w:1) + /// The range of component `s` is `[0, 99]`. + fn add_sub(s: u32, ) -> Weight { + // Minimum execution time: 37_914 nanoseconds. + Weight::from_ref_time(43_488_083 as u64) + // Standard Error: 1_631 + .saturating_add(Weight::from_ref_time(118_845 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SuperOf (r:1 w:1) + /// The range of component `s` is `[1, 100]`. + fn rename_sub(s: u32, ) -> Weight { + // Minimum execution time: 16_124 nanoseconds. + Weight::from_ref_time(18_580_462 as u64) + // Standard Error: 688 + .saturating_add(Weight::from_ref_time(67_220 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: Identity IdentityOf (r:1 w:0) + // Storage: Identity SuperOf (r:1 w:1) + // Storage: Identity SubsOf (r:1 w:1) + /// The range of component `s` is `[1, 100]`. + fn remove_sub(s: u32, ) -> Weight { + // Minimum execution time: 41_517 nanoseconds. + Weight::from_ref_time(45_123_530 as u64) + // Standard Error: 1_530 + .saturating_add(Weight::from_ref_time(105_429 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: Identity SuperOf (r:1 w:1) + // Storage: Identity SubsOf (r:1 w:1) + /// The range of component `s` is `[0, 99]`. + fn quit_sub(s: u32, ) -> Weight { + // Minimum execution time: 30_171 nanoseconds. + Weight::from_ref_time(33_355_514 as u64) + // Standard Error: 1_286 + .saturating_add(Weight::from_ref_time(114_716 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } +} --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -276,11 +276,6 @@ git = 'https://github.com/paritytech/substrate' branch = 'polkadot-v0.9.36' -[dependencies.pallet-identity] -default-features = false -git = 'https://github.com/paritytech/substrate' -branch = 'polkadot-v0.9.36' - [dependencies.pallet-balances] default-features = false git = "https://github.com/paritytech/substrate" @@ -505,6 +500,7 @@ pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } pallet-collator-selection = { default-features = false, path = "../../pallets/collator-selection" } +pallet-identity = { default-features = false, path = "../../pallets/identity" } pallet-configuration = { default-features = false, path = "../../pallets/configuration" } pallet-common = { default-features = false, path = "../../pallets/common" } pallet-structure = { default-features = false, path = "../../pallets/structure" } --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -260,11 +260,6 @@ git = 'https://github.com/paritytech/substrate' branch = 'polkadot-v0.9.36' -[dependencies.pallet-identity] -default-features = false -git = 'https://github.com/paritytech/substrate' -branch = 'polkadot-v0.9.36' - [dependencies.pallet-balances] default-features = false git = "https://github.com/paritytech/substrate" @@ -496,6 +491,7 @@ pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } pallet-collator-selection = { default-features = false, path = "../../pallets/collator-selection" } +pallet-identity = { default-features = false, path = "../../pallets/identity" } pallet-configuration = { default-features = false, path = "../../pallets/configuration" } pallet-common = { default-features = false, path = "../../pallets/common" } pallet-structure = { default-features = false, path = "../../pallets/structure" } --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -260,11 +260,6 @@ git = 'https://github.com/paritytech/substrate' branch = 'polkadot-v0.9.36' -[dependencies.pallet-identity] -default-features = false -git = 'https://github.com/paritytech/substrate' -branch = 'polkadot-v0.9.36' - [dependencies.pallet-balances] default-features = false git = "https://github.com/paritytech/substrate" @@ -487,6 +482,7 @@ pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } pallet-collator-selection = { default-features = false, path = "../../pallets/collator-selection" } +pallet-identity = { default-features = false, path = "../../pallets/identity" } pallet-configuration = { default-features = false, path = "../../pallets/configuration" } pallet-common = { default-features = false, path = "../../pallets/common" } pallet-structure = { default-features = false, path = "../../pallets/structure" } -- gitstuff