diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs index 99babcc97e192f3db0c2d9f6ae3a6bd87e88a704..b6c1bd13c23e32253b38eac3ffbd823bc45ec9ce 100644 --- a/pallets/authority-members/src/lib.rs +++ b/pallets/authority-members/src/lib.rs @@ -378,25 +378,25 @@ pub mod pallet { // Execute the annotated function in a new storage transaction. // The return type of the annotated function must be `Result`. All changes to storage performed // by the annotated function are discarded if it returns `Err`, or committed if `Ok`. - #[frame_support::transactional] /// Change the owner key of an authority member. + #[frame_support::transactional] pub fn change_owner_key( member_id: T::MemberId, new_owner_key: T::AccountId, ) -> DispatchResultWithPostInfo { - let old_owner_key = Members::<T>::mutate_exists(member_id, |maybe_member_data| { - if let Some(ref mut member_data) = maybe_member_data { - Ok(core::mem::replace( - &mut member_data.owner_key, - new_owner_key.clone(), - )) - } else { - Err(Error::<T>::MemberNotFound) - } + let old_owner_key = Members::<T>::try_mutate_exists(member_id, |maybe_member_data| { + let member_data = maybe_member_data + .as_mut() + .ok_or(Error::<T>::MemberNotFound)?; + Ok::<T::AccountId, Error<T>>(core::mem::replace( + &mut member_data.owner_key, + new_owner_key.clone(), + )) })?; let validator_id = T::ValidatorIdOf::convert(old_owner_key.clone()) .ok_or(pallet_session::Error::<T>::NoAssociatedValidatorId)?; + let session_keys = pallet_session::NextKeys::<T>::get(validator_id) .ok_or(Error::<T>::SessionKeysNotProvided)?; @@ -405,7 +405,7 @@ pub mod pallet { .dispatch_bypass_filter(frame_system::RawOrigin::Signed(old_owner_key).into())?; // Set session keys - let _post_info = pallet_session::Call::<T>::set_keys { + pallet_session::Call::<T>::set_keys { keys: session_keys, proof: vec![], } @@ -491,7 +491,7 @@ pub mod pallet { .is_ok() } - /// C&heck if member is online. + /// Check if member is online. fn is_online(member_id: &T::MemberId) -> bool { OnlineAuthorities::<T>::get() .binary_search(member_id) diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs index e804df741245c2fc738b79305e15f226033c2333..adb614267dd9e5586d1a9378ce331fbd4ed682b0 100644 --- a/pallets/distance/src/mock.rs +++ b/pallets/distance/src/mock.rs @@ -228,9 +228,9 @@ impl pallet_identity::Config for Test { type IdtyData = (); type IdtyIndex = u32; type IdtyNameValidator = IdtyNameValidatorTestImpl; + type OnKeyChange = (); type OnNewIdty = (); type OnRemoveIdty = (); - type OwnerKeyChangePermission = (); type RuntimeEvent = RuntimeEvent; type Signature = TestSignature; type Signer = UintAuthorityId; diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs index a3c550632f5febae809a1bf20cff4c28f75eb579..f0279c36d2886156b12542396d6a09368626b502 100644 --- a/pallets/duniter-wot/src/mock.rs +++ b/pallets/duniter-wot/src/mock.rs @@ -107,9 +107,9 @@ impl pallet_identity::Config for Test { type IdtyData = (); type IdtyIndex = IdtyIndex; type IdtyNameValidator = IdtyNameValidatorTestImpl; + type OnKeyChange = (); type OnNewIdty = DuniterWot; type OnRemoveIdty = DuniterWot; - type OwnerKeyChangePermission = (); type RuntimeEvent = RuntimeEvent; type Signature = TestSignature; type Signer = UintAuthorityId; diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 64f47fe83edbed60889149f263e66a6cd14f6afd..5ae90084f4cbcef3d8085e8af0572332fdbcd91d 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -134,8 +134,8 @@ pub mod pallet { /// The type used to check account worthiness. type CheckAccountWorthiness: CheckAccountWorthiness<Self>; - /// Handler that checks the necessary permissions for an identity's owner key change. - type OwnerKeyChangePermission: CheckKeyChangeAllowed<Self>; + /// A handler called when an identity's owner key change. + type OnKeyChange: KeyChange<Self>; /// Custom data to store in each identity. type IdtyData: Clone @@ -456,12 +456,6 @@ pub mod pallet { Error::<T>::OwnerKeyAlreadyUsed ); - // Ensure that the key is not currently as a validator - ensure!( - T::OwnerKeyChangePermission::check_allowed(&idty_index), - Error::<T>::OwnerKeyUsedAsValidator - ); - let block_number = frame_system::Pallet::<T>::block_number(); let maybe_old_old_owner_key = if let Some((old_owner_key, last_change)) = idty_value.old_owner_key { @@ -503,6 +497,7 @@ pub mod pallet { IdentityIndexOf::<T>::insert(&idty_value.owner_key, idty_index); Identities::<T>::insert(idty_index, idty_value); T::AccountLinker::link_identity(&new_key, idty_index)?; + T::OnKeyChange::on_changed(idty_index, new_key.clone())?; Self::deposit_event(Event::IdtyChangedOwnerKey { idty_index, new_owner_key: new_key, @@ -807,8 +802,6 @@ pub mod pallet { AccountNotExist, /// Insufficient balance to create an identity. InsufficientBalance, - /// Owner key currently used as validator. - OwnerKeyUsedAsValidator, /// Legacy revocation document format is invalid InvalidLegacyRevocationFormat, } diff --git a/pallets/identity/src/mock.rs b/pallets/identity/src/mock.rs index 70758bc813260c41b9175e9e2225338b89c35c9f..c1b7f7e7b05d1b26e068806c18b49ae629371ad7 100644 --- a/pallets/identity/src/mock.rs +++ b/pallets/identity/src/mock.rs @@ -100,9 +100,9 @@ impl pallet_identity::Config for Test { type IdtyData = (); type IdtyIndex = u64; type IdtyNameValidator = IdtyNameValidatorTestImpl; + type OnKeyChange = (); type OnNewIdty = (); type OnRemoveIdty = (); - type OwnerKeyChangePermission = (); type RuntimeEvent = RuntimeEvent; type Signature = Signature; type Signer = AccountPublic; diff --git a/pallets/identity/src/traits.rs b/pallets/identity/src/traits.rs index 8366d1cff76022599feb2d118e622128cc12a59e..9aca1dc64539c05edcd971b9ba07a9737830e5f1 100644 --- a/pallets/identity/src/traits.rs +++ b/pallets/identity/src/traits.rs @@ -95,14 +95,14 @@ impl<AccountId, IdtyIndex> LinkIdty<AccountId, IdtyIndex> for () { } } -/// Trait for checking whether a key change is allowed for a given identity. -pub trait CheckKeyChangeAllowed<T: Config> { - /// Determines if a key change is allowed for the given identity. - fn check_allowed(account_id: &T::IdtyIndex) -> bool; +/// A trait for handling identity owner key changes. +pub trait KeyChange<T: Config> { + fn on_changed(id: T::IdtyIndex, account_id: T::AccountId) -> Result<(), DispatchError>; } -impl<T: Config> CheckKeyChangeAllowed<T> for () { - fn check_allowed(_: &T::IdtyIndex) -> bool { - true +impl<T: Config> KeyChange<T> for () { + /// Called when an identity's owner key has changed. + fn on_changed(_id: T::IdtyIndex, _account_id: T::AccountId) -> Result<(), DispatchError> { + Ok(()) } } diff --git a/pallets/quota/src/mock.rs b/pallets/quota/src/mock.rs index f39b053dfe4c1f565549dd401b54bf52d8b0a1de..070d01d810c03027425d91a283e842e4856e2249 100644 --- a/pallets/quota/src/mock.rs +++ b/pallets/quota/src/mock.rs @@ -144,9 +144,9 @@ impl pallet_identity::Config for Test { type IdtyData = (); type IdtyIndex = u64; type IdtyNameValidator = IdtyNameValidatorTestImpl; + type OnKeyChange = (); type OnNewIdty = (); type OnRemoveIdty = (); - type OwnerKeyChangePermission = (); type RuntimeEvent = RuntimeEvent; type Signature = Signature; type Signer = AccountPublic; diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs index 3fdd7b2d395730e3bb4f45cbb12ae1bc7f3f1817..ba0d0f296b7af1d88ff6f10a5bb9008751bc0aaa 100644 --- a/runtime/common/src/handlers.rs +++ b/runtime/common/src/handlers.rs @@ -171,16 +171,20 @@ where } /// Runtime handler OwnerKeyChangePermission. -pub struct OwnerKeyChangePermissionHandler<Runtime>(core::marker::PhantomData<Runtime>); +pub struct KeyChangeHandler<Runtime>(core::marker::PhantomData<Runtime>); impl< - Runtime: frame_system::Config + Runtime: frame_system::Config<AccountId = AccountId> + pallet_identity::Config<IdtyIndex = IdtyIndex> + pallet_authority_members::Config<MemberId = IdtyIndex>, - > pallet_identity::traits::CheckKeyChangeAllowed<Runtime> - for OwnerKeyChangePermissionHandler<Runtime> + > pallet_identity::traits::KeyChange<Runtime> for KeyChangeHandler<Runtime> { - fn check_allowed(idty_index: &IdtyIndex) -> bool { - !pallet_authority_members::Pallet::<Runtime>::online().contains(idty_index) + fn on_changed( + idty_index: IdtyIndex, + account_id: AccountId, + ) -> Result<(), sp_runtime::DispatchError> { + pallet_authority_members::Pallet::<Runtime>::change_owner_key(idty_index, account_id) + .map_err(|e| e.error)?; + Ok(()) } } diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 04fa45d061f2ee8a191a9dd12db531d536707677..c4226e204a6addc561585c69ceddb7bc0b6b710f 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -443,9 +443,9 @@ macro_rules! pallets_config { type IdtyData = IdtyData; type IdtyIndex = IdtyIndex; type IdtyNameValidator = IdtyNameValidatorImpl; + type OnKeyChange = KeyChangeHandler<Runtime>; type OnNewIdty = OnNewIdtyHandler<Runtime>; type OnRemoveIdty = OnRemoveIdtyHandler<Runtime>; - type OwnerKeyChangePermission = OwnerKeyChangePermissionHandler<Runtime>; type RuntimeEvent = RuntimeEvent; type Signature = Signature; type Signer = <Signature as sp_runtime::traits::Verify>::Signer; diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs index 8b859ce71dbf379e7d0621231b36b124824f531e..32dd489f27875fd1f3b3dbce2b25a2bbacd73654 100644 --- a/runtime/gdev/tests/integration_tests.rs +++ b/runtime/gdev/tests/integration_tests.rs @@ -1516,154 +1516,139 @@ fn test_link_account() { }) } -/// test change owner key -#[test] -fn test_change_owner_key_validator_keys_set() { - ExtBuilder::new(1, 3, 4).build().execute_with(|| { - let genesis_hash = System::block_hash(0); - let bob = AccountKeyring::Bob.to_account_id(); - let ferdie = AccountKeyring::Ferdie.to_account_id(); - let payload = (b"icok", genesis_hash, 2u32, bob.clone()).encode(); - let signature = AccountKeyring::Ferdie.sign(&payload); - - // Bob is not an online validator, but his identity/owner_key is already associated. - // He should not be able to change its owner_key. - assert_ok!(AuthorityMembers::set_session_keys( - RuntimeOrigin::signed(AccountKeyring::Bob.to_account_id()), - create_dummy_session_keys() - )); - assert!(!pallet_authority_members::OnlineAuthorities::<Runtime>::get().contains(&2)); - assert!(pallet_authority_members::Members::<Runtime>::get(2).is_some()); - - assert_noop!( - Identity::change_owner_key( - RuntimeOrigin::signed(bob.clone()), - ferdie.clone(), - signature.into() - ), - pallet_identity::Error::<gdev_runtime::Runtime>::OwnerKeyUsedAsValidator - ); - }) -} - /// test change owner key #[test] fn test_change_owner_key_validator_online() { - ExtBuilder::new(1, 3, 4).build().execute_with(|| { - let genesis_hash = System::block_hash(0); - let alice = AccountKeyring::Alice.to_account_id(); - let ferdie = AccountKeyring::Ferdie.to_account_id(); - let payload = (b"icok", genesis_hash, 1u32, alice.clone()).encode(); - let signature = AccountKeyring::Ferdie.sign(&payload); + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![(AccountKeyring::Ferdie.to_account_id(), 8888)]) + .build() + .execute_with(|| { + let genesis_hash = System::block_hash(0); + let alice = AccountKeyring::Alice.to_account_id(); + let ferdie = AccountKeyring::Ferdie.to_account_id(); + let payload = (b"icok", genesis_hash, 1u32, alice.clone()).encode(); + let signature = AccountKeyring::Ferdie.sign(&payload); - // Alice is an online validator - assert!(pallet_authority_members::OnlineAuthorities::<Runtime>::get().contains(&1)); + // Alice is an online validator + assert!(pallet_authority_members::OnlineAuthorities::<Runtime>::get().contains(&1)); + assert_eq!( + pallet_authority_members::Members::<Runtime>::get(1) + .unwrap() + .owner_key, + alice + ); - // As an online validator she cannot change key - assert_noop!( - Identity::change_owner_key( + assert_ok!(Identity::change_owner_key( RuntimeOrigin::signed(alice.clone()), ferdie.clone(), signature.into() - ), - pallet_identity::Error::<gdev_runtime::Runtime>::OwnerKeyUsedAsValidator - ); - }) + ),); + + assert_eq!( + pallet_authority_members::Members::<Runtime>::get(1) + .unwrap() + .owner_key, + ferdie + ); + }) } /// test change owner key #[test] fn test_change_owner_key() { - ExtBuilder::new(1, 3, 4).build().execute_with(|| { - let genesis_hash = System::block_hash(0); - let charlie = AccountKeyring::Charlie.to_account_id(); - let ferdie = AccountKeyring::Ferdie.to_account_id(); - let payload = (b"icok", genesis_hash, 3u32, charlie.clone()).encode(); - let signature = AccountKeyring::Ferdie.sign(&payload); - - SmithMembers::on_smith_goes_offline(3); - // Charlie is now offline smith - assert_eq!( - SmithMembers::smiths(3), - Some(SmithMeta { - status: SmithStatus::Smith, - expires_on: Some(48), - issued_certs: vec![1, 2], - received_certs: vec![1, 2] - }) - ); + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![(AccountKeyring::Ferdie.to_account_id(), 8888)]) + .build() + .execute_with(|| { + let genesis_hash = System::block_hash(0); + let charlie = AccountKeyring::Charlie.to_account_id(); + let ferdie = AccountKeyring::Ferdie.to_account_id(); + let payload = (b"icok", genesis_hash, 3u32, charlie.clone()).encode(); + let signature = AccountKeyring::Ferdie.sign(&payload); - assert_eq!( - frame_system::Pallet::<Runtime>::get(&charlie).linked_idty, - Some(3) - ); - assert_eq!( - frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, - None - ); - // Dave can change his owner key to Ferdie's - assert_ok!(Identity::change_owner_key( - RuntimeOrigin::signed(charlie.clone()), - ferdie.clone(), - signature.into() - )); - assert_eq!( - frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, - Some(3) - ); + SmithMembers::on_smith_goes_offline(3); + // Charlie is now offline smith + assert_eq!( + SmithMembers::smiths(3), + Some(SmithMeta { + status: SmithStatus::Smith, + expires_on: Some(48), + issued_certs: vec![1, 2], + received_certs: vec![1, 2] + }) + ); - // Charlie is still an offline smith - assert_eq!( - SmithMembers::smiths(3), - Some(SmithMeta { - status: SmithStatus::Smith, - expires_on: Some(48), - issued_certs: vec![1, 2], - received_certs: vec![1, 2] - }) - ); + assert_eq!( + frame_system::Pallet::<Runtime>::get(&charlie).linked_idty, + Some(3) + ); + assert_eq!( + frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, + None + ); + // Dave can change his owner key to Ferdie's + assert_ok!(Identity::change_owner_key( + RuntimeOrigin::signed(charlie.clone()), + ferdie.clone(), + signature.into() + )); + assert_eq!( + frame_system::Pallet::<Runtime>::get(&ferdie).linked_idty, + Some(3) + ); - // Ferdie can set its session_keys and go online - frame_system::Pallet::<Runtime>::inc_providers(&ferdie); - assert_ok!(AuthorityMembers::set_session_keys( - RuntimeOrigin::signed(AccountKeyring::Ferdie.to_account_id()), - create_dummy_session_keys() - )); - assert_ok!(AuthorityMembers::go_online(RuntimeOrigin::signed( - AccountKeyring::Ferdie.to_account_id() - ))); + // Charlie is still an offline smith + assert_eq!( + SmithMembers::smiths(3), + Some(SmithMeta { + status: SmithStatus::Smith, + expires_on: Some(48), + issued_certs: vec![1, 2], + received_certs: vec![1, 2] + }) + ); - // Charlie is still an offline smith - assert_eq!( - SmithMembers::smiths(3), - Some(SmithMeta { - status: SmithStatus::Smith, - expires_on: Some(48), - issued_certs: vec![1, 2], - received_certs: vec![1, 2] - }) - ); + // Ferdie can set its session_keys and go online + frame_system::Pallet::<Runtime>::inc_providers(&ferdie); + assert_ok!(AuthorityMembers::set_session_keys( + RuntimeOrigin::signed(AccountKeyring::Ferdie.to_account_id()), + create_dummy_session_keys() + )); + assert_ok!(AuthorityMembers::go_online(RuntimeOrigin::signed( + AccountKeyring::Ferdie.to_account_id() + ))); - run_to_block(25); + // Charlie is still an offline smith + assert_eq!( + SmithMembers::smiths(3), + Some(SmithMeta { + status: SmithStatus::Smith, + expires_on: Some(48), + issued_certs: vec![1, 2], + received_certs: vec![1, 2] + }) + ); - System::assert_has_event(RuntimeEvent::AuthorityMembers( - pallet_authority_members::Event::IncomingAuthorities { members: vec![3] }, - )); - System::assert_has_event(RuntimeEvent::AuthorityMembers( - pallet_authority_members::Event::OutgoingAuthorities { members: vec![1] }, - )); + run_to_block(25); - // "Charlie" (idty 3) is now online because its identity is mapped to Ferdies's key - assert_eq!( - SmithMembers::smiths(3), - Some(SmithMeta { - status: SmithStatus::Smith, - expires_on: None, - issued_certs: vec![1, 2], - received_certs: vec![1, 2] - }) - ); - }) + System::assert_has_event(RuntimeEvent::AuthorityMembers( + pallet_authority_members::Event::IncomingAuthorities { members: vec![3] }, + )); + System::assert_has_event(RuntimeEvent::AuthorityMembers( + pallet_authority_members::Event::OutgoingAuthorities { members: vec![1] }, + )); + + // "Charlie" (idty 3) is now online because its identity is mapped to Ferdies's key + assert_eq!( + SmithMembers::smiths(3), + Some(SmithMeta { + status: SmithStatus::Smith, + expires_on: None, + issued_certs: vec![1, 2], + received_certs: vec![1, 2] + }) + ); + }) } /// members of the smith subwot can revoke their identity