Skip to content
Snippets Groups Projects
Commit 7ea31e65 authored by Pascal Engélibert's avatar Pascal Engélibert :bicyclist:
Browse files

feat(identity): explicit revocation

parent 3cb4c309
No related branches found
No related tags found
1 merge request!12feat(identity): explicit revocation
......@@ -20,7 +20,7 @@ use frame_support::{parameter_types, traits::Everything};
use frame_system as system;
use sp_core::H256;
use sp_runtime::{
testing::Header,
testing::{Header, TestSignature, UintAuthorityId},
traits::{BlakeTwo256, IdentityLookup},
};
use std::collections::BTreeMap;
......@@ -118,6 +118,8 @@ impl pallet_identity::Config for Test {
type OnIdtyChange = DuniterWot;
type MaxDisabledPeriod = MaxDisabledPeriod;
type RemoveIdentityConsumers = ();
type RevocationSigner = UintAuthorityId;
type RevocationSignature = TestSignature;
}
// Membership
......
......@@ -35,7 +35,7 @@ pub use types::*;
use crate::traits::*;
use codec::Codec;
use frame_support::dispatch::Weight;
use sp_runtime::traits::{AtLeast32BitUnsigned, One, Saturating, Zero};
use sp_runtime::traits::{AtLeast32BitUnsigned, IdentifyAccount, One, Saturating, Verify, Zero};
use sp_std::fmt::Debug;
use sp_std::prelude::*;
......@@ -93,6 +93,10 @@ pub mod pallet {
/// Handle the logic that remove all identity consumers.
/// "identity consumers" mean all things that rely on the existence of the identity.
type RemoveIdentityConsumers: RemoveIdentityConsumers<Self::IdtyIndex>;
/// Signing key of revocation payload
type RevocationSigner: IdentifyAccount<AccountId = Self::AccountId>;
/// Signature of revocation payload
type RevocationSignature: Parameter + Verify<Signer = Self::RevocationSigner>;
}
// GENESIS STUFF //
......@@ -362,6 +366,30 @@ pub mod pallet {
Ok(().into())
}
#[pallet::weight(0)]
pub fn revoke_identity(
origin: OriginFor<T>,
payload: RevocationPayload<T::AccountId, T::Hash>,
payload_sig: T::RevocationSignature,
) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
ensure!(
payload.genesis_hash
== frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero()),
Error::<T>::InvalidGenesisHash
);
ensure!(
payload.using_encoded(|bytes| payload_sig.verify(bytes, &payload.owner_key)),
Error::<T>::InvalidRevocationProof
);
if let Some(idty_index) = <IdentityIndexOf<T>>::take(payload.owner_key) {
Self::do_remove_identity(idty_index);
Ok(().into())
} else {
Err(Error::<T>::IdtyNotFound.into())
}
}
#[pallet::weight(0)]
pub fn remove_identity(
origin: OriginFor<T>,
......@@ -418,6 +446,10 @@ pub mod pallet {
#[pallet::error]
pub enum Error<T> {
/// Genesis hash does not match
InvalidGenesisHash,
/// Revocation payload signature is invalid
InvalidRevocationProof,
/// Creator not allowed to create identities
CreatorNotAllowedToCreateIdty,
/// Identity already confirmed
......
......@@ -23,7 +23,7 @@ use frame_support::{
use frame_system as system;
use sp_core::H256;
use sp_runtime::{
testing::Header,
testing::{Header, TestSignature, UintAuthorityId},
traits::{BlakeTwo256, IdentityLookup, IsMember},
};
......@@ -110,6 +110,8 @@ impl pallet_identity::Config for Test {
type OnIdtyChange = ();
type MaxDisabledPeriod = MaxDisabledPeriod;
type RemoveIdentityConsumers = ();
type RevocationSigner = UintAuthorityId;
type RevocationSignature = TestSignature;
}
// Build genesis storage according to the mock runtime.
......
......@@ -15,10 +15,12 @@
// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
use crate::mock::*;
use crate::{Error, GenesisIdty, IdtyName, IdtyValue};
use crate::{Error, GenesisIdty, IdtyName, IdtyValue, RevocationPayload};
use codec::Encode;
//use frame_support::assert_err;
use frame_support::assert_ok;
use frame_system::{EventRecord, Phase};
use sp_runtime::testing::TestSignature;
type IdtyVal = IdtyValue<u64, u64>;
......@@ -123,3 +125,59 @@ fn test_idty_creation_period() {
);
});
}
#[test]
fn test_idty_revocation() {
new_test_ext(IdentityConfig {
identities: vec![alice()],
})
.execute_with(|| {
// We need to initialize at least one block before any call
run_to_block(1);
let revocation_payload = RevocationPayload {
owner_key: 1,
genesis_hash: System::block_hash(0),
};
// Payload must be signed by the right identity
assert_eq!(
Identity::revoke_identity(
Origin::signed(1),
revocation_payload.clone(),
TestSignature(42, revocation_payload.encode())
),
Err(Error::<Test>::InvalidRevocationProof.into())
);
// Anyone can submit a revocation payload
assert_ok!(Identity::revoke_identity(
Origin::signed(42),
revocation_payload.clone(),
TestSignature(1, revocation_payload.encode())
));
let events = System::events();
assert_eq!(events.len(), 1);
assert_eq!(
events[0],
EventRecord {
phase: Phase::Initialization,
event: Event::Identity(crate::Event::IdtyRemoved { idty_index: 1 }),
topics: vec![],
}
);
run_to_block(2);
// The identity no longer exists
assert_eq!(
Identity::revoke_identity(
Origin::signed(1),
revocation_payload.clone(),
TestSignature(1, revocation_payload.encode())
),
Err(Error::<Test>::IdtyNotFound.into())
);
});
}
......@@ -85,3 +85,10 @@ pub struct IdtyValue<BlockNumber, AccountId> {
pub removable_on: BlockNumber,
pub status: IdtyStatus,
}
#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)]
pub struct RevocationPayload<AccountId, Hash> {
pub owner_key: AccountId,
// Avoid replay attack between blockchains
pub genesis_hash: Hash,
}
......@@ -367,6 +367,8 @@ macro_rules! pallets_config {
type OnIdtyChange = Wot;
type MaxDisabledPeriod = MaxDisabledPeriod;
type RemoveIdentityConsumers = RemoveIdentityConsumersImpl<Self>;
type RevocationSigner = <Signature as sp_runtime::traits::Verify>::Signer;
type RevocationSignature = Signature;
}
impl pallet_membership::Config<frame_support::instances::Instance1> for Runtime {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment