diff --git a/pallets/authority-members/README.md b/pallets/authority-members/README.md index c99560fb5fca577896fc76824947772a91e47f23..133701b9c1d25fac5b3d4912f0492786f5dda990 100644 --- a/pallets/authority-members/README.md +++ b/pallets/authority-members/README.md @@ -1,6 +1,6 @@ # Duniter authority members pallet -In a permissioned network, we have to define the set of authorities, and among these authorities, the ones taking part in the next session. That's what authority members pallet does. In practice: +In a permissioned network, we have to define the set of authorities, and among these authorities, the ones validators in the next session. That's what authority members pallet does. In practice: - it manages a `Members` set with some custom rules - it implements the `SessionManager` trait from the session frame pallet @@ -18,4 +18,12 @@ Then one can "go online" and "go offline" to enter or leave two sessions after. ## Staying in the set of authorities -If a smith is offline more than `MaxOfflineSessions`, he leaves the set of authorities. \ No newline at end of file +If a smith is offline more than `MaxOfflineSessions`, he leaves the set of authorities. + +## Some vocabulary + +*Smiths* are people allowed to forge blocks, but in details this is: + +- **smith** status required to become an authority +- **authority** status required to become validator +- **validator** status required to add blocks \ No newline at end of file diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs index 3767ba1dde7857c1fcad215fbc6d66a50c8acc8e..197d83f01f8f1bd53d2fba07ec3fbd52d4c4c9dd 100644 --- a/pallets/authority-members/src/lib.rs +++ b/pallets/authority-members/src/lib.rs @@ -220,6 +220,7 @@ pub mod pallet { #[pallet::call] impl<T: Config> Pallet<T> { #[pallet::weight(1_000_000_000)] + /// ask to leave the set of validators two sessions after pub fn go_offline(origin: OriginFor<T>) -> DispatchResultWithPostInfo { // Verification phase // let who = ensure_signed(origin)?; @@ -246,6 +247,7 @@ pub mod pallet { Ok(().into()) } #[pallet::weight(1_000_000_000)] + /// ask to join the set of validators two sessions after pub fn go_online(origin: OriginFor<T>) -> DispatchResultWithPostInfo { // Verification phase // let who = ensure_signed(origin)?; @@ -282,6 +284,7 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// declare new session keys to replace current ones pub fn set_session_keys( origin: OriginFor<T>, keys: T::KeysWrapper, @@ -314,6 +317,7 @@ pub mod pallet { Ok(().into()) } #[pallet::weight(1_000_000_000)] + /// remove an identity from the set of authorities pub fn remove_member( origin: OriginFor<T>, member_id: T::MemberId, @@ -330,8 +334,12 @@ pub mod pallet { // PUBLIC FUNCTIONS // impl<T: Config> Pallet<T> { - /// Should be transactional + // Should be transactional (if an error occurs, storage should not be modified at all) + // Execute the annotated function in a new storage transaction. + // The return type of the annotated function must be `Result`. All changes to storage performed + // by the annotated function are discarded if it returns `Err`, or committed if `Ok`. #[frame_support::transactional] + /// change owner key of an authority member pub fn change_owner_key( member_id: T::MemberId, new_owner_key: T::AccountId, @@ -370,6 +378,7 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config> Pallet<T> { + /// perform authority member removal fn do_remove_member(member_id: T::MemberId, owner_key: T::AccountId) -> Weight { if Self::is_online(member_id) { // Trigger the member deletion for next session @@ -398,6 +407,7 @@ pub mod pallet { Weight::zero() } + /// perform planned expiration of authority member for the given session pub(super) fn expire_memberships(current_session_index: SessionIndex) { for member_id in MembersExpireOn::<T>::take(current_session_index) { if let Some(member_data) = Members::<T>::get(member_id) { @@ -414,6 +424,7 @@ pub mod pallet { } } } + /// perform incoming authorities insertion fn insert_in(member_id: T::MemberId) -> bool { let not_already_inserted = IncomingAuthorities::<T>::mutate(|members_ids| { if let Err(index) = members_ids.binary_search(&member_id) { @@ -429,6 +440,7 @@ pub mod pallet { } not_already_inserted } + /// perform outgoing authority insertion fn insert_out(member_id: T::MemberId) -> bool { let not_already_inserted = OutgoingAuthorities::<T>::mutate(|members_ids| { if let Err(index) = members_ids.binary_search(&member_id) { @@ -448,21 +460,25 @@ pub mod pallet { } not_already_inserted } + /// check if member is incoming fn is_incoming(member_id: T::MemberId) -> bool { IncomingAuthorities::<T>::get() .binary_search(&member_id) .is_ok() } + /// check if member is online fn is_online(member_id: T::MemberId) -> bool { OnlineAuthorities::<T>::get() .binary_search(&member_id) .is_ok() } + /// check if member is outgoing fn is_outgoing(member_id: T::MemberId) -> bool { OutgoingAuthorities::<T>::get() .binary_search(&member_id) .is_ok() } + /// perform removal from incoming authorities fn remove_in(member_id: T::MemberId) { AuthoritiesCounter::<T>::mutate(|counter| counter.saturating_sub(1)); IncomingAuthorities::<T>::mutate(|members_ids| { @@ -471,6 +487,7 @@ pub mod pallet { } }) } + /// perform removal from online authorities fn remove_online(member_id: T::MemberId) { AuthoritiesCounter::<T>::mutate(|counter| counter.saturating_add(1)); OnlineAuthorities::<T>::mutate(|members_ids| { @@ -479,6 +496,7 @@ pub mod pallet { } }); } + /// perform removal from outgoing authorities fn remove_out(member_id: T::MemberId) { OutgoingAuthorities::<T>::mutate(|members_ids| { if let Ok(index) = members_ids.binary_search(&member_id) { @@ -486,6 +504,7 @@ pub mod pallet { } }); } + /// check that accountid is member fn verify_ownership_and_membership( who: &T::AccountId, ) -> Result<T::MemberId, DispatchError> { @@ -596,6 +615,7 @@ impl<T: Config> pallet_session::SessionManager<T::ValidatorId> for Pallet<T> { } } +// see substrate FullIdentification fn add_full_identification<T: Config>( validator_id: T::ValidatorId, ) -> Option<(T::ValidatorId, T::FullIdentification)> { @@ -604,6 +624,7 @@ fn add_full_identification<T: Config>( .map(|full_ident| (validator_id, full_ident)) } +// implement SessionManager with FullIdentification impl<T: Config> pallet_session::historical::SessionManager<T::ValidatorId, T::FullIdentification> for Pallet<T> { diff --git a/pallets/authority-members/src/types.rs b/pallets/authority-members/src/types.rs index 594cbc36c3fe8e7b39b14278171ade07b0da5d7f..f77996d6dbdc476a855c5e1c3a6adf86dd793de5 100644 --- a/pallets/authority-members/src/types.rs +++ b/pallets/authority-members/src/types.rs @@ -25,8 +25,11 @@ use sp_staking::SessionIndex; #[cfg_attr(feature = "std", derive(Debug, Deserialize, Serialize))] #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] pub struct MemberData<AccountId> { + /// session at which the membership expires pub expire_on_session: SessionIndex, + /// session before which the member must have rotated keys pub must_rotate_keys_before: SessionIndex, + /// pubkey of the member pub owner_key: AccountId, } diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs index 9c717b731d7915ec91e32b4c1885389a4fad54bd..e285dde5d0f2beb6fdd77abb682e2abcb7093eb6 100644 --- a/pallets/duniter-wot/src/lib.rs +++ b/pallets/duniter-wot/src/lib.rs @@ -162,6 +162,7 @@ where // 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), diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs index 154acb37249885046c32ead98433e2476eb04bac..5bcc202f4de65e2e3b0e66c57bb0248e3d51a8d4 100644 --- a/pallets/membership/src/lib.rs +++ b/pallets/membership/src/lib.rs @@ -133,19 +133,19 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config<I>, I: 'static = ()> { - /// A membership has acquired + /// A membership was acquired /// [idty_id] MembershipAcquired(T::IdtyId), - /// A membership has expired + /// A membership expired /// [idty_id] MembershipExpired(T::IdtyId), - /// A membership has renewed + /// A membership was renewed /// [idty_id] MembershipRenewed(T::IdtyId), - /// An identity requested membership + /// An membership was requested /// [idty_id] MembershipRequested(T::IdtyId), - /// A membership has revoked + /// A membership was revoked /// [idty_id] MembershipRevoked(T::IdtyId), /// A pending membership request has expired @@ -191,6 +191,7 @@ pub mod pallet { #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> { #[pallet::weight(1_000_000_000)] + /// request membership without checks pub fn force_request_membership( origin: OriginFor<T>, idty_id: T::IdtyId, @@ -202,6 +203,8 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// submit a membership request (must have a declared identity) + /// (only available for sub wot, automatic for main wot) pub fn request_membership( origin: OriginFor<T>, metadata: T::MetaData, @@ -218,6 +221,8 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// claim that the previously requested membership fullfills the requirements + /// (only available for sub wot, automatic for main wot) pub fn claim_membership( origin: OriginFor<T>, maybe_idty_id: Option<T::IdtyId>, @@ -244,6 +249,7 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// extend the validity period of an active membership pub fn renew_membership( origin: OriginFor<T>, maybe_idty_id: Option<T::IdtyId>, @@ -264,6 +270,8 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// revoke an active membership + /// (only available for sub wot, automatic for main wot) pub fn revoke_membership( origin: OriginFor<T>, maybe_idty_id: Option<T::IdtyId>, @@ -284,12 +292,14 @@ 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 } + /// perform the membership renewal fn do_renew_membership_inner(idty_id: T::IdtyId) -> Weight { let block_number = frame_system::pallet::Pallet::<T>::block_number(); let expire_on = block_number + T::MembershipPeriod::get(); @@ -298,6 +308,7 @@ pub mod pallet { 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, @@ -319,6 +330,7 @@ pub mod pallet { Ok(().into()) } + /// 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>, @@ -332,6 +344,7 @@ pub mod pallet { _ => Err(BadOrigin.into()), } } + /// perform the membership expiration if it has not been renewed meanwhile fn expire_memberships(block_number: T::BlockNumber) -> Weight { let mut total_weight: Weight = Weight::zero(); @@ -348,6 +361,7 @@ pub mod pallet { total_weight } + /// perform the expiration of pending membership planned at given block fn expire_pending_memberships(block_number: T::BlockNumber) -> Weight { let mut total_weight: Weight = Weight::zero(); @@ -363,21 +377,27 @@ pub mod pallet { total_weight } + /// check if identity is member 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() } } } +// implement traits + impl<T: Config<I>, I: 'static> IsInPendingMemberships<T::IdtyId> for Pallet<T, I> { fn is_in_pending_memberships(idty_id: T::IdtyId) -> bool { PendingMembership::<T, I>::contains_key(idty_id) diff --git a/xtask/src/gen_calls_doc.rs b/xtask/src/gen_calls_doc.rs index d60bd2b6a845563693b6f1f045dcbc7a8ff765e8..3307e8c209068ec21d6dbd5122011aa97828c7ba 100644 --- a/xtask/src/gen_calls_doc.rs +++ b/xtask/src/gen_calls_doc.rs @@ -129,10 +129,11 @@ impl CallCategory { "remove_identity" | "prune_item_identities_names" | "prune_item_identity_index_of", ) => Self::Root, ("Membership", "force_request_membership") => Self::Root, - ("Membership", "claim_membership" | "revoke_membership") => Self::Disabled, + ("Membership", "request_membership" | "claim_membership" | "revoke_membership") => { + Self::Disabled + } ("Cert", "force_add_cert" | "del_cert" | "remove_all_certs_received_by") => Self::Root, ("SmithsMembership", "force_request_membership") => Self::Root, - ("SmithsMembership", "claim_membership") => Self::Disabled, ("SmithsCert", "force_add_cert" | "del_cert" | "remove_all_certs_received_by") => { Self::Root }