Skip to content
Snippets Groups Projects
lib.rs 26.9 KiB
Newer Older
Éloïs's avatar
Éloïs committed
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
//
// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
Éloïs's avatar
Éloïs committed
//
// Substrate-Libre-Currency 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::type_complexity)]
Éloïs's avatar
Éloïs committed

pub mod traits;
mod types;
Éloïs's avatar
Éloïs committed

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

/*#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;*/

pub use pallet::*;
pub use types::*;
Éloïs's avatar
Éloïs committed

use crate::traits::*;
Éloïs's avatar
Éloïs committed
use codec::Codec;
Éloïs's avatar
Éloïs committed
use frame_support::dispatch::Weight;
Éloïs's avatar
Éloïs committed
use sp_runtime::traits::{AtLeast32BitUnsigned, One, Saturating, Zero};
use sp_std::fmt::Debug;
Éloïs's avatar
Éloïs committed
use sp_std::prelude::*;

#[frame_support::pallet]
pub mod pallet {
    use super::*;
    use frame_support::pallet_prelude::*;
    use frame_support::traits::StorageVersion;
Éloïs's avatar
Éloïs committed
    use frame_system::pallet_prelude::*;
    use sp_runtime::traits::IsMember;

    /// The current storage version.
    const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    #[pallet::storage_version(STORAGE_VERSION)]
    pub struct Pallet<T>(_);

    // CONFIG //
Éloïs's avatar
Éloïs committed

    #[pallet::config]
    pub trait Config: frame_system::Config {
        #[pallet::constant]
        /// Period during which the owner can confirm the new identity.
        type ConfirmPeriod: Get<Self::BlockNumber>;
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
        /// Origin allowed to add a right to an identity
        type AddRightOrigin: EnsureOrigin<Self::Origin>;
        /// Origin allowed to delete a right to an identity
        type DelRightOrigin: EnsureOrigin<Self::Origin>;
        /// Management of the authorizations of the different calls. (The default implementation only allows root)
        type EnsureIdtyCallAllowed: EnsureIdtyCallAllowed<Self>;
        /// Minimum duration between the creation of 2 identities by the same creator
        type IdtyCreationPeriod: Get<Self::BlockNumber>;
Éloïs's avatar
Éloïs committed
        ///  Identity custom data
        type IdtyData: Parameter + Member + MaybeSerializeDeserialize + Debug + Default;
        ///  Identity custom data provider
        type IdtyDataProvider: ProvideIdtyData<Self>;
Éloïs's avatar
Éloïs committed
        /// A short identity index.
        type IdtyIndex: Parameter
            + Member
            + AtLeast32BitUnsigned
            + Codec
            + Default
            + Copy
            + MaybeSerializeDeserialize
            + Debug
            + MaxEncodedLen;
Éloïs's avatar
Éloïs committed
        /// Handle logic to validate an identity name
        type IdtyNameValidator: IdtyNameValidator;
Éloïs's avatar
Éloïs committed
        /// Origin allowed to validate identity
        type IdtyValidationOrigin: EnsureOrigin<Self::Origin>;
        /// Rights that an identity can have
        type IdtyRight: IdtyRight;
        ///
        type IsMember: sp_runtime::traits::IsMember<Self::IdtyIndex>;
Éloïs's avatar
Éloïs committed
        /// On identity confirmed by it's owner
        type OnIdtyChange: OnIdtyChange<Self>;
Éloïs's avatar
Éloïs committed
        /// On right key change
        type OnRightKeyChange: OnRightKeyChange<Self>;
        #[pallet::constant]
        /// Maximum period with no rights, after this period, the identity is permanently deleted
        type MaxNoRightPeriod: Get<Self::BlockNumber>;
Éloïs's avatar
Éloïs committed
    }
    // GENESIS STUFF //
Éloïs's avatar
Éloïs committed
    #[pallet::genesis_config]
    pub struct GenesisConfig<T: Config> {
Éloïs's avatar
Éloïs committed
        pub identities: Vec<IdtyValue<T::AccountId, T::BlockNumber, T::IdtyData, T::IdtyRight>>,
Éloïs's avatar
Éloïs committed
    }

