From fc4052cef85a82733d889b434b9f2113a524d67a Mon Sep 17 00:00:00 2001
From: librelois <c@elo.tf>
Date: Sat, 17 Jul 2021 12:40:12 +0200
Subject: [PATCH] feat+ref(certification): rework pallet and add handlers

---
 pallets/certification/src/lib.rs    | 279 ++++++++++++++++++----------
 pallets/certification/src/mock.rs   | 132 +++++++++++++
 pallets/certification/src/tests.rs  | 102 ++++++++++
 pallets/certification/src/traits.rs |  55 ++++++
 4 files changed, 466 insertions(+), 102 deletions(-)
 create mode 100644 pallets/certification/src/mock.rs
 create mode 100644 pallets/certification/src/tests.rs
 create mode 100644 pallets/certification/src/traits.rs

diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs
index ebe684a06..56e834d34 100644
--- a/pallets/certification/src/lib.rs
+++ b/pallets/certification/src/lib.rs
@@ -16,8 +16,14 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
+pub mod traits;
+
+#[cfg(test)]
+mod mock;
+
 pub use pallet::*;
 
+use crate::traits::*;
 use codec::Codec;
 use sp_runtime::traits::{AtLeast32BitUnsigned, Zero};
 use sp_std::collections::btree_map::BTreeMap;
