diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 567ebc9dfef3b06123aeb25e79dd2c52b8d286da..1a755d6fef7a8943d789976050e013489e6e1636 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -36,7 +36,7 @@ use crate::traits::*; use codec::Codec; use frame_support::dispatch::Weight; use frame_system::RawOrigin; -use sp_runtime::traits::{AtLeast32BitUnsigned, One, Saturating, Zero}; +use sp_runtime::traits::{AtLeast32BitUnsigned, IdentifyAccount, One, Saturating, Zero}; use sp_std::fmt::Debug; use sp_std::prelude::*; @@ -45,6 +45,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_support::traits::StorageVersion; + use frame_system::offchain::SignedPayload; use frame_system::pallet_prelude::*; use sp_membership::traits::MembershipAction as _; @@ -59,7 +60,7 @@ pub mod pallet { // CONFIG // #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + frame_system::offchain::SigningTypes { #[pallet::constant] /// Period during which the owner can confirm the new identity. type ConfirmPeriod: Get<Self::BlockNumber>; @@ -251,7 +252,11 @@ pub mod pallet { // 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> { + impl<T: Config> Pallet<T> + where + <T as frame_system::offchain::SigningTypes>::Public: + IdentifyAccount<AccountId = T::AccountId>, + { #[pallet::weight(0)] pub fn create_identity( origin: OriginFor<T>, @@ -549,6 +554,65 @@ pub mod pallet { } } #[pallet::weight(0)] + pub fn revoke_identity( + origin: OriginFor<T>, + payload: RevocationPayload< + <T as frame_system::offchain::SigningTypes>::Public, + T::IdtyIndex, + >, + payload_sig: <T as frame_system::offchain::SigningTypes>::Signature, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + if !payload.verify(payload_sig) { + return Err(Error::<T>::BadProof.into()); + } + let idty_index = payload.idty; + + if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) { + if idty_value.owner_key != payload.owner_key.into_account() { + return Err(Error::<T>::RequireToBeOwner.into()); + } + if idty_value.status != IdtyStatus::Validated { + return Err(Error::<T>::IdtyNotValidated.into()); + } + + for (right, subkey_opt) in idty_value.rights.drain(..) { + let name = idty_value.name.clone(); + + let old_key_opt = if let Some(ref subkey) = subkey_opt { + Some(subkey.clone()) + } else if right.allow_owner_key() { + Some(idty_value.owner_key.clone()) + } else { + None + }; + + 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, + ); + } + } + + 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); + + Ok(().into()) + } else { + Err(Error::<T>::IdtyNotFound.into()) + } + } + #[pallet::weight(0)] pub fn set_right_subkey( origin: OriginFor<T>, idty_index: T::IdtyIndex, @@ -608,6 +672,7 @@ pub mod pallet { #[pallet::error] pub enum Error<T> { + BadProof, /// Creator not exist CreatorNotExist, /// Creator not allowed to create identities diff --git a/pallets/identity/src/tests.rs b/pallets/identity/src/tests.rs index 264b324c27088c4e643ef6aea9ceb9fe1aaaa374..eb4f386e114cc7aafbaf7beec4292bcf83f6d408 100644 --- a/pallets/identity/src/tests.rs +++ b/pallets/identity/src/tests.rs @@ -247,3 +247,58 @@ fn test_two_identities() { assert_eq!(idty2.removable_on, 7); }); } + +#[test] +fn test_revoke_identity() { + let identities = vec![crate::IdtyValue { + name: IdtyName(vec![0]), + expire_on: 5, + owner_key: 1, + removable_on: 0, + renewable_on: 3, + rights: vec![(Right::Right1, None), (Right::Right2, Some(10))], + status: crate::IdtyStatus::Validated, + data: (), + }]; + + new_test_ext(IdentityConfig { identities }).execute_with(|| { + // Should have one identity + assert_eq!(Identity::identities_count(), 1); + + // We need to initialize at least one block before any call + run_to_block(1); + + // Delete right Right1 for IdtyName(vec![1]) + // Should succes and trigger the correct event + assert_ok!(Identity::revoke_identity(Origin::root(), 1)); + let events = System::events(); + assert_eq!(events.len(), 2); + assert_eq!( + events[0], + EventRecord { + phase: Phase::Initialization, + event: Event::Identity(crate::Event::IdtyLostRight( + IdtyName(vec![0]), + Right::Right1 + )), + topics: vec![], + } + ); + assert_eq!( + events[1], + EventRecord { + phase: Phase::Initialization, + event: Event::Identity(crate::Event::IdtyLostRight( + IdtyName(vec![0]), + Right::Right2 + )), + topics: vec![], + } + ); + + // The identity has no more rights, the inactivity period must start to run + let idty = Identity::identity(1).expect("idty not found"); + assert!(idty.rights.is_empty()); + assert_eq!(idty.removable_on, 5); + }); +} diff --git a/pallets/identity/src/types.rs b/pallets/identity/src/types.rs index b4231157bae636c39221a8d7f7bd10c305c83912..d7d59a687ea3b5db6f7fc9555e05ab4308c6bae9 100644 --- a/pallets/identity/src/types.rs +++ b/pallets/identity/src/types.rs @@ -113,3 +113,21 @@ where } } } + +#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)] +pub struct RevocationPayload< + AccountId: Decode + Encode + TypeInfo, + IdtyIndex: Decode + Encode + TypeInfo, +> { + pub owner_key: AccountId, + pub idty: IdtyIndex, +} + +impl<T: frame_system::offchain::SigningTypes, IdtyIndex: Decode + Encode + TypeInfo> + frame_system::offchain::SignedPayload<T> + for RevocationPayload<<T as frame_system::offchain::SigningTypes>::Public, IdtyIndex> +{ + fn public(&self) -> <T as frame_system::offchain::SigningTypes>::Public { + self.owner_key.clone() + } +} diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 8fb5add3976024f04e99472b2ee4a8cb777bb506..3bd61aacf2832d03c06dd23a6df690be485dac7a 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -202,6 +202,10 @@ macro_rules! pallets_config { type MaxNoRightPeriod = MaxNoRightPeriod; type Membership = Membership; } + impl SigningTypes for Runtime { + type Public = <Signature as Verify>::Signer; + type Signature = Signature; + } impl pallet_membership::Config<frame_support::instances::Instance1> for Runtime { type IsIdtyAllowedToClaimMembership = ();