    #[cfg(feature = "std")]
    impl<T: Config> Default for GenesisConfig<T> {
        fn default() -> Self {
            Self {
                identities: Default::default(),
            }
        }
    }

    #[pallet::genesis_build]
    impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
        fn build(&self) {
Éloïs's avatar
Éloïs committed
            let mut names = sp_std::collections::btree_set::BTreeSet::new();
            for idty_value in &self.identities {
                assert!(
Éloïs's avatar
Éloïs committed
                    !names.contains(&idty_value.name),
                    "Idty name {:?} is present twice",
                    &idty_value.name
Éloïs's avatar
Éloïs committed
                if idty_value.status == IdtyStatus::Validated {
                    if idty_value.rights.is_empty() {
                        assert!(idty_value.removable_on > T::BlockNumber::zero());
Éloïs's avatar
Éloïs committed
                    } else {
                        assert!(idty_value.removable_on == T::BlockNumber::zero());
Éloïs's avatar
Éloïs committed
                    }
                } else {
                    assert!(idty_value.removable_on > T::BlockNumber::zero());
Éloïs's avatar
Éloïs committed
                    assert!(idty_value.rights.is_empty())
                }
Éloïs's avatar
Éloïs committed
                names.insert(idty_value.name.clone());
            // We need to sort identities to ensure determinisctic result
            let mut identities = self.identities.clone();
Éloïs's avatar
Éloïs committed
            identities.sort_by(|idty_val_1, idty_val_2| idty_val_1.name.cmp(&idty_val_2.name));
Éloïs's avatar
Éloïs committed
            <IdentitiesCount<T>>::put(self.identities.len() as u64);
            for idty_value in &identities {
                let idty_index = Pallet::<T>::get_next_idty_index();
                if idty_value.removable_on > T::BlockNumber::zero() {
                    <IdentitiesRemovableOn<T>>::append(
                        idty_value.removable_on,
                        (idty_index, idty_value.status),
                    )
Éloïs's avatar
Éloïs committed
                }
                <Identities<T>>::insert(idty_index, idty_value);
    // STORAGE //

    /// Identities
    #[pallet::storage]
    #[pallet::getter(fn identity)]
    pub type Identities<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::IdtyIndex,
        IdtyValue<T::AccountId, T::BlockNumber, T::IdtyData, T::IdtyRight>,
        OptionQuery,
    >;

    /// IdentitiesByDid
    #[pallet::storage]
    #[pallet::getter(fn identity_by_did)]
    pub type IdentitiesByDid<T: Config> =
        StorageMap<_, Blake2_128Concat, IdtyName, T::IdtyIndex, ValueQuery>;

    #[pallet::storage]
    pub(super) type NextIdtyIndex<T: Config> = StorageValue<_, T::IdtyIndex, ValueQuery>;

    #[pallet::storage]
    #[pallet::getter(fn identities_count)]
    pub(super) type IdentitiesCount<T: Config> = StorageValue<_, u64, ValueQuery>;

    /// Identities by removed block
    #[pallet::storage]
    #[pallet::getter(fn removable_on)]
    pub type IdentitiesRemovableOn<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::BlockNumber,
        Vec<(T::IdtyIndex, IdtyStatus)>,
        ValueQuery,
    >;

Éloïs's avatar
Éloïs committed
    // HOOKS //

    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
        fn on_initialize(n: T::BlockNumber) -> Weight {
            if n > T::BlockNumber::zero() {
                Self::prune_identities(n)
Éloïs's avatar
Éloïs committed
        }
    }

    // EVENTS //

    // Pallets use events to inform users when important changes are made.
    // https://substrate.dev/docs/en/knowledgebase/runtime/events
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// A new identity has been created
        /// [idty, owner_key]
Éloïs's avatar
Éloïs committed
        IdtyCreated(IdtyName, T::AccountId),
Éloïs's avatar
Éloïs committed
        /// An identity has been confirmed by it's owner
        /// [idty]
Éloïs's avatar
Éloïs committed
        IdtyConfirmed(IdtyName),
Éloïs's avatar
Éloïs committed
        /// An identity has been validated
        /// [idty]
Éloïs's avatar
Éloïs committed
        IdtyValidated(IdtyName),
Éloïs's avatar
Éloïs committed
        /// An identity has acquired a new right
        /// [idty, right]
Éloïs's avatar
Éloïs committed
        IdtyAcquireRight(IdtyName, T::IdtyRight),
Éloïs's avatar
Éloïs committed
        /// An identity lost a right
        /// [idty, righ]
Éloïs's avatar
Éloïs committed
        IdtyLostRight(IdtyName, T::IdtyRight),
Éloïs's avatar
Éloïs committed
        /// An identity has modified a subkey associated with a right
Éloïs's avatar
Éloïs committed
        /// [idty_name, right, old_subkey_opt, new_subkey_opt]
Éloïs's avatar
Éloïs committed
        IdtySetRightSubKey(
Éloïs's avatar
Éloïs committed
            IdtyName,
Éloïs's avatar
Éloïs committed
            T::IdtyRight,
            Option<T::AccountId>,
            Option<T::AccountId>,
        ),
    }

    // CALLS //

    // Dispatchable functions allows users to interact with the pallet and invoke state changes.
    // These functions materialize as "extrinsics", which are often compared to transactions.
    // Dispatchable functions must be annotated with a weight and must return a DispatchResult.
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(0)]
        pub fn create_identity(
            origin: OriginFor<T>,
Éloïs's avatar
Éloïs committed
            idty_name: IdtyName,
Éloïs's avatar
Éloïs committed
            owner_key: T::AccountId,
        ) -> DispatchResultWithPostInfo {
            // Verification phase //
            let who = ensure_signed(origin)?;

            let creator_idty_val =
                Identities::<T>::try_get(&creator).map_err(|_| Error::<T>::CreatorNotExist)?;

            let expected_account = if let Ok(index) = creator_idty_val
                .rights
                .binary_search_by(|(right_, _)| right_.cmp(&T::IdtyRight::create_idty_right()))
            {
                creator_idty_val.rights[index]
                    .1
                    .clone()
                    .unwrap_or(creator_idty_val.owner_key)
            } else {
                return Err(Error::<T>::CreatorNotHaveRightToCreateIdty.into());
            };

            if who != expected_account {
                return Err(Error::<T>::RequireToBeOwner.into());
            }

            let block_number = frame_system::pallet::Pallet::<T>::block_number();

            if creator_idty_val.next_creatable_identity_on > block_number {
                return Err(Error::<T>::NotRespectIdtyCreationPeriod.into());
            }

            if !T::EnsureIdtyCallAllowed::can_create_identity(creator) {
                return Err(Error::<T>::CreatorNotAllowedToCreateIdty.into());
            }
Éloïs's avatar
Éloïs committed
            if !T::IdtyNameValidator::validate(&idty_name) {
                return Err(Error::<T>::IdtyNameInvalid.into());
            }
            if <IdentitiesByDid<T>>::contains_key(&idty_name) {
                return Err(Error::<T>::IdtyNameAlreadyExist.into());
            // Apply phase //

            <Identities<T>>::mutate_exists(creator, |idty_val_opt| {
                if let Some(ref mut idty_val) = idty_val_opt {
                    idty_val.next_creatable_identity_on =
                        block_number + T::IdtyCreationPeriod::get();
                }
            });

            let idty_data =
                T::IdtyDataProvider::provide_identity_data(creator, &idty_name, &owner_key);

Éloïs's avatar
Éloïs committed
            let removable_on = block_number + T::ConfirmPeriod::get();

Éloïs's avatar
Éloïs committed
            let idty_index = Self::get_next_idty_index();
Éloïs's avatar
Éloïs committed
            <Identities<T>>::insert(
Éloïs's avatar
Éloïs committed
                IdtyValue {
                    name: idty_name.clone(),
                    next_creatable_identity_on: T::BlockNumber::zero(),
Éloïs's avatar
Éloïs committed
                    owner_key: owner_key.clone(),
                    removable_on,
                    rights: Vec::with_capacity(0),
                    status: IdtyStatus::Created,
                    data: idty_data,
Éloïs's avatar
Éloïs committed
                },
            );
Éloïs's avatar
Éloïs committed
            <IdentitiesByDid<T>>::insert(idty_name.clone(), idty_index);
            IdentitiesRemovableOn::<T>::append(removable_on, (idty_index, IdtyStatus::Created));
Éloïs's avatar
Éloïs committed
            Self::inc_identities_counter();
Éloïs's avatar
Éloïs committed
            Self::deposit_event(Event::IdtyCreated(idty_name, owner_key));
            T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Created { creator });
Éloïs's avatar
Éloïs committed
            Ok(().into())
        }
        #[pallet::weight(0)]
        pub fn confirm_identity(
            origin: OriginFor<T>,
Éloïs's avatar
Éloïs committed
            idty_name: IdtyName,
Éloïs's avatar
Éloïs committed
        ) -> DispatchResultWithPostInfo {
            let who = ensure_signed(origin)?;

            if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) {
Éloïs's avatar
Éloïs committed
                if who == idty_value.owner_key {
                    if idty_value.status != IdtyStatus::Created {
                        return Err(Error::<T>::IdtyAlreadyConfirmed.into());
                    }
                    if idty_value.name != idty_name {
                        return Err(Error::<T>::NotSameIdtyName.into());
                    }
                    if !T::EnsureIdtyCallAllowed::can_confirm_identity(idty_index) {
                        return Err(Error::<T>::NotAllowedToConfirmIdty.into());
                    }
Éloïs's avatar
Éloïs committed

                    idty_value.status = IdtyStatus::ConfirmedByOwner;

                    <Identities<T>>::insert(idty_index, idty_value);
Éloïs's avatar
Éloïs committed
                    Self::deposit_event(Event::IdtyConfirmed(idty_name));
                    T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Confirmed);
Éloïs's avatar
Éloïs committed
                    Ok(().into())
                } else {
                    Err(Error::<T>::RequireToBeOwner.into())
                }
            } else {
                Err(Error::<T>::IdtyNotFound.into())
            }
        }
        #[pallet::weight(0)]
        pub fn validate_identity(
            origin: OriginFor<T>,
            idty_rights: Vec<T::IdtyRight>,
Éloïs's avatar
Éloïs committed
        ) -> DispatchResultWithPostInfo {
            T::IdtyValidationOrigin::ensure_origin(origin)?;
Éloïs's avatar
Éloïs committed

            if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) {
Éloïs's avatar
Éloïs committed
                match idty_value.status {
                    IdtyStatus::Created => Err(Error::<T>::IdtyNotConfirmedByOwner.into()),
                    IdtyStatus::ConfirmedByOwner => {
                        if !T::EnsureIdtyCallAllowed::can_validate_identity(idty_index) {
                            return Err(Error::<T>::NotAllowedToValidateIdty.into());
                        }
                        idty_value.removable_on = T::BlockNumber::zero();
                        idty_value.rights =
                            idty_rights.iter().map(|right| (*right, None)).collect();
Éloïs's avatar
Éloïs committed
                        idty_value.status = IdtyStatus::Validated;
Éloïs's avatar
Éloïs committed
                        let name = idty_value.name.clone();
                        let owner_key = idty_value.owner_key.clone();
Éloïs's avatar
Éloïs committed

                        <Identities<T>>::insert(idty_index, idty_value);
                        if idty_rights.is_empty() {
                            let block_number = frame_system::pallet::Pallet::<T>::block_number();
                            let removable_on = block_number + T::MaxNoRightPeriod::get();
                            <IdentitiesRemovableOn<T>>::append(
                                removable_on,
                                (idty_index, IdtyStatus::Validated),
                            );
                        }
Éloïs's avatar
Éloïs committed
                        Self::deposit_event(Event::IdtyValidated(name.clone()));
                        T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Validated);
                        for right in idty_rights {
Éloïs's avatar
Éloïs committed
                            Self::deposit_event(Event::IdtyAcquireRight(name.clone(), right));
                            if right.allow_owner_key() {
                                T::OnRightKeyChange::on_right_key_change(
                                    idty_index,
                                    right,
                                    None,
                                    Some(owner_key.clone()),
                                );
                            }
Éloïs's avatar
Éloïs committed
                        }
                        Ok(().into())
                    }
                    IdtyStatus::Validated => Err(Error::<T>::IdtyAlreadyValidated.into()),
Éloïs's avatar
Éloïs committed
                }
            } else {
                Err(Error::<T>::IdtyNotFound.into())
            }
        }
        #[pallet::weight(0)]
        pub fn add_right(
            origin: OriginFor<T>,
Éloïs's avatar
Éloïs committed
            right: T::IdtyRight,
        ) -> DispatchResultWithPostInfo {
            T::AddRightOrigin::ensure_origin(origin)?;

            if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) {
Éloïs's avatar
Éloïs committed
                if idty_value.status != IdtyStatus::Validated {
                    return Err(Error::<T>::IdtyNotValidated.into());
                }

                if !T::IsMember::is_member(&idty_index) {
                    return Err(Error::<T>::IdtyNotMember.into());
                }

Éloïs's avatar
Éloïs committed
                if let Err(index) = idty_value
                    .rights
                    .binary_search_by(|(right_, _)| right_.cmp(&right))
Éloïs's avatar
Éloïs committed
                {
Éloïs's avatar
Éloïs committed
                    let name = idty_value.name.clone();
Éloïs's avatar
Éloïs committed
                    let new_key = if right.allow_owner_key() {
                        Some(idty_value.owner_key.clone())
                    } else {
                        None
                    };

                    idty_value.removable_on = T::BlockNumber::zero();
                    idty_value.rights.insert(index, (right, None));
                    <Identities<T>>::insert(idty_index, idty_value);
Éloïs's avatar
Éloïs committed
                    Self::deposit_event(Event::<T>::IdtyAcquireRight(name, right));
Éloïs's avatar
Éloïs committed
                    if new_key.is_some() {
                        T::OnRightKeyChange::on_right_key_change(idty_index, right, None, new_key);
Éloïs's avatar
Éloïs committed
                    }
                    Ok(().into())
                } else {
                    Err(Error::<T>::RightAlreadyAdded.into())
                }
            } else {
                Err(Error::<T>::IdtyNotFound.into())
            }
        }
        #[pallet::weight(0)]
        pub fn del_right(
            origin: OriginFor<T>,
Éloïs's avatar
Éloïs committed
            right: T::IdtyRight,
        ) -> DispatchResultWithPostInfo {
            T::DelRightOrigin::ensure_origin(origin)?;

            if let Ok(idty_value) = <Identities<T>>::try_get(idty_index) {
Éloïs's avatar
Éloïs committed
                if idty_value.status != IdtyStatus::Validated {
                    return Err(Error::<T>::IdtyNotValidated.into());
                }

                Self::do_remove_right(idty_index, idty_value, right)
Éloïs's avatar
Éloïs committed
            } else {
                Err(Error::<T>::IdtyNotFound.into())
            }
        }
        #[pallet::weight(0)]
        pub fn set_right_subkey(
            origin: OriginFor<T>,
Éloïs's avatar
Éloïs committed
            right: T::IdtyRight,
            subkey_opt: Option<T::AccountId>,
        ) -> DispatchResultWithPostInfo {
            let who = ensure_signed(origin)?;

            if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) {
Éloïs's avatar
Éloïs committed
                if who == idty_value.owner_key {
                    if idty_value.status != IdtyStatus::Validated {
                        return Err(Error::<T>::IdtyNotValidated.into());
                    }

                    if !T::IsMember::is_member(&idty_index) {
                        return Err(Error::<T>::IdtyNotMember.into());
                    }

Éloïs's avatar
Éloïs committed
                    if let Ok(index) = idty_value
                        .rights
                        .binary_search_by(|(right_, _)| right_.cmp(&right))
Éloïs's avatar
Éloïs committed
                    {
Éloïs's avatar
Éloïs committed
                        let name = idty_value.name.clone();
Éloïs's avatar
Éloïs committed
                        let old_subkey_opt = idty_value.rights[index].1.clone();
                        idty_value.rights[index].1 = subkey_opt.clone();
                        let new_key = if let Some(ref subkey) = subkey_opt {
                            Some(subkey.clone())
                        } else if right.allow_owner_key() {
                            Some(idty_value.owner_key.clone())
                        } else {
                            None
                        };

                        <Identities<T>>::insert(idty_index, idty_value);
Éloïs's avatar
Éloïs committed
                        Self::deposit_event(Event::<T>::IdtySetRightSubKey(
Éloïs's avatar
Éloïs committed
                            name,
Éloïs's avatar
Éloïs committed
                            right,
                            old_subkey_opt.clone(),
                            subkey_opt,
                        ));
                        T::OnRightKeyChange::on_right_key_change(
Éloïs's avatar
Éloïs committed
                            right,
                            old_subkey_opt,
                            new_key,
                        );
                        Ok(().into())
                    } else {
                        Err(Error::<T>::RightNotExist.into())
                    }
                } else {
                    Err(Error::<T>::RequireToBeOwner.into())
                }
            } else {
                Err(Error::<T>::IdtyNotFound.into())
            }
        }

        #[pallet::weight(0)]
        pub fn remove_all_rights(
            origin: OriginFor<T>,
            idty_index: T::IdtyIndex,
        ) -> DispatchResultWithPostInfo {
            ensure_root(origin)?;

            if let Ok(idty_value) = <Identities<T>>::try_get(idty_index) {
                for (right, _key_opt) in &idty_value.rights {
                    Self::do_remove_right(idty_index, idty_value.clone(), *right)?;
                }
                Ok(().into())
            } else {
                Err(Error::<T>::IdtyNotFound.into())
            }
        }

        #[pallet::weight(0)]
        pub fn remove_identity(
            origin: OriginFor<T>,
            idty_index: T::IdtyIndex,
        ) -> DispatchResultWithPostInfo {
            ensure_root(origin)?;

            Self::do_remove_identity(idty_index);

            Ok(().into())
        }
