diff --git a/docs/api/runtime-calls.md b/docs/api/runtime-calls.md index 2289d6262e1cb43b69ed3128b5ad12575996f079..95ffd53436b05d24801de03d0548b0b4c565bb06 100644 --- a/docs/api/runtime-calls.md +++ b/docs/api/runtime-calls.md @@ -13,7 +13,7 @@ through on-chain governance mechanisms. ## User calls -There are **68** user calls from **21** pallets. +There are **69** user calls from **21** pallets. ### Scheduler - 2 @@ -652,12 +652,23 @@ change sufficient ref count for given key ### Membership - 42 -#### renew_membership - 3 +#### claim_membership - 1 -<details><summary><code>renew_membership(maybe_idty_id)</code></summary> +<details><summary><code>claim_membership()</code></summary> + +```rust +``` +</details> + + +claim pending membership to become actual memberhip +the requested membership must fullfill requirements + +#### renew_membership - 2 + +<details><summary><code>renew_membership()</code></summary> ```rust -maybe_idty_id: Option<T::IdtyId> ``` </details> @@ -666,7 +677,7 @@ extend the validity period of an active membership ### Cert - 43 -#### add_cert - 1 +#### add_cert - 0 <details><summary><code>add_cert(issuer, receiver)</code></summary> @@ -685,7 +696,7 @@ The origin must be allow to certify. ### SmithMembership - 52 -#### request_membership - 1 +#### request_membership - 0 <details><summary><code>request_membership(metadata)</code></summary> @@ -698,37 +709,34 @@ metadata: T::MetaData submit a membership request (must have a declared identity) (only available for sub wot, automatic for main wot) -#### claim_membership - 2 +#### claim_membership - 1 -<details><summary><code>claim_membership(maybe_idty_id)</code></summary> +<details><summary><code>claim_membership()</code></summary> ```rust -maybe_idty_id: Option<T::IdtyId> ``` </details> -claim that the previously requested membership fullfills the requirements -(only available for sub wot, automatic for main wot) +claim pending membership to become actual memberhip +the requested membership must fullfill requirements -#### renew_membership - 3 +#### renew_membership - 2 -<details><summary><code>renew_membership(maybe_idty_id)</code></summary> +<details><summary><code>renew_membership()</code></summary> ```rust -maybe_idty_id: Option<T::IdtyId> ``` </details> extend the validity period of an active membership -#### revoke_membership - 4 +#### revoke_membership - 3 -<details><summary><code>revoke_membership(maybe_idty_id)</code></summary> +<details><summary><code>revoke_membership()</code></summary> ```rust -maybe_idty_id: Option<T::IdtyId> ``` </details> @@ -738,7 +746,7 @@ revoke an active membership ### SmithCert - 53 -#### add_cert - 1 +#### add_cert - 0 <details><summary><code>add_cert(issuer, receiver)</code></summary> @@ -1353,7 +1361,7 @@ May only be called from `T::RejectOrigin`. ## Root calls -There are **26** root calls from **12** pallets. +There are **22** root calls from **10** pallets. ### System - 0 @@ -1637,38 +1645,9 @@ names: Vec<IdtyName> remove identity names from storage -### Membership - 42 - -#### force_request_membership - 0 - -<details><summary><code>force_request_membership(idty_id, metadata)</code></summary> - -```rust -idty_id: T::IdtyId -metadata: T::MetaData -``` -</details> - - -request membership without checks - ### Cert - 43 -#### force_add_cert - 0 - -<details><summary><code>force_add_cert(issuer, receiver, verify_rules)</code></summary> - -```rust -issuer: T::IdtyIndex -receiver: T::IdtyIndex -verify_rules: bool -``` -</details> - - -add a certification without checks (only root) - -#### del_cert - 2 +#### del_cert - 1 <details><summary><code>del_cert(issuer, receiver)</code></summary> @@ -1681,7 +1660,7 @@ receiver: T::IdtyIndex remove a certification (only root) -#### remove_all_certs_received_by - 3 +#### remove_all_certs_received_by - 2 <details><summary><code>remove_all_certs_received_by(idty_index)</code></summary> @@ -1693,38 +1672,9 @@ idty_index: T::IdtyIndex remove all certifications received by an identity (only root) -### SmithMembership - 52 - -#### force_request_membership - 0 - -<details><summary><code>force_request_membership(idty_id, metadata)</code></summary> - -```rust -idty_id: T::IdtyId -metadata: T::MetaData -``` -</details> - - -request membership without checks - ### SmithCert - 53 -#### force_add_cert - 0 - -<details><summary><code>force_add_cert(issuer, receiver, verify_rules)</code></summary> - -```rust -issuer: T::IdtyIndex -receiver: T::IdtyIndex -verify_rules: bool -``` -</details> - - -add a certification without checks (only root) - -#### del_cert - 2 +#### del_cert - 1 <details><summary><code>del_cert(issuer, receiver)</code></summary> @@ -1737,7 +1687,7 @@ receiver: T::IdtyIndex remove a certification (only root) -#### remove_all_certs_received_by - 3 +#### remove_all_certs_received_by - 2 <details><summary><code>remove_all_certs_received_by(idty_index)</code></summary> @@ -1774,7 +1724,7 @@ The dispatch origin for this call must be _Root_. ## Disabled calls -There are **7** disabled calls from **3** pallets. +There are **6** disabled calls from **3** pallets. ### System - 0 @@ -1844,7 +1794,7 @@ usually means being a stash account). ### Membership - 42 -#### request_membership - 1 +#### request_membership - 0 <details><summary><code>request_membership(metadata)</code></summary> @@ -1857,25 +1807,11 @@ metadata: T::MetaData submit a membership request (must have a declared identity) (only available for sub wot, automatic for main wot) -#### claim_membership - 2 - -<details><summary><code>claim_membership(maybe_idty_id)</code></summary> - -```rust -maybe_idty_id: Option<T::IdtyId> -``` -</details> - - -claim that the previously requested membership fullfills the requirements -(only available for sub wot, automatic for main wot) - -#### revoke_membership - 4 +#### revoke_membership - 3 -<details><summary><code>revoke_membership(maybe_idty_id)</code></summary> +<details><summary><code>revoke_membership()</code></summary> ```rust -maybe_idty_id: Option<T::IdtyId> ``` </details> diff --git a/pallets/certification/src/benchmarking.rs b/pallets/certification/src/benchmarking.rs index 65e343a4ef900ff6d28719aef954999c90dc80cb..25f12579e4f83a6ec0002e6ef96ca828f939573d 100644 --- a/pallets/certification/src/benchmarking.rs +++ b/pallets/certification/src/benchmarking.rs @@ -34,7 +34,7 @@ fn assert_has_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::R fn add_certs<T: Config<I>, I: 'static>(i: u32, receiver: T::IdtyIndex) -> Result<(), &'static str> { Pallet::<T, I>::remove_all_certs_received_by(RawOrigin::Root.into(), receiver)?; for j in 1..i { - Pallet::<T, I>::force_add_cert(RawOrigin::Root.into(), j.into(), receiver, false)?; + Pallet::<T, I>::do_add_cert_checked(RawOrigin::Root.into(), j.into(), receiver, false)?; } assert!( CertsByReceiver::<T, I>::get(receiver).len() as u32 == i - 1, @@ -48,16 +48,6 @@ benchmarks_instance_pallet! { where T::IdtyIndex: From<u32>, } - force_add_cert { - let issuer: T::IdtyIndex = 1.into(); - let receiver: T::IdtyIndex = 2.into(); - Pallet::<T, I>::del_cert(RawOrigin::Root.into(), issuer, receiver)?; - let receiver_cert: u32 = StorageIdtyCertMeta::<T, I>::get(receiver).received_count; - let issuer_cert: u32 = StorageIdtyCertMeta::<T, I>::get(issuer).issued_count; - }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), issuer, receiver, true) - verify { - assert_has_event::<T, I>(Event::<T, I>::NewCert{ issuer: issuer, issuer_issued_count: issuer_cert + 1, receiver: receiver, receiver_received_count: receiver_cert + 1 }.into()); - } add_cert { let issuer: T::IdtyIndex = 1.into(); let caller: T::AccountId = T::OwnerKeyOf::convert(issuer).unwrap(); @@ -73,7 +63,7 @@ benchmarks_instance_pallet! { del_cert { let issuer: T::IdtyIndex = 1.into(); let receiver: T::IdtyIndex = 0.into(); - Pallet::<T, I>::force_add_cert(RawOrigin::Root.into(), issuer, receiver, false)?; + Pallet::<T, I>::do_add_cert_checked(RawOrigin::Root.into(), issuer, receiver, false)?; let receiver_cert: u32 = StorageIdtyCertMeta::<T, I>::get(receiver).received_count; let issuer_cert: u32 = StorageIdtyCertMeta::<T, I>::get(issuer).issued_count; }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), issuer, receiver) diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs index b062da444944a9dcf3ed9bde2cb954c45fc3c0b5..f1bcb550a16eb71cd6acf5a9bc64b448b9324f27 100644 --- a/pallets/certification/src/lib.rs +++ b/pallets/certification/src/lib.rs @@ -276,45 +276,6 @@ pub mod pallet { #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> { - /// add a certification without checks (only root) - #[pallet::weight(T::WeightInfo::force_add_cert())] - pub fn force_add_cert( - origin: OriginFor<T>, - issuer: T::IdtyIndex, - receiver: T::IdtyIndex, - verify_rules: bool, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - // Forbid self cert - ensure!(issuer != receiver, Error::<T, I>::CannotCertifySelf); - - let block_number = frame_system::pallet::Pallet::<T>::block_number(); - - if verify_rules { - // Verify rule MinReceivedCertToBeAbleToIssueCert - let issuer_idty_cert_meta = StorageIdtyCertMeta::<T, I>::get(issuer); - ensure!( - issuer_idty_cert_meta.received_count - >= T::MinReceivedCertToBeAbleToIssueCert::get(), - Error::<T, I>::NotEnoughCertReceived - ); - - // Verify rule MaxByIssuer - ensure!( - issuer_idty_cert_meta.issued_count < T::MaxByIssuer::get(), - Error::<T, I>::IssuedTooManyCert - ); - - // Verify rule CertPeriod - ensure!( - block_number >= issuer_idty_cert_meta.next_issuable_on, - Error::<T, I>::NotRespectCertPeriod - ); - }; - - Self::do_add_cert(block_number, issuer, receiver) - } /// Add a new certification or renew an existing one /// /// - `receiver`: the account receiving the certification from the origin @@ -392,6 +353,41 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config<I>, I: 'static> Pallet<T, I> { + /// add a certification without checks + pub fn do_add_cert_checked( + issuer: T::IdtyIndex, + receiver: T::IdtyIndex, + verify_rules: bool, + ) -> DispatchResultWithPostInfo { + // Forbid self cert + ensure!(issuer != receiver, Error::<T, I>::CannotCertifySelf); + + let block_number = frame_system::pallet::Pallet::<T>::block_number(); + + if verify_rules { + // Verify rule MinReceivedCertToBeAbleToIssueCert + let issuer_idty_cert_meta = StorageIdtyCertMeta::<T, I>::get(issuer); + ensure!( + issuer_idty_cert_meta.received_count + >= T::MinReceivedCertToBeAbleToIssueCert::get(), + Error::<T, I>::NotEnoughCertReceived + ); + + // Verify rule MaxByIssuer + ensure!( + issuer_idty_cert_meta.issued_count < T::MaxByIssuer::get(), + Error::<T, I>::IssuedTooManyCert + ); + + // Verify rule CertPeriod + ensure!( + block_number >= issuer_idty_cert_meta.next_issuable_on, + Error::<T, I>::NotRespectCertPeriod + ); + }; + + Self::do_add_cert(block_number, issuer, receiver) + } /// perform cert addition or renewal fn do_add_cert( block_number: T::BlockNumber, diff --git a/pallets/certification/src/weights.rs b/pallets/certification/src/weights.rs index 54665716e423e023d0e38068563f0b7d9cf8d882..2d4ed1c30565b3491f9eae58819f41714654e2dd 100644 --- a/pallets/certification/src/weights.rs +++ b/pallets/certification/src/weights.rs @@ -20,7 +20,7 @@ use frame_support::weights::{constants::RocksDbWeight, Weight}; /// Weight functions needed for pallet_universal_dividend. pub trait WeightInfo { - fn force_add_cert() -> Weight; + fn do_add_cert_checked() -> Weight; fn add_cert() -> Weight; fn del_cert() -> Weight; fn remove_all_certs_received_by(i: u32) -> Weight; @@ -32,7 +32,7 @@ impl WeightInfo for () { // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Cert StorageCertsRemovableOn (r:1 w:1) // Storage: Cert CertsByReceiver (r:1 w:1) - fn force_add_cert() -> Weight { + fn do_add_cert_checked() -> Weight { // Minimum execution time: 221_467 nanoseconds. Weight::from_ref_time(227_833_000 as u64) .saturating_add(RocksDbWeight::get().reads(5 as u64)) diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs index e285dde5d0f2beb6fdd77abb682e2abcb7093eb6..50a41c70b5184d96a8fa1598b958a80cfb32aad1 100644 --- a/pallets/duniter-wot/src/lib.rs +++ b/pallets/duniter-wot/src/lib.rs @@ -122,12 +122,14 @@ pub mod pallet { } } +// implement identity call checks impl<AccountId, T: Config<I>, I: 'static> pallet_identity::traits::CheckIdtyCallAllowed<T> for Pallet<T, I> where T: frame_system::Config<AccountId = AccountId> + pallet_membership::Config<I>, { fn check_create_identity(creator: IdtyIndex) -> Result<(), DispatchError> { + // main WoT constraints if !T::IsSubWot::get() { let cert_meta = pallet_certification::Pallet::<T, I>::idty_cert_meta(creator); // perform all checks @@ -144,43 +146,44 @@ where Error::<T, I>::IdtyCreationPeriodNotRespected ); } + // no constraints for subwot Ok(()) } 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( - RawOrigin::Root.into(), idty_index, Default::default(), ) .map_err(|e| e.error)?; } + // no constraints for subwot Ok(()) } fn check_validate_identity(idty_index: IdtyIndex) -> Result<(), DispatchError> { + // main WoT constraints if !T::IsSubWot::get() { - // TODO replace this code by the commented one for distance feature - /*let idty_cert_meta = pallet_certification::Pallet::<T, I>::idty_cert_meta(idty_index); - idty_cert_meta.received_count >= T::MinCertForMembership::get() as u32*/ - // in the main wot, automatically claim membership when the identity is validated - pallet_membership::Pallet::<T, I>::claim_membership( - RawOrigin::Root.into(), - Some(idty_index), - ) - .map_err(|e| e.error)?; + // check if identity is allowed to claim membership to the main wot + // (will be called automatically after) + pallet_membership::Pallet::<T, I>::check_allowed_to_claim(idty_index)?; } + // no constraints for subwot Ok(()) } fn check_change_identity_address(idty_index: IdtyIndex) -> Result<(), DispatchError> { + // sub WoT prevents from changing identity if T::IsSubWot::get() { ensure!( !pallet_membership::Pallet::<T, I>::is_member(&idty_index), Error::<T, I>::NotAllowedToChangeIdtyAddress ); } + // no constraints for main wot Ok(()) } fn check_remove_identity(idty_index: IdtyIndex) -> Result<(), DispatchError> { + // identity can not be removed when member of a subwot (smith in this case) if T::IsSubWot::get() { ensure!( !pallet_membership::Pallet::<T, I>::is_member(&idty_index), @@ -191,6 +194,7 @@ where } } +// implement cert call checks impl<T: Config<I>, I: 'static> pallet_certification::traits::CheckCertAllowed<IdtyIndex> for Pallet<T, I> { @@ -215,7 +219,26 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::CheckCertAllowed<Id } } -impl<T: Config<I>, I: 'static> sp_membership::traits::CheckCallAllowed<IdtyIndex> for Pallet<T, I> { +// implement membership call checks +impl<T: Config<I>, I: 'static> sp_membership::traits::CheckMembershipCallAllowed<IdtyIndex> + for Pallet<T, I> +{ + // membership request is only possible for subwot and when identity is validated + fn check_idty_allowed_to_request_membership( + idty_index: &IdtyIndex, + ) -> Result<(), DispatchError> { + if let Some(idty_value) = pallet_identity::Pallet::<T>::identity(idty_index) { + ensure!( + T::IsSubWot::get() && idty_value.status == IdtyStatus::Validated, + Error::<T, I>::IdtyNotAllowedToRequestMembership + ); + } else { + return Err(Error::<T, I>::IdtyNotFound.into()); + } + Ok(()) + } + + // membership claim is only possible when enough certs are received (both wots) fn check_idty_allowed_to_claim_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> { let idty_cert_meta = pallet_certification::Pallet::<T, I>::idty_cert_meta(idty_index); ensure!( @@ -225,6 +248,7 @@ impl<T: Config<I>, I: 'static> sp_membership::traits::CheckCallAllowed<IdtyIndex Ok(()) } + // membership renewal is only possible when identity is validated fn check_idty_allowed_to_renew_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> { if let Some(idty_value) = pallet_identity::Pallet::<T>::identity(idty_index) { ensure!( @@ -236,22 +260,9 @@ impl<T: Config<I>, I: 'static> sp_membership::traits::CheckCallAllowed<IdtyIndex } Ok(()) } - - fn check_idty_allowed_to_request_membership( - idty_index: &IdtyIndex, - ) -> Result<(), DispatchError> { - if let Some(idty_value) = pallet_identity::Pallet::<T>::identity(idty_index) { - ensure!( - T::IsSubWot::get() && idty_value.status == IdtyStatus::Validated, - Error::<T, I>::IdtyNotAllowedToRequestMembership - ); - } else { - return Err(Error::<T, I>::IdtyNotFound.into()); - } - Ok(()) - } } +// implement membership event handler impl<T: Config<I>, I: 'static, MetaData> sp_membership::traits::OnEvent<IdtyIndex, MetaData> for Pallet<T, I> where @@ -259,18 +270,14 @@ where { fn on_event(membership_event: &sp_membership::Event<IdtyIndex, MetaData>) -> Weight { match membership_event { - sp_membership::Event::<IdtyIndex, MetaData>::MembershipAcquired(_, _) => {} - // Membership expiration cases: - // Triggered by the membership pallet: we should remove the identity only for the main - // wot - sp_membership::Event::<IdtyIndex, MetaData>::MembershipExpired(idty_index) => { + sp_membership::Event::<IdtyIndex, MetaData>::MembershipAcquired(idty_index, _) => { if !T::IsSubWot::get() { - Self::dispath_idty_call(pallet_identity::Call::remove_identity { - idty_index: *idty_index, - idty_name: None, - }); + // 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(_) => {} // Membership revocation cases: // - Triggered by main identity removal: the underlying identity will be removed by the // caller. @@ -291,21 +298,23 @@ where } } +// implement identity event handler impl<T: Config<I>, I: 'static> pallet_identity::traits::OnIdtyChange<T> for Pallet<T, I> { fn on_idty_change(idty_index: IdtyIndex, idty_event: &IdtyEvent<T>) -> Weight { match idty_event { IdtyEvent::Created { creator } => { - if let Err(e) = <pallet_certification::Pallet<T, I>>::force_add_cert( - frame_system::Origin::<T>::Root.into(), - *creator, - idty_index, - true, + if let Err(e) = <pallet_certification::Pallet<T, I>>::do_add_cert_checked( + *creator, idty_index, true, ) { sp_std::if_std! { println!("fail to force add cert: {:?}", e) } } } + IdtyEvent::Validated => { + // auto claim membership on main wot + <pallet_membership::Pallet<T, I>>::try_claim_membership(idty_index); + } IdtyEvent::Removed { status } => { if *status != IdtyStatus::Validated { if let Err(e) = @@ -320,12 +329,14 @@ impl<T: Config<I>, I: 'static> pallet_identity::traits::OnIdtyChange<T> for Pall } } } - IdtyEvent::Confirmed | IdtyEvent::Validated | IdtyEvent::ChangedOwnerKey { .. } => {} + IdtyEvent::Confirmed | IdtyEvent::ChangedOwnerKey { .. } => {} } Weight::zero() } } +// implement certification event handlers +// new cert handler impl<T: Config<I>, I: 'static> pallet_certification::traits::OnNewcert<IdtyIndex> for Pallet<T, I> { fn on_new_cert( _issuer: IdtyIndex, @@ -340,6 +351,7 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnNewcert<IdtyIndex } } +// remove cert handler impl<T: Config<I>, I: 'static> pallet_certification::traits::OnRemovedCert<IdtyIndex> for Pallet<T, I> { @@ -354,13 +366,12 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnRemovedCert<IdtyI && pallet_membership::Pallet::<T, I>::is_member(&receiver) { if T::IsSubWot::get() { - // Revoke receiver membership - let call = pallet_membership::Call::<T, I>::revoke_membership { - maybe_idty_id: Some(receiver), - }; - if let Err(e) = call.dispatch_bypass_filter(RawOrigin::Root.into()) { + // expire receiver membership + // it gives him a bit of time to get back enough certs + if let Err(e) = <pallet_membership::Pallet<T, I>>::force_expire_membership(receiver) + { sp_std::if_std! { - println!("fail to dispatch membership call: {:?}", e) + println!("fail to expire membership: {:?}", e) } } } else { diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs index 6b9bf90cf785016ca0029d28a92b13b5a4df5905..a2e645a13ad0d224feb9537529ef129737d0f657 100644 --- a/pallets/duniter-wot/src/mock.rs +++ b/pallets/duniter-wot/src/mock.rs @@ -143,7 +143,7 @@ parameter_types! { } impl pallet_membership::Config<Instance1> for Test { - type CheckCallAllowed = DuniterWot; + type CheckMembershipCallAllowed = DuniterWot; type IdtyId = IdtyIndex; type IdtyIdOf = IdentityIndexOf<Self>; type AccountIdOf = (); @@ -198,7 +198,7 @@ parameter_types! { } impl pallet_membership::Config<Instance2> for Test { - type CheckCallAllowed = SmithSubWot; + type CheckMembershipCallAllowed = SmithSubWot; type IdtyId = IdtyIndex; type IdtyIdOf = IdentityIndexOf<Self>; type AccountIdOf = (); diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs index 3c51f2f6b500683277837c1db6b8a5dd9db77d37..7f98d970f7f134e5207d1ecbe1771c8ec39c4640 100644 --- a/pallets/duniter-wot/src/tests.rs +++ b/pallets/duniter-wot/src/tests.rs @@ -55,6 +55,7 @@ fn test_creator_not_allowed_to_create_idty() { }); } +/// test smith joining workflow #[test] fn test_join_smiths() { new_test_ext(5, 3).execute_with(|| { @@ -79,27 +80,26 @@ fn test_join_smiths() { // Then, Dave should be able to claim his membership run_to_block(4); - assert_ok!(SmithMembership::claim_membership( - RuntimeOrigin::signed(4), - Some(4) - )); + assert_ok!(SmithMembership::claim_membership(RuntimeOrigin::signed(4),)); System::assert_has_event(RuntimeEvent::SmithMembership( pallet_membership::Event::MembershipAcquired(4), )); }); } +/// test smith membership expiry after cert expiration #[test] -fn test_smith_certs_expirations_should_revoke_smith_membership() { +fn test_smith_certs_expirations_should_expire_smith_membership() { new_test_ext(5, 3).execute_with(|| { // After block #10, alice membership should be revoked due to smith certs expiration run_to_block(10); System::assert_has_event(RuntimeEvent::SmithMembership( - pallet_membership::Event::MembershipRevoked(1), + pallet_membership::Event::MembershipExpired(1), )); }); } +/// test that smith can not change owner key #[test] fn test_smith_member_cant_change_its_idty_address() { new_test_ext(5, 3).execute_with(|| { @@ -124,6 +124,7 @@ fn test_smith_member_cant_change_its_idty_address() { }); } +/// members of the smith subwot can not remove their identity #[test] fn test_smith_member_cant_revoke_its_idty() { new_test_ext(5, 3).execute_with(|| { @@ -147,6 +148,7 @@ fn test_smith_member_cant_revoke_its_idty() { }); } +/// test identity creation and that a first cert is emitted #[test] fn test_create_idty_ok() { new_test_ext(5, 2).execute_with(|| { @@ -173,6 +175,7 @@ fn test_create_idty_ok() { }); } +/// test identity validation #[test] fn test_new_idty_validation() { new_test_ext(5, 2).execute_with(|| { @@ -197,6 +200,12 @@ fn test_new_idty_validation() { receiver_received_count: 2, })); + // Ferdie should not be able to claim membership + // assert_noop!( + // Membership::claim_membership(RuntimeOrigin::signed(6)), + // pallet_membership::Error::<Test, Instance1>::xxx + // ); + // Anyone should be able to validate Ferdie identity run_to_block(5); assert_ok!(Identity::validate_identity(RuntimeOrigin::signed(42), 6)); @@ -301,15 +310,16 @@ fn test_revoke_idty() { }); } +/// test that expired membership lose the identity and can not be certified #[test] -fn test_idty_membership_expire_them_requested() { +fn test_idty_membership_expire() { new_test_ext(3, 2).execute_with(|| { run_to_block(4); // Alice renews her membership - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1), None)); + assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); // Bob renews his membership - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2), None)); + assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); // Charlie's membership should expire at block #8 run_to_block(8); @@ -318,11 +328,22 @@ fn test_idty_membership_expire_them_requested() { System::assert_has_event(RuntimeEvent::Membership( pallet_membership::Event::MembershipExpired(3), )); + // membership expiry should not trigger identity removal + assert!(!System::events().iter().any(|record| record.event + == RuntimeEvent::Identity(pallet_identity::Event::IdtyRemoved { idty_index: 3 }))); + // it should be moved to pending membership instead + assert!(Membership::pending_membership(3).is_some()); + + // then pending membership should expire and identity should finally be removed + run_to_block(11); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::PendingMembershipExpired(3), + )); System::assert_has_event(RuntimeEvent::Identity( pallet_identity::Event::IdtyRemoved { idty_index: 3 }, )); - // Charlie's identity should be removed at block #8 + // Charlie's identity should be removed at block #11 assert!(Identity::identity(3).is_none()); // Alice can't renew her cert to Charlie diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 6307e2d3d3c60ce48070d64f62e002e3d67202ef..06514db7121b3129749770de06ff6521450dc892 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -367,6 +367,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::validate_identity())] /// validate the owned identity (must meet the main wot requirements) + // automatically claim membership if not done pub fn validate_identity( origin: OriginFor<T>, idty_index: T::IdtyIndex, @@ -499,6 +500,7 @@ pub mod pallet { ensure!( if let Some((ref old_owner_key, last_change)) = idty_value.old_owner_key { + // old owner key can also revoke the identity until the period expired revocation_key == idty_value.owner_key || (&revocation_key == old_owner_key && frame_system::Pallet::<T>::block_number() @@ -509,8 +511,10 @@ pub mod pallet { Error::<T>::InvalidRevocationKey ); + // make sure that no wot prevents identity removal T::CheckIdtyCallAllowed::check_remove_identity(idty_index)?; + // then check payload signature let genesis_hash = frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero()); let revocation_payload = RevocationPayload { genesis_hash, @@ -523,6 +527,7 @@ pub mod pallet { Error::<T>::InvalidRevocationSig ); + // finally if all checks pass, remove identity Self::do_remove_identity(idty_index); Ok(().into()) } @@ -639,6 +644,22 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config> Pallet<T> { + /// try to validate identity + // (used when membership is claimed first) + pub fn try_validate_identity(idty_index: T::IdtyIndex) { + if let Some(mut idty_value) = Identities::<T>::get(idty_index) { + // only does something if identity is not yet validated + if idty_value.status != IdtyStatus::Validated { + idty_value.removable_on = T::BlockNumber::zero(); + idty_value.status = IdtyStatus::Validated; + + <Identities<T>>::insert(idty_index, idty_value); + Self::deposit_event(Event::IdtyValidated { idty_index }); + } + // already validated, no need to re-validate + } + } + /// perform identity removal pub(super) fn do_remove_identity(idty_index: T::IdtyIndex) -> Weight { if let Some(idty_val) = Identities::<T>::get(idty_index) { diff --git a/pallets/identity/src/types.rs b/pallets/identity/src/types.rs index 58efc4d0ff9f1327a68f302685a2927c82e3bdac..1d633e29399e956f75a3117ce403ac349670a62e 100644 --- a/pallets/identity/src/types.rs +++ b/pallets/identity/src/types.rs @@ -75,12 +75,18 @@ impl<'de> serde::Deserialize<'de> for IdtyName { /// status of the identity /// used for temporary period before validation +/// also used for buffer when losing membership before being deleted #[cfg_attr(feature = "std", derive(Deserialize, Serialize))] #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum IdtyStatus { + /// created through a first certification Created, + /// confirmed by owner with a name published ConfirmedByOwner, + /// validated by the main web of trust Validated, + // disabled by the main web of trust, deletion planned + // Disabled, } impl Default for IdtyStatus { fn default() -> Self { diff --git a/pallets/membership/src/benchmarking.rs b/pallets/membership/src/benchmarking.rs index 0e0daff567c54f258cab599e1fdf221b4664f054..3196d10c52dfe0238103d77be1a0515585dbf0e7 100644 --- a/pallets/membership/src/benchmarking.rs +++ b/pallets/membership/src/benchmarking.rs @@ -37,12 +37,6 @@ benchmarks_instance_pallet! { where T::IdtyId: From<u32>, } - force_request_membership { - let idty: T::IdtyId = 5.into(); - }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), idty, T::MetaData ::default() ) - verify { - assert_has_event::<T, I>(Event::<T, I>::MembershipRequested(idty).into()); - } request_membership { // Dave identity (4) // for main wot, no constraints @@ -57,7 +51,7 @@ benchmarks_instance_pallet! { call.dispatch_bypass_filter(caller_origin).ok(); } verify { - if T::CheckCallAllowed::check_idty_allowed_to_request_membership(&idty).is_ok() { + if T::CheckMembershipCallAllowed::check_idty_allowed_to_request_membership(&idty).is_ok() { assert_has_event::<T, I>(Event::<T, I>::MembershipRequested(idty).into()); } } diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs index b8beaa20e2178cae4c8a3fc5917780f36100d566..b998ce5b5b35f4eb57910ad69b09dd88fce6ebf3 100644 --- a/pallets/membership/src/lib.rs +++ b/pallets/membership/src/lib.rs @@ -63,12 +63,12 @@ pub mod pallet { #[pallet::config] pub trait Config<I: 'static = ()>: frame_system::Config { /// Ask the runtime whether the identity can perform membership operations - type CheckCallAllowed: CheckCallAllowed<Self::IdtyId>; + type CheckMembershipCallAllowed: CheckMembershipCallAllowed<Self::IdtyId>; /// Something that identifies an identity type IdtyId: Copy + MaybeSerializeDeserialize + Parameter + Ord; - /// Something that give the IdtyId on an account id + /// Something that gives the IdtyId of an AccountId type IdtyIdOf: Convert<Self::AccountId, Option<Self::IdtyId>>; - /// Something that give the account id on an OdtyId + /// 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>; @@ -201,18 +201,6 @@ pub mod pallet { #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> { - /// request membership without checks - #[pallet::weight(T::WeightInfo::force_request_membership())] - pub fn force_request_membership( - origin: OriginFor<T>, - idty_id: T::IdtyId, - metadata: T::MetaData, - ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - - Self::do_request_membership(idty_id, metadata) - } - /// submit a membership request (must have a declared identity) /// (only available for sub wot, automatic for main wot) #[pallet::weight(T::WeightInfo::request_membership())] @@ -226,56 +214,39 @@ pub mod pallet { if !metadata.validate(&who) { return Err(Error::<T, I>::InvalidMetaData.into()); } - T::CheckCallAllowed::check_idty_allowed_to_request_membership(&idty_id)?; + T::CheckMembershipCallAllowed::check_idty_allowed_to_request_membership(&idty_id)?; Self::do_request_membership(idty_id, metadata) } - /// claim that the previously requested membership fullfills the requirements - /// (only available for sub wot, automatic for main wot) + /// claim pending membership to become actual memberhip + /// the requested membership must fullfill requirements + // for main wot claim_membership is called automatically when validating identity #[pallet::weight(T::WeightInfo::claim_membership())] - pub fn claim_membership( - origin: OriginFor<T>, - maybe_idty_id: Option<T::IdtyId>, - ) -> DispatchResultWithPostInfo { - // Verify phase - let idty_id = Self::ensure_origin_and_get_idty_id(origin, maybe_idty_id)?; - - ensure!( - !Membership::<T, I>::contains_key(idty_id), - Error::<T, I>::MembershipAlreadyAcquired - ); - - T::CheckCallAllowed::check_idty_allowed_to_claim_membership(&idty_id)?; - - let metadata = PendingMembership::<T, I>::take(idty_id) - .ok_or(Error::<T, I>::MembershipRequestNotFound)?; - - // Apply phase - Self::do_renew_membership_inner(idty_id); - Self::deposit_event(Event::MembershipAcquired(idty_id)); - T::OnEvent::on_event(&sp_membership::Event::MembershipAcquired(idty_id, metadata)); + pub fn claim_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo { + // get identity + let idty_id = Self::get_idty_id(origin)?; + Self::check_allowed_to_claim(idty_id)?; + Self::do_claim_membership(idty_id); Ok(().into()) } /// extend the validity period of an active membership #[pallet::weight(T::WeightInfo::renew_membership())] - pub fn renew_membership( - origin: OriginFor<T>, - maybe_idty_id: Option<T::IdtyId>, - ) -> DispatchResultWithPostInfo { + pub fn renew_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo { // Verify phase - let idty_id = Self::ensure_origin_and_get_idty_id(origin, maybe_idty_id)?; - - ensure!( - Self::get_membership(&idty_id).is_some(), - Error::<T, I>::MembershipNotFound - ); + let idty_id = Self::get_idty_id(origin)?; + let membership_data = + Membership::<T, I>::get(idty_id).ok_or(Error::<T, I>::MembershipNotFound)?; - T::CheckCallAllowed::check_idty_allowed_to_renew_membership(&idty_id)?; + T::CheckMembershipCallAllowed::check_idty_allowed_to_renew_membership(&idty_id)?; - let _ = Self::do_renew_membership(idty_id); + // apply phase + Self::unschedule_membership_expiry(idty_id, membership_data.expire_on); + Self::insert_membership_and_schedule_expiry(idty_id); + Self::deposit_event(Event::MembershipRenewed(idty_id)); + T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id)); Ok(().into()) } @@ -283,18 +254,12 @@ pub mod pallet { /// revoke an active membership /// (only available for sub wot, automatic for main wot) #[pallet::weight(T::WeightInfo::revoke_membership())] - pub fn revoke_membership( - origin: OriginFor<T>, - maybe_idty_id: Option<T::IdtyId>, - ) -> DispatchResultWithPostInfo { + pub fn revoke_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo { // Verify phase - let idty_id = Self::ensure_origin_and_get_idty_id(origin, maybe_idty_id)?; + let idty_id = Self::get_idty_id(origin)?; // Apply phase - if Self::remove_membership(&idty_id) { - Self::deposit_event(Event::MembershipRevoked(idty_id)); - T::OnEvent::on_event(&sp_membership::Event::MembershipRevoked(idty_id)); - } + Self::do_revoke_membership(idty_id); Ok(().into()) } @@ -303,27 +268,60 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config<I>, I: 'static> Pallet<T, I> { - /// perform the membership renewal and emit events - pub(super) fn do_renew_membership(idty_id: T::IdtyId) -> Weight { - let total_weight = Self::do_renew_membership_inner(idty_id); - Self::deposit_event(Event::MembershipRenewed(idty_id)); - T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id)); - total_weight + /// force request membership + pub fn force_request_membership( + idty_id: T::IdtyId, + metadata: T::MetaData, + ) -> DispatchResultWithPostInfo { + Self::do_request_membership(idty_id, metadata) } - /// perform the membership renewal - fn do_renew_membership_inner(idty_id: T::IdtyId) -> Weight { + + /// force expire membership + pub fn force_expire_membership(idty_id: T::IdtyId) -> DispatchResultWithPostInfo { + let new_expire_on = frame_system::pallet::Pallet::<T>::block_number() + + T::PendingMembershipPeriod::get(); + Self::do_expire_membership(idty_id, new_expire_on); + + Ok(().into()) + } + + /// try to claim membership if not already done + pub fn try_claim_membership(idty_id: T::IdtyId) { + if Self::check_allowed_to_claim(idty_id).is_ok() { + Self::do_claim_membership(idty_id); + } + // else { should not try to claim membership if not allowed} + } + + /// force revoke membership + pub fn force_revoke_membership(idty_id: T::IdtyId) { + Self::do_revoke_membership(idty_id); + } + + /// unschedule membership expiry + fn unschedule_membership_expiry(idty_id: T::IdtyId, block_number: T::BlockNumber) { + let mut scheduled = MembershipsExpireOn::<T, I>::get(block_number); + + if let Some(pos) = scheduled.iter().position(|x| *x == idty_id) { + scheduled.swap_remove(pos); + MembershipsExpireOn::<T, I>::set(block_number, scheduled); + } + } + /// schedule membership expiry + fn insert_membership_and_schedule_expiry(idty_id: T::IdtyId) { let block_number = frame_system::pallet::Pallet::<T>::block_number(); let expire_on = block_number + T::MembershipPeriod::get(); - Self::insert_membership(idty_id, MembershipData { expire_on }); + Membership::<T, I>::insert(idty_id, MembershipData { expire_on }); MembershipsExpireOn::<T, I>::append(expire_on, idty_id); - Weight::zero() } + /// perform the membership request fn do_request_membership( idty_id: T::IdtyId, metadata: T::MetaData, ) -> DispatchResultWithPostInfo { + // checks if PendingMembership::<T, I>::contains_key(idty_id) { return Err(Error::<T, I>::MembershipAlreadyRequested.into()); } @@ -334,6 +332,7 @@ pub mod pallet { let block_number = frame_system::pallet::Pallet::<T>::block_number(); let expire_on = block_number + T::PendingMembershipPeriod::get(); + // apply membership request PendingMembership::<T, I>::insert(idty_id, metadata); PendingMembershipsExpireOn::<T, I>::append(expire_on, idty_id); Self::deposit_event(Event::MembershipRequested(idty_id)); @@ -341,38 +340,71 @@ pub mod pallet { Ok(().into()) } + + /// check that membership can be claimed + pub fn check_allowed_to_claim(idty_id: T::IdtyId) -> Result<(), DispatchError> { + PendingMembership::<T, I>::get(idty_id) + .ok_or(Error::<T, I>::MembershipRequestNotFound)?; + // enough certifications and distance rule for example + T::CheckMembershipCallAllowed::check_idty_allowed_to_claim_membership(&idty_id)?; + Ok(()) + } + + /// perform membership claim + fn do_claim_membership(idty_id: T::IdtyId) { + if let Some(metadata) = PendingMembership::<T, I>::take(idty_id) { + 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)); + } + // else { unreachable if check_allowed_to_claim called before } + } + + /// perform membership revokation + fn do_revoke_membership(idty_id: T::IdtyId) { + if let Some(membership_data) = Membership::<T, I>::take(idty_id) { + Self::unschedule_membership_expiry(idty_id, membership_data.expire_on); + Self::deposit_event(Event::MembershipRevoked(idty_id)); + T::OnEvent::on_event(&sp_membership::Event::MembershipRevoked(idty_id)); + } + } + + /// perform mebership 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()); + PendingMembershipsExpireOn::<T, I>::append(expire_on, idty_id); + } // else should not happen + + Self::deposit_event(Event::MembershipExpired(idty_id)); + T::OnEvent::on_event(&sp_membership::Event::MembershipExpired(idty_id)) + } + /// check the origin and get identity id if valid - fn ensure_origin_and_get_idty_id( - origin: OriginFor<T>, - maybe_idty_id: Option<T::IdtyId>, - ) -> Result<T::IdtyId, DispatchError> { - match origin.into() { - Ok(RawOrigin::Root) => { - maybe_idty_id.ok_or_else(|| Error::<T, I>::IdtyIdNotFound.into()) - } - Ok(RawOrigin::Signed(account_id)) => T::IdtyIdOf::convert(account_id) - .ok_or_else(|| Error::<T, I>::IdtyIdNotFound.into()), - _ => Err(BadOrigin.into()), + fn get_idty_id(origin: OriginFor<T>) -> Result<T::IdtyId, DispatchError> { + if let Ok(RawOrigin::Signed(account_id)) = origin.into() { + T::IdtyIdOf::convert(account_id).ok_or_else(|| Error::<T, I>::IdtyIdNotFound.into()) + } else { + Err(BadOrigin.into()) } } - /// perform the membership expiration if it has not been renewed meanwhile + /// perform the membership expiry scheduled at given block + // MembershipExpired events should be handeled by main wot and delete identity + // expired membership get back to pending membership fn expire_memberships(block_number: T::BlockNumber) -> Weight { let mut total_weight: Weight = Weight::zero(); + let new_expire_on = block_number + T::PendingMembershipPeriod::get(); for idty_id in MembershipsExpireOn::<T, I>::take(block_number) { - if let Some(member_data) = Self::get_membership(&idty_id) { - if member_data.expire_on == block_number { - Self::remove_membership(&idty_id); - Self::deposit_event(Event::MembershipExpired(idty_id)); - total_weight += - T::OnEvent::on_event(&sp_membership::Event::MembershipExpired(idty_id)); - } - } + // remove membership (take) + total_weight += Self::do_expire_membership(idty_id, new_expire_on); } total_weight } /// perform the expiration of pending membership planned at given block + // only expire pending membership if still pending fn expire_pending_memberships(block_number: T::BlockNumber) -> Weight { let mut total_weight: Weight = Weight::zero(); @@ -392,18 +424,6 @@ pub mod pallet { pub(super) fn is_member_inner(idty_id: &T::IdtyId) -> bool { Membership::<T, I>::contains_key(idty_id) } - /// insert membership in storage - fn insert_membership(idty_id: T::IdtyId, membership_data: MembershipData<T::BlockNumber>) { - Membership::<T, I>::insert(idty_id, membership_data); - } - /// retreive membership from storage - fn get_membership(idty_id: &T::IdtyId) -> Option<MembershipData<T::BlockNumber>> { - Membership::<T, I>::try_get(idty_id).ok() - } - /// remove membership from storage - fn remove_membership(idty_id: &T::IdtyId) -> bool { - Membership::<T, I>::take(idty_id).is_some() - } } } diff --git a/pallets/membership/src/mock.rs b/pallets/membership/src/mock.rs index e51038e9a57e6b5c0566caf4c6b2c5a9e74dc890..b4a65ac6a33a5779c5fe77df9f95b06a26180393 100644 --- a/pallets/membership/src/mock.rs +++ b/pallets/membership/src/mock.rs @@ -83,7 +83,7 @@ parameter_types! { } impl pallet_membership::Config for Test { - type CheckCallAllowed = (); + type CheckMembershipCallAllowed = (); type IdtyId = IdtyId; type IdtyIdOf = ConvertInto; type AccountIdOf = ConvertInto; diff --git a/pallets/membership/src/tests.rs b/pallets/membership/src/tests.rs index b730a80f4474d9d46c11683a250548c055c9da90..f11ab9680a18bc8206a4bb5d8d114781b7235b93 100644 --- a/pallets/membership/src/tests.rs +++ b/pallets/membership/src/tests.rs @@ -16,12 +16,15 @@ use crate::mock::*; use crate::{Error, Event}; -use frame_support::assert_ok; +use frame_support::{assert_noop, assert_ok}; use maplit::btreemap; use sp_membership::traits::*; use sp_membership::MembershipData; use sp_runtime::traits::IsMember; +// alias +type RtEvent = RuntimeEvent; + fn default_gen_conf() -> DefaultMembershipConfig { DefaultMembershipConfig { memberships: btreemap![ @@ -49,10 +52,10 @@ fn test_genesis_build() { fn test_membership_already_acquired() { new_test_ext(default_gen_conf()).execute_with(|| { run_to_block(1); - // Merbership 0 cannot be reclaimed - assert_eq!( - DefaultMembership::claim_membership(RuntimeOrigin::signed(0), None), - Err(Error::<Test, _>::MembershipAlreadyAcquired.into()) + // Membership 0 cannot be reclaimed because there is no membership request + assert_noop!( + DefaultMembership::claim_membership(RuntimeOrigin::signed(0)), + Error::<Test, _>::MembershipRequestNotFound ); }); } @@ -61,73 +64,97 @@ fn test_membership_already_acquired() { fn test_membership_request_not_found() { new_test_ext(default_gen_conf()).execute_with(|| { run_to_block(1); - // Merbership 0 cannot be reclaimed - assert_eq!( - DefaultMembership::claim_membership(RuntimeOrigin::signed(1), None), - Err(Error::<Test, _>::MembershipRequestNotFound.into()) + // Membership 0 cannot be reclaimed + assert_noop!( + DefaultMembership::claim_membership(RuntimeOrigin::signed(1)), + Error::<Test, _>::MembershipRequestNotFound ); }); } +/// test membership expiration +// membership should be moved to pending membership and expire after #[test] -fn test_membership_renewal() { +fn test_membership_expiration() { new_test_ext(default_gen_conf()).execute_with(|| { + // Membership 0 should not expired on block #2 run_to_block(2); - // Merbership 0 can be renewable on block #2 - assert_ok!(DefaultMembership::renew_membership( - RuntimeOrigin::signed(0), - None - ),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipRenewed(0)) - ); + assert!(DefaultMembership::is_member(&0)); + // Membership 0 should expire on block #3 + run_to_block(3); + assert!(!DefaultMembership::is_member(&0)); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipExpired(0))); + // it should be added to pending membership and expire on block #6 + run_to_block(6); + System::assert_has_event(RtEvent::DefaultMembership(Event::PendingMembershipExpired( + 0, + ))); }); } +/// test membership renewal +// there is no limit for membership renewal outside wot rules (number of certs, distance rule) #[test] -fn test_membership_expiration() { +fn test_membership_renewal() { new_test_ext(default_gen_conf()).execute_with(|| { - // Merbership 0 should not expired on block #2 + // membership still valid at block 2 run_to_block(2); - assert!(DefaultMembership::is_member(&0),); - // Merbership 0 should expire on block #3 + assert!(DefaultMembership::is_member(&0)); + // Membership 0 can be renewed + assert_ok!(DefaultMembership::renew_membership(RuntimeOrigin::signed( + 0 + ),)); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRenewed(0))); + // membership should not expire at block 3 to 6 because it has been renewed run_to_block(3); - assert!(!DefaultMembership::is_member(&0),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipExpired(0)) + assert!(DefaultMembership::is_member(&0)); + run_to_block(6); + assert!(DefaultMembership::is_member(&0)); + // membership should expire at block 7 (2+5) + run_to_block(7); + assert!(!DefaultMembership::is_member(&0)); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipExpired(0))); + }); +} + +/// test membership renewal for non member identity +#[test] +fn test_membership_renewal_nope() { + new_test_ext(default_gen_conf()).execute_with(|| { + run_to_block(2); + assert!(!DefaultMembership::is_member(&1)); + // Membership 1 can not be renewed + assert_noop!( + DefaultMembership::renew_membership(RuntimeOrigin::signed(1)), + Error::<Test, _>::MembershipNotFound, ); + run_to_block(3); + assert!(!DefaultMembership::is_member(&1)); }); } +/// test membership revocation #[test] fn test_membership_revocation() { new_test_ext(default_gen_conf()).execute_with(|| { run_to_block(1); - // Merbership 0 can be revocable on block #1 - assert_ok!(DefaultMembership::revoke_membership( - RuntimeOrigin::signed(0), - None - ),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipRevoked(0)) - ); + // Membership 0 can be revocable on block #1 + assert_ok!(DefaultMembership::revoke_membership(RuntimeOrigin::signed( + 0 + ),)); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRevoked(0))); // Membership 0 can re-request membership run_to_block(5); assert_ok!(DefaultMembership::request_membership( RuntimeOrigin::signed(0), () - ),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipRequested(0)) - ); + )); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRequested(0))); }); } +/// test pending membership expiration #[test] fn test_pending_membership_expiration() { new_test_ext(Default::default()).execute_with(|| { @@ -136,68 +163,58 @@ fn test_pending_membership_expiration() { assert_ok!(DefaultMembership::request_membership( RuntimeOrigin::signed(0), () - ),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipRequested(0)) - ); + )); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRequested(0))); // Then, idty 0 shold still in pending memberships until PendingMembershipPeriod ended run_to_block(PendingMembershipPeriod::get()); - assert!(DefaultMembership::is_in_pending_memberships(0),); + assert!(DefaultMembership::is_in_pending_memberships(0)); // Then, idty 0 request should expire after PendingMembershipPeriod run_to_block(1 + PendingMembershipPeriod::get()); - assert!(!DefaultMembership::is_in_pending_memberships(0),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::PendingMembershipExpired(0)) - ); + assert!(!DefaultMembership::is_in_pending_memberships(0)); + System::assert_has_event(RtEvent::DefaultMembership(Event::PendingMembershipExpired( + 0, + ))); }) } +/// test membership workflow +// - request membership +// - claim membership +// - renew membership +// - membership expiry #[test] fn test_membership_workflow() { new_test_ext(Default::default()).execute_with(|| { - // Idty 0 request membership + // - Idty 0 request membership run_to_block(1); assert_ok!(DefaultMembership::request_membership( RuntimeOrigin::signed(0), () - ),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipRequested(0)) - ); + )); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRequested(0))); - // Then, idty 0 claim membership + // - Then, idty 0 claim membership run_to_block(2); - assert_ok!(DefaultMembership::claim_membership( - RuntimeOrigin::signed(0), - None - ),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipAcquired(0)) - ); + assert_ok!(DefaultMembership::claim_membership(RuntimeOrigin::signed( + 0 + ),)); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAcquired(0))); - // Then, idty 0 claim renewal, should success + // - Then, idty 0 claim renewal, should success run_to_block(2); - assert_ok!(DefaultMembership::renew_membership( - RuntimeOrigin::signed(0), - None - ),); + assert_ok!(DefaultMembership::renew_membership(RuntimeOrigin::signed( + 0 + ),)); - // Then, idty 0 shoul still member until membership period ended - run_to_block(2 + MembershipPeriod::get() - 1); + // idty 0 should still be member until membership period ended + run_to_block(6); // 2 + 5 - 1 assert!(DefaultMembership::is_member(&0)); - // Then, idty 0 shoul expire after membership period - run_to_block(2 + MembershipPeriod::get()); - assert!(!DefaultMembership::is_member(&0),); - assert_eq!( - System::events()[0].event, - RuntimeEvent::DefaultMembership(Event::MembershipExpired(0)) - ); + // - Then, idty 0 should expire after membership period + run_to_block(7); // 2 + 5 + assert!(!DefaultMembership::is_member(&0)); + System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipExpired(0))); }); } diff --git a/primitives/membership/src/traits.rs b/primitives/membership/src/traits.rs index 9ab0d5b4e953b5d722ebd7c9c8e8bc51aaced363..0480a64f38aff07a12448a012e7860fbcd82c6a8 100644 --- a/primitives/membership/src/traits.rs +++ b/primitives/membership/src/traits.rs @@ -16,13 +16,13 @@ use frame_support::pallet_prelude::*; -pub trait CheckCallAllowed<IdtyId> { +pub trait CheckMembershipCallAllowed<IdtyId> { fn check_idty_allowed_to_claim_membership(idty_id: &IdtyId) -> Result<(), DispatchError>; fn check_idty_allowed_to_renew_membership(idty_id: &IdtyId) -> Result<(), DispatchError>; fn check_idty_allowed_to_request_membership(idty_id: &IdtyId) -> Result<(), DispatchError>; } -impl<IdtyId> CheckCallAllowed<IdtyId> for () { +impl<IdtyId> CheckMembershipCallAllowed<IdtyId> for () { fn check_idty_allowed_to_claim_membership(_: &IdtyId) -> Result<(), DispatchError> { Ok(()) } diff --git a/resources/metadata.scale b/resources/metadata.scale index 06d4f6891828dfade5738b4d106249ba753edd96..6c236f8d95a610f8b1044687302dd08fea17a14d 100644 Binary files a/resources/metadata.scale and b/resources/metadata.scale differ diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs index 2ea464a2fbe5e04e98e7bb2ff7d0ffb8b684f7e9..95a6ccabdf9bab3144fbc8163c37e7fbac4de79a 100644 --- a/runtime/common/src/handlers.rs +++ b/runtime/common/src/handlers.rs @@ -24,6 +24,7 @@ use frame_support::Parameter; use pallet_identity::IdtyEvent; use sp_runtime::traits::IsMember; +// new session handler pub struct OnNewSessionHandler<Runtime>(core::marker::PhantomData<Runtime>); impl<Runtime> pallet_authority_members::traits::OnNewSession for OnNewSessionHandler<Runtime> where @@ -35,8 +36,8 @@ where } } +// identity change runtime handler pub struct OnIdtyChangeHandler<Runtime>(core::marker::PhantomData<Runtime>); - impl<T> pallet_identity::traits::OnIdtyChange<T> for OnIdtyChangeHandler<T> where T: frame_system::Config<AccountId = AccountId>, @@ -47,14 +48,8 @@ where fn on_idty_change(idty_index: IdtyIndex, idty_event: &IdtyEvent<T>) -> Weight { match idty_event { IdtyEvent::Validated => { - pallet_identity::Identities::<T>::mutate_exists(idty_index, |idty_val_opt| { - if let Some(ref mut idty_val) = idty_val_opt { - idty_val.data = IdtyData { - first_eligible_ud: - pallet_universal_dividend::Pallet::<T>::init_first_eligible_ud(), - } - } - }); + // when identity is validated, it starts getting right to UD + // but this is handeled by membership event handler (MembershipAcquired) } IdtyEvent::ChangedOwnerKey { new_owner_key } => { if let Err(e) = pallet_authority_members::Pallet::<T>::change_owner_key( @@ -73,8 +68,8 @@ where } } +// membership event runtime handler pub struct OnMembershipEventHandler<Inner, Runtime>(core::marker::PhantomData<(Inner, Runtime)>); - impl< Inner: sp_membership::traits::OnEvent<IdtyIndex, ()>, Runtime: frame_system::Config<AccountId = AccountId> @@ -85,7 +80,9 @@ impl< { fn on_event(membership_event: &sp_membership::Event<IdtyIndex, ()>) -> Weight { (match membership_event { - sp_membership::Event::MembershipRevoked(idty_index) => { + // when membership is removed, call on_removed_member handler which auto claims UD + sp_membership::Event::MembershipRevoked(idty_index) + | sp_membership::Event::MembershipExpired(idty_index) => { if let Some(idty_value) = pallet_identity::Identities::<Runtime>::get(idty_index) { if let Some(first_ud_index) = idty_value.data.first_eligible_ud.into() { pallet_universal_dividend::Pallet::<Runtime>::on_removed_member( @@ -99,15 +96,31 @@ impl< Runtime::DbWeight::get().reads(1) } } - _ => Weight::zero(), + // when main membership is acquired, it starts getting right to UD + 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 { + first_eligible_ud: + pallet_universal_dividend::Pallet::<Runtime>::init_first_eligible_ud( + ), + } + } + }); + Weight::zero() + } + // in other case, ther is nothing to do + sp_membership::Event::MembershipRenewed(_) + | sp_membership::Event::MembershipRequested(_) + | sp_membership::Event::PendingMembershipExpired(_) => Weight::zero(), }) + Inner::on_event(membership_event) } } +// smith membership event handler pub struct OnSmithMembershipEventHandler<Inner, Runtime>( core::marker::PhantomData<(Inner, Runtime)>, ); - impl< IdtyIndex: Copy + Parameter, SessionKeysWrapper: Clone, @@ -167,6 +180,7 @@ impl< } } +// authority member removal handler pub struct OnRemovedAuthorityMemberHandler<Runtime>(core::marker::PhantomData<Runtime>); impl<Runtime> pallet_authority_members::traits::OnRemovedMember<IdtyIndex> for OnRemovedAuthorityMemberHandler<Runtime> @@ -174,18 +188,14 @@ where Runtime: frame_system::Config + pallet_membership::Config<Instance2, IdtyId = IdtyIndex>, { fn on_removed_member(idty_index: IdtyIndex) -> Weight { - if let Err(e) = pallet_membership::Pallet::<Runtime, Instance2>::revoke_membership( - frame_system::RawOrigin::Root.into(), - Some(idty_index), - ) { - sp_std::if_std! { - println!("fail to revoke membership: {:?}", e) - } - } + // TODO investigate why we should remove smith membership when removing authority member + pallet_membership::Pallet::<Runtime, Instance2>::force_revoke_membership(idty_index); + // TODO investigate why weight zero Weight::zero() } } +// identity removal handler pub struct RemoveIdentityConsumersImpl<Runtime>(core::marker::PhantomData<Runtime>); impl<Runtime> pallet_identity::traits::RemoveIdentityConsumers<IdtyIndex> for RemoveIdentityConsumersImpl<Runtime> @@ -198,33 +208,16 @@ where fn remove_idty_consumers(idty_index: IdtyIndex) -> Weight { // Remove smith member if pallet_membership::Pallet::<Runtime, Instance2>::is_member(&idty_index) { - if let Err(e) = pallet_membership::Pallet::<Runtime, Instance2>::revoke_membership( - frame_system::RawOrigin::Root.into(), - Some(idty_index), - ) { - log::error!( - target: "runtime::common", - "Logic error: fail to revoke smith membership in remove_idty_consumers(): {:?}", - e - ); - } + pallet_membership::Pallet::<Runtime, Instance2>::force_revoke_membership(idty_index); } // Remove "classic" member - if let Err(e) = pallet_membership::Pallet::<Runtime, Instance1>::revoke_membership( - frame_system::RawOrigin::Root.into(), - Some(idty_index), - ) { - log::error!( - target: "runtime::common", - "Logic error: fail to revoke membership in remove_idty_consumers(): {:?}", - e - ); - } + pallet_membership::Pallet::<Runtime, Instance1>::force_revoke_membership(idty_index); Weight::zero() } } +// spend treasury handler pub struct TreasurySpendFunds<Runtime>(core::marker::PhantomData<Runtime>); impl<Runtime> pallet_treasury::SpendFunds<Runtime> for TreasurySpendFunds<Runtime> where diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index d89fdcb779f922a95a726f44b0c074015dca761e..3aa82401a4d6241deb34ac7b58c0505987abe48d 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -449,7 +449,7 @@ macro_rules! pallets_config { } impl pallet_membership::Config<frame_support::instances::Instance1> for Runtime { - type CheckCallAllowed = Wot; + type CheckMembershipCallAllowed = Wot; type IdtyId = IdtyIndex; type IdtyIdOf = common_runtime::providers::IdentityIndexOf<Self>; type AccountIdOf = common_runtime::providers::IdentityAccountIdProvider<Self>; @@ -486,7 +486,7 @@ macro_rules! pallets_config { } impl pallet_membership::Config<Instance2> for Runtime { - type CheckCallAllowed = SmithSubWot; + type CheckMembershipCallAllowed = SmithSubWot; type IdtyId = IdtyIndex; type IdtyIdOf = common_runtime::providers::IdentityIndexOf<Self>; type AccountIdOf = common_runtime::providers::IdentityAccountIdProvider<Self>; diff --git a/runtime/common/src/weights/pallet_certification_cert.rs b/runtime/common/src/weights/pallet_certification_cert.rs index 44140695115a66338b96c25d907af876ec43d299..acd4cd7aa9018d5a6e04a77e83e191f25ab6187c 100644 --- a/runtime/common/src/weights/pallet_certification_cert.rs +++ b/runtime/common/src/weights/pallet_certification_cert.rs @@ -14,21 +14,21 @@ // 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/>. -//! Autogenerated weights for `common_runtime::certification` +//! Autogenerated weights for `pallet_certification` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `benjamin-xps139380`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-05-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Hugo`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("gdev-benchmark"), DB CACHE: 1024 // Executed Command: // ./target/release/duniter // benchmark // pallet -// --chain=dev +// --chain=gdev-benchmark // --steps=50 // --repeat=20 -// --pallet=common_runtime::certification +// --pallet=pallet-certification // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -43,16 +43,16 @@ use frame_support::{traits::Get, weights::Weight}; use sp_std::marker::PhantomData; -/// Weight functions for `common_runtime::certification`. +/// Weight functions for `pallet_certification`. pub struct WeightInfo<T>(PhantomData<T>); impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> { // Storage: Cert StorageIdtyCertMeta (r:2 w:2) // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Cert StorageCertsRemovableOn (r:1 w:1) // Storage: Cert CertsByReceiver (r:1 w:1) - fn force_add_cert() -> Weight { - // Minimum execution time: 125_388 nanoseconds. - Weight::from_ref_time(140_411_000 as u64) + fn do_add_cert_checked() -> Weight { + // Minimum execution time: 48_363 nanoseconds. + Weight::from_ref_time(59_125_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -62,8 +62,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> // Storage: Cert StorageCertsRemovableOn (r:1 w:1) // Storage: Cert CertsByReceiver (r:1 w:1) fn add_cert() -> Weight { - // Minimum execution time: 147_817 nanoseconds. - Weight::from_ref_time(153_330_000 as u64) + // Minimum execution time: 57_408 nanoseconds. + Weight::from_ref_time(69_281_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -72,8 +72,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Membership Membership (r:1 w:0) fn del_cert() -> Weight { - // Minimum execution time: 125_695 nanoseconds. - Weight::from_ref_time(150_360_000 as u64) + // Minimum execution time: 49_562 nanoseconds. + Weight::from_ref_time(61_695_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -84,10 +84,10 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> /// The range of component `i` is `[2, 1000]`. /// The range of component `i` is `[2, 1000]`. fn remove_all_certs_received_by(i: u32, ) -> Weight { - // Minimum execution time: 129_529 nanoseconds. - Weight::from_ref_time(132_136_000 as u64) - // Standard Error: 75_161 - .saturating_add(Weight::from_ref_time(38_016_325 as u64).saturating_mul(i as u64)) + // Minimum execution time: 49_307 nanoseconds. + Weight::from_ref_time(49_926_000 as u64) + // Standard Error: 61_521 + .saturating_add(Weight::from_ref_time(15_865_456 as u64).saturating_mul(i as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(i as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) diff --git a/runtime/common/src/weights/pallet_certification_smith_cert.rs b/runtime/common/src/weights/pallet_certification_smith_cert.rs index 024b4b8a5a072227baf782dea642320a11b60226..6f735ab2bc1e8746e9869b468028be48cde89864 100644 --- a/runtime/common/src/weights/pallet_certification_smith_cert.rs +++ b/runtime/common/src/weights/pallet_certification_smith_cert.rs @@ -14,21 +14,21 @@ // 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/>. -//! Autogenerated weights for `common_runtime::certification` +//! Autogenerated weights for `pallet_certification` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `benjamin-xps139380`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-05-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Hugo`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("gdev-benchmark"), DB CACHE: 1024 // Executed Command: // ./target/release/duniter // benchmark // pallet -// --chain=dev +// --chain=gdev-benchmark // --steps=50 // --repeat=20 -// --pallet=common_runtime::certification +// --pallet=pallet-certification // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -43,16 +43,16 @@ use frame_support::{traits::Get, weights::Weight}; use sp_std::marker::PhantomData; -/// Weight functions for `common_runtime::certification`. +/// Weight functions for `pallet_certification`. pub struct WeightInfo<T>(PhantomData<T>); impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> { // Storage: SmithCert StorageIdtyCertMeta (r:2 w:2) // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: SmithCert StorageCertsRemovableOn (r:1 w:1) // Storage: SmithCert CertsByReceiver (r:1 w:1) - fn force_add_cert() -> Weight { - // Minimum execution time: 125_287 nanoseconds. - Weight::from_ref_time(128_059_000 as u64) + fn do_add_cert_checked() -> Weight { + // Minimum execution time: 57_241 nanoseconds. + Weight::from_ref_time(69_631_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -62,8 +62,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> // Storage: SmithCert StorageCertsRemovableOn (r:1 w:1) // Storage: SmithCert CertsByReceiver (r:1 w:1) fn add_cert() -> Weight { - // Minimum execution time: 147_626 nanoseconds. - Weight::from_ref_time(152_571_000 as u64) + // Minimum execution time: 65_925 nanoseconds. + Weight::from_ref_time(79_971_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -72,8 +72,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: SmithMembership Membership (r:1 w:0) fn del_cert() -> Weight { - // Minimum execution time: 126_824 nanoseconds. - Weight::from_ref_time(130_111_000 as u64) + // Minimum execution time: 50_260 nanoseconds. + Weight::from_ref_time(62_319_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -84,10 +84,10 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> /// The range of component `i` is `[2, 1000]`. /// The range of component `i` is `[2, 1000]`. fn remove_all_certs_received_by(i: u32, ) -> Weight { - // Minimum execution time: 130_926 nanoseconds. - Weight::from_ref_time(133_225_000 as u64) - // Standard Error: 66_989 - .saturating_add(Weight::from_ref_time(37_841_523 as u64).saturating_mul(i as u64)) + // Minimum execution time: 50_222 nanoseconds. + Weight::from_ref_time(50_794_000 as u64) + // Standard Error: 48_765 + .saturating_add(Weight::from_ref_time(15_587_131 as u64).saturating_mul(i as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(i as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) diff --git a/runtime/common/src/weights/pallet_identity.rs b/runtime/common/src/weights/pallet_identity.rs index 8d398bb67fecee565114b62076e49ee60dc0f9a3..f438c79f70232d494bf5c9e8239ee27be2136293 100644 --- a/runtime/common/src/weights/pallet_identity.rs +++ b/runtime/common/src/weights/pallet_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Axiom-Team +// Copyright 2021-2022 Axiom-Team // // This file is part of Duniter-v2S. // @@ -14,11 +14,11 @@ // 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/>. -//! Autogenerated weights for `common_runtime::identity` +//! Autogenerated weights for `pallet_identity` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `benjamin-xps139380`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` +//! DATE: 2023-05-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Hugo`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("gdev-benchmark"), DB CACHE: 1024 // Executed Command: @@ -28,13 +28,13 @@ // --chain=gdev-benchmark // --steps=50 // --repeat=20 -// --pallet=common_runtime::identity +// --pallet=pallet-identity // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 // --header=./file_header.txt -// --output=./runtime/common/src/weights/pallet_identity.rs +// --output=./runtime/common/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -43,7 +43,7 @@ use frame_support::{traits::Get, weights::Weight}; use sp_std::marker::PhantomData; -/// Weight functions for `common_runtime::identity`. +/// Weight functions for `pallet_identity`. pub struct WeightInfo<T>(PhantomData<T>); impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { // Storage: Identity IdentityIndexOf (r:2 w:1) @@ -57,8 +57,8 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { // Storage: Cert StorageCertsRemovableOn (r:1 w:1) // Storage: Cert CertsByReceiver (r:1 w:1) fn create_identity() -> Weight { - // Minimum execution time: 440_987 nanoseconds. - Weight::from_ref_time(462_747_000 as u64) + // Minimum execution time: 121_934 nanoseconds. + Weight::from_ref_time(138_522_000 as u64) .saturating_add(T::DbWeight::get().reads(13 as u64)) .saturating_add(T::DbWeight::get().writes(11 as u64)) } @@ -70,8 +70,8 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Membership PendingMembershipsExpireOn (r:1 w:1) fn confirm_identity() -> Weight { - // Minimum execution time: 186_617 nanoseconds. - Weight::from_ref_time(309_527_000 as u64) + // Minimum execution time: 86_584 nanoseconds. + Weight::from_ref_time(98_246_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -84,8 +84,8 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { // Storage: Membership MembershipsExpireOn (r:1 w:1) // Storage: UniversalDividend CurrentUdIndex (r:1 w:0) fn validate_identity() -> Weight { - // Minimum execution time: 299_920 nanoseconds. - Weight::from_ref_time(320_025_000 as u64) + // Minimum execution time: 91_201 nanoseconds. + Weight::from_ref_time(94_836_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(5 as u64)) } @@ -93,29 +93,29 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { // Storage: Identity Identities (r:1 w:1) // Storage: SmithMembership Membership (r:1 w:0) // Storage: System BlockHash (r:1 w:0) - // Storage: System Account (r:1 w:1) + // Storage: System Account (r:2 w:2) // Storage: AuthorityMembers Members (r:1 w:0) fn change_owner_key() -> Weight { - // Minimum execution time: 442_260 nanoseconds. - Weight::from_ref_time(728_714_000 as u64) - .saturating_add(T::DbWeight::get().reads(7 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Minimum execution time: 213_666 nanoseconds. + Weight::from_ref_time(247_995_000 as u64) + .saturating_add(T::DbWeight::get().reads(8 as u64)) + .saturating_add(T::DbWeight::get().writes(5 as u64)) } // Storage: Identity Identities (r:1 w:1) // Storage: SmithMembership Membership (r:1 w:0) // Storage: System BlockHash (r:1 w:0) // Storage: Membership Membership (r:1 w:1) // Storage: Identity CounterForIdentities (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Storage: System Account (r:2 w:2) // Storage: Cert CertsByReceiver (r:1 w:1) // Storage: Cert StorageIdtyCertMeta (r:2 w:2) // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Identity IdentityIndexOf (r:0 w:1) fn revoke_identity() -> Weight { - // Minimum execution time: 494_407 nanoseconds. - Weight::from_ref_time(800_824_000 as u64) - .saturating_add(T::DbWeight::get().reads(10 as u64)) - .saturating_add(T::DbWeight::get().writes(8 as u64)) + // Minimum execution time: 227_151 nanoseconds. + Weight::from_ref_time(284_771_000 as u64) + .saturating_add(T::DbWeight::get().reads(11 as u64)) + .saturating_add(T::DbWeight::get().writes(9 as u64)) } // Storage: Identity Identities (r:1 w:1) // Storage: SmithMembership Membership (r:1 w:0) @@ -128,24 +128,24 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { // Storage: Identity IdentityIndexOf (r:0 w:1) // Storage: Identity IdentitiesNames (r:0 w:1) fn remove_identity() -> Weight { - // Minimum execution time: 302_574 nanoseconds. - Weight::from_ref_time(504_132_000 as u64) + // Minimum execution time: 110_561 nanoseconds. + Weight::from_ref_time(119_198_000 as u64) .saturating_add(T::DbWeight::get().reads(9 as u64)) .saturating_add(T::DbWeight::get().writes(9 as u64)) } // Storage: Identity IdentitiesNames (r:0 w:20) /// The range of component `i` is `[1, 1000]`. fn prune_item_identities_names(i: u32, ) -> Weight { - // Minimum execution time: 22_533 nanoseconds. - Weight::from_ref_time(282_674_421 as u64) - // Standard Error: 170_391 - .saturating_add(Weight::from_ref_time(5_660_460 as u64).saturating_mul(i as u64)) + // Minimum execution time: 10_675 nanoseconds. + Weight::from_ref_time(10_809_000 as u64) + // Standard Error: 3_281 + .saturating_add(Weight::from_ref_time(1_492_089 as u64).saturating_mul(i as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(i as u64))) } // Storage: System Account (r:1 w:1) fn fix_sufficients() -> Weight { - // Minimum execution time: 112_793 nanoseconds. - Weight::from_ref_time(122_192_000 as u64) + // Minimum execution time: 23_639 nanoseconds. + Weight::from_ref_time(29_075_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } diff --git a/runtime/common/src/weights/pallet_membership_membership.rs b/runtime/common/src/weights/pallet_membership_membership.rs index 799f51a0c1560633e85124c3673957343a829d07..ddbc76f57a19b1cc2edc4948e39522101838c418 100644 --- a/runtime/common/src/weights/pallet_membership_membership.rs +++ b/runtime/common/src/weights/pallet_membership_membership.rs @@ -17,18 +17,18 @@ //! Autogenerated weights for `pallet_membership` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-26, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `benjamin-xps139380`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-05-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Hugo`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("gdev-benchmark"), DB CACHE: 1024 // Executed Command: -// target/release/duniter +// ./target/release/duniter // benchmark // pallet -// --chain=dev +// --chain=gdev-benchmark // --steps=50 // --repeat=20 -// --pallet=pallet_membership +// --pallet=pallet-membership // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -51,16 +51,16 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Membership PendingMembershipsExpireOn (r:1 w:1) fn force_request_membership() -> Weight { - // Minimum execution time: 89_725 nanoseconds. - Weight::from_ref_time(98_333_000 as u64) + // Minimum execution time: 42_024 nanoseconds. + Weight::from_ref_time(55_713_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Identity IdentityIndexOf (r:1 w:0) // Storage: Identity Identities (r:1 w:0) fn request_membership() -> Weight { - // Minimum execution time: 48_477 nanoseconds. - Weight::from_ref_time(50_689_000 as u64) + // Minimum execution time: 19_511 nanoseconds. + Weight::from_ref_time(37_123_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) } // Storage: Identity IdentityIndexOf (r:1 w:0) @@ -71,8 +71,8 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: Membership CounterForMembership (r:1 w:1) // Storage: Membership MembershipsExpireOn (r:1 w:1) fn claim_membership() -> Weight { - // Minimum execution time: 144_079 nanoseconds. - Weight::from_ref_time(146_565_000 as u64) + // Minimum execution time: 80_875 nanoseconds. + Weight::from_ref_time(117_287_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -82,8 +82,8 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: Membership MembershipsExpireOn (r:1 w:1) fn renew_membership() -> Weight { - // Minimum execution time: 120_859 nanoseconds. - Weight::from_ref_time(124_222_000 as u64) + // Minimum execution time: 69_195 nanoseconds. + Weight::from_ref_time(91_620_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -93,8 +93,8 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: Identity Identities (r:1 w:0) // Storage: UniversalDividend CurrentUdIndex (r:1 w:0) fn revoke_membership() -> Weight { - // Minimum execution time: 109_486 nanoseconds. - Weight::from_ref_time(113_303_000 as u64) + // Minimum execution time: 64_419 nanoseconds. + Weight::from_ref_time(103_812_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } diff --git a/runtime/common/src/weights/pallet_membership_smith_membership.rs b/runtime/common/src/weights/pallet_membership_smith_membership.rs index 2ecd9b61ebea89cfb5c195359a9958e9cc22c914..cdc459c197885d84fe14505ef96a0c13d25f72c8 100644 --- a/runtime/common/src/weights/pallet_membership_smith_membership.rs +++ b/runtime/common/src/weights/pallet_membership_smith_membership.rs @@ -17,18 +17,18 @@ //! Autogenerated weights for `pallet_membership` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-26, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `benjamin-xps139380`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-05-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Hugo`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("gdev-benchmark"), DB CACHE: 1024 // Executed Command: -// target/release/duniter +// ./target/release/duniter // benchmark // pallet -// --chain=dev +// --chain=gdev-benchmark // --steps=50 // --repeat=20 -// --pallet=pallet_membership +// --pallet=pallet-membership // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -51,22 +51,17 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: SmithMembership PendingMembershipsExpireOn (r:1 w:1) fn force_request_membership() -> Weight { - // Minimum execution time: 96_077 nanoseconds. - Weight::from_ref_time(98_570_000 as u64) + // Minimum execution time: 42_163 nanoseconds. + Weight::from_ref_time(49_619_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Identity IdentityIndexOf (r:1 w:0) // Storage: Identity Identities (r:1 w:0) - // Storage: SmithMembership PendingMembership (r:1 w:1) - // Storage: SmithMembership Membership (r:1 w:0) - // Storage: Parameters ParametersStorage (r:1 w:0) - // Storage: SmithMembership PendingMembershipsExpireOn (r:1 w:1) fn request_membership() -> Weight { - // Minimum execution time: 122_839 nanoseconds. - Weight::from_ref_time(125_861_000 as u64) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 20_399 nanoseconds. + Weight::from_ref_time(24_906_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) } // Storage: Identity IdentityIndexOf (r:2 w:0) // Storage: SmithMembership Membership (r:2 w:1) @@ -76,8 +71,8 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: SmithMembership CounterForMembership (r:1 w:1) // Storage: SmithMembership MembershipsExpireOn (r:1 w:1) fn claim_membership() -> Weight { - // Minimum execution time: 165_369 nanoseconds. - Weight::from_ref_time(167_607_000 as u64) + // Minimum execution time: 95_541 nanoseconds. + Weight::from_ref_time(157_882_000 as u64) .saturating_add(T::DbWeight::get().reads(9 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } @@ -87,8 +82,8 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: Parameters ParametersStorage (r:1 w:0) // Storage: SmithMembership MembershipsExpireOn (r:1 w:1) fn renew_membership() -> Weight { - // Minimum execution time: 121_761 nanoseconds. - Weight::from_ref_time(125_210_000 as u64) + // Minimum execution time: 70_298 nanoseconds. + Weight::from_ref_time(109_069_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -103,8 +98,8 @@ impl<T: frame_system::Config> pallet_membership::WeightInfo for WeightInfo<T> { // Storage: System Account (r:1 w:1) // Storage: Session KeyOwner (r:0 w:4) fn revoke_membership() -> Weight { - // Minimum execution time: 234_173 nanoseconds. - Weight::from_ref_time(239_334_000 as u64) + // Minimum execution time: 138_537 nanoseconds. + Weight::from_ref_time(179_540_000 as u64) .saturating_add(T::DbWeight::get().reads(9 as u64)) .saturating_add(T::DbWeight::get().writes(12 as u64)) } diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs index f7cf7b58c7208de5221946b71ea1c1d6937a9e66..90350ab3d19a960342d232d1d1c6e6a366392651 100644 --- a/runtime/gdev/src/lib.rs +++ b/runtime/gdev/src/lib.rs @@ -168,30 +168,16 @@ mod benches { } pub struct BaseCallFilter; -#[cfg(not(feature = "runtime-benchmarks"))] -impl Contains<RuntimeCall> for BaseCallFilter { - fn contains(call: &RuntimeCall) -> bool { - !matches!( - call, - /*RuntimeCall::System( - frame_system::Call::remark { .. } | frame_system::Call::remark_with_event { .. } - ) | */ - RuntimeCall::Membership( - pallet_membership::Call::request_membership { .. } - | pallet_membership::Call::claim_membership { .. } - | pallet_membership::Call::revoke_membership { .. } - ) | RuntimeCall::Session(_) - ) - } -} -#[cfg(feature = "runtime-benchmarks")] + +// implement filter impl Contains<RuntimeCall> for BaseCallFilter { fn contains(call: &RuntimeCall) -> bool { !matches!( call, + // in main web of trust, membership request and revoke are handeled through identity pallet + // the user can not call them directly RuntimeCall::Membership( pallet_membership::Call::request_membership { .. } - | pallet_membership::Call::claim_membership { .. } | pallet_membership::Call::revoke_membership { .. } ) | RuntimeCall::Session(_) ) diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs index 60d0bd62d7fc306cbb76ed08697ad25b8c6a9855..438ffa81fad84b4e9ced32960beeef81171b283b 100644 --- a/runtime/gdev/tests/common/mod.rs +++ b/runtime/gdev/tests/common/mod.rs @@ -102,7 +102,7 @@ impl ExtBuilder { cert_validity_period: 10_000, idty_confirm_period: 40, idty_creation_period: 50, - membership_period: 1_000, + membership_period: 100, pending_membership_period: 500, ud_creation_period: 10, ud_reeval_period: 10 * 20, diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs index 0588a74aabc46df705eca45fcea44c9ab94be44c..22d1fb05626ce146ea8fde5e57fe23c0b0d0af4f 100644 --- a/runtime/gdev/tests/integration_tests.rs +++ b/runtime/gdev/tests/integration_tests.rs @@ -26,7 +26,7 @@ use sp_runtime::MultiAddress; #[test] fn verify_treasury_account() { - println!("{}", Treasury::account_id()); + // println!("{}", Treasury::account_id()); } #[test] @@ -81,6 +81,7 @@ fn test_genesis_build() { }); } +/// test calling remove_identity #[test] fn test_remove_identity() { ExtBuilder::new(1, 3, 4).build().execute_with(|| { @@ -103,6 +104,104 @@ fn test_remove_identity() { )); }); } + +/// test identity is validated when membership is claimed +#[test] +fn test_validate_identity_when_claim() { + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![(AccountKeyring::Ferdie.to_account_id(), 1000)]) + .build() + .execute_with(|| { + run_to_block(1); + // alice create identity for Eve + assert_ok!(Identity::create_identity( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + AccountKeyring::Eve.to_account_id(), + )); + run_to_block(2); + // eve confirms her identity + assert_ok!(Identity::confirm_identity( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + "Eeeeeveeeee".into(), + )); + run_to_block(3); + // eve gets certified by bob and charlie + assert_ok!(Cert::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(), + 2, + 5 + )); + assert_ok!(Cert::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(), + 3, + 5 + )); + + // eve can claim her membership + assert_ok!(Membership::claim_membership( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + )); + + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipAcquired(5), + )); + + // ferdie can not validate eve identity because already validated + assert_noop!( + Identity::validate_identity( + frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into(), + 5, + ), + pallet_identity::Error::<Runtime>::IdtyAlreadyValidated + ); + }); +} + +/// test membership expiry +#[test] +fn test_membership_expiry() { + ExtBuilder::new(1, 3, 4).build().execute_with(|| { + run_to_block(100); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipExpired(1), + )); + // membership expiry should not trigger identity removal + assert!(!System::events().iter().any(|record| record.event + == RuntimeEvent::Identity(pallet_identity::Event::IdtyRemoved { idty_index: 1 }))); + }); +} + +/// test membership renewal +#[test] +fn test_membership_renewal() { + ExtBuilder::new(1, 3, 4).build().execute_with(|| { + // renew at block 2 + run_to_block(2); + assert_ok!(Membership::renew_membership( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + )); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipRenewed(1), + )); + + // renew at block 3 + run_to_block(3); + assert_ok!(Membership::renew_membership( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + )); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipRenewed(1), + )); + + // should expire at block 103 = 3+100 + run_to_block(103); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipExpired(1), + )); + }); +} + +// test that UD are auto claimed when identity is removed #[test] fn test_remove_identity_after_one_ud() { ExtBuilder::new(1, 3, 4).build().execute_with(|| { @@ -147,6 +246,95 @@ fn test_remove_identity_after_one_ud() { }); } +/// test that UD are auto claimed when membership expires +/// and that claimed UD matches expectations +#[test] +fn test_ud_claimed_membership_on_and_off() { + ExtBuilder::new(1, 3, 4).build().execute_with(|| { + // UD are created every 10 blocks from block 0 + run_to_block(10); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::NewUdCreated { + amount: 1000, + index: 1, + monetary_mass: 4000, + members_count: 4, + }, + )); + // UD not claimed, still initial balance to 0 + assert_eq!( + Balances::free_balance(AccountKeyring::Alice.to_account_id()), + 0 + ); + + run_to_block(11); + // alice identity expires + assert_ok!(Membership::force_expire_membership(1)); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::UdsAutoPaidAtRemoval { + count: 1, + total: 1_000, + who: AccountKeyring::Alice.to_account_id(), + }, + )); + // alice balances should be increased by 1 UD + assert_eq!( + Balances::free_balance(AccountKeyring::Alice.to_account_id()), + 1000 + ); + + // UD number 2 + run_to_block(20); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::NewUdCreated { + amount: 1000, + index: 2, + monetary_mass: 7000, // 4000 + 3 × 1000 + members_count: 3, // alice is not member at this UD + }, + )); + + // alice claims back her membership + assert_ok!(Membership::claim_membership( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into() + )); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipAcquired(1), + )); + + // UD number 3 + run_to_block(30); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::NewUdCreated { + amount: 1000, + index: 3, + monetary_mass: 11000, // 7000 + 4 × 1000 + members_count: 4, // alice is member again at this UD + }, + )); + + // one block later, alice claims her new UD + run_to_block(31); + assert_ok!(UniversalDividend::claim_uds( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into() + )); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::UdsClaimed { + count: 1, + total: 1_000, + who: AccountKeyring::Alice.to_account_id(), + }, + )); + assert_eq!( + Balances::free_balance(AccountKeyring::Alice.to_account_id()), + 2000 // one more UD + ); + + // println!("{:?}", System::events()); + }); +} + +/// test when root removes and identity, all consumers should be deleted #[test] fn test_remove_smith_identity() { ExtBuilder::new(1, 3, 4).build().execute_with(|| { @@ -382,6 +570,7 @@ fn test_create_new_idty_without_founds() { }); } +/// test that newly validated identity gets initialized with the next UD #[test] fn test_validate_new_idty_after_few_uds() { ExtBuilder::new(1, 3, 4) @@ -425,17 +614,72 @@ fn test_validate_new_idty_after_few_uds() { 5, )); - // The new member should have first_eligible_ud equal to one + // The new member should have first_eligible_ud equal to three + assert!(Identity::identity(5).is_some()); + assert_eq!( + Identity::identity(5).unwrap().data, + IdtyData { + // first eligible UD will be at block 30 + first_eligible_ud: pallet_universal_dividend::FirstEligibleUd::from(3), + } + ); + }); +} + +/// test that newly validated identity gets initialized with the next UD +// even when the method used is membership claim +#[test] +fn test_claim_memberhsip_after_few_uds() { + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![ + (AccountKeyring::Alice.to_account_id(), 1_000), + (AccountKeyring::Bob.to_account_id(), 1_000), + (AccountKeyring::Charlie.to_account_id(), 1_000), + (AccountKeyring::Eve.to_account_id(), 1_000), + ]) + .build() + .execute_with(|| { + run_to_block(21); + + // Should be able to create an identity + assert_ok!(Identity::create_identity( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + AccountKeyring::Eve.to_account_id(), + )); + + // At next block, the created identity should be confirmed by its owner + run_to_block(22); + assert_ok!(Identity::confirm_identity( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + pallet_identity::IdtyName::from("Eve"), + )); + + // At next block, Bob should be able to certify the new identity + run_to_block(23); + assert_ok!(Cert::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(), + 2, + 5, + )); + + // eve should be able to claim her membership + assert_ok!(Membership::claim_membership( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + )); + + // The new member should have first_eligible_ud equal to three assert!(Identity::identity(5).is_some()); assert_eq!( Identity::identity(5).unwrap().data, IdtyData { + // first eligible UD will be at block 30 first_eligible_ud: pallet_universal_dividend::FirstEligibleUd::from(3), } ); }); } +/// test oneshot accounts #[test] fn test_oneshot_accounts() { ExtBuilder::new(1, 3, 4) diff --git a/xtask/src/gen_calls_doc.rs b/xtask/src/gen_calls_doc.rs index 775619e038091cdcb9f1692f68f65ec8958acfab..543c880f6ee1f13626bd8119246b9c895053dd21 100644 --- a/xtask/src/gen_calls_doc.rs +++ b/xtask/src/gen_calls_doc.rs @@ -128,15 +128,9 @@ impl CallCategory { "Identity", "remove_identity" | "prune_item_identities_names" | "prune_item_identity_index_of", ) => Self::Root, - ("Membership", "force_request_membership") => Self::Root, - ("Membership", "request_membership" | "claim_membership" | "revoke_membership") => { - Self::Disabled - } - ("Cert", "force_add_cert" | "del_cert" | "remove_all_certs_received_by") => Self::Root, - ("SmithMembership", "force_request_membership") => Self::Root, - ("SmithCert", "force_add_cert" | "del_cert" | "remove_all_certs_received_by") => { - Self::Root - } + ("Membership", "request_membership" | "revoke_membership") => Self::Disabled, + ("Cert", "del_cert" | "remove_all_certs_received_by") => Self::Root, + ("SmithCert", "del_cert" | "remove_all_certs_received_by") => Self::Root, ("TechnicalCommittee", "set_members" | "disapprove_proposal") => Self::Root, ("Utility", "dispatch_as") => Self::Root, ("Treasury", "approve_proposal" | "reject_proposal") => Self::OtherOrigin,