From f97f5c9620ca1aa9283cabf566972dd975af97ca Mon Sep 17 00:00:00 2001 From: Hugo Trentesaux <hugo.trentesaux@lilo.org> Date: Tue, 28 Feb 2023 16:15:31 +0100 Subject: [PATCH] Runtime documentation (nodes/rust/duniter-v2s!133) * fix renaming RuntimeCall * cargo xtask gen-calls-doc and update metadata * doc storage * more doc * doc identity * doc calls --- docs/api/runtime-calls.md | 253 +++++++++++++------------ end2end-tests/tests/common/balances.rs | 2 +- pallets/authority-members/README.md | 12 +- pallets/authority-members/src/lib.rs | 31 ++- pallets/authority-members/src/types.rs | 3 + pallets/certification/src/lib.rs | 7 + pallets/certification/src/types.rs | 4 + pallets/duniter-wot/src/lib.rs | 1 + pallets/identity/src/lib.rs | 21 +- pallets/identity/src/types.rs | 26 +++ pallets/membership/src/lib.rs | 35 +++- pallets/universal-dividend/src/lib.rs | 21 +- resources/metadata.scale | Bin 127710 -> 131886 bytes xtask/src/gen_calls_doc.rs | 5 +- 14 files changed, 290 insertions(+), 131 deletions(-) diff --git a/docs/api/runtime-calls.md b/docs/api/runtime-calls.md index ad32cf07e..42672b72f 100644 --- a/docs/api/runtime-calls.md +++ b/docs/api/runtime-calls.md @@ -25,7 +25,7 @@ There are **68** user calls from **21** pallets. when: T::BlockNumber maybe_periodic: Option<schedule::Period<T::BlockNumber>> priority: schedule::Priority -call: Box<CallOrHashOf<T>> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -50,11 +50,11 @@ Cancel an anonymously scheduled task. <details><summary><code>schedule_named(id, when, maybe_periodic, priority, call)</code></summary> ```rust -id: Vec<u8> +id: TaskName when: T::BlockNumber maybe_periodic: Option<schedule::Period<T::BlockNumber>> priority: schedule::Priority -call: Box<CallOrHashOf<T>> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -66,7 +66,7 @@ Schedule a named task. <details><summary><code>cancel_named(id)</code></summary> ```rust -id: Vec<u8> +id: TaskName ``` </details> @@ -81,7 +81,7 @@ Cancel a named scheduled task. after: T::BlockNumber maybe_periodic: Option<schedule::Period<T::BlockNumber>> priority: schedule::Priority -call: Box<CallOrHashOf<T>> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -94,11 +94,11 @@ Anonymously schedule a task after a delay. <details><summary><code>schedule_named_after(id, after, maybe_periodic, priority, call)</code></summary> ```rust -id: Vec<u8> +id: TaskName after: T::BlockNumber maybe_periodic: Option<schedule::Period<T::BlockNumber>> priority: schedule::Priority -call: Box<CallOrHashOf<T>> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -131,7 +131,7 @@ be reported. <details><summary><code>transfer(dest, value)</code></summary> ```rust -dest: <T::Lookup as StaticLookup>::Source +dest: AccountIdLookupOf<T> value: T::Balance ``` </details> @@ -151,7 +151,7 @@ The dispatch origin for this call must be `Signed` by the transactor. <details><summary><code>transfer_keep_alive(dest, value)</code></summary> ```rust -dest: <T::Lookup as StaticLookup>::Source +dest: AccountIdLookupOf<T> value: T::Balance ``` </details> @@ -169,7 +169,7 @@ origin account. <details><summary><code>transfer_all(dest, keep_alive)</code></summary> ```rust -dest: <T::Lookup as StaticLookup>::Source +dest: AccountIdLookupOf<T> keep_alive: bool ``` </details> @@ -265,7 +265,7 @@ and the remaining amount to another account. </details> - +ask to leave the set of validators two sessions after #### go_online - 1 @@ -276,7 +276,7 @@ and the remaining amount to another account. </details> - +ask to join the set of validators two sessions after #### set_session_keys - 2 @@ -288,7 +288,7 @@ keys: T::KeysWrapper </details> - +declare new session keys to replace current ones ### Grandpa - 15 @@ -356,6 +356,11 @@ hash: T::Hash Clear an unrequested preimage from the runtime storage. +If `len` is provided, then it will be a much cheaper operation. + +- `hash`: The hash of the preimage to be removed from the store. +- `len`: The length of the preimage of `hash`. + #### request_preimage - 2 <details><summary><code>request_preimage(hash)</code></summary> @@ -443,7 +448,39 @@ Transaction fees will be waived if the member is voting on any particular propos for the first time and the call is successful. Subsequent vote changes will charge a fee. -#### close - 4 +#### close_old_weight - 4 + +<details><summary><code>close_old_weight(proposal_hash, index, proposal_weight_bound, length_bound)</code></summary> + +```rust +proposal_hash: T::Hash +index: ProposalIndex +proposal_weight_bound: OldWeight +length_bound: u32 +``` +</details> + + +Close a vote that is either approved, disapproved or whose voting period has ended. + +May be called by any signed account in order to finish voting and close the proposal. + +If called before the end of the voting period it will only close the vote if it is +has enough votes to be approved or disapproved. + +If called after the end of the voting period abstentions are counted as rejections +unless there is a prime member set and the prime member cast an approval. + +If the close operation completes successfully with disapproval, the transaction fee will +be waived. Otherwise execution of the approved operation will be charged to the caller. + ++ `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed +proposal. ++ `length_bound`: The upper bound for the length of the proposal in storage. Checked via +`storage::read` so it is `size_of::<u32>() == 4` larger than the pure length. + + +#### close - 6 <details><summary><code>close(proposal_hash, index, proposal_weight_bound, length_bound)</code></summary> @@ -514,19 +551,6 @@ value: BalanceOf<T> Transfer some liquid free balance to another account, in milliUD. -#### force_set_first_eligible_ud - 3 - -<details><summary><code>force_set_first_eligible_ud(who, first_eligible_ud)</code></summary> - -```rust -who: T::AccountId -first_eligible_ud: FirstEligibleUd -``` -</details> - - - - ### Identity - 41 #### create_identity - 0 @@ -571,7 +595,7 @@ idty_index: T::IdtyIndex </details> - +validate the owned identity (must meet the main wot requirements) #### change_owner_key - 3 @@ -624,22 +648,10 @@ inc: bool </details> - +change sufficient ref count for given key ### Membership - 42 -#### request_membership - 1 - -<details><summary><code>request_membership(metadata)</code></summary> - -```rust -metadata: T::MetaData -``` -</details> - - - - #### renew_membership - 3 <details><summary><code>renew_membership(maybe_idty_id)</code></summary> @@ -650,7 +662,7 @@ maybe_idty_id: Option<T::IdtyId> </details> - +extend the validity period of an active membership ### Cert - 43 @@ -683,7 +695,21 @@ metadata: T::MetaData </details> +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) #### renew_membership - 3 @@ -695,7 +721,7 @@ maybe_idty_id: Option<T::IdtyId> </details> - +extend the validity period of an active membership #### revoke_membership - 4 @@ -707,7 +733,8 @@ maybe_idty_id: Option<T::IdtyId> </details> - +revoke an active membership +(only available for sub wot, automatic for main wot) ### SmithsCert - 53 @@ -801,7 +828,7 @@ The dispatch origin for this call must be _Signed_. ```rust other_signatories: Vec<T::AccountId> -call: Box<<T as Config>::Call> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -819,14 +846,13 @@ Result is equivalent to the dispatched result. #### as_multi - 1 -<details><summary><code>as_multi(threshold, other_signatories, maybe_timepoint, call, store_call, max_weight)</code></summary> +<details><summary><code>as_multi(threshold, other_signatories, maybe_timepoint, call, max_weight)</code></summary> ```rust threshold: u16 other_signatories: Vec<T::AccountId> maybe_timepoint: Option<Timepoint<T::BlockNumber>> -call: OpaqueCall<T> -store_call: bool +call: Box<<T as Config>::RuntimeCall> max_weight: Weight ``` </details> @@ -941,9 +967,9 @@ Request a randomness <details><summary><code>proxy(real, force_proxy_type, call)</code></summary> ```rust -real: T::AccountId +real: AccountIdLookupOf<T> force_proxy_type: Option<T::ProxyType> -call: Box<<T as Config>::Call> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -960,13 +986,12 @@ Parameters: - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call. - `call`: The call to be made by the `real` account. - #### add_proxy - 1 <details><summary><code>add_proxy(delegate, proxy_type, delay)</code></summary> ```rust -delegate: T::AccountId +delegate: AccountIdLookupOf<T> proxy_type: T::ProxyType delay: T::BlockNumber ``` @@ -983,13 +1008,12 @@ Parameters: - `delay`: The announcement period required of the initial proxy. Will generally be zero. - #### remove_proxy - 2 <details><summary><code>remove_proxy(delegate, proxy_type, delay)</code></summary> ```rust -delegate: T::AccountId +delegate: AccountIdLookupOf<T> proxy_type: T::ProxyType delay: T::BlockNumber ``` @@ -1004,7 +1028,6 @@ Parameters: - `proxy`: The account that the `caller` would like to remove as a proxy. - `proxy_type`: The permissions currently enabled for the removed proxy account. - #### remove_proxies - 3 <details><summary><code>remove_proxies()</code></summary> @@ -1018,13 +1041,12 @@ Unregister all proxy accounts for the sender. The dispatch origin for this call must be _Signed_. -WARNING: This may be called on accounts created by `anonymous`, however if done, then +WARNING: This may be called on accounts created by `pure`, however if done, then the unreserved fees will be inaccessible. **All access to this account will be lost.** +#### create_pure - 4 -#### anonymous - 4 - -<details><summary><code>anonymous(proxy_type, delay, index)</code></summary> +<details><summary><code>create_pure(proxy_type, delay, index)</code></summary> ```rust proxy_type: T::ProxyType @@ -1053,13 +1075,12 @@ same sender, with the same parameters. Fails if there are insufficient funds to pay for deposit. +#### kill_pure - 5 -#### kill_anonymous - 5 - -<details><summary><code>kill_anonymous(spawner, proxy_type, index, height, ext_index)</code></summary> +<details><summary><code>kill_pure(spawner, proxy_type, index, height, ext_index)</code></summary> ```rust -spawner: T::AccountId +spawner: AccountIdLookupOf<T> proxy_type: T::ProxyType index: u16 height: T::BlockNumber @@ -1068,30 +1089,29 @@ ext_index: u32 </details> -Removes a previously spawned anonymous proxy. +Removes a previously spawned pure proxy. WARNING: **All access to this account will be lost.** Any funds held in it will be inaccessible. Requires a `Signed` origin, and the sender account must have been created by a call to -`anonymous` with corresponding parameters. - -- `spawner`: The account that originally called `anonymous` to create this account. -- `index`: The disambiguation index originally passed to `anonymous`. Probably `0`. -- `proxy_type`: The proxy type originally passed to `anonymous`. -- `height`: The height of the chain when the call to `anonymous` was processed. -- `ext_index`: The extrinsic index in which the call to `anonymous` was processed. +`pure` with corresponding parameters. -Fails with `NoPermission` in case the caller is not a previously created anonymous -account whose `anonymous` call has corresponding parameters. +- `spawner`: The account that originally called `pure` to create this account. +- `index`: The disambiguation index originally passed to `pure`. Probably `0`. +- `proxy_type`: The proxy type originally passed to `pure`. +- `height`: The height of the chain when the call to `pure` was processed. +- `ext_index`: The extrinsic index in which the call to `pure` was processed. +Fails with `NoPermission` in case the caller is not a previously created pure +account whose `pure` call has corresponding parameters. #### announce - 6 <details><summary><code>announce(real, call_hash)</code></summary> ```rust -real: T::AccountId +real: AccountIdLookupOf<T> call_hash: CallHashOf<T> ``` </details> @@ -1113,13 +1133,12 @@ Parameters: - `real`: The account that the proxy will make a call on behalf of. - `call_hash`: The hash of the call to be made by the `real` account. - #### remove_announcement - 7 <details><summary><code>remove_announcement(real, call_hash)</code></summary> ```rust -real: T::AccountId +real: AccountIdLookupOf<T> call_hash: CallHashOf<T> ``` </details> @@ -1136,13 +1155,12 @@ Parameters: - `real`: The account that the proxy will make a call on behalf of. - `call_hash`: The hash of the call to be made by the `real` account. - #### reject_announcement - 8 <details><summary><code>reject_announcement(delegate, call_hash)</code></summary> ```rust -delegate: T::AccountId +delegate: AccountIdLookupOf<T> call_hash: CallHashOf<T> ``` </details> @@ -1159,16 +1177,15 @@ Parameters: - `delegate`: The account that previously announced the call. - `call_hash`: The hash of the call to be made. - #### proxy_announced - 9 <details><summary><code>proxy_announced(delegate, real, force_proxy_type, call)</code></summary> ```rust -delegate: T::AccountId -real: T::AccountId +delegate: AccountIdLookupOf<T> +real: AccountIdLookupOf<T> force_proxy_type: Option<T::ProxyType> -call: Box<<T as Config>::Call> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -1185,7 +1202,6 @@ Parameters: - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call. - `call`: The call to be made by the `real` account. - ### Utility - 64 #### batch - 0 @@ -1193,7 +1209,7 @@ Parameters: <details><summary><code>batch(calls)</code></summary> ```rust -calls: Vec<<T as Config>::Call> +calls: Vec<<T as Config>::RuntimeCall> ``` </details> @@ -1215,7 +1231,7 @@ bypassing `frame_system::Config::BaseCallFilter`). ```rust index: u16 -call: Box<<T as Config>::Call> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -1239,7 +1255,7 @@ The dispatch origin for this call must be _Signed_. <details><summary><code>batch_all(calls)</code></summary> ```rust -calls: Vec<<T as Config>::Call> +calls: Vec<<T as Config>::RuntimeCall> ``` </details> @@ -1261,7 +1277,7 @@ bypassing `frame_system::Config::BaseCallFilter`). <details><summary><code>force_batch(calls)</code></summary> ```rust -calls: Vec<<T as Config>::Call> +calls: Vec<<T as Config>::RuntimeCall> ``` </details> @@ -1286,7 +1302,7 @@ bypassing `frame_system::Config::BaseCallFilter`). ```rust value: BalanceOf<T, I> -beneficiary: <T::Lookup as StaticLookup>::Source +beneficiary: AccountIdLookupOf<T> ``` </details> @@ -1302,7 +1318,7 @@ proposal is awarded. ```rust amount: BalanceOf<T, I> -beneficiary: <T::Lookup as StaticLookup>::Source +beneficiary: AccountIdLookupOf<T> ``` </details> @@ -1455,7 +1471,7 @@ not been enacted yet. <details><summary><code>set_balance(who, new_free, new_reserved)</code></summary> ```rust -who: <T::Lookup as StaticLookup>::Source +who: AccountIdLookupOf<T> new_free: T::Balance new_reserved: T::Balance ``` @@ -1476,8 +1492,8 @@ The dispatch origin for this call is `root`. <details><summary><code>force_transfer(source, dest, value)</code></summary> ```rust -source: <T::Lookup as StaticLookup>::Source -dest: <T::Lookup as StaticLookup>::Source +source: AccountIdLookupOf<T> +dest: AccountIdLookupOf<T> value: T::Balance ``` </details> @@ -1491,7 +1507,7 @@ specified. <details><summary><code>force_unreserve(who, amount)</code></summary> ```rust -who: <T::Lookup as StaticLookup>::Source +who: AccountIdLookupOf<T> amount: T::Balance ``` </details> @@ -1513,7 +1529,7 @@ member_id: T::MemberId </details> - +remove an identity from the set of authorities ### Grandpa - 15 @@ -1607,7 +1623,7 @@ idty_name: Option<IdtyName> </details> - +remove an identity from storage #### prune_item_identities_names - 6 @@ -1619,7 +1635,7 @@ names: Vec<IdtyName> </details> - +remove identity names from storage ### Membership - 42 @@ -1634,7 +1650,7 @@ metadata: T::MetaData </details> - +request membership without checks ### Cert - 43 @@ -1650,7 +1666,7 @@ verify_rules: bool </details> - +add a certification without checks (only root) #### del_cert - 2 @@ -1663,7 +1679,7 @@ receiver: T::IdtyIndex </details> - +remove a certification (only root) #### remove_all_certs_received_by - 3 @@ -1675,7 +1691,7 @@ idty_index: T::IdtyIndex </details> - +remove all certifications received by an identity (only root) ### SmithsMembership - 52 @@ -1690,7 +1706,7 @@ metadata: T::MetaData </details> - +request membership without checks ### SmithsCert - 53 @@ -1706,7 +1722,7 @@ verify_rules: bool </details> - +add a certification without checks (only root) #### del_cert - 2 @@ -1719,7 +1735,7 @@ receiver: T::IdtyIndex </details> - +remove a certification (only root) #### remove_all_certs_received_by - 3 @@ -1731,7 +1747,7 @@ idty_index: T::IdtyIndex </details> - +remove all certifications received by an identity (only root) ### Utility - 64 @@ -1741,7 +1757,7 @@ idty_index: T::IdtyIndex ```rust as_origin: Box<T::PalletsOrigin> -call: Box<<T as Config>::Call> +call: Box<<T as Config>::RuntimeCall> ``` </details> @@ -1758,7 +1774,7 @@ The dispatch origin for this call must be _Root_. ## Disabled calls -There are **7** disabled calls from **4** pallets. +There are **7** disabled calls from **3** pallets. ### System - 0 @@ -1828,21 +1844,22 @@ usually means being a stash account). ### Membership - 42 -#### claim_membership - 2 +#### request_membership - 1 -<details><summary><code>claim_membership(maybe_idty_id)</code></summary> +<details><summary><code>request_membership(metadata)</code></summary> ```rust -maybe_idty_id: Option<T::IdtyId> +metadata: T::MetaData ``` </details> +submit a membership request (must have a declared identity) +(only available for sub wot, automatic for main wot) +#### claim_membership - 2 -#### revoke_membership - 4 - -<details><summary><code>revoke_membership(maybe_idty_id)</code></summary> +<details><summary><code>claim_membership(maybe_idty_id)</code></summary> ```rust maybe_idty_id: Option<T::IdtyId> @@ -1850,13 +1867,12 @@ 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 -### SmithsMembership - 52 - -#### claim_membership - 2 - -<details><summary><code>claim_membership(maybe_idty_id)</code></summary> +<details><summary><code>revoke_membership(maybe_idty_id)</code></summary> ```rust maybe_idty_id: Option<T::IdtyId> @@ -1864,5 +1880,6 @@ maybe_idty_id: Option<T::IdtyId> </details> - +revoke an active membership +(only available for sub wot, automatic for main wot) diff --git a/end2end-tests/tests/common/balances.rs b/end2end-tests/tests/common/balances.rs index 8149199de..47be32477 100644 --- a/end2end-tests/tests/common/balances.rs +++ b/end2end-tests/tests/common/balances.rs @@ -28,7 +28,7 @@ pub async fn set_balance(client: &Client, who: AccountKeyring, amount: u64) -> R .create_signed( &gdev::tx() .sudo() - .sudo(gdev::runtime_types::gdev_runtime::Call::Balances( + .sudo(gdev::runtime_types::gdev_runtime::RuntimeCall::Balances( pallet_balances::pallet::Call::set_balance { who: MultiAddress::Id(who.to_account_id()), new_free: amount, diff --git a/pallets/authority-members/README.md b/pallets/authority-members/README.md index c99560fb5..133701b9c 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 3767ba1dd..21b23b64d 100644 --- a/pallets/authority-members/src/lib.rs +++ b/pallets/authority-members/src/lib.rs @@ -128,37 +128,45 @@ pub mod pallet { // STORAGE // + /// maps member id to account id #[pallet::storage] #[pallet::getter(fn account_id_of)] pub type AccountIdOf<T: Config> = StorageMap<_, Twox64Concat, T::MemberId, T::AccountId, OptionQuery>; + /// count the number of authorities #[pallet::storage] #[pallet::getter(fn authorities_counter)] pub type AuthoritiesCounter<T: Config> = StorageValue<_, u32, ValueQuery>; + /// list incoming authorities #[pallet::storage] #[pallet::getter(fn incoming)] pub type IncomingAuthorities<T: Config> = StorageValue<_, Vec<T::MemberId>, ValueQuery>; + /// list online authorities #[pallet::storage] #[pallet::getter(fn online)] pub type OnlineAuthorities<T: Config> = StorageValue<_, Vec<T::MemberId>, ValueQuery>; + /// list outgoing authorities #[pallet::storage] #[pallet::getter(fn outgoing)] pub type OutgoingAuthorities<T: Config> = StorageValue<_, Vec<T::MemberId>, ValueQuery>; + /// maps member id to member data #[pallet::storage] #[pallet::getter(fn member)] pub type Members<T: Config> = StorageMap<_, Twox64Concat, T::MemberId, MemberData<T::AccountId>, OptionQuery>; + /// maps session index to the list of member id set to expire at this session #[pallet::storage] #[pallet::getter(fn members_expire_on)] pub type MembersExpireOn<T: Config> = StorageMap<_, Twox64Concat, SessionIndex, Vec<T::MemberId>, ValueQuery>; + /// maps session index to the list of member id forced to rotate keys before this session #[pallet::storage] #[pallet::getter(fn must_rotate_keys_before)] pub type MustRotateKeysBefore<T: Config> = @@ -220,6 +228,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 +255,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 +292,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 +325,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 +342,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 +386,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 +415,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 +432,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 +448,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 +468,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 +495,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 +504,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 +512,7 @@ pub mod pallet { } }); } + /// check that accountid is member fn verify_ownership_and_membership( who: &T::AccountId, ) -> Result<T::MemberId, DispatchError> { @@ -596,6 +623,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 +632,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 594cbc36c..f77996d6d 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/certification/src/lib.rs b/pallets/certification/src/lib.rs index 57a1d7e3c..7099ab19a 100644 --- a/pallets/certification/src/lib.rs +++ b/pallets/certification/src/lib.rs @@ -269,6 +269,7 @@ pub mod pallet { #[pallet::call] impl<T: Config<I>, I: 'static> Pallet<T, I> { + /// add a certification without checks (only root) #[pallet::weight(1_000_000_000)] pub fn force_add_cert( origin: OriginFor<T>, @@ -355,6 +356,7 @@ pub mod pallet { Self::do_add_cert(block_number, issuer, receiver) } + /// remove a certification (only root) #[pallet::weight(1_000_000_000)] pub fn del_cert( origin: OriginFor<T>, @@ -366,6 +368,7 @@ pub mod pallet { Ok(().into()) } + /// remove all certifications received by an identity (only root) #[pallet::weight(1_000_000_000)] pub fn remove_all_certs_received_by( origin: OriginFor<T>, @@ -382,6 +385,7 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config<I>, I: 'static> Pallet<T, I> { + /// perform cert add fn do_add_cert( block_number: T::BlockNumber, issuer: T::IdtyIndex, @@ -438,6 +442,7 @@ pub mod pallet { Ok(().into()) } + /// remove the certifications due to expire on the given block fn prune_certifications(block_number: T::BlockNumber) -> Weight { let mut total_weight = Weight::zero(); @@ -449,6 +454,7 @@ pub mod pallet { total_weight } + /// perform the certification removal fn remove_cert_inner( issuer: T::IdtyIndex, receiver: T::IdtyIndex, @@ -505,6 +511,7 @@ pub mod pallet { } } +// implement setting next_issuable_on for certification period impl<T: Config<I>, I: 'static> SetNextIssuableOn<T::BlockNumber, T::IdtyIndex> for Pallet<T, I> { fn set_next_issuable_on(idty_index: T::IdtyIndex, next_issuable_on: T::BlockNumber) -> Weight { <StorageIdtyCertMeta<T, I>>::mutate_exists(idty_index, |cert_meta_opt| { diff --git a/pallets/certification/src/types.rs b/pallets/certification/src/types.rs index 4ab2c51cd..7eadcabc5 100644 --- a/pallets/certification/src/types.rs +++ b/pallets/certification/src/types.rs @@ -20,10 +20,14 @@ use codec::{Decode, Encode}; use frame_support::pallet_prelude::*; use scale_info::TypeInfo; +/// certification metadata attached to an identity #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct IdtyCertMeta<BlockNumber: Default> { + /// count issued certifications pub issued_count: u32, + /// block before which identity not allowed to issue a new certification pub next_issuable_on: BlockNumber, + /// number of certifications received pub received_count: u32, } diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs index 9c717b731..e285dde5d 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/identity/src/lib.rs b/pallets/identity/src/lib.rs index fc2dfe4e5..ab95a439d 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -169,6 +169,7 @@ pub mod pallet { // STORAGE // + /// maps identity index to identity value #[pallet::storage] #[pallet::getter(fn identity)] pub type Identities<T: Config> = CountedStorageMap< @@ -179,19 +180,22 @@ pub mod pallet { OptionQuery, >; + /// maps account id to identity index #[pallet::storage] #[pallet::getter(fn identity_index_of)] pub type IdentityIndexOf<T: Config> = StorageMap<_, Blake2_128, T::AccountId, T::IdtyIndex, OptionQuery>; + /// maps identity name to null type (simply a set) #[pallet::storage] #[pallet::getter(fn identity_by_did)] pub type IdentitiesNames<T: Config> = StorageMap<_, Blake2_128, IdtyName, (), OptionQuery>; + /// counter of the identity index to give to the next identity #[pallet::storage] pub(super) type NextIdtyIndex<T: Config> = StorageValue<_, T::IdtyIndex, ValueQuery>; - /// Identities by removed block + /// maps block number to the list of identities set to be removed at this bloc #[pallet::storage] #[pallet::getter(fn removable_on)] pub type IdentitiesRemovableOn<T: Config> = @@ -312,6 +316,7 @@ pub mod pallet { T::OnIdtyChange::on_idty_change(idty_index, &IdtyEvent::Created { creator }); Ok(().into()) } + /// Confirm the creation of an identity and give it a name /// /// - `idty_name`: the name uniquely associated to this identity. Must match the validation rules defined by the runtime. @@ -355,7 +360,9 @@ pub mod pallet { T::OnIdtyChange::on_idty_change(idty_index, &IdtyEvent::Confirmed); Ok(().into()) } + #[pallet::weight(1_000_000_000)] + /// validate the owned identity (must meet the main wot requirements) pub fn validate_identity( origin: OriginFor<T>, idty_index: T::IdtyIndex, @@ -517,6 +524,7 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// remove an identity from storage pub fn remove_identity( origin: OriginFor<T>, idty_index: T::IdtyIndex, @@ -533,6 +541,7 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// remove identity names from storage pub fn prune_item_identities_names( origin: OriginFor<T>, names: Vec<IdtyName>, @@ -547,6 +556,7 @@ pub mod pallet { } #[pallet::weight(1_000_000_000)] + /// change sufficient ref count for given key pub fn fix_sufficients( origin: OriginFor<T>, owner_key: T::AccountId, @@ -625,6 +635,7 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config> Pallet<T> { + /// perform identity removal pub(super) fn do_remove_identity(idty_index: T::IdtyIndex) -> Weight { if let Some(idty_val) = Identities::<T>::get(idty_index) { let _ = T::RemoveIdentityConsumers::remove_idty_consumers(idty_index); @@ -645,6 +656,7 @@ pub mod pallet { } Weight::zero() } + /// incremental counter for identity index fn get_next_idty_index() -> T::IdtyIndex { if let Ok(next_index) = <NextIdtyIndex<T>>::try_get() { <NextIdtyIndex<T>>::put(next_index.saturating_add(T::IdtyIndex::one())); @@ -654,6 +666,7 @@ pub mod pallet { T::IdtyIndex::one() } } + /// remove identities planned for removal at the given block if their status did not change fn prune_identities(block_number: T::BlockNumber) -> Weight { let mut total_weight = Weight::zero(); @@ -670,16 +683,21 @@ pub mod pallet { } } +// implement getting owner key of identity index + impl<T: Config> sp_runtime::traits::Convert<T::IdtyIndex, Option<T::AccountId>> for Pallet<T> { fn convert(idty_index: T::IdtyIndex) -> Option<T::AccountId> { Identities::<T>::get(idty_index).map(|idty_val| idty_val.owner_key) } } +// implement StoredMap trait for this pallet + impl<T> frame_support::traits::StoredMap<T::AccountId, T::IdtyData> for Pallet<T> where T: Config, { + /// get identity data for an account id fn get(key: &T::AccountId) -> T::IdtyData { if let Some(idty_index) = Self::identity_index_of(key) { if let Some(idty_val) = Identities::<T>::get(idty_index) { @@ -691,6 +709,7 @@ where Default::default() } } + /// mutate an account fiven a function of its data fn try_mutate_exists<R, E: From<sp_runtime::DispatchError>>( key: &T::AccountId, f: impl FnOnce(&mut Option<T::IdtyData>) -> Result<R, E>, diff --git a/pallets/identity/src/types.rs b/pallets/identity/src/types.rs index ba1cdf8d0..58efc4d0f 100644 --- a/pallets/identity/src/types.rs +++ b/pallets/identity/src/types.rs @@ -23,17 +23,25 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_std::vec::Vec; +/// events related to identity pub enum IdtyEvent<T: crate::Config> { + /// creation of a new identity by an other Created { creator: T::IdtyIndex }, + /// confirmation of an identity (with a given name) Confirmed, + /// validation of an identity Validated, + /// changing the owner key of the identity ChangedOwnerKey { new_owner_key: T::AccountId }, + /// removing an identity Removed { status: IdtyStatus }, } +/// name of the identity, ascii encoded #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub struct IdtyName(pub Vec<u8>); +/// implement scale string typeinfo for encoding impl scale_info::TypeInfo for IdtyName { type Identity = str; @@ -65,6 +73,8 @@ impl<'de> serde::Deserialize<'de> for IdtyName { } } +/// status of the identity +/// used for temporary period before validation #[cfg_attr(feature = "std", derive(Deserialize, Serialize))] #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum IdtyStatus { @@ -78,28 +88,44 @@ impl Default for IdtyStatus { } } +/// identity value (as in key/value) #[cfg_attr(feature = "std", derive(Debug, Deserialize, Serialize))] #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] pub struct IdtyValue<BlockNumber, AccountId, IdtyData> { + /// data shared between pallets defined by runtime + /// only contains first_eligible_ud in our case pub data: IdtyData, + /// block before which creating a new identity is not allowed pub next_creatable_identity_on: BlockNumber, + /// previous owner key of this identity (optional) pub old_owner_key: Option<(AccountId, BlockNumber)>, + /// current owner key of this identity pub owner_key: AccountId, + /// block before which this identity can not be removed + /// used only for temporary period before validation + /// equals 0 for a validated identity pub removable_on: BlockNumber, + /// current status of the identity (until validation) pub status: IdtyStatus, } +/// payload to define a new owner key #[derive(Clone, Copy, Encode, RuntimeDebug)] pub struct NewOwnerKeyPayload<'a, AccountId, IdtyIndex, Hash> { + /// hash of the genesis block // Avoid replay attack between networks pub genesis_hash: &'a Hash, + /// identity index pub idty_index: IdtyIndex, + /// old owner key of the identity pub old_owner_key: &'a AccountId, } #[derive(Clone, Copy, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)] pub struct RevocationPayload<IdtyIndex, Hash> { + /// hash of the genesis block // Avoid replay attack between networks pub genesis_hash: Hash, + /// identity index pub idty_index: IdtyIndex, } diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs index 154acb372..475287c40 100644 --- a/pallets/membership/src/lib.rs +++ b/pallets/membership/src/lib.rs @@ -108,21 +108,26 @@ pub mod pallet { // STORAGE // + /// maps identity id to membership data + // (expiration block for instance) #[pallet::storage] #[pallet::getter(fn membership)] pub type Membership<T: Config<I>, I: 'static = ()> = CountedStorageMap<_, Twox64Concat, T::IdtyId, MembershipData<T::BlockNumber>, OptionQuery>; + /// maps block number to the list of identity id set to expire at this block #[pallet::storage] #[pallet::getter(fn memberships_expire_on)] pub type MembershipsExpireOn<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, T::BlockNumber, Vec<T::IdtyId>, ValueQuery>; + /// maps identity id to pending membership metadata #[pallet::storage] #[pallet::getter(fn pending_membership)] pub type PendingMembership<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, T::IdtyId, T::MetaData, OptionQuery>; + /// maps block number to the list of memberships set to expire at this block #[pallet::storage] #[pallet::getter(fn pending_memberships_expire_on)] pub type PendingMembershipsExpireOn<T: Config<I>, I: 'static = ()> = @@ -133,19 +138,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 +196,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 +208,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 +226,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 +254,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 +275,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 +297,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 +313,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 +335,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 +349,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 +366,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 +382,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/pallets/universal-dividend/src/lib.rs b/pallets/universal-dividend/src/lib.rs index 41992650d..73d5af1eb 100644 --- a/pallets/universal-dividend/src/lib.rs +++ b/pallets/universal-dividend/src/lib.rs @@ -254,7 +254,9 @@ pub mod pallet { // INTERNAL FUNCTIONS // impl<T: Config> Pallet<T> { + /// create universal dividend fn create_ud(members_count: BalanceOf<T>) { + // get current value of UD and monetary mass let ud_amount = <CurrentUd<T>>::get(); let monetary_mass = <MonetaryMass<T>>::get(); @@ -263,9 +265,14 @@ pub mod pallet { core::mem::replace(next_ud_index, next_ud_index.saturating_add(1)) }); + // compute the new monetary mass let new_monetary_mass = monetary_mass.saturating_add(ud_amount.saturating_mul(members_count)); + + // update the storage value of the monetary mass MonetaryMass::<T>::put(new_monetary_mass); + + // emit an event to inform blockchain users that the holy UNIVERSAL DIVIDEND was created Self::deposit_event(Event::NewUdCreated { amount: ud_amount, index: ud_index, @@ -273,6 +280,8 @@ pub mod pallet { monetary_mass: new_monetary_mass, }); } + + /// claim all due universal dividend at a time fn do_claim_uds(who: &T::AccountId) -> DispatchResultWithPostInfo { T::MembersStorage::try_mutate_exists(who, |maybe_first_eligible_ud| { if let Some(FirstEligibleUd(Some(ref mut first_ud_index))) = maybe_first_eligible_ud @@ -304,6 +313,8 @@ pub mod pallet { } }) } + + /// like balance.transfer, but give an amount in UD fn do_transfer_ud( origin: OriginFor<T>, dest: <T::Lookup as StaticLookup>::Source, @@ -321,11 +332,14 @@ pub mod pallet { )?; Ok(().into()) } + + /// reevaluate the value of the universal dividend fn reeval_ud(members_count: BalanceOf<T>) { + // get current value and monetary mass let ud_amount = <CurrentUd<T>>::get(); - let monetary_mass = <MonetaryMass<T>>::get(); + // compute new value let new_ud_amount = Self::reeval_ud_formula( ud_amount, T::SquareMoneyGrowthRate::get(), @@ -336,6 +350,7 @@ pub mod pallet { ), ); + // update the storage value and the history of past reevals CurrentUd::<T>::put(new_ud_amount); PastReevals::<T>::mutate(|past_reevals| { if past_reevals.len() == T::MaxPastReeval::get() as usize { @@ -352,6 +367,8 @@ pub mod pallet { members_count, }); } + + /// formula for Universal Dividend reevaluation fn reeval_ud_formula( ud_t: BalanceOf<T>, c_square: Perbill, @@ -406,6 +423,8 @@ pub mod pallet { pub fn init_first_eligible_ud() -> FirstEligibleUd { CurrentUdIndex::<T>::get().into() } + /// function to call when removing a member + /// auto-claims UDs pub fn on_removed_member(first_ud_index: UdIndex, who: &T::AccountId) -> Weight { let current_ud_index = CurrentUdIndex::<T>::get(); if first_ud_index < current_ud_index { diff --git a/resources/metadata.scale b/resources/metadata.scale index ff4c1c40a4d58bf8b3d8dc74373830583ef1ddbb..06d4f6891828dfade5738b4d106249ba753edd96 100644 GIT binary patch delta 17461 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	dyw-Ywo$DdwhmIvL+eM(tT@+(SZN=nrz`bu&a}B zqn*(_GXEk#N($muX%haV639lGv|}_{%^7rkf@Z$ZU%(l4amBre|3tM`OFp5`HGAMP zja@q)(!BSr-6059(Zd{~rD3f}i4}!LBWjqt)a2KZ@;Z+7K+H;m#UrPA7qmVqCf%hD z2BoX2>zuNjMx)n{K-wAWx53oARMznD?Q4L$yzcHV`A2c^Jv>os`_+5yLMCOoEIX<V z77XLBdmr2IrYiQ173s>2-#|Mx|6~$wk2`-d6YnqoWId$O%n=j3FFY=*@U|^J+dGYJ zQMHJtIUIl(fJHW}^auVf*^Qzi;T)M2WD=w8Rd&fGyIhE=ptoG-Mk85oueK{TiGX-2 zK;l8W8Q}$l4*Ei&04r6y=%p>mS>h3sjQg!q=5yZNk+fWH!k+DjOgJnCqbwOcP6R2C zHJj|BQEY6S_=;1uA~+x+f?zh`E3>;vs&m?{v^;SbO<FvgKEjTpajDo^itfVGTMZ%b z2EDO;DvIgkXA3ZvTl4H|aN0ZRx&Kk)i;O+v;0^Bsdqx93wC?2(FYcW$;uH!mO~K!3 zFLfk-w+Zq_{2~p~zq<~%wkUvFKVFjJUAo@}cUi?R4tzcNj_ZqxYeX{N-H|uRixoOp zv<ltqYcD_Qzl{&??Rr;-nfp(Jd^ZrB9Pmo+Omd^NPO8COqlTj{h$*kcSv95c6~zg5 zORLNX10hIbkgOhKgM|B~0X-$D2hz?U0O?W<YaIr|)ZJSgvt@yHstW;bIpY<4%gIlc zI0l4w_3j2PX<THw>+|OtzK_*p6m&;dU-(9N>s}cuVA!_y)k$#4+wtn05a^)WJ2PRi zcjA%RFk&%4TA32G6>)th!1;IBT?R{cwUeFm#dxfiT@iRuil(rUK)k`hhl(0i@aE?b zU0}4>U2?VEVhVupH1pUfw0Bj<Qo=R}pewhSXzQ_9?03ttqA)t{7&nIa5>n_7j|~gQ z4wM}?)cfqQXF?cysI#-z<MxYx8}MJ~MuBV;6gSE*0WMC^@6d4{rrVX;MAJ{DLnK{t z%8bhT=Bb_dYwoJzLM?0-=r6LgFRcANU<~}jcV@y2F$4n2>Q`X<DjPy*Jt~o6MQju! zS5#|0(;U>4R@vRCSSvhcR3X$HS1Nt;bS^yQ{o83Hz;>_k-2*Ubuh0WumYUp6d`F#z z@ptoGv`*rSj7|+5_nvOjKJ-&KvfkS;RBR74>|xA?6cJrSjqPb`d@nQmI%sO`b_)p; zcNrTDwtBQDp1L}-(ag0U4?iTuq)T;nX8_SeOTlLbo}!<>R|VT?!C4OJymWRL{=RuO z@&4TcND2TuF=rVeyKx;<g^EYHR|s|!(Q~~OI^&=L{_Vpi0^T2>8Sfh>v_o3e<jG}t z2sFdsHsG?Y3kd2>=z!VXVE4GWmKc}2=!YLBw%pPV)K^@dS}Q6RDpU{NpsWgRCX#>E z8n{t5NCErUyQrzBQqcT1Nj>1UG95^duWXuR@VM<(JZCa0#r_pn+1;sh&__wAIQox< zwx~w~+FY|0WkP{=6%|}*wtHMiHZUNRq9D#V3|xmc0If2LQLyy%pBcM=%tU_RXC^jB zB_6{_2b~jzv?9#hn4@_?oST~jH1P(b8#lfnhG^?Yqm;qKR*GtiQ(kFbuIvT3Rs1{H zL+-dlb<ntTqlJS4y_lasm!11TUbk!#@fCx~gezl|oo+J<r*f!=CNg-{CI1@X;ha0w zd-U9J(DmrRtqp@}(hKLOM2gqCwaj8+Ut2&sLaN2p4%+B~TX0qnbJ@)96#h=(ytUy! zSUN=%+kZ)?a!mF8n#`w)FL@{0yWuqH!XgDsDH4wUe;}vAae+gBBdGXa^I^32f&Yq7 z#RMHIgO+Qr-8S0W_~{D*MjP>$&EMyCZM&QzQ1zc8F%F_#pBJEex8P676Z_yJ1)Q-I z4zzSTWR5HEgy9)P@wM@Agg$;vPq%$OlK%NxLNM_XNbx@Rr`@6cGW`G@KLBx{^XpE3 zq4kD{IOebZnz0$rN8kp%`d9ttzkr5m02U^9-L&7N-@f0Z^ZkZld^QVm820%e+Pei6 z?BsCBO8#yi&_6zs#-tH&pU<O$AHWnHnyqSdcp94`0k;(k;tg*3Zk-S%wV<)7njg0* zh~!w%`P$LlfN6Taf%<=-l^QC<@0o9NvLA;)GP@847Y64B5jEp6uu99uhJy(T+2i4m zBXW3@od^fV@HC@>r&jZl$eX#`7Xf-asPLnzVu|n52$-#^)h)-JOQ{|M2p<{HMRN9S z*ytqVc8^|TV^bAQ14fT{>Lnwf>hA%x4N7XpVm2TO64JUIz>tC+^OWcW119d=cJHkI zu1yNk%N9q$>yb~^@Illldk3347)C%IE7d?&qQ4j;Apcl#Yecu);16gs^4PN)m=deh z$&iq{T0G2cl4|rd8CMu$L9r}68l*7)`&5<_4U_zpm2(Re03*h8vMD2nJrWI*hXz<c zV5RpJ$zdNv!~I(R?p&Z>X;lNd$CAP_n>iSCDYz-w(B(GDh(F<)Snf8dG`%!I2+jAw z5ZEb9m>pQ??#A2+{T>3WY$zl`iqA3><^r3hg^_nv4y7-3itoo-co^8@!(qgvyRn7A z)OT^+cQp%7^P!sU0&bZ{oxe%Zr{1pUQ<*mbra&|M`W_e(6@+2&OcZuv3`oAQiIA>J z>k+d)*fZanBtSICPgHXgQ6cvVdwUEd`o`&?LKxE*yP--sM)!A&V82X;zXoWA1!($? zj)58hI(*4vVFY>{>;XNDx^o!o+mHqrQY`jeO@~Z=9XTHIFj)JpCOh=-B(Iv21+NI| zV6=B!tiBiI{g!*>dx3z8UCjm^#IV6R@J&Gs4^&K;^l+-sjrl<ijC$;n2Zh!k+3dD- z9(0&6rNO4`Gl(Yea;1zXMr4Q`%wUDw?@9e&<qJU@5u)nhZBqfH>%&!kBzmB#m&tGy z2dcV2O9tYdfRE9=tvG&NB>Ef01~rQ1RYek~3Jy;aRJVmE30Mzzj0ARN62!CGNiYnO z*y>3z21c{zCqWvdvXl4*3$a2CoMAP2kgvdQY*#MK8gQl?`y;7-5<~N#)L(5fq_fUE z$eh+sY>s5ABGUqpI+`T5X#ouJ6-@>g2P67AvykJ{<-><6w1l07FoX>)09(Y=-g)D* z9a|Wf?b4u$EA-d@v;gAq^Z;^aBVNxm(;;yf#G{(K5Wgx`LHs=bEg{A1-a^P8T*~LW zP({jmyWmR`J2%k9{Gcx8@-DK5Y=`*i28#jh9xkmK)MnFm7CIeL!{>8ab5&$9%bgAr zgk>rOBZss9wFDx4bUNJV*EAT|w=-bu5Jc=Wp3E%F&y$gONEk7zn1<nsFDM2{u&Q`g zi4U_%@T9EobIdFyNa3@YupT@<YYAGeel(6~x@D%;24<=)Xr|V(H|~eV#7jK~SA`yo zj+_X85z@wIX|0N^_dPHRTKdy9Y+y6XVDg}IJQcDV(LTo>FN4(NjYw5g@7I$}ocv}L z!9Oe@aRPZtx!bPNlkM!oG8j1sMMS{_vJn#4slrT^IG=6~N<ZvowsP14ds#>YdLH}O zgB36)u7g8A{#33xXrlM^4xh_n%n`p1AL`Zn11ijBiSvccg<qqg?6c%Yblgli{*<5c z!Jv6P$Om_d99Hquq!H}rRWNvPrwa3J_*p9HRPiH6(#hVcLO1Own?4`zh2!j@`7k%) z=*`60zy%N%Y*CNj{66_;U{y~9^?ri)epE%elnKQ|pEImw0j%lA7~w2ySP0AEJUhJ* zii_0{Z$j_cWp;0aij*F}K1~Yf7oG<M9c2-so99oukWG<2EqAc^MKCfC^AS1v7dZ7& zn&HBw1r|;@<ishusBkW6$S1wSMRgw+8ML=tW^56R4%1fI?bFTmzN3p^cYlO5;_6ML zbup5E8G{o}zh?1EV88Gu75#E-yble;%zg>VUvBOp<=enof!HgA-zp=;GaLlzan<lK zMC1uMOj`{L`mtg|!^OywfKfMjY<TADtj2(yU0i~G=AW%_xuuZ<eM*i425)%0jIhld zyBpK-9I}z;WvaxjrQHy{;?82VZg|J0V3C2E?bxgs9m@I!OzD*o@G%u3!s4p3E5tDi z+1-R&`)RBt$x1Z@qKtA2uXr;SqnsTCJm+r2aZ#C}u^16I<Vc~4`v5=n#aVd_-$*v) zWS7HDvcHl-(S<!I)!f)=;2a9sQIJW_PNB)<Y(Zz5go;}^TL{hbvn%d(1mMf=?j?-9 zF!|H9D438xN>IiXCN|ze!0#pZK)DsAE|*7k78z_<j}L^8f~dt)E&dmhl1#~`hQo>+ znnJwZh^etM$L}$Eu;IIIsMu7+TUXiV+l>4y)roGA!bP~a2Rl%znt6AAI^2iqt=Pkn zo;R6ePjTOeiihd9q9ecwg0p;Ka9x}VsXG3?AI=pFWNik}vTM~aen2!w#x-~tWr$`O zHE<tFou>v2gA+tZ;S5x|4Q>x64M&SS<Bov=Mq+Hvk_?cM6)OfiQE}pJPE;JK23iGv z!L34r-V}CR8I!~{X0%A~56d+PB1shycpsj`lYjBNp1`6C-ghA)j|Nkdavsg+kPv3A z1wD5EEWZfsWG!T)iz*tS7({lz5eq}u8Y7H{Q1<WqDU6*n!URll(wN{L2xr+Qbl@V` zQWK=Z0QRT}-$k-tnP5833CQS04`LH!NFC`wAqZ#~XXIw>BT?+XWynY5L0^ZLXlAWL zzk4wIc^yndkLtrZn1l}V(0b6t#fla4_~!<a$&PS57ZJJ9>~i;KvC`R*dYHvdSkYUF z&o8wpGS-WbGrbu{25hJuvT#O9?Z}FXb(-->WWTiIkV4ocJIuftNq0b^(2uU!Hx87N zlI7q;XEAd*3}X>1AQp!vt$;}}GZbkwp%pJP*qRh=0rvC?ScF`>W`R`XBEbohkqgNR z1^uyzQ~zZVWz)YbVvp(k2Ux^H_O=yL|H1kN#s5I9iYWCPVUd*iqFnIP{<vH)mqmG? z1jE)v9*~9kBF_@_{n3MmF8$E!@WtHhFB8c!*1Q_#gMnY-F&i|T4X;L{jE-)0Kgh*k z$`<Y(W>HJbVh>oL#Fo}T+`l5oU$e4r)}URU%EmThH&a=4Gw9-1aE`1Z@hGI4kW|wx zq}B#VwFQw{%RXp^ABMN_#%o24vuf8uN`D+ojI*w{Kv_Qq3mciY6*fd{x|uy@ZG*7L zJ-uG(X4cvU<AtY0I(>-_wzK_hkT3|Nb$>jT%i}TM2W_yIhhpm=hB=cuKv!w5x8WY| zlwrFNQ;bQG2G}d&fw>+d9=su<v`;)Bc8CYX!{Sk~OFSczeeC=?7|wL-aEm?QzqhZ0 zaX28Qyki~ivIqHnJ{<NJWv_?ih)xmkQxOA3P(IUoIG_=Zipp0T<F~+w(Br(%6Rctj zNElPCz#=%oHg193D8zjwgo*u01*FSQ@C#n=42xng6%nsF3@+hbRk{`a26|Td6#O5^ zW+hK!%sIpN)2Cr0z+9iS4VDNnpEW;=h{<*KmH%RT4qBm<eexWnv@F9cBduP^MD*tk zxK>~SW!R!x&u>HUgD2_!y<H&S2YIU2nCk@u2DEyktr=Ky3h&9*KTlhR_g`^`r>DXc z(;uG4LJ6=!Pjd((?eiXV3#1}Y5hq$aB~54TJ75~LvES@~{ZSjBp!GjK9fYq4sg2HJ zkL-lGBQ_$wtKpy0=>wrcI)A_p$rJ3GozRY3@0#afBBn;D$C8*mN-hYdPbmwGZgXX2 zPA+@vd3XnQvgclaim-NEUIK3RKQF*sT_ideMmwgH%5J%mf2xFEx!`+kq4Kd3X4{5n zcC-1rz!WDo<2Q%bC4b(RKgqxk^szI$U|b6Z%hG-5q^_JGrB9PqW-ABJK{&m~R?9tB zWGLXh<8g$<Q<SD#49n%L^sJnT5>Hd{JNlT5zz@=8B%}{&kO2n)8r9htT$rsMtAD*x z(gu*$2-}a`IOiCbr7~$Z+|Qob4e^4Qz;=F(BGR=R^eRB-B>!hnDuk%mkA8-};2GBO zGgu2}d=q{Sg#w1pp1p7g-})x}8$1o@;=TRvuo&W4%8MARwz88i!Zm32y|E8k1hk4X z_QPtt{O5iYv`$~y0az_SCOhVZ2T;!Lc^O@?%f1CKV;wws>gs@FsytOIYhwsRANnO^ zsn8mIc@Q3hGS>Vm0t$=Sp;uuZJm!mf4J{QcV>1q+SQyyDhd`$`E8lCvlfGXZf-f=h zBVc9EAA!juMNC66I9!1<e4HpABz2!Gm`&{b5f~9EwgwVBa6Z{DIx$~%wzB4!O6&)a z%x+u@AbU_38zc*QsuBu<-HPvd-UA{`N`)EaN_yIDO=|p2{K;#<4Q7{n-1T^fcUvzl z-Q8e5hf*Q9RlGmz$b~BzqFI3h7aQ*|Mt>HC#3PveK!f5&2PWT?+Z37QV@U;n!$6`P z4hm11_ykShf~AZTBhvsSoI0Llhi_$tj{oyKjPL@jnWGbi*K)Eb=gOSlM)uBBQId^* znRa4gxm)(9;nm7m&XbFJdw>uDc15y{Ui7<9bV3Gr*o{u~@|&3SYYbJcGvlw}6Ijnm zU&q1?%=$XGRU1`h?Bmy=Uf8eqmA!!?4KK1aM`1hGj(Zaxg-vYNoA3hc@s<1reRFt< zIgdeE(#r&Pss_c+bmEuN4g+-GXk&QThw1*i*@a^mQ0!&Hk3$<u{I=sr3SMDj-r|<u zx9BZc$v3`}Ct$A7e3*@T8_~TkcJOU@1iuF>IEl%zXV~CVuyFXt1kS2rvgbSMotOe! z?lj|f&jgwTxWXPj1<9y!|8@%UqAsdF;Q|h81wcc3nGNg0km@R%(}hfQu=XyDW4`vC z=)wl#H3ENPfBrofc{sY{9ry_T!ls;tcyW^eH`tQX&;SPC8>dlUg%FV~e-~0!*G0lI z&f}z>dKW@e5H7NJ--Xd>14UfRt^TF#!4rB!q^AS}FOR(C4^Z18w)-phsu!1*o{CCS zOwUr%CQ+aGp$Qja5{{ov67kS2J@FF~-rmMP1?8u~Qi_Xb^<z#77sS-;Iheo0qfli7 z#BAdh{s|xvulRKtK>(CpE^#+Az)AA2Qu&p?wIP+BDpkGQDIf{|S~Z|ANqjca1Cp(L zztIie1PEDjxLv?L1O0L99Z;BBp1Z{JMom3R!zsXK0YZH5{sF=Ok4-*&58|f8qy%&o zzXMbBJDqp?#4S_fw_<+1<X?;_TFwTZ1!FnaztP{t<;_esA-COJ-lEpZ7}=l?VOS1d zo+EeU!GQ@>+zJNq!vCuRZgtobXF*!fSAKd4^vv3w_<;ig3`E@Gf1ic-`DRk|0lHK< zEcyZ>9dp@%576tG%f9>oM)9uzqS<Q~;6c`M4jNIoub)Fy<2p+_4+Xfjh|GE(bpM?5 zJ+Tu{;{I7C{IG!7M)!mlvhyF|EZ%X#j|%7r;DrAX<F{uoLRw_0n1sqr;J5$@zIQJ| zu7F#4)W;AzMAVhx=jdj1z)=6yAwsD*mreQ@#)iNN0aAS{KL(9}I-!n0ZDv3D91)fm zK84ZfqaObhR-q$M_!%C{FJ?8LA#3ey<!9&?WU}L*!3uN-rd)yqklEr(a0vH@p_kFu z&hu4XhIL}}dhy=)`EIiX^ZW>06fni((p7YYH?XWPpbkTcr@lbzy@~zy3z!*Q4DotQ zaZvJkr`v6&&Aw4zq9>0b1S|gv!^F+(k6$66z1^4hHH1LRPO)&eC`u(oJogg6g?>PG z+AEbWANGmu1xjfp;?fK73r~ch4~UvvrL3wkP|_i4^h(K0{3zaKb{!NYe~mKyzYs7h l-raOqlv28>8|9;7dz$j(ckonQVmmwX4a9|<5!;W8{}-ZdVF&;K delta 14231 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 ZknhZp-+Xpjag^_rz*DU97~gq_|94fu*46+3 diff --git a/xtask/src/gen_calls_doc.rs b/xtask/src/gen_calls_doc.rs index d60bd2b6a..3307e8c20 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 } -- GitLab