Éloïs's avatar
Éloïs committed
    }

    // ERRORS //

    #[pallet::error]
    pub enum Error<T> {
        /// Creator not exist
        CreatorNotExist,
        /// Creator not allowed to create identities
        CreatorNotAllowedToCreateIdty,
        /// Creator not have right to create identities
        CreatorNotHaveRightToCreateIdty,
Éloïs's avatar
Éloïs committed
        /// Identity already confirmed
        IdtyAlreadyConfirmed,
        /// Identity already validated
        IdtyAlreadyValidated,
        /// You are not allowed to create a new identity now
        IdtyCreationNotAllowed,
Éloïs's avatar
Éloïs committed
        /// Identity name already exist
        IdtyNameAlreadyExist,
        /// Idty name invalid
        IdtyNameInvalid,
Éloïs's avatar
Éloïs committed
        /// Identity not confirmed by owner
        IdtyNotConfirmedByOwner,
        /// Identity not found
        IdtyNotFound,
        /// Idty not member
        IdtyNotMember,
Éloïs's avatar
Éloïs committed
        /// Identity not validated
        IdtyNotValidated,
        /// Identity not yet renewable
        IdtyNotYetRenewable,
        /// Not allowed to confirm identity
        NotAllowedToConfirmIdty,
        /// Not allowed to validate identity
        NotAllowedToValidateIdty,
        /// Not same identity name
        NotSameIdtyName,
Éloïs's avatar
Éloïs committed
        /// This operation requires to be the owner of the identity
        RequireToBeOwner,
        /// Right already added
        RightAlreadyAdded,
        /// Right not exist
        RightNotExist,
        /// Not respect IdtyCreationPeriod
        NotRespectIdtyCreationPeriod,
    // PUBLIC FUNCTIONS //

    impl<T: Config> Pallet<T> {
        pub fn set_idty_data(idty_index: T::IdtyIndex, idty_data: T::IdtyData) {
            Identities::<T>::mutate_exists(idty_index, |idty_val_opt| {
                if let Some(ref mut idty_val) = idty_val_opt {
                    idty_val.data = idty_data;
                }
Éloïs's avatar
Éloïs committed
    // INTERNAL FUNCTIONS //

    impl<T: Config> Pallet<T> {
        fn dec_identities_counter() {
            if let Ok(counter) = <IdentitiesCount<T>>::try_get() {
                <IdentitiesCount<T>>::put(counter.saturating_sub(1));
            } else {
                panic!("storage corrupted")
            }
        pub(super) fn do_remove_identity(idty_index: T::IdtyIndex) -> Weight {
            if let Some(idty_val) = <Identities<T>>::take(idty_index) {
                <IdentitiesByDid<T>>::remove(idty_val.name);
            }
            Self::dec_identities_counter();
            T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Removed);
            0
        }
        pub(super) fn do_remove_right(
            idty_index: T::IdtyIndex,
            mut idty_value: IdtyValue<T::AccountId, T::BlockNumber, T::IdtyData, T::IdtyRight>,
            right: T::IdtyRight,
        ) -> DispatchResultWithPostInfo {
            if let Ok(index) = idty_value
                .rights
                .binary_search_by(|(right_, _)| right_.cmp(&right))
            {
                let name = idty_value.name.clone();
                let old_key_opt = if let Some(ref subkey) = idty_value.rights[index].1 {
                    Some(subkey.clone())
                } else if right.allow_owner_key() {
                    Some(idty_value.owner_key.clone())
                } else {
                    None
                };
                idty_value.rights.remove(index);

                if idty_value.rights.is_empty() {
                    let block_number = frame_system::pallet::Pallet::<T>::block_number();
                    let removable_on = block_number + T::MaxNoRightPeriod::get();
                    idty_value.removable_on = removable_on;
                    <IdentitiesRemovableOn<T>>::append(
                        removable_on,
                        (idty_index, IdtyStatus::Validated),
                    );
                }

                <Identities<T>>::insert(idty_index, idty_value);
                Self::deposit_event(Event::<T>::IdtyLostRight(name, right));
                if old_key_opt.is_some() {
                    T::OnRightKeyChange::on_right_key_change(idty_index, right, old_key_opt, None);
                }
                Ok(().into())
            } else {
                Err(Error::<T>::RightNotExist.into())
            }
Éloïs's avatar
Éloïs committed
        fn get_next_idty_index() -> T::IdtyIndex {
            if let Ok(next_index) = <NextIdtyIndex<T>>::try_get() {
                <NextIdtyIndex<T>>::put(next_index.saturating_add(T::IdtyIndex::one()));
                next_index
            } else {
                <NextIdtyIndex<T>>::put(T::IdtyIndex::one() + T::IdtyIndex::one());
                T::IdtyIndex::one()
Éloïs's avatar
Éloïs committed
        }
        fn inc_identities_counter() {
            if let Ok(counter) = <IdentitiesCount<T>>::try_get() {
                <IdentitiesCount<T>>::put(counter.saturating_add(1));
            } else {
                <IdentitiesCount<T>>::put(1);
            }
        }
        fn prune_identities(block_number: T::BlockNumber) -> Weight {
            let mut total_weight: Weight = 0;

            for (idty_index, idty_status) in IdentitiesRemovableOn::<T>::take(block_number) {
                if let Ok(idty_val) = <Identities<T>>::try_get(idty_index) {
                    if idty_val.removable_on == block_number && idty_val.status == idty_status {
                        total_weight += Self::do_remove_identity(idty_index)