@@ -31,12 +37,12 @@ pub mod pallet {
 
     /// Configure the pallet by specifying the parameters and types on which it depends.
     pub trait Config<I: Instance = DefaultInstance>: frame_system::Config {
-        /// Duration after which a certification is renewable
-        type ChainabilityPeriod: Get<Self::BlockNumber>;
         /// Origin allowed to add a certification
-        type AddCertOrigin: EnsureOrigin<(Self::Origin, Self::IdtyIndex)>;
+        type AddCertOrigin: EnsureOrigin<(Self::Origin, Self::IdtyIndex, Self::IdtyIndex)>;
+        /// Minimum duration between two certifications issued by the same issuer
+        type CertPeriod: Get<Self::BlockNumber>;
         /// Origin allowed to delete a certification
-        type DelCertOrigin: EnsureOrigin<(Self::Origin, Self::IdtyIndex)>;
+        type DelCertOrigin: EnsureOrigin<(Self::Origin, Self::IdtyIndex, Self::IdtyIndex)>;
         /// The overarching event type.
         type Event: From<Event<Self, I>> + Into<<Self as frame_system::Config>::Event>;
         /// A short identity index.
@@ -50,9 +56,13 @@ pub mod pallet {
             + Debug
             + MaxEncodedLen;
         /// Maximum number of active certifications by issuer
-        type MaxByIssuer: Get<u32>;
-        /// Minimum duration between two certifications issued by the same issuer
-        type SignPeriod: Get<Self::BlockNumber>;
+        type MaxByIssuer: Get<u8>;
+        /// Handler for NewCert event
+        type OnNewcert: OnNewcert<Self::IdtyIndex>;
+        /// Handler for Removed event
+        type OnRemovedCert: OnRemovedCert<Self::IdtyIndex>;
+        /// Duration after which a certification is renewable
+        type RenewablePeriod: Get<Self::BlockNumber>;
         /// Duration of validity of a certification
         type ValidityPeriod: Get<Self::BlockNumber>;
     }
@@ -62,11 +72,11 @@ pub mod pallet {
             <T as Config<I>>::IdtyIndex,
         {
             /// New certification
-            /// \[issuer, receiver\]
-            NewCert(IdtyIndex, IdtyIndex),
+            /// \[issuer, issuer_issued_count, receiver, receiver_received_count\]
+            NewCert(IdtyIndex,u8,  IdtyIndex, u32),
             /// Removed certification
-            /// \[issuer, receiver, expiration\]
-            RemovedCert(IdtyIndex, IdtyIndex, bool),
+            /// \[issuer, issuer_issued_count, receiver, receiver_received_count, expiration\]
+            RemovedCert(IdtyIndex, u8, IdtyIndex, u32, bool),
             /// Renewed certification
             /// \[issuer, receiver\]
             RenewedCert(IdtyIndex, IdtyIndex),
@@ -75,12 +85,14 @@ pub mod pallet {
 
     frame_support::decl_error! {
         pub enum Error for Module<T: Config<I>, I: Instance> {
+            /// An identity must receive certifications before it can issue them.
+            IdtyMustReceiveCertsBeforeCanIssue,
             /// This identity has already issued the maximum number of certifications
             IssuedTooManyCert,
             /// This certification has already been issued or renewed recently
-            NotRespectChainabilityPeriodPeriod,
+            NotRespectRenewablePeriod,
             /// This identity has already issued a certification too recently
-            NotRespectSignPeriod,
+            NotRespectCertPeriod,
         }
     }
 
@@ -101,15 +113,27 @@ pub mod pallet {
 
     #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]
     pub struct CertValue<T: Config<I>, I: Instance> {
-        receiver: T::IdtyIndex,
         chainable_on: T::BlockNumber,
         removable_on: T::BlockNumber,
+        phantom: PhantomData<I>,
     }
 
-    #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug)]
-    pub struct CertsByIssuer<T: Config<I>, I: Instance> {
-        certs: Vec<CertValue<T, I>>,
+    #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]
+    pub struct IdtyCertMeta<T: Config<I>, I: Instance> {
+        issued_count: u8,
         next_issuable_on: T::BlockNumber,
+        received_count: u32,
+        phantom: PhantomData<I>,
+    }
+    impl<T: Config<I>, I: Instance> Default for IdtyCertMeta<T, I> {
+        fn default() -> Self {
+            Self {
+                issued_count: 0,
+                next_issuable_on: T::BlockNumber::zero(),
+                received_count: 0,
+                phantom: PhantomData,
+            }
+        }
     }
 
     frame_support::decl_storage! {
@@ -117,11 +141,16 @@ pub mod pallet {
             /// Storage version of the pallet.
             StorageVersion get(fn storage_version): Releases;
             /// Certifications by issuer
-            pub StorageCertsByIssuer get(fn certs_by_issuer):
-            map hasher(twox_64_concat) T::IdtyIndex => CertsByIssuer<T, I> = CertsByIssuer {
-                certs: vec![],
+            pub StorageIdtyCertMeta get(fn certs_by_issuer):
+            map hasher(twox_64_concat) T::IdtyIndex => IdtyCertMeta<T, I> = IdtyCertMeta {
+                issued_count: 0,
                 next_issuable_on: T::BlockNumber::zero(),
+                received_count: 0,
+                phantom: PhantomData,
             };
+            pub StorageCertsByIssuer get(fn cert):
+                double_map hasher(identity) T::IdtyIndex, hasher(identity) T::IdtyIndex
+                => Option<CertValue<T, I>>;
             /// Certifications by receiver
             pub StorageCertsByReceiver get(fn certs_by_receiver):
             map hasher(twox_64_concat) T::IdtyIndex => Vec<T::IdtyIndex>;
@@ -133,34 +162,44 @@ pub mod pallet {
             config(phantom): sp_std::marker::PhantomData<I>;
             config(certs_by_issuer): BTreeMap<T::IdtyIndex, BTreeSet<T::IdtyIndex>>;
             build(|config| {
+                let mut cert_meta_by_issuer = BTreeMap::<T::IdtyIndex, IdtyCertMeta<T, I>>::new();
                 let mut certs_by_receiver = BTreeMap::<T::IdtyIndex, Vec<T::IdtyIndex>>::new();
                 for (issuer, receivers) in &config.certs_by_issuer {
                     assert!(!receivers.contains(issuer), "Identity cannot tcertify it-self.");
                     assert!(!receivers.len() <= T::MaxByIssuer::get() as usize, "Identity n°{:?} exceed MaxByIssuer.", issuer);
+
+                    cert_meta_by_issuer.insert(*issuer, IdtyCertMeta {
+                        issued_count: receivers.len() as u8,
+                        next_issuable_on: T::CertPeriod::get(),
+                        received_count: 0,
+                        phantom: PhantomData,
+                    });
                     for receiver in receivers {
                         certs_by_receiver.entry(*receiver).or_default().push(*issuer);
                     }
                 }
 
                 <StorageVersion<I>>::put(Releases::V1_0_0);
+                // Write StorageCertsByReceiver
+                for (receiver, issuers) in certs_by_receiver {
+                    cert_meta_by_issuer.entry(receiver).and_modify(|cert_meta| cert_meta.received_count = issuers.len() as u32);
+                    <StorageCertsByReceiver<T, I>>::insert(receiver, issuers);
+                }
+                // Write StorageIdtyCertMeta
+                for (issuer, cert_meta) in cert_meta_by_issuer {
+                    <StorageIdtyCertMeta<T, I>>::insert(issuer, cert_meta);
+                }
+                // Write StorageCertsByIssuer && StorageCertsRemovableOn
                 let mut all_couples = Vec::new();
                 for (issuer, receivers) in &config.certs_by_issuer {
-                    let mut certs = Vec::with_capacity(receivers.len());
                     for receiver in receivers {
                         all_couples.push((*issuer, *receiver));
-                        certs.push(CertValue {
-                            receiver: *receiver,
-                            chainable_on: T::ChainabilityPeriod::get(),
+                        <StorageCertsByIssuer<T, I>>::insert(issuer, receiver, CertValue {
+                            chainable_on: T::RenewablePeriod::get(),
                             removable_on: T::ValidityPeriod::get(),
+                            phantom: PhantomData,
                         });
-                        let received_certs = certs_by_receiver.remove(receiver).unwrap_or_default();
-                        <StorageCertsByReceiver<T, I>>::insert(receiver, received_certs);
-
                     }
-                    <StorageCertsByIssuer<T, I>>::insert(issuer, CertsByIssuer {
-                        certs,
-                        next_issuable_on: T::SignPeriod::get(),
-                    });
                 }
                 <StorageCertsRemovableOn<T, I>>::insert(T::ValidityPeriod::get(), all_couples);
             });
@@ -181,74 +220,100 @@ pub mod pallet {
 
             #[weight = 0]
             pub fn add_cert(origin, issuer: T::IdtyIndex, receiver: T::IdtyIndex) {
-                T::AddCertOrigin::ensure_origin((origin, issuer))?;
+                T::AddCertOrigin::ensure_origin((origin, issuer, receiver))?;
 
                 let block_number = frame_system::pallet::Pallet::<T>::block_number();
-                let mut create = false;
-                let removable_on = block_number + T::ValidityPeriod::get();
-                let certs = if let Ok(CertsByIssuer { mut certs, next_issuable_on }) = <StorageCertsByIssuer<T, I>>::try_get(issuer) {
-                    if next_issuable_on > block_number {
-                        return Err(Error::<T, I>::NotRespectSignPeriod.into());
-                    } else if certs.len() >= T::MaxByIssuer::get() as usize {
+
+                let (create, issuer_issued_count) = if let Ok(mut issuer_idty_cert_meta) = <StorageIdtyCertMeta<T, I>>::try_get(issuer) {
+                    // Verify rules CertPeriod and MaxByIssuer
+                    if issuer_idty_cert_meta.next_issuable_on > block_number {
+                        return Err(Error::<T, I>::NotRespectCertPeriod.into());
+                    } else if issuer_idty_cert_meta.issued_count >= T::MaxByIssuer::get() {
                         return Err(Error::<T, I>::IssuedTooManyCert.into());
                     }
-                    let insert_index = match certs.binary_search_by(
-                        |CertValue {
-                             receiver: receiver_,
-                             ..
-                         }| receiver.cmp(&receiver_),
-                    ) {
-                        Ok(index) => {
-                            if certs[index].chainable_on > block_number {
-                                return Err(Error::<T, I>::NotRespectChainabilityPeriodPeriod.into());
-                            }
-                            create = true;
-                            index
-                        },
-                        Err(index) => index,
+
+                    // Verify rule RenewablePeriod
+                    let create = if let Ok(CertValue { chainable_on, .. }) = <StorageCertsByIssuer<T, I>>::try_get(issuer, receiver) {
+                        if chainable_on > block_number {
+                            return Err(Error::<T, I>::NotRespectRenewablePeriod.into());
+                        }
+                        false
+                    } else {
+                        true
                     };
-                    certs.insert(insert_index, CertValue {
-                        receiver,
-                        chainable_on: block_number + T::ChainabilityPeriod::get(),
-                        removable_on,
-                    });
-                    certs
+
+                    // Write StorageIdtyCertMeta for issuer
+                    issuer_idty_cert_meta.issued_count = issuer_idty_cert_meta.issued_count.saturating_add(1);
+                    let issuer_issued_count = issuer_idty_cert_meta.issued_count;
+                    issuer_idty_cert_meta.next_issuable_on = block_number + T::CertPeriod::get();
+                    <StorageIdtyCertMeta<T, I>>::insert(issuer, issuer_idty_cert_meta);
+
+                    (create, issuer_issued_count)
                 } else {
-                    vec![CertValue {
-                        receiver,
-                        chainable_on: block_number + T::ChainabilityPeriod::get(),
-                        removable_on,
-                    }]
+                    // An identity must receive certifications before it can issue them.
+                    return Err(Error::<T, I>::IdtyMustReceiveCertsBeforeCanIssue.into());
                 };
 
-                <StorageCertsByIssuer<T, I>>::insert(issuer, CertsByIssuer {
-                    certs,
-                    next_issuable_on: block_number + T::SignPeriod::get()
+                // Write StorageIdtyCertMeta for receiver
+                let receiver_received_count = <StorageIdtyCertMeta<T, I>>::mutate_exists(receiver, |cert_meta_opt| {
+                    let cert_meta = cert_meta_opt.get_or_insert(IdtyCertMeta::default());
+                    cert_meta.received_count = cert_meta.received_count.saturating_add(1);
+                    cert_meta.received_count
                 });
-                if !create {
+
+                // Write StorageCertsRemovableOn and StorageCertsByIssuer
+                let cert_value = CertValue {
+                    chainable_on: block_number + T::RenewablePeriod::get(),
+                    removable_on: block_number + T::ValidityPeriod::get(),
+                    phantom: PhantomData,
+                };
+                <StorageCertsRemovableOn<T, I>>::append(cert_value.removable_on, (issuer, receiver));
+                <StorageCertsByIssuer<T, I>>::insert(issuer, receiver, cert_value);
+
+                if create {
+                    // Write StorageCertsByReceiver
                     <StorageCertsByReceiver<T, I>>::mutate_exists(receiver, |issuers_opt| {
                         let issuers = issuers_opt.get_or_insert(vec![]);
                         if let Err(index) = issuers.binary_search(&issuer) {
                             issuers.insert(index, issuer);
                         }
                     });
-                }
-                <StorageCertsRemovableOn<T, I>>::append(removable_on, (issuer, receiver));
-
-                if create {
-                    Self::deposit_event(RawEvent::NewCert(issuer, receiver));
+                    Self::deposit_event(RawEvent::NewCert(issuer, issuer_issued_count, receiver, receiver_received_count));
+                    T::OnNewcert::on_new_cert(issuer, issuer_issued_count, receiver, receiver_received_count);
                 } else {
                     Self::deposit_event(RawEvent::RenewedCert(issuer, receiver));
                 }
             }
             #[weight = 0]
             pub fn del_cert(origin, issuer: T::IdtyIndex, receiver: T::IdtyIndex) {
-                T::DelCertOrigin::ensure_origin((origin, issuer))?;
+                T::DelCertOrigin::ensure_origin((origin, issuer, receiver))?;
                 Self::remove_cert_inner(issuer, receiver, None);
             }
         }
     }
 
+    // PUBLIC FUNCTIONS //
+
+    impl<T: Config<I>, I: Instance> Module<T, I> {
+        pub fn is_idty_allowed_to_create_cert(idty_index: T::IdtyIndex) -> bool {
+            if let Ok(cert_meta) = <StorageIdtyCertMeta<T, I>>::try_get(idty_index) {
+                cert_meta.next_issuable_on <= frame_system::pallet::Pallet::<T>::block_number()
+                    && cert_meta.issued_count < T::MaxByIssuer::get()
+            } else {
+                true
+            }
+        }
+        pub fn on_idty_removed(idty_index: T::IdtyIndex) -> Weight {
+            let mut total_weight: Weight = 0;
+            if let Ok(issuers) = <StorageCertsByReceiver<T, I>>::try_get(idty_index) {
+                for issuer in issuers {
+                    total_weight += Self::remove_cert_inner(issuer, idty_index, None);
+                }
+            }
+            total_weight
+        }
+    }
+
     // INTERNAL FUNCTIONS //
 
     impl<T: Config<I>, I: Instance> Module<T, I> {
@@ -271,38 +336,48 @@ pub mod pallet {
             receiver: T::IdtyIndex,
             block_number_opt: Option<T::BlockNumber>,
         ) -> Weight {
-            if let Ok(mut certs_by_issuer) = <StorageCertsByIssuer<T, I>>::try_get(issuer) {
-                if let Ok(index) = certs_by_issuer.certs.binary_search_by(
-                    |CertValue {
-                         receiver: receiver_,
-                         ..
-                     }| receiver.cmp(&receiver_),
-                ) {
-                    if let Some(cert_val) = certs_by_issuer.certs.get(index) {
-                        if Some(cert_val.removable_on) == block_number_opt
-                            || block_number_opt.is_none()
-                        {
-                            certs_by_issuer.certs.remove(index);
-                            <StorageCertsByIssuer<T, I>>::insert(issuer, certs_by_issuer);
-                            if let Ok(mut certs_by_receiver) =
-                                <StorageCertsByReceiver<T, I>>::try_get(receiver)
-                            {
-                                if let Ok(index) = certs_by_receiver
-                                    .binary_search_by(|issuer_| issuer.cmp(&issuer_))
-                                {
-                                    certs_by_receiver.remove(index);
-                                }
-                            }
-                            Self::deposit_event(RawEvent::RemovedCert(
-                                issuer,
-                                receiver,
-                                block_number_opt.is_some(),
-                            ));
-                        }
+            let mut total_weight: Weight = 0;
+            let mut removed = false;
+            <StorageCertsByIssuer<T, I>>::mutate_exists(issuer, receiver, |cert_val_opt| {
+                if let Some(cert_val) = cert_val_opt {
+                    if Some(cert_val.removable_on) == block_number_opt || block_number_opt.is_none()
+                    {
+                        removed = true;
                     }
                 }
+                if removed {
+                    cert_val_opt.take();
+                }
+            });
+            if removed {
+                let issuer_issued_count =
+                    <StorageIdtyCertMeta<T, I>>::mutate_exists(issuer, |cert_meta_opt| {
+                        let cert_meta = cert_meta_opt.get_or_insert(IdtyCertMeta::default());
+                        cert_meta.issued_count = cert_meta.issued_count.saturating_sub(1);
+                        cert_meta.issued_count
+                    });
+                let receiver_received_count =
+                    <StorageIdtyCertMeta<T, I>>::mutate_exists(receiver, |cert_meta_opt| {
+                        let cert_meta = cert_meta_opt.get_or_insert(IdtyCertMeta::default());
+                        cert_meta.received_count = cert_meta.received_count.saturating_sub(1);
+                        cert_meta.received_count
+                    });
+                Self::deposit_event(RawEvent::RemovedCert(
+                    issuer,
+                    issuer_issued_count,
+                    receiver,
+                    receiver_received_count,
+                    block_number_opt.is_some(),
+                ));
+                total_weight += T::OnRemovedCert::on_removed_cert(
+                    issuer,
+                    issuer_issued_count,
+                    receiver,
+                    receiver_received_count,
+                    block_number_opt.is_some(),
+                );
             }
-            0
+            total_weight
         }
     }
 }
diff --git a/pallets/certification/src/mock.rs b/pallets/certification/src/mock.rs
new file mode 100644
index 000000000..3f9841c78
--- /dev/null
+++ b/pallets/certification/src/mock.rs
@@ -0,0 +1,132 @@
+// 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, either version 3 of the License.
+//
+// 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/>.
+
+use crate::{self as pallet_certification};
+use frame_support::{
+    parameter_types,
+    traits::{OnFinalize, OnInitialize},
+};
+use frame_system as system;
+use sp_core::H256;
+use sp_runtime::{
+    testing::Header,
+    traits::{BlakeTwo256, IdentityLookup},
+    BuildStorage,
+};
+
+type AccountId = u64;
+type BlockNumber = u64;
+type Block = frame_system::mocking::MockBlock<Test>;
+type IdtyIndex = u64;
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+
+// Configure a mock runtime to test the pallet.
+frame_support::construct_runtime!(
+    pub enum Test where
+        Block = Block,
+        NodeBlock = Block,
+        UncheckedExtrinsic = UncheckedExtrinsic,
+    {
+        System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
+        DefaultCertification: pallet_certification::{Pallet, Call, Event<T>, Storage, Config<T>},
+    }
+);
+
+parameter_types! {
+    pub const BlockHashCount: u64 = 250;
+    pub const SS58Prefix: u8 = 42;
+}
+
+impl system::Config for Test {
+    type BaseCallFilter = ();
+    type BlockWeights = ();
+    type BlockLength = ();
+    type DbWeight = ();
+    type Origin = Origin;
+    type Call = Call;
+    type Index = u64;
+    type BlockNumber = BlockNumber;
+    type Hash = H256;
+    type Hashing = BlakeTwo256;
+    type AccountId = AccountId;
+    type Lookup = IdentityLookup<Self::AccountId>;
+    type Header = Header;
+    type Event = Event;
+    type BlockHashCount = BlockHashCount;
+    type Version = ();
+    type PalletInfo = PalletInfo;
+    type AccountData = ();
+    type OnNewAccount = ();
+    type OnKilledAccount = ();
+    type SystemWeightInfo = ();
+    type SS58Prefix = SS58Prefix;
+    type OnSetCode = ();
+}
+
+parameter_types! {
+    pub const MaxByIssuer: u8 = 3;
+    pub const RenewablePeriod: BlockNumber = 2;
+    pub const CertPeriod: u64 = 2;
+    pub const ValidityPeriod: u64 = 5;
+}
+
+pub struct EnsureRoot;
+impl frame_support::traits::EnsureOrigin<(Origin, IdtyIndex, IdtyIndex)> for EnsureRoot {
+    type Success = ();
+
+    fn try_origin(o: (Origin, IdtyIndex, IdtyIndex)) -> Result<Self::Success, (Origin, IdtyIndex, IdtyIndex)> {
+        match o.0.clone().into() {
+            Ok(system::RawOrigin::Root) => Ok(()),
+            _ => Err(o),
+        }
+    }
+}
+
+impl pallet_certification::Config for Test {
+    type AddCertOrigin = EnsureRoot;
+    type CertPeriod = CertPeriod;
+    type DelCertOrigin = EnsureRoot;
+    type Event = Event;
+    type IdtyIndex = IdtyIndex;
+    type MaxByIssuer = MaxByIssuer;
+    type OnNewcert = ();
+    type OnRemovedCert = ();
+    type RenewablePeriod = RenewablePeriod;
+    type ValidityPeriod = ValidityPeriod;
+}
+
+// Build genesis storage according to the mock runtime.
+pub fn new_test_ext(
+    gen_conf: pallet_certification::GenesisConfig<Test>,
+) -> sp_io::TestExternalities {
+    GenesisConfig {
+        system: SystemConfig::default(),
+        default_certification: gen_conf,
+    }
+    .build_storage()
+    .unwrap()
+    .into()
+}
+
+pub fn run_to_block(n: u64) {
+    while System::block_number() < n {
+        DefaultCertification::on_finalize(System::block_number());
+        System::on_finalize(System::block_number());
+        System::set_block_number(System::block_number() + 1);
+        System::on_initialize(System::block_number());
+        DefaultCertification::on_initialize(System::block_number());
+    }
+}
diff --git a/pallets/certification/src/tests.rs b/pallets/certification/src/tests.rs
new file mode 100644
index 000000000..aadb17e76
--- /dev/null
+++ b/pallets/certification/src/tests.rs
@@ -0,0 +1,102 @@
+// 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, either version 3 of the License.
+//
+// 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/>.
+
+use crate::mock::IdtyIndex;
+use crate::mock::*;
+use crate::Error;
+use frame_support::assert_err;
+use frame_support::assert_ok;
+use frame_system::{EventRecord, Phase};
+
+#[test]
+fn test_no_certs() {
+    let certifications = Vec::with_capacity(0);
+    new_test_ext(DefaultCertificationConfig { certifications }).execute_with(|| {
+        //assert_eq!(DefaultCertification::identities_count(), 0);
+    });
+}
+/*
+#[test]
+fn test_two_identities() {
+    let identities = vec![
+        crate::IdtyValue {
+            did: Did(0),
+            owner_key: 1,
+            removable_on: None,
+            rights: vec![(Right::Right2, Some(10))],
+            status: crate::IdtyStatus::Validated,
+            data: (),
+        },
+        crate::IdtyValue {
+            did: Did(1),
+            owner_key: 2,
+            removable_on: None,
+            rights: vec![(Right::Right1, Some(20))],
+            status: crate::IdtyStatus::Validated,
+            data: (),
+        },
+    ];
+
+    new_test_ext(IdentityConfig { identities }).execute_with(|| {
+        // Should have two identities
+        assert_eq!(Identity::identities_count(), 2);
+
+        // We need to initialize at least one block before any call
+        run_to_block(1);
+
+        // Add right Right1 for Did(0)
+        // Should succes and trigger the correct event
+        assert_ok!(Identity::add_right(Origin::root(), 0, Right::Right1));
+        let events = System::events();
+        assert_eq!(events.len(), 1);
+        assert_eq!(
+            events[0],
+            EventRecord {
+                phase: Phase::Initialization,
+                event: Event::Identity(crate::Event::IdtyAcquireRight(Did(0), Right::Right1)),
+                topics: vec![],
+            }
+        );
+        // Add right Right2 for Did(0)
+        // Should fail because Did(0) already have this right
+        assert_err!(
+            Identity::add_right(Origin::root(), 0, Right::Right2),
+            Error::<Test>::RightAlreadyAdded
+        );
+
+        run_to_block(3);
+
+        // Delete right Right1 for Did(1)
+        // Should succes and trigger the correct event
+        assert_ok!(Identity::del_right(Origin::root(), 1, Right::Right1));
+        let events = System::events();
+        assert_eq!(events.len(), 2);
+        assert_eq!(
+            events[1],
+            EventRecord {
+                phase: Phase::Initialization,
+                event: Event::Identity(crate::Event::IdtyLostRight(Did(1), Right::Right1)),
+                topics: vec![],
+            }
+        );
+
+        // The Did(1) identity has no more rights, the inactivity period must start to run
+        let idty2 = Identity::identity(1);
+        assert!(idty2.rights.is_empty());
+        assert_eq!(idty2.removable_on, Some(7));
+    });
+}
+*/
\ No newline at end of file
diff --git a/pallets/certification/src/traits.rs b/pallets/certification/src/traits.rs
new file mode 100644
index 000000000..329ba1b07
--- /dev/null
+++ b/pallets/certification/src/traits.rs
@@ -0,0 +1,55 @@
+// 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, either version 3 of the License.
+//
+// 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/>.
+
+pub trait OnNewcert<IdtyIndex> {
+    fn on_new_cert(
+        issuer: IdtyIndex,
+        issuer_issued_count: u8,
+        receiver: IdtyIndex,
+        receiver_received_count: u32,
+    ) -> frame_support::dispatch::Weight;
+}
+impl<IdtyIndex> OnNewcert<IdtyIndex> for () {
+    fn on_new_cert(
+        _issuer: IdtyIndex,
+        _issuer_issued_count: u8,
+        _receiver: IdtyIndex,
+        _receiver_received_count: u32,
+    ) -> frame_support::dispatch::Weight {
+        0
+    }
+}
+
+pub trait OnRemovedCert<IdtyIndex> {
+    fn on_removed_cert(
+        issuer: IdtyIndex,
+        issuer_issued_count: u8,
+        receiver: IdtyIndex,
+        receiver_received_count: u32,
+        expiration: bool,
+    ) -> frame_support::dispatch::Weight;
+}
+impl<IdtyIndex> OnRemovedCert<IdtyIndex> for () {
+    fn on_removed_cert(
+        _issuer: IdtyIndex,
+        _issuer_issued_count: u8,
+        _receiver: IdtyIndex,
+        _receiver_received_count: u32,
+        _expiration: bool,
+    ) -> frame_support::dispatch::Weight {
+        0
+    }
+}
-- 
GitLab