From 78b408eb2ce9b5cfd10557ee43b249594421202d Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo@trentesaux.fr>
Date: Tue, 14 Nov 2023 13:35:05 +0100
Subject: [PATCH] remove membership metadata

---
 pallets/authority-members/src/benchmarking.rs |   2 +-
 pallets/authority-members/src/lib.rs          |   8 +-
 pallets/authority-members/src/mock.rs         |   1 -
 pallets/distance/src/mock.rs                  |   1 -
 pallets/duniter-wot/src/lib.rs                |  58 +++++++++-----
 pallets/duniter-wot/src/mock.rs               |   2 -
 pallets/duniter-wot/src/tests.rs              |   7 +-
 pallets/membership/src/benchmarking.rs        |   4 +-
 pallets/membership/src/lib.rs                 |  43 ++++------
 pallets/membership/src/mock.rs                |   1 -
 pallets/membership/src/tests.rs               |   3 -
 primitives/membership/src/lib.rs              |   4 +-
 primitives/membership/src/traits.rs           |   8 +-
 resources/metadata.scale                      | Bin 133447 -> 133159 bytes
 runtime/common/src/entities.rs                |  71 -----------------
 runtime/common/src/handlers.rs                |  51 +++---------
 runtime/common/src/pallets_config.rs          |   3 -
 runtime/gdev/tests/fixme_tests.rs             |  75 ++++++++++++++++++
 runtime/gdev/tests/integration_tests.rs       |  66 +++++++++++++--
 19 files changed, 214 insertions(+), 194 deletions(-)
 create mode 100644 runtime/gdev/tests/fixme_tests.rs

diff --git a/pallets/authority-members/src/benchmarking.rs b/pallets/authority-members/src/benchmarking.rs
index d19e73ccf..c0dcc62b8 100644
--- a/pallets/authority-members/src/benchmarking.rs
+++ b/pallets/authority-members/src/benchmarking.rs
@@ -59,7 +59,7 @@ benchmarks! {
         let caller: T::AccountId = Members::<T>::get(id).unwrap().owner_key;
         let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
             let validator_id = T::ValidatorIdOf::convert(caller.clone()).unwrap();
-            let session_keys: T::KeysWrapper = pallet_session::NextKeys::<T>::get(validator_id).unwrap().into();
+            let session_keys: T::Keys = pallet_session::NextKeys::<T>::get(validator_id).unwrap().into();
         }: _<T::RuntimeOrigin>(caller_origin, session_keys)
      remove_member {
         let id: T::MemberId = OnlineAuthorities::<T>::get()[0];
diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs
index f5d86823d..7884482b3 100644
--- a/pallets/authority-members/src/lib.rs
+++ b/pallets/authority-members/src/lib.rs
@@ -66,7 +66,6 @@ pub mod pallet {
     pub trait Config:
         frame_system::Config + pallet_session::Config + pallet_session::historical::Config
     {
-        type KeysWrapper: Parameter + Into<Self::Keys> + From<Self::Keys>;
         type IsMember: IsMember<Self::MemberId>;
         type OnNewSession: OnNewSession;
         type OnRemovedMember: OnRemovedMember<Self::MemberId>;
@@ -295,15 +294,12 @@ pub mod pallet {
         /// declare new session keys to replace current ones
         #[pallet::call_index(2)]
         #[pallet::weight(<T as pallet::Config>::WeightInfo::set_session_keys())]
-        pub fn set_session_keys(
-            origin: OriginFor<T>,
-            keys: T::KeysWrapper,
-        ) -> DispatchResultWithPostInfo {
+        pub fn set_session_keys(origin: OriginFor<T>, keys: T::Keys) -> DispatchResultWithPostInfo {
             let who = ensure_signed(origin.clone())?;
             let member_id = Self::verify_ownership_and_membership(&who)?;
 
             let _post_info = pallet_session::Call::<T>::set_keys {
-                keys: keys.into(),
+                keys,
                 proof: vec![],
             }
             .dispatch_bypass_filter(origin)?;
diff --git a/pallets/authority-members/src/mock.rs b/pallets/authority-members/src/mock.rs
index c5cfce266..d04f0f47f 100644
--- a/pallets/authority-members/src/mock.rs
+++ b/pallets/authority-members/src/mock.rs
@@ -151,7 +151,6 @@ impl IsMember<u64> for TestIsSmithMember {
 }
 
 impl pallet_authority_members::Config for Test {
-    type KeysWrapper = MockSessionKeys;
     type IsMember = TestIsSmithMember;
     type MaxAuthorities = ConstU32<4>;
     type MemberId = u64;
diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs
index 977c4228c..5d580b137 100644
--- a/pallets/distance/src/mock.rs
+++ b/pallets/distance/src/mock.rs
@@ -180,7 +180,6 @@ impl<T: pallet_identity::Config> sp_runtime::traits::Convert<T::AccountId, Optio
 }
 
 impl pallet_authority_members::Config for Test {
-    type KeysWrapper = MockSessionKeys;
     type IsMember = TestIsSmithMember;
     type MaxAuthorities = ConstU32<4>;
     type MemberId = u32;
diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs
index 1b3b9bdb6..eb9fc59e0 100644
--- a/pallets/duniter-wot/src/lib.rs
+++ b/pallets/duniter-wot/src/lib.rs
@@ -127,8 +127,8 @@ pub mod pallet {
         NotAllowedToRemoveIdty,
         /// Issuer can not emit cert because it is not validated
         IssuerCanNotEmitCert,
-        /// Can not issue cert to unconfirmed identity
-        CertToUnconfirmedIdty,
+        /// Can not issue cert to identity without membership or pending membership
+        CertToUndefined,
         /// Issuer or receiver not found
         IdtyNotFound,
     }
@@ -164,11 +164,9 @@ where
     fn check_confirm_identity(idty_index: IdtyIndex) -> Result<(), DispatchError> {
         // main WoT automatic action
         if !T::IsSubWot::get() {
-            pallet_membership::Pallet::<T, I>::force_request_membership(
-                idty_index,
-                Default::default(),
-            )
-            .map_err(|e| e.error)?;
+            // force add a membership request to the main WoT
+            pallet_membership::Pallet::<T, I>::force_request_membership(idty_index)
+                .map_err(|e| e.error)?;
         }
         // no constraints for subwot
         Ok(())
@@ -209,12 +207,16 @@ where
 // implement cert call checks
 impl<T: Config<I>, I: 'static> pallet_certification::traits::CheckCertAllowed<IdtyIndex>
     for Pallet<T, I>
+// TODO add the following where clause once checks can be done on pallet instance
+// where
+//     T: pallet_membership::Config<I>,
 {
     // check the following:
     // - issuer has identity
     // - issuer identity is validated
     // - receiver has identity
     // - receiver identity is confirmed or validated
+    // - receiver has membership
     //
     // /!\ do not check the following:
     // - receiver has membership
@@ -224,7 +226,13 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::CheckCertAllowed<Id
     // - issuer can issue cert even if he lost his membership
     //   (not renewed or passed below cert threshold and above again without claiming membership)
     // this is counterintuitive behavior but not a big problem
+    //
+    // TODO to fix this strange behavior, we will have to make the tests
+    // (CheckCertAllowed and CheckMembershipCallAllowed) run on the relevant instance
+    // i.e. Cert for Wot, SmithCert for SmithWot...
+    // → see issue #136
     fn check_cert_allowed(issuer: IdtyIndex, receiver: IdtyIndex) -> Result<(), DispatchError> {
+        // issuer checks
         // ensure issuer has validated identity
         if let Some(issuer_data) = pallet_identity::Pallet::<T>::identity(issuer) {
             ensure!(
@@ -234,15 +242,30 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::CheckCertAllowed<Id
         } else {
             return Err(Error::<T, I>::IdtyNotFound.into());
         }
+        // issue #136 this has to be done on the correct instance of membership pallet
+        // // ensure issuer has membership
+        // if pallet_membership::Pallet::<T, I>::membership(issuer).is_none() {
+        //     // improvement: give reason why issuer can not emit cert (not member)
+        //     return Err(Error::<T, I>::IssuerCanNotEmitCert.into());
+        // }
+
+        // receiver checks
         // ensure receiver has confirmed or validated identity
         if let Some(receiver_data) = pallet_identity::Pallet::<T>::identity(receiver) {
             match receiver_data.status {
                 IdtyStatus::ConfirmedByOwner | IdtyStatus::Validated => {} // able to receive cert
-                IdtyStatus::Created => return Err(Error::<T, I>::CertToUnconfirmedIdty.into()),
+                IdtyStatus::Created => return Err(Error::<T, I>::CertToUndefined.into()),
             };
         } else {
             return Err(Error::<T, I>::IdtyNotFound.into());
         }
+        // issue #136 this has to be done on the correct instance of membership pallet
+        // // ensure receiver has a membership or a pending membership
+        // if pallet_membership::Pallet::<T, I>::pending_membership(issuer).is_none()
+        //     && pallet_membership::Pallet::<T, I>::membership(issuer).is_none()
+        // {
+        //     return Err(Error::<T, I>::CertToUndefined.into());
+        // }
         Ok(())
     }
 }
@@ -299,31 +322,30 @@ impl<T: Config<I>, I: 'static> sp_membership::traits::CheckMembershipCallAllowed
 }
 
 // implement membership event handler
-impl<T: Config<I>, I: 'static, MetaData> sp_membership::traits::OnEvent<IdtyIndex, MetaData>
-    for Pallet<T, I>
+impl<T: Config<I>, I: 'static> sp_membership::traits::OnEvent<IdtyIndex> for Pallet<T, I>
 where
-    T: pallet_membership::Config<I, MetaData = MetaData>,
+    T: pallet_membership::Config<I>,
 {
-    fn on_event(membership_event: &sp_membership::Event<IdtyIndex, MetaData>) -> Weight {
+    fn on_event(membership_event: &sp_membership::Event<IdtyIndex>) -> Weight {
         match membership_event {
-            sp_membership::Event::<IdtyIndex, MetaData>::MembershipAcquired(idty_index, _) => {
+            sp_membership::Event::<IdtyIndex>::MembershipAcquired(idty_index) => {
                 if !T::IsSubWot::get() {
                     // when membership is acquired, validate identity
                     // (only used on first membership acquiry)
                     pallet_identity::Pallet::<T>::try_validate_identity(*idty_index);
                 }
             }
-            sp_membership::Event::<IdtyIndex, MetaData>::MembershipExpired(_) => {}
+            sp_membership::Event::<IdtyIndex>::MembershipExpired(_) => {}
             // Membership revocation cases:
             // - Triggered by main identity removal: the underlying identity will be removed by the
             // caller.
             // - Triggered by the membership pallet: it's only possible for a sub-wot, so we
             // should not remove the underlying identity
             // So, in any case, we must do nothing
-            sp_membership::Event::<IdtyIndex, MetaData>::MembershipRevoked(_) => {}
-            sp_membership::Event::<IdtyIndex, MetaData>::MembershipRenewed(_) => {}
-            sp_membership::Event::<IdtyIndex, MetaData>::MembershipRequested(_) => {}
-            sp_membership::Event::<IdtyIndex, MetaData>::PendingMembershipExpired(idty_index) => {
+            sp_membership::Event::<IdtyIndex>::MembershipRevoked(_) => {}
+            sp_membership::Event::<IdtyIndex>::MembershipRenewed(_) => {}
+            sp_membership::Event::<IdtyIndex>::MembershipRequested(_) => {}
+            sp_membership::Event::<IdtyIndex>::PendingMembershipExpired(idty_index) => {
                 Self::dispatch_idty_call(pallet_identity::Call::remove_identity {
                     idty_index: *idty_index,
                     idty_name: None,
diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs
index cd25f68a9..29454df22 100644
--- a/pallets/duniter-wot/src/mock.rs
+++ b/pallets/duniter-wot/src/mock.rs
@@ -151,7 +151,6 @@ impl pallet_membership::Config<Instance1> for Test {
     type IdtyIdOf = IdentityIndexOf<Self>;
     type AccountIdOf = ();
     type MembershipPeriod = MembershipPeriod;
-    type MetaData = ();
     type OnEvent = DuniterWot;
     type RuntimeEvent = RuntimeEvent;
     type WeightInfo = ();
@@ -209,7 +208,6 @@ impl pallet_membership::Config<Instance2> for Test {
     type IdtyIdOf = IdentityIndexOf<Self>;
     type AccountIdOf = ();
     type MembershipPeriod = SmithMembershipPeriod;
-    type MetaData = ();
     type OnEvent = SmithSubWot;
     type PendingMembershipPeriod = SmithPendingMembershipPeriod;
     type WeightInfo = ();
diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs
index 6cefede1f..24d0a2c14 100644
--- a/pallets/duniter-wot/src/tests.rs
+++ b/pallets/duniter-wot/src/tests.rs
@@ -79,10 +79,9 @@ fn test_join_smiths() {
         run_to_block(2);
 
         // Dave shoud be able to request smith membership
-        assert_ok!(SmithMembership::request_membership(
-            RuntimeOrigin::signed(4),
-            ()
-        ));
+        assert_ok!(SmithMembership::request_membership(RuntimeOrigin::signed(
+            4
+        ),));
         System::assert_has_event(RuntimeEvent::SmithMembership(
             pallet_membership::Event::MembershipRequested(4),
         ));
diff --git a/pallets/membership/src/benchmarking.rs b/pallets/membership/src/benchmarking.rs
index da0ce28b7..11c95849e 100644
--- a/pallets/membership/src/benchmarking.rs
+++ b/pallets/membership/src/benchmarking.rs
@@ -46,7 +46,7 @@ benchmarks_instance_pallet! {
         let caller: T::AccountId = T::AccountIdOf::convert(idty.clone()).unwrap();
         let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
         // Lazily prepare call as this extrinsic will always return an errror when in subwot
-        let call = Call::<T, I>::request_membership { metadata: T::MetaData ::default()};
+        let call = Call::<T, I>::request_membership { };
     }: {
         call.dispatch_bypass_filter(caller_origin).ok();
     }
@@ -58,7 +58,7 @@ benchmarks_instance_pallet! {
     claim_membership {
         let idty: T::IdtyId = 3.into();
         Membership::<T, I>::take(idty);
-        PendingMembership::<T, I>::insert(idty.clone(), T::MetaData::default());
+        PendingMembership::<T, I>::insert(idty.clone(), ());
         let caller: T::AccountId = T::AccountIdOf::convert(idty.clone()).unwrap();
         let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
         T::BenchmarkSetupHandler::force_status_ok(&idty, &caller);
diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs
index 1b7a2d07e..3855bf927 100644
--- a/pallets/membership/src/lib.rs
+++ b/pallets/membership/src/lib.rs
@@ -81,13 +81,11 @@ pub mod pallet {
         type IdtyIdOf: Convert<Self::AccountId, Option<Self::IdtyId>>;
         /// Something that gives the AccountId of an IdtyId
         type AccountIdOf: Convert<Self::IdtyId, Option<Self::AccountId>>;
-        /// Optional metadata
-        type MetaData: Default + Parameter + Validate<Self::AccountId>;
         #[pallet::constant]
         /// Maximum life span of a non-renewable membership (in number of blocks)
         type MembershipPeriod: Get<Self::BlockNumber>;
         /// On event handler
-        type OnEvent: OnEvent<Self::IdtyId, Self::MetaData>;
+        type OnEvent: OnEvent<Self::IdtyId>;
         #[pallet::constant]
         /// Maximum period (in number of blocks), where an identity can remain pending subscription.
         type PendingMembershipPeriod: Get<Self::BlockNumber>;
@@ -140,11 +138,11 @@ pub mod pallet {
     pub type MembershipsExpireOn<T: Config<I>, I: 'static = ()> =
         StorageMap<_, Twox64Concat, T::BlockNumber, Vec<T::IdtyId>, ValueQuery>;
 
-    /// maps identity id to pending membership metadata
+    /// identities with pending membership request
     #[pallet::storage]
     #[pallet::getter(fn pending_membership)]
     pub type PendingMembership<T: Config<I>, I: 'static = ()> =
-        StorageMap<_, Twox64Concat, T::IdtyId, T::MetaData, OptionQuery>;
+        StorageMap<_, Twox64Concat, T::IdtyId, (), OptionQuery>;
 
     /// maps block number to the list of memberships set to expire at this block
     #[pallet::storage]
@@ -181,8 +179,6 @@ pub mod pallet {
 
     #[pallet::error]
     pub enum Error<T, I = ()> {
-        /// Invalid meta data
-        InvalidMetaData,
         /// Identity id not found
         IdtyIdNotFound,
         /// Membership already acquired
@@ -218,19 +214,14 @@ pub mod pallet {
         /// (only available for sub wot, automatic for main wot)
         #[pallet::call_index(0)]
         #[pallet::weight(T::WeightInfo::request_membership())]
-        pub fn request_membership(
-            origin: OriginFor<T>,
-            metadata: T::MetaData,
-        ) -> DispatchResultWithPostInfo {
+        pub fn request_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
             let who = ensure_signed(origin)?;
 
             let idty_id = T::IdtyIdOf::convert(who.clone()).ok_or(Error::<T, I>::IdtyIdNotFound)?;
-            if !metadata.validate(&who) {
-                return Err(Error::<T, I>::InvalidMetaData.into());
-            }
+
             T::CheckMembershipCallAllowed::check_idty_allowed_to_request_membership(&idty_id)?;
 
-            Self::do_request_membership(idty_id, metadata)
+            Self::do_request_membership(idty_id)
         }
 
         /// claim membership  
@@ -288,11 +279,8 @@ pub mod pallet {
 
     impl<T: Config<I>, I: 'static> Pallet<T, I> {
         /// force request membership
-        pub fn force_request_membership(
-            idty_id: T::IdtyId,
-            metadata: T::MetaData,
-        ) -> DispatchResultWithPostInfo {
-            Self::do_request_membership(idty_id, metadata)
+        pub fn force_request_membership(idty_id: T::IdtyId) -> DispatchResultWithPostInfo {
+            Self::do_request_membership(idty_id)
         }
 
         /// force expire membership
@@ -336,10 +324,7 @@ pub mod pallet {
         }
 
         /// perform the membership request
-        fn do_request_membership(
-            idty_id: T::IdtyId,
-            metadata: T::MetaData,
-        ) -> DispatchResultWithPostInfo {
+        fn do_request_membership(idty_id: T::IdtyId) -> DispatchResultWithPostInfo {
             // checks
             if PendingMembership::<T, I>::contains_key(idty_id) {
                 return Err(Error::<T, I>::MembershipAlreadyRequested.into());
@@ -352,7 +337,7 @@ pub mod pallet {
             let expire_on = block_number + T::PendingMembershipPeriod::get();
 
             // apply membership request
-            PendingMembership::<T, I>::insert(idty_id, metadata);
+            PendingMembership::<T, I>::insert(idty_id, ());
             PendingMembershipsExpireOn::<T, I>::append(expire_on, idty_id);
             Self::deposit_event(Event::MembershipRequested(idty_id));
             T::OnEvent::on_event(&sp_membership::Event::MembershipRequested(idty_id));
@@ -371,10 +356,10 @@ pub mod pallet {
 
         /// perform membership claim
         fn do_claim_membership(idty_id: T::IdtyId) {
-            if let Some(metadata) = PendingMembership::<T, I>::take(idty_id) {
+            if PendingMembership::<T, I>::take(idty_id).is_some() {
                 Self::insert_membership_and_schedule_expiry(idty_id);
                 Self::deposit_event(Event::MembershipAcquired(idty_id));
-                T::OnEvent::on_event(&sp_membership::Event::MembershipAcquired(idty_id, metadata));
+                T::OnEvent::on_event(&sp_membership::Event::MembershipAcquired(idty_id));
             }
             // else { unreachable if check_allowed_to_claim called before }
         }
@@ -388,11 +373,11 @@ pub mod pallet {
             }
         }
 
-        /// perform mebership expiration
+        /// perform membership expiration
         // add pending membership and schedule expiry of pending membership
         fn do_expire_membership(idty_id: T::IdtyId, expire_on: T::BlockNumber) -> Weight {
             if Membership::<T, I>::take(idty_id).is_some() {
-                PendingMembership::<T, I>::insert(idty_id, T::MetaData::default());
+                PendingMembership::<T, I>::insert(idty_id, ());
                 PendingMembershipsExpireOn::<T, I>::append(expire_on, idty_id);
             } // else should not happen
 
diff --git a/pallets/membership/src/mock.rs b/pallets/membership/src/mock.rs
index d97f1f86a..74f44e85b 100644
--- a/pallets/membership/src/mock.rs
+++ b/pallets/membership/src/mock.rs
@@ -88,7 +88,6 @@ impl pallet_membership::Config for Test {
     type IdtyIdOf = ConvertInto;
     type AccountIdOf = ConvertInto;
     type MembershipPeriod = MembershipPeriod;
-    type MetaData = ();
     type OnEvent = ();
     type PendingMembershipPeriod = PendingMembershipPeriod;
     type RuntimeEvent = RuntimeEvent;
diff --git a/pallets/membership/src/tests.rs b/pallets/membership/src/tests.rs
index f11ab9680..a899b4424 100644
--- a/pallets/membership/src/tests.rs
+++ b/pallets/membership/src/tests.rs
@@ -148,7 +148,6 @@ fn test_membership_revocation() {
         run_to_block(5);
         assert_ok!(DefaultMembership::request_membership(
             RuntimeOrigin::signed(0),
-            ()
         ));
         System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRequested(0)));
     });
@@ -162,7 +161,6 @@ fn test_pending_membership_expiration() {
         run_to_block(1);
         assert_ok!(DefaultMembership::request_membership(
             RuntimeOrigin::signed(0),
-            ()
         ));
         System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRequested(0)));
 
@@ -191,7 +189,6 @@ fn test_membership_workflow() {
         run_to_block(1);
         assert_ok!(DefaultMembership::request_membership(
             RuntimeOrigin::signed(0),
-            ()
         ));
         System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRequested(0)));
 
diff --git a/primitives/membership/src/lib.rs b/primitives/membership/src/lib.rs
index 2d74c6230..f59fcde5d 100644
--- a/primitives/membership/src/lib.rs
+++ b/primitives/membership/src/lib.rs
@@ -27,9 +27,9 @@ use scale_info::TypeInfo;
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
 
-pub enum Event<IdtyId, MetaData = ()> {
+pub enum Event<IdtyId> {
     /// A membership has acquired
-    MembershipAcquired(IdtyId, MetaData),
+    MembershipAcquired(IdtyId),
     /// A membership has expired
     MembershipExpired(IdtyId),
     /// A membership has renewed
diff --git a/primitives/membership/src/traits.rs b/primitives/membership/src/traits.rs
index 0480a64f3..2b68516ca 100644
--- a/primitives/membership/src/traits.rs
+++ b/primitives/membership/src/traits.rs
@@ -38,12 +38,12 @@ pub trait IsInPendingMemberships<IdtyId> {
     fn is_in_pending_memberships(idty_id: IdtyId) -> bool;
 }
 
-pub trait OnEvent<IdtyId, MetaData> {
-    fn on_event(event: &crate::Event<IdtyId, MetaData>) -> Weight;
+pub trait OnEvent<IdtyId> {
+    fn on_event(event: &crate::Event<IdtyId>) -> Weight;
 }
 
-impl<IdtyId, MetaData> OnEvent<IdtyId, MetaData> for () {
-    fn on_event(_: &crate::Event<IdtyId, MetaData>) -> Weight {
+impl<IdtyId> OnEvent<IdtyId> for () {
+    fn on_event(_: &crate::Event<IdtyId>) -> Weight {
         Weight::zero()
     }
 }
diff --git a/resources/metadata.scale b/resources/metadata.scale
index 2680c3b02a4ca74e19ed66cc13eb88883db14aef..157b25e4f0edd9fcfa7acfd37ac5e71a25f7c187 100644
GIT binary patch
delta 4804
zcmX>;iDUT$4z}FXl0-h!jch+M7&#~Z&!}VkIJqu!7URdsl3A&Ytefkzidh&{Cl}_&
zPY%kLW7M2nm|w>jIr&3=HA^KE<I>3wb7Ll-E>L8tWnw%D=J*t<GB!^BkRLz!VxbmG
zD-+{guyAydF=OXs!}9pa?}{`TdnbR$PncX$tjMyIiE%Gj!OvnjmX$CQ%S&V!*MbfC
zT%yi$l8NymSWQ)_2FqC{#+_geQ<*Z$MVRi^G9|{VU=8f$nv8cR8<w}SJY-^A305gq
zA;b6*?1f1c@=T1(lRxIiZ5FKTQRR>cv9j_`tt{TWW}Xe>WRWGm7!@b;FLhy5oE);$
zhDnuadY&(%_++jH(u}g3)fT*AX4ITKaj6fZ?qvJrk(<qzu`@F2Znj^(o|RE|^7}Qx
z5{68S9<GI@nPvIOi6xo&c{Uz83VwDB43pojk(gYxR*A)wiP3WMueAn~Pps8r^qu^D
zZ3Ls~WW^1_leeuGW3yyp31+mNT)$3V!IOzmLL*E^Ay|`vQKC3CIX^E2Nr=G`tmol6
zD`s0Jp2;8Ah)y<MFU06LdBaU{M#sr<>lIX8nHUwKOD(Jw%uHe#7+jec7?>Rx7#KJh
zJee36SU4=1Y?&B*!KQ$04usn*7m8^!lF;OZ>qRG<Y_Q=C1sf9xGA5E~vZ1cz<Y^le
z8Dl5!*r35!xcT9R6h_9_$!?pX7!xNi+?36jIGJ~|5o79Phs`R|nM{lUVX4VB{soDJ
zrK!H9IVG8iDJeyE3=FB0>o==&W->7_Fy%5a<W63{*_f%2Y4U45waHvt_#{i27(I%M
zj7&`pEv>AAGt=`DOG=AU85l|@8*WkHNA+>#=7cTPEKIdblM4;yHvirs&&1fcS$elI
z3uEhM|NZ>}jGdeBpTEV()XOwE-&$hx<%>s{7$<I?aV4ISapLA5SC=y~&YisA`eeq1
zlTB`1W}G;g<7O@6#K~<pHNl~*WQD^mj0_^K$tlH&EDTeb7#&hUOk}4rF)&P>%yUal
zY9>5-Ok)`sW`d%}5ga{pC%fEI=UfO10zpQGrIYtRw%L60Rs|EwN+zCw$s0LDHb*~~
zWMW)9x%hq`<HpHM4^}a5oV@nIbjH1#T_5%_GH#rF<&mEBRwhORP!I>C7A0lo<k&GV
zYz3LPlZj#HWZB31#(SAqB>W3NDU^jJgz+E~BZq)reqJgA1A{<ter_rY!$Bqn21W)j
z#lpdGl!@W!<od_<j3*K9Ig9Kbri)C|tE(BMH&1*i&c=9k^UC+@nHX<Q-uFvwbNwef
z4#u0C_x;+$#dvdiAuFRg(_N<N^P3r!rte{8T*>s1X>xp^==2&kMrX#S+YhiYYKSmC
zoqm9g(P_K3G~*{m#;4QeWEs_&UNTMA3l*OpC(9_w{+5ZcCM-31a^Y6l?XzVW->{;H
ztX5_WU}F3@{f{c+H57?&YK-;FoNqxPag>qa<McW$Mh(WV)0b;8S}^@&n*LCW(U<8j
z({uxEMsKFCOp^tZB&JW*W^@9}MKKCbf1}NqBl?$#L#DVOJ~_WARUj-sGX)e(jN5Z{
z7+u*JS*JHzG8#^Ik6;wy<YZ=GVA{#Zz`OmwG2<~tMnRCA*z|x<Miq5YW|m+E1{MZM
zW=086G=<nPFo-gP^5sKD2?j}!PElqCS&$AxXBLQBMI^O~AQdbOs?5l-sL6~Gi<->f
zSk#>EXwE1zeY+W>IHT_L^Ja|djHc7Sn=$IhSu!(*1cOVL#G=ZuAh&>`RF};3)Z!8b
zW(EdbkhTh8LuN*Uu%a}i{AM`)j5(td4=BTh6{Q6f<>#kOw=-vypKfWv7|Lh~(k3%~
zwFP4$7QMF9<t-T#86ClXQ+I_1geQ{cT)_b$A;91X@}(;?gD>;s1GOUC-&!)hWn>K8
ze%qSy4l`ru^adwJiRl;Y86yQFnHe=)D@uwI-EtDsiy0Uq!E%ZyVk``?%!~r1hDH_)
z46)1%46N)7i6A#XVnj3*>X2L{hopjynSRTG(T6XSnNcPvHL*B9ub6=$6C&Z`$f(4b
z3vy&CGeaTBFhkK&s5zBL=9EH|Uv*@R;j9EHFJ)$^g(z=?DsM$n-Uw12$khr`*~rY$
z$vl}+OltdAC&rIVjJ?yJyE1BVBfK(kx{Mp6Ez?xy$$Hb|w^z9_nlUlXoW9Y6F@<q1
z*b0$_P%D-qSuuTs2c!7(B2UJ2uB9Mr7BVxeWS)FsuGI9uo{W}EYe8(q?U7!fe6(@;
zDj&u|wXNV-*<r%46Qp4;Gs9l60D~+O!$C0XD6<K}(dmx9jM|JRx99pYCNnagoqo}e
zaT?>r=^6fvDU4UApY>-nWxNV<79`ZC+XgTSG2YxB5Wwio%y<_eA$<dq<{u)t`60;O
z8w^jUe+*{S6MD(az`>Bo$iTqxl9`bugn{AZbi)wFGRC*lSA;OyGk%=@B7`xJ@h#j)
zGscf_wu<^&XqbFOGXE>c{I|>uKf%5cU|{&mJlRlJdOC9$<2fcqmdS}*M5aFvV+><t
zoo*M-c#eyc1(aVE1Q>Xye=LM%dL}`Z$uheowiib*sxvW)LL|;aF$yzEPJa-^xPei2
z`^0F*%S?=lU?~k%Xx`RjL6ifkU~@ScB^WeW7#NsTSr~L#CO<f@H2pyw<55P#?aSgB
z`Itp*oKuTRLh?iNQc}}0^HNh7xL9Nvr=Lq=6f?|BNzE(CEU8o|&n(HvFD+5XP0dY8
zEh^5)EKtZVQYc8xOUcYjM-rX>BZ+Y?qv>>;R7Q#E>yjDuxh+{3Bp3x!5=#;-r@u&M
zbYrxguAjn~DCo+<sNtDXQW;#5SW;Tdz+gFjVG3iWku3{yIpoNKQ4Tq>fXg8e#lpcL
zz~Bn9D_F!6<UwB+24Aq31zcGe0$C>KTT5-<o65L~g)ww`Mi!$tQzXme1COPqFDPf^
z6Oyn1S?`(RmtW$RUz(S~z%c!3IinFH<8+A%MoUJf>5bWpMvTnUTPqk18Cj-puV8dx
zWS#z}g0YV!mIYFOPQRGLSj3VD<!z77Wjx2ol*%%>;jzSY=X^#p#?0we`HT&Wt<!(!
zGrBQmf@4ZP7aCKINHLWQadsgKL*evE1&r!^JDC`G7-fnRa|?1(i%X{;FJO#jECuV=
zuY~HaMbcjh*1wZUfT0!?5S1(pjUYOgg`pLsNo@MeLPlN9P8LQTr;wu5)Zo+-8&E~j
z$-<%l69CoTpo)Z-iGg8yV<Dr&^Z+MD>FG*Ej6#ePx0@C*#xpWbrB#w)nn|6QnTrw^
zTnoX$!ob3?bbD1bBO?>z%IP(=j4K$|PFJmC)L>i-3LC@e^>vKGj2pL4u47!o!nG9?
zZaY~RcCt)9AD}ortC`W5aqskb&5W104}wiQ$|AvV6s%G7Bs3<@BE`hX=?7aFCkmeh
z8FP|_;UZYiRTc?`s}Mamp?dBj>A5+5bt_{X*IkgFn=A|uStiE^if{L7W2|Rlddf0c
zFH~~+-c&};>2Et2XEMH=-rmXB$@mszoHNr$mdSFD<))W(F$yq#o!;8TsLJ${W%`;f
zMop%_EYtIJ7$vs9?_!+5$jCUop@%VAYa){fxO!t`Wo2MtFkxU|U}a@sU{T;?<z*FQ
z6=juVRb<s=HDqProF352C^cQam(hTc7sPgD6a-OH(>L@os!#XtWt5)&sh3e{`cx20
zzmHLkTb7lP$F(StfkBpa@<k?*?InGTa|9R_rwh(!G+|Q(*{3<(dp@HAqw4gW`Ha$x
znh^6A&1ckM)P=BL&Sz9$He}_Pd{16_yZQpg#r%xC(@(Ew^kNj;F1CSjH8Z2&_CuQ)
zXEQR&PLJEl7|E!*eaBWtDHcZC>6dphRx#R6_uIudhtYBS>s^ciER4R}ZT2&AGO+|Q
zPh^}Pc#tu1`h*3H9Md-)WIV<k%Dez1q`rO8Ax2|n#>DB@jxzQz7EVt-#%Re{JALjk
zMqS3*?Wd11u4QKIoL+f~(T=fq`i@hKDvT4i-#x|nlbLbq^y}vs<r!yA|9+0qfN}12
zjq{9a7#UYizj}eOm~riN&x?%dj2owKyvSI>xOKbQCB`~N#+}=jUS^yOa=qnMMoY$n
z(~GY%da0-~G45etJju)_<6e}Qmr{_(2xCZyfXc^IMuxM~AA&So+%A2MF_)QDlZo*F
z$8^U1jAGjt-(+lNWW2du{WfD66XV_K^X@VRF+QFC@h;;urk~8yJMS?zF-A`3xzBio
z=`Zv2NB0?nr7D>i-!L#TvWS?37Ni#?rlk58Wu|B5F)-Fn_kX~s$;djr@&ThIKPL+>
z3#hbhWMcfmz$iHV-~&c8M$zd%9x&E1N^UQI$f(T7*gAdoBgTA2+3nnq850;86{qJs
zVU%I)oZk6_F^6&I_HR!Z7c#QwvdHmFkNd=^HT~!_Mh8a2>1@v#)fi2;>po}9V`TK4
zKIa8v5M%H5_b(W^7#Rbn|9Z)&#TYvM-zUaTqCG4OENc{?^-yMNF}Nn0Zt$8>WU}rP
z_US&a84DPfPT%{Q@d0Dx^m%U>)fr=_ulmZkjB(}m+_#LmjEsrXZ@**IU`(Cf^`6ms
zy8U}bb|{-8AU{9Hh>-y-vxV{CcKZ*ELX3>P(}O-TRx+-ge&!=%IOERgDxVnj7$;5-
z{=`_yICc8TPmCrcI1Mz~pgR4)YsR~bGpAR4Wi*7j?(Fu!Zy?vroxcA&qYmdn7DGl*
zNMGFk<vXJ_6XVk92EQ0pnHRFGoF4IuQJeAV_Lg6a;Y^Ghr{DR*Sgx=Y6fFTksfDGf
z#U;f-sfj6-jwP-I`N<iKj4Z4YTc;QQWlUwfJN@Qg#uCQ8)BXN2$}%3Dp7W1!597t@
zn*SLsc^)z`dNN8l1Qg|0R8IG2ViMcl@}Dt{h3_GYy-a9HW=>{FB?IHj=|7m6d>Efj
zcV%HpV|+P%4GU8n<J;-RtV}K<jLeLoj4}oRMfnB!#fdpi`FSZHw|BELX)`i@oxYuo
zX)@!->E7&2VN5?+wl8L9VqvWR%i_q$%A(-moRONG?U$bym0FY^oSK)CTExlP;{X;7
zE=WxdOD!tS%+KRx6|ew{g;YRA1X%?Pz#{Icd8x&j#iFbV5EZ`pMJ0(K7fZ4#Xn<At
z<>w`*Dzf(IfO+AmnduoNin^>l3C<abMd_&_MTvREiOHZYYd~UUZfahMA#0B+D*zgY
B(eeNQ

delta 5005
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
SrR;#j%G}hv5=+(|T~+`eM+zJO

diff --git a/runtime/common/src/entities.rs b/runtime/common/src/entities.rs
index 823ae2a71..7ee895b7f 100644
--- a/runtime/common/src/entities.rs
+++ b/runtime/common/src/entities.rs
@@ -14,7 +14,6 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
 
-use super::AccountId;
 use frame_support::pallet_prelude::*;
 use scale_info::TypeInfo;
 #[cfg(feature = "std")]
@@ -34,40 +33,6 @@ macro_rules! declare_session_keys {
                     pub authority_discovery: AuthorityDiscovery,
                 }
             }
-
-            #[derive(Clone, codec::Decode, Debug, codec::Encode, Eq, PartialEq)]
-            pub struct SessionKeysWrapper(pub SessionKeys);
-
-            impl From<SessionKeysWrapper> for SessionKeys {
-                fn from(keys_wrapper: SessionKeysWrapper) -> SessionKeys {
-                    keys_wrapper.0
-                }
-            }
-             impl From<SessionKeys> for SessionKeysWrapper {
-                fn from(session_keys: SessionKeys) -> SessionKeysWrapper {
-                    SessionKeysWrapper(session_keys)
-                }
-            }
-
-            impl scale_info::TypeInfo for SessionKeysWrapper {
-                type Identity = [u8; 128];
-
-                fn type_info() -> scale_info::Type {
-                    Self::Identity::type_info()
-                }
-            }
-
-            // Dummy implementation only for benchmarking
-            impl Default for SessionKeysWrapper {
-                fn default() -> Self {
-                    SessionKeysWrapper(SessionKeys{
-                        grandpa: sp_core::ed25519::Public([0u8; 32]).into(),
-                        babe: sp_core::sr25519::Public([0u8; 32]).into(),
-                        im_online: sp_core::sr25519::Public([0u8; 32]).into(),
-                        authority_discovery: sp_core::sr25519::Public([0u8; 32]).into(),
-                    })
-                }
-            }
         }
     }
 }
@@ -94,42 +59,6 @@ impl From<IdtyData> for pallet_universal_dividend::FirstEligibleUd {
     }
 }
 
-#[cfg_attr(feature = "std", derive(Deserialize, Serialize))]
-#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
-pub struct SmithMembershipMetaData<SessionKeysWrapper> {
-    pub owner_key: AccountId,
-    pub p2p_endpoint: sp_runtime::RuntimeString,
-    pub session_keys: SessionKeysWrapper,
-}
-
-impl<SessionKeysWrapper: Default> Default for SmithMembershipMetaData<SessionKeysWrapper> {
-    #[cfg(not(feature = "runtime-benchmarks"))]
-    fn default() -> Self {
-        unreachable!()
-    }
-    #[cfg(feature = "runtime-benchmarks")]
-    // dummy implementation for benchmarking
-    fn default() -> Self {
-        SmithMembershipMetaData {
-            owner_key: AccountId::from([
-                // Dave (FIXME avoid stupid metadata)
-                48, 103, 33, 33, 29, 84, 4, 189, 157, 168, 142, 2, 4, 54, 10, 26, 154, 184, 184,
-                124, 102, 193, 188, 47, 205, 211, 127, 60, 34, 34, 204, 32,
-            ]),
-            p2p_endpoint: sp_runtime::RuntimeString::default(),
-            session_keys: SessionKeysWrapper::default(),
-        }
-    }
-}
-
-impl<SessionKeysWrapper> sp_membership::traits::Validate<AccountId>
-    for SmithMembershipMetaData<SessionKeysWrapper>
-{
-    fn validate(&self, who: &AccountId) -> bool {
-        &self.owner_key == who
-    }
-}
-
 #[cfg_attr(feature = "std", derive(Deserialize, Serialize))]
 #[derive(
     Encode, Decode, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo,
diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs
index e05a97d4f..540054ab3 100644
--- a/runtime/common/src/handlers.rs
+++ b/runtime/common/src/handlers.rs
@@ -71,14 +71,14 @@ where
 // membership event runtime handler
 pub struct OnMembershipEventHandler<Inner, Runtime>(core::marker::PhantomData<(Inner, Runtime)>);
 impl<
-        Inner: sp_membership::traits::OnEvent<IdtyIndex, ()>,
+        Inner: sp_membership::traits::OnEvent<IdtyIndex>,
         Runtime: frame_system::Config<AccountId = AccountId>
             + pallet_identity::Config<IdtyData = IdtyData, IdtyIndex = IdtyIndex>
-            + pallet_membership::Config<Instance1, MetaData = ()>
+            + pallet_membership::Config<Instance1>
             + pallet_universal_dividend::Config,
-    > sp_membership::traits::OnEvent<IdtyIndex, ()> for OnMembershipEventHandler<Inner, Runtime>
+    > sp_membership::traits::OnEvent<IdtyIndex> for OnMembershipEventHandler<Inner, Runtime>
 {
-    fn on_event(membership_event: &sp_membership::Event<IdtyIndex, ()>) -> Weight {
+    fn on_event(membership_event: &sp_membership::Event<IdtyIndex>) -> Weight {
         (match membership_event {
             // when membership is removed, call on_removed_member handler which auto claims UD
             sp_membership::Event::MembershipRevoked(idty_index)
@@ -97,7 +97,7 @@ impl<
                 }
             }
             // when main membership is acquired, it starts getting right to UD
-            sp_membership::Event::MembershipAcquired(idty_index, _) => {
+            sp_membership::Event::MembershipAcquired(idty_index) => {
                 pallet_identity::Identities::<Runtime>::mutate_exists(idty_index, |idty_val_opt| {
                     if let Some(ref mut idty_val) = idty_val_opt {
                         idty_val.data = IdtyData {
@@ -123,43 +123,18 @@ pub struct OnSmithMembershipEventHandler<Inner, Runtime>(
 );
 impl<
         IdtyIndex: Copy + Parameter,
-        SessionKeysWrapper: Clone,
-        Inner: sp_membership::traits::OnEvent<IdtyIndex, SmithMembershipMetaData<SessionKeysWrapper>>,
+        Inner: sp_membership::traits::OnEvent<IdtyIndex>,
         Runtime: frame_system::Config<AccountId = AccountId>
             + pallet_identity::Config<IdtyIndex = IdtyIndex>
-            + pallet_authority_members::Config<KeysWrapper = SessionKeysWrapper, MemberId = IdtyIndex>
-            + pallet_membership::Config<
-                Instance2,
-                MetaData = SmithMembershipMetaData<SessionKeysWrapper>,
-            >,
-    > sp_membership::traits::OnEvent<IdtyIndex, SmithMembershipMetaData<SessionKeysWrapper>>
-    for OnSmithMembershipEventHandler<Inner, Runtime>
+            + pallet_authority_members::Config<MemberId = IdtyIndex>
+            + pallet_membership::Config<Instance2>,
+    > sp_membership::traits::OnEvent<IdtyIndex> for OnSmithMembershipEventHandler<Inner, Runtime>
 {
-    fn on_event(
-        membership_event: &sp_membership::Event<
-            IdtyIndex,
-            SmithMembershipMetaData<SessionKeysWrapper>,
-        >,
-    ) -> Weight {
+    fn on_event(membership_event: &sp_membership::Event<IdtyIndex>) -> Weight {
         (match membership_event {
-            sp_membership::Event::MembershipAcquired(
-                _idty_index,
-                SmithMembershipMetaData {
-                    owner_key,
-                    session_keys,
-                    ..
-                },
-            ) => {
-                let call = pallet_authority_members::Call::<Runtime>::set_session_keys {
-                    keys: session_keys.clone(),
-                };
-                if let Err(e) = call.dispatch_bypass_filter(
-                    frame_system::Origin::<Runtime>::Signed(owner_key.clone()).into(),
-                ) {
-                    sp_std::if_std! {
-                        println!("fail to set session keys: {:?}", e)
-                    }
-                }
+            sp_membership::Event::MembershipAcquired(_idty_index) => {
+                // nothing when smith membership acquired
+                // user will have to claim authority membership
                 Weight::zero()
             }
             sp_membership::Event::MembershipRevoked(idty_index) => {
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index 6b4954128..80efaf60f 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -236,7 +236,6 @@ macro_rules! pallets_config {
         }
         impl pallet_authority_members::Config for Runtime {
             type RuntimeEvent = RuntimeEvent;
-            type KeysWrapper = opaque::SessionKeysWrapper;
             type IsMember = SmithMembership;
             type OnNewSession = OnNewSessionHandler<Runtime>;
             type OnRemovedMember = OnRemovedAuthorityMemberHandler<Runtime>;
@@ -488,7 +487,6 @@ macro_rules! pallets_config {
             type IdtyIdOf = common_runtime::providers::IdentityIndexOf<Self>;
             type AccountIdOf = common_runtime::providers::IdentityAccountIdProvider<Self>;
             type MembershipPeriod = MembershipPeriod;
-            type MetaData = ();
             type OnEvent = OnMembershipEventHandler<Wot, Runtime>;
             type PendingMembershipPeriod = PendingMembershipPeriod;
             type RuntimeEvent = RuntimeEvent;
@@ -538,7 +536,6 @@ macro_rules! pallets_config {
             type IdtyIdOf = common_runtime::providers::IdentityIndexOf<Self>;
             type AccountIdOf = common_runtime::providers::IdentityAccountIdProvider<Self>;
             type MembershipPeriod = SmithMembershipPeriod;
-            type MetaData = SmithMembershipMetaData<opaque::SessionKeysWrapper>;
             type OnEvent = OnSmithMembershipEventHandler<SmithSubWot, Runtime>;
             type PendingMembershipPeriod = SmithPendingMembershipPeriod;
             type RuntimeEvent = RuntimeEvent;
diff --git a/runtime/gdev/tests/fixme_tests.rs b/runtime/gdev/tests/fixme_tests.rs
new file mode 100644
index 000000000..28b9777e8
--- /dev/null
+++ b/runtime/gdev/tests/fixme_tests.rs
@@ -0,0 +1,75 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S 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.
+//
+// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+// these integration tests show current behavior that is counter-intuitive or outside specs
+// they should failed after the related issue is fixed
+
+mod common;
+
+use common::*;
+use frame_support::assert_ok;
+use gdev_runtime::*;
+use sp_keyring::AccountKeyring;
+
+/// issue #136
+/// a smith should not be able to add a smith cert to an
+/// identity who has no smith membership or pending membership
+#[test]
+fn can_add_smith_cert_without_pending_membership_or_membership() {
+    // 3 smith (1. Alice, 2. Bob, 3. Charlie)
+    // 4 identities (4. Dave)
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        run_to_block(1);
+
+        // FIXME Bob can add new smith cert to to dave even he did not requested smith membership
+        assert_ok!(SmithCert::add_cert(
+            frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+            2, // bob
+            4  // dave
+        ));
+    });
+}
+
+/// issue #136
+/// an identity should not be able to add cert
+/// when its membership is suspended
+#[test]
+fn can_still_issue_cert_when_membership_lost() {
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        run_to_block(1);
+
+        // expire Bob membership (could happen for negative distance evaluation for example)
+        assert_ok!(Membership::force_expire_membership(
+            2, // Bob
+        ));
+        System::assert_has_event(RuntimeEvent::Membership(
+            pallet_membership::Event::MembershipExpired(2),
+        ));
+
+        // FIXME this should not be possible
+        assert_ok!(Cert::add_cert(
+            frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+            2, // Bob
+            3, // Charlie
+        ));
+        System::assert_has_event(RuntimeEvent::Cert(
+            pallet_certification::Event::RenewedCert {
+                issuer: 2,
+                receiver: 3,
+            },
+        ));
+    });
+}
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index b05256314..982a7ccc9 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -676,14 +676,6 @@ fn test_smith_certification() {
             2  // bob
         ));
 
-        // THIS IS STRANGE BEHAVIOR
-        // bob can add new smith cert to to dave even he did not requested smith membership
-        assert_ok!(SmithCert::add_cert(
-            frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
-            2, // bob
-            4  // dave
-        ));
-
         // charlie can not add new cert to eve (no identity)
         assert_noop!(
             SmithCert::add_cert(
@@ -697,6 +689,64 @@ fn test_smith_certification() {
     });
 }
 
+/// test the full process to join smith from main wot member to authority member
+#[test]
+fn test_smith_process() {
+    ExtBuilder::new(1, 3, 4).with_initial_balances(vec![(AccountKeyring::Dave.to_account_id(), 1_000)])
+    .build().execute_with(|| {
+        run_to_block(1);
+
+        let alice = AccountKeyring::Alice.to_account_id();
+        let bob = AccountKeyring::Bob.to_account_id();
+        let charlie = AccountKeyring::Charlie.to_account_id();
+
+        // Eve can not request smith membership because not member of the smith wot
+        assert_noop!(SmithMembership::request_membership(
+            frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into()),
+            pallet_membership::Error::<gdev_runtime::Runtime, pallet_membership::Instance2>::IdtyIdNotFound,
+        );
+
+        // Dave can request smith membership (currently optional)
+        assert_ok!(SmithMembership::request_membership(
+            frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
+        ));
+
+        assert_eq!(SmithMembership::pending_membership(5), None); // none for Eve
+        assert_eq!(SmithMembership::pending_membership(4), Some(())); // Dave
+
+        // then Alice Bob and Charlie can certify Dave
+        assert_ok!(SmithCert::add_cert(frame_system::RawOrigin::Signed(alice).into(), 1, 4));
+        assert_ok!(SmithCert::add_cert(frame_system::RawOrigin::Signed(bob).into(), 2, 4));
+        assert_ok!(SmithCert::add_cert(frame_system::RawOrigin::Signed(charlie).into(), 3, 4));
+
+        // with these three smith certs, Dave can claim membership
+        assert_ok!(SmithMembership::claim_membership(
+            frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
+        ));
+
+        // Dave is then member of the smith wot
+        assert_eq!(SmithMembership::membership(4), Some(sp_membership::MembershipData {
+            expire_on: 1001, // 1 + 1000
+        }));
+
+        // Dave can set his (dummy) session keys
+        assert_ok!(AuthorityMembers::set_session_keys(
+            frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
+            gdev_runtime::opaque::SessionKeys{
+                grandpa: sp_core::ed25519::Public([0u8; 32]).into(),
+                babe: sp_core::sr25519::Public([0u8; 32]).into(),
+                im_online: sp_core::sr25519::Public([0u8; 32]).into(),
+                authority_discovery: sp_core::sr25519::Public([0u8; 32]).into(),
+            }
+        ));
+
+        // Dave can go online
+        assert_ok!(AuthorityMembers::go_online(
+            frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
+        ));
+    })
+}
+
 /// test create new account with balance lower than existential deposit
 // the treasury gets the dust
 #[test]
-- 
GitLab