From 79eed31c6fb246ce70821d9d026027324079149c Mon Sep 17 00:00:00 2001 From: Hugo Trentesaux <hugo.trentesaux@lilo.org> Date: Wed, 17 Jan 2024 23:27:21 +0100 Subject: [PATCH] automatically claim membership (nodes/rust/duniter-v2s!219) * increase node spawn timeout to avoid failure * add points in docstrings * review * add missing constant annotation and update doc * detail integration test * comments * update metadata * refac certification checks * clean up error messages and namings * automatic membership after distance eval add more details to idty creation test cargo check ok add integration test and fix behavior fix benchmarks for renaming cargo check ok cargo test ok add certification count check update benchmarks update distance setup handler fix weight info update metadata and generated doc fix cucumber distance pallet unit test stub update metadata and generate doc after rebase make membership claim no-op an error update metadata with new error wip add comments add membership renewal antispam and clean up pending membership parameters test it not compatible with keeping distance result change renewal antispam metadata fix param for other runtimes fix e2e test remove membership calls adjust integration tests accordingly update metadata fix cucumber clean up benchmarks and other tests wip --- docs/api/runtime-calls.md | 90 ++---- docs/api/runtime-errors.md | 156 +++++---- docs/api/runtime-events.md | 34 +- .../identity_creation.feature | 2 - end2end-tests/cucumber-genesis/default.json | 14 +- end2end-tests/cucumber-genesis/wot.json | 14 +- end2end-tests/tests/common/membership.rs | 40 --- end2end-tests/tests/common/mod.rs | 5 +- end2end-tests/tests/cucumber_tests.rs | 35 -- live-tests/tests/sanity_gdev.rs | 2 - node/src/chain_spec/gdev.rs | 5 +- node/src/chain_spec/gen_genesis_data.rs | 21 +- node/src/chain_spec/gtest.rs | 2 +- pallets/authority-members/src/lib.rs | 18 +- pallets/certification/src/benchmarking.rs | 2 +- pallets/certification/src/lib.rs | 130 ++++---- pallets/certification/src/weights.rs | 2 +- pallets/distance/src/benchmarking.rs | 54 ++-- pallets/distance/src/lib.rs | 301 +++++++++++------- pallets/distance/src/mock.rs | 16 +- pallets/distance/src/tests.rs | 108 +++++++ pallets/distance/src/traits.rs | 24 +- pallets/distance/src/weights.rs | 116 +++---- pallets/duniter-test-parameters/src/lib.rs | 2 +- pallets/duniter-wot/src/lib.rs | 192 +++++++---- pallets/duniter-wot/src/mock.rs | 7 +- pallets/duniter-wot/src/tests.rs | 50 +-- pallets/duniter-wot/src/traits.rs | 14 - pallets/identity/src/lib.rs | 6 +- pallets/membership/README.md | 18 +- pallets/membership/src/benchmarking.rs | 44 +-- pallets/membership/src/lib.rs | 152 ++++----- pallets/membership/src/mock.rs | 13 +- pallets/membership/src/tests.rs | 72 ++--- pallets/smith-members/src/lib.rs | 6 +- pallets/universal-dividend/src/lib.rs | 15 +- primitives/membership/src/traits.rs | 12 +- resources/gdev.yaml | 7 +- resources/metadata.scale | Bin 129557 -> 128457 bytes runtime/common/src/pallets_config.rs | 7 +- runtime/common/src/providers.rs | 36 +-- .../src/weights/pallet_certification.rs | 8 +- runtime/common/src/weights/pallet_distance.rs | 122 ++++--- runtime/common/src/weights/pallet_identity.rs | 4 +- runtime/g1/src/lib.rs | 5 +- runtime/g1/src/parameters.rs | 2 +- runtime/gdev/src/lib.rs | 10 +- runtime/gdev/src/parameters.rs | 5 + runtime/gdev/tests/common/mod.rs | 2 +- runtime/gdev/tests/integration_tests.rs | 282 ++++++++++++---- runtime/gdev/tests/xt_tests.rs | 1 + runtime/gtest/src/lib.rs | 2 +- runtime/gtest/src/parameters.rs | 2 +- 53 files changed, 1270 insertions(+), 1019 deletions(-) delete mode 100644 end2end-tests/tests/common/membership.rs create mode 100644 pallets/distance/src/tests.rs diff --git a/docs/api/runtime-calls.md b/docs/api/runtime-calls.md index 033203a40..3c96e8358 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 **79** user calls from **22** pallets. +There are **77** user calls from **21** pallets. ### Account - 1 @@ -831,51 +831,6 @@ payload_sig: T::Signature Link an account to an identity -### Membership - 42 - -#### claim_membership - 1 - -<details><summary><code>claim_membership()</code></summary> - -Taking 0.0213 % of a block. - -```rust -``` -</details> - - -claim membership -it must fullfill the requirements (certs, distance) -TODO #159 for main wot claim_membership is called automatically when distance is evaluated positively -for smith wot, it means joining the authority members - -#### renew_membership - 2 - -<details><summary><code>renew_membership()</code></summary> - -Taking 0.0164 % of a block. - -```rust -``` -</details> - - -extend the validity period of an active membership - -#### revoke_membership - 3 - -<details><summary><code>revoke_membership()</code></summary> - -Taking 0.0586 % of a block. - -```rust -``` -</details> - - -revoke an active membership -(only available for sub wot, automatic for main wot) - ### Certification - 43 #### add_cert - 0 @@ -932,20 +887,37 @@ remove all certifications received by an identity (only root) <details><summary><code>request_distance_evaluation()</code></summary> -Taking 0.0187 % of a block. +Taking 0.06 % of a block. + +```rust +``` +</details> + + +Request caller identity to be evaluated +positive evaluation will result in claim/renew membership +negative evaluation will result in slash for caller + +#### request_distance_evaluation_for - 4 + +<details><summary><code>request_distance_evaluation_for(target)</code></summary> + +Taking 0.0805 % of a block. ```rust +target: T::IdtyIndex ``` </details> -Request an identity to be evaluated +Request target identity to be evaluated +only possible for unvalidated identity #### update_evaluation - 1 <details><summary><code>update_evaluation(computation_result)</code></summary> -Taking 0.0195 % of a block. +Taking 0.0914 % of a block. ```rust computation_result: ComputationResult @@ -954,12 +926,13 @@ computation_result: ComputationResult (Inherent) Push an evaluation result to the pool +this is called internally by validators (= inherent) #### force_update_evaluation - 2 <details><summary><code>force_update_evaluation(evaluator, computation_result)</code></summary> -Taking 0.0122 % of a block. +Taking 0.0759 % of a block. ```rust evaluator: <T as frame_system::Config>::AccountId @@ -968,28 +941,21 @@ computation_result: ComputationResult </details> -Push an evaluation result to the pool +Force push an evaluation result to the pool -#### force_set_distance_status - 3 +#### force_valid_distance_status - 3 -<details><summary><code>force_set_distance_status(identity, status)</code></summary> +<details><summary><code>force_valid_distance_status(identity)</code></summary> -Taking 0.011 % of a block. +Taking 0.074 % of a block. ```rust identity: <T as pallet_identity::Config>::IdtyIndex -status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)> ``` </details> -Set the distance evaluation status of an identity - -Removes the status if `status` is `None`. - -* `status.0` is the account for whom the price will be unreserved or slashed - when the evaluation completes. -* `status.1` is the status of the evaluation. +Force set the distance evaluation status of an identity ### AtomicSwap - 50 diff --git a/docs/api/runtime-errors.md b/docs/api/runtime-errors.md index ebb10eb1a..620c1674c 100644 --- a/docs/api/runtime-errors.md +++ b/docs/api/runtime-errors.md @@ -1,6 +1,6 @@ # Runtime errors -There are **178** errors from **35** pallets. +There are **176** errors from **35** pallets. <ul> <li>System - 0 @@ -386,21 +386,21 @@ A smith has a limited stock of certifications <details> <summary> <code>AlreadyIncoming</code> - 0</summary> -Member already incoming +Member already incoming. </details> </li> <li> <details> <summary> <code>AlreadyOnline</code> - 1</summary> -Member already online +Member already online. </details> </li> <li> <details> <summary> <code>AlreadyOutgoing</code> - 2</summary> -Member already outgoing +Member already outgoing. </details> </li> <li> @@ -414,42 +414,42 @@ Owner key is invalid as a member. <details> <summary> <code>MemberBlacklisted</code> - 4</summary> -Member is blacklisted +Member is blacklisted. </details> </li> <li> <details> <summary> <code>MemberNotBlacklisted</code> - 5</summary> -Member is not blacklisted +Member is not blacklisted. </details> </li> <li> <details> <summary> <code>MemberNotFound</code> - 6</summary> -Member not found +Member not found. </details> </li> <li> <details> <summary> <code>NotOnlineNorIncoming</code> - 7</summary> -Neither online nor scheduled +Neither online nor scheduled. </details> </li> <li> <details> <summary> <code>NotMember</code> - 8</summary> -Not member +Not member. </details> </li> <li> <details> <summary> <code>SessionKeysNotProvided</code> - 9</summary> -Session keys not provided +Session keys not provided. </details> </li> <li> @@ -740,113 +740,57 @@ This account is not allowed to claim UDs. <li> <details> <summary> -<code>NotEnoughCertsToClaimMembership</code> - 0</summary> -Insufficient certifications received to claim membership. -</details> -</li> -<li> -<details> -<summary> -<code>DistanceIsInvalid</code> - 1</summary> -Distance is invalid. -</details> -</li> -<li> -<details> -<summary> -<code>DistanceNotEvaluated</code> - 2</summary> -Distance is not evaluated. -</details> -</li> -<li> -<details> -<summary> -<code>DistanceEvaluationPending</code> - 3</summary> -Distance evaluation has been requested but is still pending -</details> -</li> -<li> -<details> -<summary> -<code>DistanceEvaluationNotRequested</code> - 4</summary> -Distance evaluation has not been requested -</details> -</li> -<li> -<details> -<summary> -<code>IdtyNotAllowedToRequestMembership</code> - 5</summary> -Identity is not allowed to request membership. +<code>NotEnoughCerts</code> - 0</summary> +Insufficient certifications received. </details> </li> <li> <details> <summary> -<code>IdtyNotAllowedToRenewMembership</code> - 6</summary> -Identity not allowed to renew membership. +<code>TargetStatusInvalid</code> - 1</summary> +Target status is incompatible with this operation. </details> </li> <li> <details> <summary> -<code>IdtyCreationPeriodNotRespected</code> - 7</summary> +<code>IdtyCreationPeriodNotRespected</code> - 2</summary> Identity creation period not respected. </details> </li> <li> <details> <summary> -<code>NotEnoughReceivedCertsToCreateIdty</code> - 8</summary> +<code>NotEnoughReceivedCertsToCreateIdty</code> - 3</summary> Insufficient received certifications to create identity. </details> </li> <li> <details> <summary> -<code>MaxEmittedCertsReached</code> - 9</summary> +<code>MaxEmittedCertsReached</code> - 4</summary> Maximum number of emitted certifications reached. </details> </li> <li> <details> <summary> -<code>NotAllowedToChangeIdtyAddress</code> - 10</summary> -Not allowed to change identity address. -</details> -</li> -<li> -<details> -<summary> -<code>NotAllowedToRemoveIdty</code> - 11</summary> -Not allowed to remove identity. -</details> -</li> -<li> -<details> -<summary> -<code>IssuerNotMember</code> - 12</summary> +<code>IssuerNotMember</code> - 5</summary> Issuer cannot emit a certification because it is not member. </details> </li> <li> <details> <summary> -<code>CertToUnconfirmed</code> - 13</summary> -Cannot issue a certification to an unconfirmed identity -</details> -</li> -<li> -<details> -<summary> -<code>CertToRevoked</code> - 14</summary> -Cannot issue a certification to a revoked identity +<code>IdtyNotFound</code> - 6</summary> +Issuer or receiver not found. </details> </li> <li> <details> <summary> -<code>IdtyNotFound</code> - 15</summary> -Issuer or receiver not found. +<code>MembershipRenewalPeriodNotRespected</code> - 7</summary> +Membership can only be renewed after an antispam delay. </details> </li> </ul> @@ -1011,6 +955,13 @@ Membership already acquired. Membership not found. </details> </li> +<li> +<details> +<summary> +<code>AlreadyMember</code> - 3</summary> +Already member, can not claim membership. +</details> +</li> </ul> </li> <li>Certification - 43 @@ -1019,21 +970,21 @@ Membership not found. <details> <summary> <code>CannotCertifySelf</code> - 0</summary> -Identity cannot certify itself +Identity cannot certify itself. </details> </li> <li> <details> <summary> <code>IssuedTooManyCert</code> - 1</summary> -Identity has already issued the maximum number of certifications +Identity has already issued the maximum number of certifications. </details> </li> <li> <details> <summary> <code>IssuerNotFound</code> - 2</summary> -Issuer not found +Issuer not found. </details> </li> <li> @@ -1085,31 +1036,66 @@ No author for this block. <li> <details> <summary> -<code>NoIdentity</code> - 4</summary> +<code>CallerHasNoIdentity</code> - 4</summary> Caller has no identity. </details> </li> <li> <details> <summary> -<code>QueueFull</code> - 5</summary> +<code>CallerIdentityNotFound</code> - 5</summary> +Caller identity not found. +</details> +</li> +<li> +<details> +<summary> +<code>CallerNotMember</code> - 6</summary> +Caller not member. +</details> +</li> +<li> +<details> +<summary> +<code>CallerStatusInvalid</code> - 7</summary> + +</details> +</li> +<li> +<details> +<summary> +<code>TargetIdentityNotFound</code> - 8</summary> +Target identity not found. +</details> +</li> +<li> +<details> +<summary> +<code>QueueFull</code> - 9</summary> Evaluation queue is full. </details> </li> <li> <details> <summary> -<code>TooManyEvaluators</code> - 6</summary> +<code>TooManyEvaluators</code> - 10</summary> Too many evaluators in the current evaluation pool. </details> </li> <li> <details> <summary> -<code>WrongResultLength</code> - 7</summary> +<code>WrongResultLength</code> - 11</summary> Evaluation result has a wrong length. </details> </li> +<li> +<details> +<summary> +<code>TargetMustBeUnvalidated</code> - 12</summary> +Targeted distance evaluation request is only possible for an unvalidated identity. +</details> +</li> </ul> </li> <li>AtomicSwap - 50 diff --git a/docs/api/runtime-events.md b/docs/api/runtime-events.md index 9aa809263..566e73839 100644 --- a/docs/api/runtime-events.md +++ b/docs/api/runtime-events.md @@ -1,6 +1,6 @@ # Runtime events -There are **127** events from **35** pallets. +There are **128** events from **35** pallets. <ul> <li>System - 0 @@ -650,7 +650,7 @@ no args <details> <summary> <code>InvitationSent(idty_index, invited_by)</code> - 0</summary> -An identity is being inivited to become a smith +An identity is being inivited to become a smith. ```rust idty_index: T::IdtyIndex @@ -663,7 +663,7 @@ invited_by: T::IdtyIndex <details> <summary> <code>InvitationAccepted(idty_index)</code> - 1</summary> -The invitation has been accepted +The invitation has been accepted. ```rust idty_index: T::IdtyIndex @@ -700,7 +700,7 @@ idty_index: T::IdtyIndex <details> <summary> <code>SmithExcluded(idty_index)</code> - 4</summary> -A smith has been removed from the smiths set +A smith has been removed from the smiths set. ```rust idty_index: T::IdtyIndex @@ -1286,7 +1286,20 @@ expire_on: BlockNumberFor<T> <li> <details> <summary> -<code>MembershipRemoved(member, reason)</code> - 1</summary> +<code>MembershipRenewed(member, expire_on)</code> - 1</summary> +A membership was renewed. + +```rust +member: T::IdtyId +expire_on: BlockNumberFor<T> +``` + +</details> +</li> +<li> +<details> +<summary> +<code>MembershipRemoved(member, reason)</code> - 2</summary> A membership was removed. ```rust @@ -1360,11 +1373,11 @@ who: T::AccountId <li> <details> <summary> -<code>EvaluationUpdated(evaluator)</code> - 1</summary> -A distance evaluation was updated. +<code>EvaluatedValid(idty_index)</code> - 1</summary> +Distance rule was found valid. ```rust -evaluator: T::AccountId +idty_index: T::IdtyIndex ``` </details> @@ -1372,12 +1385,11 @@ evaluator: T::AccountId <li> <details> <summary> -<code>EvaluationStatusForced(idty_index, status)</code> - 2</summary> -A distance status was forced. +<code>EvaluatedInvalid(idty_index)</code> - 2</summary> +Distance rule was found invalid. ```rust idty_index: T::IdtyIndex -status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)> ``` </details> diff --git a/end2end-tests/cucumber-features/identity_creation.feature b/end2end-tests/cucumber-features/identity_creation.feature index f8bffeacc..7de97f182 100644 --- a/end2end-tests/cucumber-features/identity_creation.feature +++ b/end2end-tests/cucumber-features/identity_creation.feature @@ -29,6 +29,4 @@ Feature: Identity creation Then dave should have distance result in 1 session When alice runs distance oracle When 30 blocks later - Then dave should have distance ok - When dave claims membership Then dave identity should be member diff --git a/end2end-tests/cucumber-genesis/default.json b/end2end-tests/cucumber-genesis/default.json index 245e91e31..cfd66ba93 100644 --- a/end2end-tests/cucumber-genesis/default.json +++ b/end2end-tests/cucumber-genesis/default.json @@ -53,7 +53,7 @@ "idty_confirm_period": 40, "idty_creation_period": 50, "membership_period": 1000, - "pending_membership_period": 500, + "membership_renewal_period": 500, "ud_creation_period": 60000, "ud_reeval_period": 600000, "smith_cert_max_by_issuer": 8, @@ -64,9 +64,15 @@ "wot_min_cert_for_membership": 2 }, "clique_smiths": [ - { "name": "Alice" }, - { "name": "Bob" }, - { "name": "Charlie" } + { + "name": "Alice" + }, + { + "name": "Bob" + }, + { + "name": "Charlie" + } ], "sudo_key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "technical_committee": [ diff --git a/end2end-tests/cucumber-genesis/wot.json b/end2end-tests/cucumber-genesis/wot.json index 6b1fad670..813fc6fc1 100644 --- a/end2end-tests/cucumber-genesis/wot.json +++ b/end2end-tests/cucumber-genesis/wot.json @@ -62,7 +62,7 @@ "idty_confirm_period": 40, "idty_creation_period": 50, "membership_period": 1000, - "pending_membership_period": 500, + "membership_renewal_period": 500, "ud_creation_period": 60000, "ud_reeval_period": 600000, "smith_cert_max_by_issuer": 8, @@ -73,9 +73,15 @@ "wot_min_cert_for_membership": 2 }, "clique_smiths": [ - { "name": "Alice" }, - { "name": "Bob" }, - { "name": "Charlie" } + { + "name": "Alice" + }, + { + "name": "Bob" + }, + { + "name": "Charlie" + } ], "sudo_key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "technical_committee": [ diff --git a/end2end-tests/tests/common/membership.rs b/end2end-tests/tests/common/membership.rs deleted file mode 100644 index d800f3387..000000000 --- a/end2end-tests/tests/common/membership.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021 Axiom-Team -// -// This file is part of Substrate-Libre-Currency. -// -// Substrate-Libre-Currency is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, version 3 of the License. -// -// Substrate-Libre-Currency is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. - -use super::*; -use sp_keyring::AccountKeyring; -use subxt::tx::PairSigner; - -pub async fn claim_membership(client: &Client, from: AccountKeyring) -> Result<()> { - let from = PairSigner::new(from.pair()); - - let _events = create_block_with_extrinsic( - client, - client - .tx() - .create_signed( - &gdev::tx().membership().claim_membership(), - &from, - BaseExtrinsicParamsBuilder::new(), - ) - .await - .unwrap(), - ) - .await - .unwrap(); - - Ok(()) -} diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs index 6dbbbda5b..d69a00c15 100644 --- a/end2end-tests/tests/common/mod.rs +++ b/end2end-tests/tests/common/mod.rs @@ -20,7 +20,6 @@ pub mod balances; pub mod cert; pub mod distance; pub mod identity; -pub mod membership; pub mod oneshot; #[subxt::subxt( @@ -234,9 +233,9 @@ fn spawn_full_node( let timeout = if let Ok(duration_string) = std::env::var("DUNITER_END2END_TESTS_SPAWN_NODE_TIMEOUT") { - duration_string.parse().unwrap_or(4) + duration_string.parse().unwrap_or(10) } else { - 4 + 10 }; wait_until_log_line( diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs index 7832a4a3d..48d7c30be 100644 --- a/end2end-tests/tests/cucumber_tests.rs +++ b/end2end-tests/tests/cucumber_tests.rs @@ -333,14 +333,6 @@ async fn confirm_identity(world: &mut DuniterWorld, from: String, pseudo: String common::identity::confirm_identity(world.client(), from, pseudo).await } -#[allow(clippy::needless_pass_by_ref_mut)] -#[when(regex = r#"([a-zA-Z]+) claims membership"#)] -async fn claim_membership(world: &mut DuniterWorld, from: String) -> Result<()> { - // input names to keyrings - let from = AccountKeyring::from_str(&from).expect("unknown from"); - common::membership::claim_membership(world.client(), from).await -} - #[allow(clippy::needless_pass_by_ref_mut)] #[when(regex = r#"([a-zA-Z]+) requests distance evaluation"#)] async fn request_distance_evaluation(world: &mut DuniterWorld, who: String) -> Result<()> { @@ -541,33 +533,6 @@ async fn should_have_distance_result_in_sessions( Err(anyhow::anyhow!("no evaluation in given pool").into()) } -#[allow(clippy::needless_pass_by_ref_mut)] -#[then(regex = r"([a-zA-Z]+) should have distance ok")] -async fn should_have_distance_ok(world: &mut DuniterWorld, who: String) -> Result<()> { - let who = AccountKeyring::from_str(&who).unwrap().to_account_id(); - - let idty_id = world - .read(&gdev::storage().identity().identity_index_of(&who.into())) - .await - .await? - .unwrap(); - - match world - .read(&gdev::storage().distance().identity_distance_status(idty_id)) - .await - .await? - { - Some((_, gdev::runtime_types::pallet_distance::types::DistanceStatus::Valid)) => Ok(()), - Some((_, gdev::runtime_types::pallet_distance::types::DistanceStatus::Invalid)) => { - Err(anyhow::anyhow!("invalid distance status").into()) - } - Some((_, gdev::runtime_types::pallet_distance::types::DistanceStatus::Pending)) => { - Err(anyhow::anyhow!("pending distance status").into()) - } - None => Err(anyhow::anyhow!("no distance status").into()), - } -} - use gdev::runtime_types::pallet_identity::types::IdtyStatus; // status from string diff --git a/live-tests/tests/sanity_gdev.rs b/live-tests/tests/sanity_gdev.rs index d0ae6434c..e326b7191 100644 --- a/live-tests/tests/sanity_gdev.rs +++ b/live-tests/tests/sanity_gdev.rs @@ -156,8 +156,6 @@ mod verifier { Self { errors: Vec::new() } } - // FIXME why async functions when called with await? - /// method to run all storage tests pub(super) async fn verify_storage(&mut self, storage: &Storage) -> anyhow::Result<()> { self.verify_accounts(&storage.accounts).await; diff --git a/node/src/chain_spec/gdev.rs b/node/src/chain_spec/gdev.rs index e927d483b..a66be70d2 100644 --- a/node/src/chain_spec/gdev.rs +++ b/node/src/chain_spec/gdev.rs @@ -87,7 +87,7 @@ fn get_parameters(parameters_from_file: &Option<GenesisParameters>) -> CommonPar identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(), identity_idty_creation_period: parameters_from_file.idty_creation_period, membership_membership_period: parameters_from_file.membership_period, - membership_pending_membership_period: parameters_from_file.pending_membership_period, + membership_membership_renewal_period: parameters_from_file.membership_renewal_period, cert_max_by_issuer: parameters_from_file.cert_max_by_issuer, cert_min_received_cert_to_be_able_to_issue_cert: parameters_from_file .cert_min_received_cert_to_issue_cert, @@ -373,6 +373,7 @@ fn get_local_chain_parameters() -> Option<GenesisParameters> { let babe_epoch_duration = get_env("DUNITER_BABE_EPOCH_DURATION", 30) as u64; let cert_validity_period = get_env("DUNITER_CERT_VALIDITY_PERIOD", 1_000); let membership_period = get_env("DUNITER_MEMBERSHIP_PERIOD", 1_000); + let membership_renewal_period = get_env("DUNITER_MEMBERSHIP_RENEWAL_PERIOD", 1_000); let ud_creation_period = get_env("DUNITER_UD_CREATION_PERIOD", 60_000); let ud_reeval_period = get_env("DUNITER_UD_REEEVAL_PERIOD", 1_200_000); Some(GenesisParameters { @@ -384,7 +385,7 @@ fn get_local_chain_parameters() -> Option<GenesisParameters> { idty_confirm_period: 40, idty_creation_period: 50, membership_period, - pending_membership_period: 500, + membership_renewal_period, ud_creation_period, ud_reeval_period, smith_cert_max_by_issuer: 8, diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs index 56481245e..2565f3d4a 100644 --- a/node/src/chain_spec/gen_genesis_data.rs +++ b/node/src/chain_spec/gen_genesis_data.rs @@ -553,6 +553,15 @@ where G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32 ) } + if common_parameters.membership_membership_renewal_period / DAYS + != G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS + { + warn!( + "parameter `membership_renewal_period` ({} days) is different from Ğ1's ({} days)", + common_parameters.membership_membership_renewal_period as f32 / DAYS as f32, + G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32 + ) + } if common_parameters.cert_cert_period / DAYS != G1_DUNITER_V1_SIGPERIOD / DUNITER_V1_DAYS { warn!( "parameter `cert_period` ({} days) is different from Ğ1's ({} days)", @@ -807,8 +816,8 @@ fn dump_genesis_info(info: GenesisInfo) { get_best_unit_and_diviser_for_blocks(p.identity_idty_creation_period); let (membership_membership_period, membership_membership_period_unit) = get_best_unit_and_diviser_for_blocks(p.membership_membership_period); - let (membership_pending_membership_period, membership_pending_membership_period_unit) = - get_best_unit_and_diviser_for_blocks(p.membership_pending_membership_period); + let (membership_membership_renewal_period, membership_membership_renewal_period_unit) = + get_best_unit_and_diviser_for_blocks(p.membership_membership_renewal_period); let (cert_cert_period, cert_cert_period_unit) = get_best_unit_and_diviser_for_blocks(p.cert_cert_period); let (cert_max_by_issuer, cert_max_by_issuer_unit) = @@ -857,7 +866,7 @@ fn dump_genesis_info(info: GenesisInfo) { - identity.change_owner_key_period: {} {} - identity.idty_creation_period: {} {} - membership.membership_period: {} {} - - membership.pending_membership_period: {} {} + - membership.membership_renewal_period: {} {} - cert.cert_period: {} {} - cert.max_by_issuer: {} {} - cert.min_received_cert_to_be_able_to_issue_cert: {} {} @@ -907,8 +916,8 @@ fn dump_genesis_info(info: GenesisInfo) { identity_idty_creation_period_unit, membership_membership_period, membership_membership_period_unit, - membership_pending_membership_period, - membership_pending_membership_period_unit, + membership_membership_renewal_period, + membership_membership_renewal_period_unit, cert_cert_period, cert_cert_period_unit, cert_max_by_issuer, @@ -1980,7 +1989,7 @@ pub struct CommonParameters { pub identity_change_owner_key_period: u32, pub identity_idty_creation_period: u32, pub membership_membership_period: u32, - pub membership_pending_membership_period: u32, + pub membership_membership_renewal_period: u32, pub cert_cert_period: u32, pub cert_max_by_issuer: u32, pub cert_min_received_cert_to_be_able_to_issue_cert: u32, diff --git a/node/src/chain_spec/gtest.rs b/node/src/chain_spec/gtest.rs index d01ee0cce..e7814f6d0 100644 --- a/node/src/chain_spec/gtest.rs +++ b/node/src/chain_spec/gtest.rs @@ -91,7 +91,7 @@ fn get_parameters(_: &Option<GenesisParameters>) -> CommonParameters { identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(), identity_idty_creation_period: parameters::IdtyCreationPeriod::get(), membership_membership_period: parameters::MembershipPeriod::get(), - membership_pending_membership_period: parameters::PendingMembershipPeriod::get(), + membership_membership_renewal_period: parameters::MembershipRenewalPeriod::get(), cert_max_by_issuer: parameters::MaxByIssuer::get(), cert_min_received_cert_to_be_able_to_issue_cert: parameters::MinReceivedCertToBeAbleToIssueCert::get(), diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs index d4ebf6b3f..3494331ad 100644 --- a/pallets/authority-members/src/lib.rs +++ b/pallets/authority-members/src/lib.rs @@ -177,25 +177,25 @@ pub mod pallet { #[pallet::error] pub enum Error<T> { - /// Member already incoming + /// Member already incoming. AlreadyIncoming, - /// Member already online + /// Member already online. AlreadyOnline, - /// Member already outgoing + /// Member already outgoing. AlreadyOutgoing, /// Owner key is invalid as a member. MemberIdNotFound, - /// Member is blacklisted + /// Member is blacklisted. MemberBlacklisted, - /// Member is not blacklisted + /// Member is not blacklisted. MemberNotBlacklisted, - /// Member not found + /// Member not found. MemberNotFound, - /// Neither online nor scheduled + /// Neither online nor scheduled. NotOnlineNorIncoming, - /// Not member + /// Not member. NotMember, - /// Session keys not provided + /// Session keys not provided. SessionKeysNotProvided, /// Too many authorities. TooManyAuthorities, diff --git a/pallets/certification/src/benchmarking.rs b/pallets/certification/src/benchmarking.rs index 553e34846..add49d644 100644 --- a/pallets/certification/src/benchmarking.rs +++ b/pallets/certification/src/benchmarking.rs @@ -80,7 +80,7 @@ benchmarks! { assert!(CertsByReceiver::<T>::get(receiver).is_empty() ); } on_initialize { - assert!(StorageCertsRemovableOn::<T>::try_get(T::BlockNumber::zero()).is_err()); + assert!(CertsRemovableOn::<T>::try_get(T::BlockNumber::zero()).is_err()); }: {Pallet::<T>::on_initialize(T::BlockNumber::zero());} do_remove_cert_noop { }: {Pallet::<T>::do_remove_cert(100.into(), 101.into(), Some(T::BlockNumber::zero()));} diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs index aab81a264..f3cc3c0c4 100644 --- a/pallets/certification/src/lib.rs +++ b/pallets/certification/src/lib.rs @@ -58,7 +58,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { #[pallet::constant] - /// Minimum duration between two certifications issued by the same issuer + /// Minimum duration between two certifications issued by the same issuer. type CertPeriod: Get<Self::BlockNumber>; /// A short identity index. type IdtyIndex: Parameter @@ -70,27 +70,26 @@ pub mod pallet { + MaybeSerializeDeserialize + Debug + MaxEncodedLen; - /// Something that give the owner key of an identity + /// Something that give the owner key of an identity. type OwnerKeyOf: Convert<Self::IdtyIndex, Option<Self::AccountId>>; - /// + /// Provide method to check that cert is allowed. type CheckCertAllowed: CheckCertAllowed<Self::IdtyIndex>; #[pallet::constant] - /// Maximum number of active certifications by issuer + /// Maximum number of active certifications by issuer. type MaxByIssuer: Get<u32>; - /// Minimum number of certifications that must be received to be able to issue - /// certifications. + /// Minimum number of certifications received to be allowed to issue a certification. #[pallet::constant] type MinReceivedCertToBeAbleToIssueCert: Get<u32>; - /// Handler for NewCert event + /// Handler for NewCert event. type OnNewcert: OnNewcert<Self::IdtyIndex>; - /// Handler for Removed event + /// Handler for Removed event. type OnRemovedCert: OnRemovedCert<Self::IdtyIndex>; /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; - /// Type representing the weight of this pallet + /// Type representing the weight of this pallet. type WeightInfo: WeightInfo; #[pallet::constant] - /// Duration of validity of a certification + /// Duration of validity of a certification. type ValidityPeriod: Get<Self::BlockNumber>; } @@ -191,31 +190,31 @@ pub mod pallet { ); StorageIdtyCertMeta::<T>::insert(issuer, cert_meta); } - // Write storage StorageCertsRemovableOn + // Write storage CertsRemovableOn for (removable_on, certs) in certs_removable_on { - StorageCertsRemovableOn::<T>::insert(removable_on, certs); + CertsRemovableOn::<T>::insert(removable_on, certs); } } } // STORAGE // - /// Certifications metada by issuer + /// Certifications metada by issuer. #[pallet::storage] #[pallet::getter(fn idty_cert_meta)] pub type StorageIdtyCertMeta<T: Config> = StorageMap<_, Twox64Concat, T::IdtyIndex, IdtyCertMeta<T::BlockNumber>, ValueQuery>; - /// Certifications by receiver + /// Certifications by receiver. #[pallet::storage] #[pallet::getter(fn certs_by_receiver)] pub type CertsByReceiver<T: Config> = StorageMap<_, Twox64Concat, T::IdtyIndex, Vec<(T::IdtyIndex, T::BlockNumber)>, ValueQuery>; - /// Certifications removable on + /// Certifications removable on. #[pallet::storage] #[pallet::getter(fn certs_removable_on)] - pub type StorageCertsRemovableOn<T: Config> = + pub type CertsRemovableOn<T: Config> = StorageMap<_, Twox64Concat, T::BlockNumber, Vec<(T::IdtyIndex, T::IdtyIndex)>, OptionQuery>; // EVENTS // @@ -245,11 +244,11 @@ pub mod pallet { #[pallet::error] pub enum Error<T> { - /// Identity cannot certify itself + /// Identity cannot certify itself. CannotCertifySelf, - /// Identity has already issued the maximum number of certifications + /// Identity has already issued the maximum number of certifications. IssuedTooManyCert, - /// Issuer not found + /// Issuer not found. IssuerNotFound, /// Insufficient certifications received. NotEnoughCertReceived, @@ -282,9 +281,16 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; + // Verify caller ownership + let issuer_owner_key = + T::OwnerKeyOf::convert(issuer).ok_or(Error::<T>::IssuerNotFound)?; + ensure!(issuer_owner_key == who, DispatchError::BadOrigin); + let block_number = frame_system::pallet::Pallet::<T>::block_number(); - Self::check_cert_allowed(who, issuer, receiver, block_number)?; - Self::do_add_cert(block_number, issuer, receiver) + Self::check_add_cert(issuer, receiver, block_number)?; + Self::do_add_cert(block_number, issuer, receiver); + + Ok(().into()) } /// remove a certification (only root) @@ -324,44 +330,23 @@ pub mod pallet { receiver: T::IdtyIndex, verify_rules: bool, ) -> DispatchResultWithPostInfo { - // Forbid self cert - ensure!(issuer != receiver, Error::<T>::CannotCertifySelf); - let block_number = frame_system::pallet::Pallet::<T>::block_number(); if verify_rules { - // Verify rule MinReceivedCertToBeAbleToIssueCert - let issuer_idty_cert_meta = StorageIdtyCertMeta::<T>::get(issuer); - ensure!( - issuer_idty_cert_meta.received_count - >= T::MinReceivedCertToBeAbleToIssueCert::get(), - Error::<T>::NotEnoughCertReceived - ); - - // Verify rule MaxByIssuer - ensure!( - issuer_idty_cert_meta.issued_count < T::MaxByIssuer::get(), - Error::<T>::IssuedTooManyCert - ); - - // Verify rule CertPeriod - ensure!( - block_number >= issuer_idty_cert_meta.next_issuable_on, - Error::<T>::NotRespectCertPeriod - ); + // only verify internal rules if asked + Self::check_add_cert_internal(issuer, receiver, block_number)?; }; - Self::do_add_cert(block_number, issuer, receiver) + Self::do_add_cert(block_number, issuer, receiver); + + Ok(().into()) } + /// perform cert addition or renewal - fn do_add_cert( - block_number: T::BlockNumber, - issuer: T::IdtyIndex, - receiver: T::IdtyIndex, - ) -> DispatchResultWithPostInfo { - // Write StorageCertsRemovableOn + fn do_add_cert(block_number: T::BlockNumber, issuer: T::IdtyIndex, receiver: T::IdtyIndex) { + // Write CertsRemovableOn let removable_on = block_number + T::ValidityPeriod::get(); - <StorageCertsRemovableOn<T>>::append(removable_on, (issuer, receiver)); + <CertsRemovableOn<T>>::append(removable_on, (issuer, receiver)); // Write CertsByReceiver let mut created = false; @@ -414,17 +399,16 @@ pub mod pallet { }); // emit CertRenewed event Self::deposit_event(Event::CertRenewed { issuer, receiver }); - } - - Ok(().into()) + }; } + /// remove the certifications due to expire on the given block // (run at on_initialize step) fn prune_certifications(block_number: T::BlockNumber) -> Weight { // See on initialize for the overhead weight accounting let mut total_weight = Weight::zero(); - if let Some(certs) = StorageCertsRemovableOn::<T>::take(block_number) { + if let Some(certs) = CertsRemovableOn::<T>::take(block_number) { for (issuer, receiver) in certs { total_weight += Self::do_remove_cert(issuer, receiver, Some(block_number)); } @@ -432,6 +416,7 @@ pub mod pallet { total_weight } + /// perform the certification removal /// if block number is given only remove cert if still set to expire at this block number pub fn do_remove_cert( @@ -495,24 +480,19 @@ pub mod pallet { } /// check cert allowed - // first internal checks - // then external checks - fn check_cert_allowed( - caller_key: T::AccountId, + // 1. no self cert + // 2. issuer received cert count + // 3. issuer max emitted cert + // 4. issuer cert period + fn check_add_cert_internal( issuer: T::IdtyIndex, receiver: T::IdtyIndex, block_number: T::BlockNumber, ) -> DispatchResult { - // --- first internal checks // 1. Forbid self cert ensure!(issuer != receiver, Error::<T>::CannotCertifySelf); - // 2. Verify caller ownership - let issuer_owner_key = - T::OwnerKeyOf::convert(issuer).ok_or(Error::<T>::IssuerNotFound)?; - ensure!(issuer_owner_key == caller_key, DispatchError::BadOrigin); - - // 3. Verify rule MinReceivedCertToBeAbleToIssueCert + // 2. Verify rule MinReceivedCertToBeAbleToIssueCert // (this number can differ from the one necessary to be member) let issuer_idty_cert_meta = <StorageIdtyCertMeta<T>>::get(issuer); ensure!( @@ -521,18 +501,32 @@ pub mod pallet { Error::<T>::NotEnoughCertReceived ); - // 4. Verify rule MaxByIssuer + // 3. Verify rule MaxByIssuer ensure!( issuer_idty_cert_meta.issued_count < T::MaxByIssuer::get(), Error::<T>::IssuedTooManyCert ); - // 5. Verify rule CertPeriod + // 4. Verify rule CertPeriod ensure!( block_number >= issuer_idty_cert_meta.next_issuable_on, Error::<T>::NotRespectCertPeriod ); + Ok(()) + } + + /// check cert allowed + // first internal checks + // then external checks + fn check_add_cert( + issuer: T::IdtyIndex, + receiver: T::IdtyIndex, + block_number: T::BlockNumber, + ) -> DispatchResult { + // internal checks + Self::check_add_cert_internal(issuer, receiver, block_number)?; + // --- then external checks // - issuer is member // - receiver is confirmed diff --git a/pallets/certification/src/weights.rs b/pallets/certification/src/weights.rs index 4a5bc2f40..1f5918b2a 100644 --- a/pallets/certification/src/weights.rs +++ b/pallets/certification/src/weights.rs @@ -33,7 +33,7 @@ impl WeightInfo for () { // Storage: Identity Identities (r:2 w:0) // Storage: Cert StorageIdtyCertMeta (r:2 w:2) // Storage: Parameters ParametersStorage (r:1 w:0) - // Storage: Cert StorageCertsRemovableOn (r:1 w:1) + // Storage: Cert CertsRemovableOn (r:1 w:1) // Storage: Cert CertsByReceiver (r:1 w:1) fn add_cert() -> Weight { // Minimum execution time: 259_247 nanoseconds. diff --git a/pallets/distance/src/benchmarking.rs b/pallets/distance/src/benchmarking.rs index 33ba9419b..c762bde19 100644 --- a/pallets/distance/src/benchmarking.rs +++ b/pallets/distance/src/benchmarking.rs @@ -50,16 +50,36 @@ benchmarks! { T: pallet_balances::Config, T::Balance: From<u64>, T::BlockNumber: From<u32>, } + + // request distance evaluation request_distance_evaluation { - let idty = T::IdtyIndex::one(); - let caller: T::AccountId = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key; - let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); - let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let idty = T::IdtyIndex::one(); + let caller: T::AccountId = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key; + let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); + let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); }: _<T::RuntimeOrigin>(caller_origin.clone()) verify { - assert!(IdentityDistanceStatus::<T>::get(idty) == Some((caller.clone(), DistanceStatus::Pending)), "Request not added"); + assert!(PendingEvaluationRequest::<T>::get(idty) == Some(caller.clone()), "Request not added"); assert_has_event::<T>(Event::<T>::EvaluationRequested { idty_index: idty, who: caller }.into()); } + + // request distance evaluation for + request_distance_evaluation_for { + let idty = T::IdtyIndex::one(); + let caller: T::AccountId = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key; + let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); + let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let target = 2u32; + // set target status since targeted distance evaluation only allowed for unvalidated + pallet_identity::Identities::<T>::mutate(target, + |idty_val| idty_val.as_mut().unwrap().status = pallet_identity::IdtyStatus::Unvalidated); + }: _<T::RuntimeOrigin>(caller_origin.clone(), target) + verify { + assert!(PendingEvaluationRequest::<T>::get(target) == Some(caller.clone()), "Request not added"); + assert_has_event::<T>(Event::<T>::EvaluationRequested { idty_index: target, who: caller }.into()); + } + + // update evaluation update_evaluation { let digest_data = sp_consensus_babe::digests::PreDigest::SecondaryPlain( sp_consensus_babe::digests::SecondaryPlainPreDigest { authority_index: 0u32, slot: Default::default() }); @@ -71,27 +91,25 @@ benchmarks! { let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); let i in 1 .. MAX_EVALUATIONS_PER_SESSION => populate_pool::<T>(i)?; }: _<T::RuntimeOrigin>(RawOrigin::None.into(), ComputationResult{distances: vec![Perbill::one(); i as usize]}) - verify { - assert_has_event::<T>(Event::<T>::EvaluationUpdated { evaluator: caller }.into()); - } + + // force update evaluation force_update_evaluation { let idty = T::IdtyIndex::one(); let caller: T::AccountId = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key; let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); let i in 1 .. MAX_EVALUATIONS_PER_SESSION => populate_pool::<T>(i)?; }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), caller.clone(), ComputationResult{distances: vec![Perbill::one(); i as usize]}) + + // force valid distance status + force_valid_distance_status { + let idty = T::IdtyIndex::one(); + let caller: T::AccountId = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key; + }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), idty) verify { - assert_has_event::<T>(Event::<T>::EvaluationUpdated { evaluator: caller }.into()); - } - force_set_distance_status { - let idty = T::IdtyIndex::one(); - let caller: T::AccountId = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key; - let status = Some((caller.clone(), DistanceStatus::Valid)); - }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), idty, status.clone()) - verify { - assert!(IdentityDistanceStatus::<T>::get(idty) == Some((caller, DistanceStatus::Valid)), "Status not set"); - assert_has_event::<T>(Event::<T>::EvaluationStatusForced { idty_index: idty, status }.into()); + assert_has_event::<T>(Event::<T>::EvaluatedValid { idty_index: idty }.into()); } + + // on finalize on_finalize { DidUpdate::<T>::set(true); }: { Pallet::<T>::on_finalize(Default::default()); } diff --git a/pallets/distance/src/lib.rs b/pallets/distance/src/lib.rs index d7d131cd8..1790134a0 100644 --- a/pallets/distance/src/lib.rs +++ b/pallets/distance/src/lib.rs @@ -17,7 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod median; -mod traits; +pub mod traits; mod types; mod weights; @@ -26,6 +26,8 @@ pub mod benchmarking; #[cfg(test)] mod mock; +#[cfg(test)] +mod tests; pub use pallet::*; pub use traits::*; @@ -37,6 +39,7 @@ use pallet_authority_members::SessionIndex; use sp_distance::{InherentError, INHERENT_IDENTIFIER}; use sp_inherents::{InherentData, InherentIdentifier}; use sp_std::convert::TryInto; +use sp_std::prelude::*; type IdtyIndex = u32; @@ -66,6 +69,7 @@ pub mod pallet { + pallet_identity::Config<IdtyIndex = IdtyIndex> + pallet_session::Config { + /// Currency type used in this pallet (used for reserve/slash) type Currency: ReservableCurrency<Self::AccountId>; /// Amount reserved during evaluation #[pallet::constant] @@ -79,12 +83,14 @@ pub mod pallet { /// Minimum ratio of accessible referees #[pallet::constant] type MinAccessibleReferees: Get<Perbill>; - /// Number of session to keep a positive evaluation result - type ResultExpiration: Get<u32>; /// The overarching event type. type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// Type representing the weight of this pallet type WeightInfo: WeightInfo; + /// Handler for successful distance evaluation + type OnValidDistanceStatus: OnValidDistanceStatus<Self>; + /// Trait to check that distance evaluation request is allowed + type CheckRequestDistanceEvaluation: CheckRequestDistanceEvaluation<Self>; } // STORAGE // @@ -128,35 +134,20 @@ pub mod pallet { pub type EvaluationBlock<T: Config> = StorageValue<_, <T as frame_system::Config>::Hash, ValueQuery>; - /// Distance evaluation status by identity + /// Pending evaluation requesters /// - /// * `.0` is the account who requested an evaluation and reserved the price, + /// account who requested an evaluation and reserved the price, /// for whom the price will be unreserved or slashed when the evaluation completes. - /// * `.1` is the status of the evaluation. #[pallet::storage] - #[pallet::getter(fn identity_distance_status)] - pub type IdentityDistanceStatus<T: Config> = StorageMap< + #[pallet::getter(fn pending_evaluation_request)] + pub type PendingEvaluationRequest<T: Config> = StorageMap< _, Twox64Concat, <T as pallet_identity::Config>::IdtyIndex, - (<T as frame_system::Config>::AccountId, DistanceStatus), + <T as frame_system::Config>::AccountId, OptionQuery, >; - /// Identities by distance status expiration session index - #[pallet::storage] - #[pallet::getter(fn distance_status_expire_on)] - pub type DistanceStatusExpireOn<T: Config> = StorageMap< - _, - Twox64Concat, - u32, - BoundedVec< - <T as pallet_identity::Config>::IdtyIndex, - ConstU32<MAX_EVALUATIONS_PER_SESSION>, - >, - ValueQuery, - >; - /// Did evaluation get updated in this block? #[pallet::storage] pub(super) type DidUpdate<T: Config> = StorageValue<_, bool, ValueQuery>; @@ -176,13 +167,10 @@ pub mod pallet { idty_index: T::IdtyIndex, who: T::AccountId, }, - /// A distance evaluation was updated. - EvaluationUpdated { evaluator: T::AccountId }, - /// A distance status was forced. - EvaluationStatusForced { - idty_index: T::IdtyIndex, - status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>, - }, + /// Distance rule was found valid. + EvaluatedValid { idty_index: T::IdtyIndex }, + /// Distance rule was found invalid. + EvaluatedInvalid { idty_index: T::IdtyIndex }, } // ERRORS // @@ -198,13 +186,23 @@ pub mod pallet { /// No author for this block. NoAuthor, /// Caller has no identity. - NoIdentity, + CallerHasNoIdentity, + /// Caller identity not found. + CallerIdentityNotFound, + /// Caller not member. + CallerNotMember, + // Caller status can only be Unvalidated, Member or NotMember. + CallerStatusInvalid, + /// Target identity not found. + TargetIdentityNotFound, /// Evaluation queue is full. QueueFull, /// Too many evaluators in the current evaluation pool. TooManyEvaluators, /// Evaluation result has a wrong length. WrongResultLength, + /// Targeted distance evaluation request is only possible for an unvalidated identity. + TargetMustBeUnvalidated, } #[pallet::hooks] @@ -228,32 +226,45 @@ pub mod pallet { #[pallet::call] impl<T: Config> Pallet<T> { - /// Request an identity to be evaluated + /// Request caller identity to be evaluated + /// positive evaluation will result in claim/renew membership + /// negative evaluation will result in slash for caller #[pallet::call_index(0)] #[pallet::weight(<T as pallet::Config>::WeightInfo::request_distance_evaluation())] pub fn request_distance_evaluation(origin: OriginFor<T>) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let idty = - pallet_identity::IdentityIndexOf::<T>::get(&who).ok_or(Error::<T>::NoIdentity)?; + let idty = Self::check_request_distance_evaluation_self(&who)?; - ensure!( - IdentityDistanceStatus::<T>::get(idty) - != Some((who.clone(), DistanceStatus::Pending)), - Error::<T>::AlreadyInEvaluation - ); + Pallet::<T>::do_request_distance_evaluation(&who, idty)?; + Ok(().into()) + } + + /// Request target identity to be evaluated + /// only possible for unvalidated identity + #[pallet::call_index(4)] + #[pallet::weight(<T as pallet::Config>::WeightInfo::request_distance_evaluation_for())] + pub fn request_distance_evaluation_for( + origin: OriginFor<T>, + target: T::IdtyIndex, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + Self::check_request_distance_evaluation_for(&who, target)?; - Pallet::<T>::do_request_distance_evaluation(who, idty)?; + Pallet::<T>::do_request_distance_evaluation(&who, target)?; Ok(().into()) } /// (Inherent) Push an evaluation result to the pool + /// this is called internally by validators (= inherent) #[pallet::call_index(1)] #[pallet::weight(<T as pallet::Config>::WeightInfo::update_evaluation(MAX_EVALUATIONS_PER_SESSION))] pub fn update_evaluation( origin: OriginFor<T>, computation_result: ComputationResult, ) -> DispatchResult { + // no origin = inherent ensure_none(origin)?; ensure!( !DidUpdate::<T>::exists(), @@ -267,7 +278,8 @@ pub mod pallet { Ok(()) } - /// Push an evaluation result to the pool + /// Force push an evaluation result to the pool + // (it is convenient to have this call in end2end tests) #[pallet::call_index(2)] #[pallet::weight(<T as pallet::Config>::WeightInfo::force_update_evaluation(MAX_EVALUATIONS_PER_SESSION))] pub fn force_update_evaluation( @@ -280,60 +292,21 @@ pub mod pallet { Pallet::<T>::do_update_evaluation(evaluator, computation_result) } - /// Set the distance evaluation status of an identity - /// - /// Removes the status if `status` is `None`. - /// - /// * `status.0` is the account for whom the price will be unreserved or slashed - /// when the evaluation completes. - /// * `status.1` is the status of the evaluation. + /// Force set the distance evaluation status of an identity + // (it is convenient to have this in test network) #[pallet::call_index(3)] - #[pallet::weight(<T as pallet::Config>::WeightInfo::force_set_distance_status())] - pub fn force_set_distance_status( + #[pallet::weight(<T as pallet::Config>::WeightInfo::force_valid_distance_status())] + pub fn force_valid_distance_status( origin: OriginFor<T>, identity: <T as pallet_identity::Config>::IdtyIndex, - status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>, ) -> DispatchResult { ensure_root(origin)?; - IdentityDistanceStatus::<T>::set(identity, status.clone()); - DistanceStatusExpireOn::<T>::mutate( - pallet_session::CurrentIndex::<T>::get() + T::ResultExpiration::get(), - move |identities| { - identities - .try_push(identity) - .map_err(|_| Error::<T>::TooManyEvaluationsInBlock) - }, - )?; - Self::deposit_event(Event::EvaluationStatusForced { - idty_index: identity, - status, - }); + Self::do_valid_distance_status(identity); Ok(()) } } - // BENCHMARK FUNCTIONS // - - impl<T: Config> Pallet<T> { - /// Force the distance status using IdtyIndex and AccountId - /// only to prepare identity for benchmarking. - pub fn set_distance_status( - identity: <T as pallet_identity::Config>::IdtyIndex, - status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>, - ) -> DispatchResult { - IdentityDistanceStatus::<T>::set(identity, status); - DistanceStatusExpireOn::<T>::mutate( - pallet_session::CurrentIndex::<T>::get() + T::ResultExpiration::get(), - move |identities| { - identities - .try_push(identity) - .map_err(|_| Error::<T>::TooManyEvaluationsInBlock.into()) - }, - ) - } - } - // INTERNAL FUNCTIONS // impl<T: Config> Pallet<T> { @@ -398,55 +371,119 @@ pub mod pallet { } } + /// check that request distance evaluation is allowed + fn check_request_distance_evaluation_self( + who: &T::AccountId, + ) -> Result<<T as pallet_identity::Config>::IdtyIndex, DispatchError> { + // caller has an identity + let idty_index = pallet_identity::IdentityIndexOf::<T>::get(who) + .ok_or(Error::<T>::CallerHasNoIdentity)?; + let idty = pallet_identity::Identities::<T>::get(idty_index) + .ok_or(Error::<T>::CallerIdentityNotFound)?; + // caller is (Unvalidated, Member, NotMember) + ensure!( + idty.status == pallet_identity::IdtyStatus::Unvalidated + || idty.status == pallet_identity::IdtyStatus::Member + || idty.status == pallet_identity::IdtyStatus::NotMember, + Error::<T>::CallerStatusInvalid + ); + Self::check_request_distance_evaluation_common(idty_index)?; + Ok(idty_index) + } + + /// check that targeted request distance evaluation is allowed + fn check_request_distance_evaluation_for( + who: &T::AccountId, + target: <T as pallet_identity::Config>::IdtyIndex, + ) -> Result<(), DispatchError> { + // caller has an identity + let caller_idty_index = pallet_identity::IdentityIndexOf::<T>::get(who) + .ok_or(Error::<T>::CallerHasNoIdentity)?; + let caller_idty = pallet_identity::Identities::<T>::get(caller_idty_index) + .ok_or(Error::<T>::CallerIdentityNotFound)?; + // caller is member + ensure!( + caller_idty.status == pallet_identity::IdtyStatus::Member, + Error::<T>::CallerNotMember + ); + // target has an identity + let target_idty = pallet_identity::Identities::<T>::get(target) + .ok_or(Error::<T>::TargetIdentityNotFound)?; + // target is unvalidated + ensure!( + target_idty.status == pallet_identity::IdtyStatus::Unvalidated, + Error::<T>::TargetMustBeUnvalidated + ); + Self::check_request_distance_evaluation_common(target)?; + Ok(()) + } + + // common checks between check_request_distance_evaluation _self and _for + fn check_request_distance_evaluation_common( + target: <T as pallet_identity::Config>::IdtyIndex, + ) -> Result<(), DispatchError> { + // no pending evaluation request + ensure!( + PendingEvaluationRequest::<T>::get(target).is_none(), + Error::<T>::AlreadyInEvaluation + ); + // external validation (wot) + // - membership renewal antispam + // - target has received enough certifications + T::CheckRequestDistanceEvaluation::check_request_distance_evaluation(target) + } + + /// request distance evaluation in current pool fn do_request_distance_evaluation( - who: T::AccountId, + who: &T::AccountId, idty_index: <T as pallet_identity::Config>::IdtyIndex, ) -> Result<(), DispatchError> { Pallet::<T>::mutate_current_pool( pallet_session::CurrentIndex::<T>::get(), |current_pool| { + // extrinsics are transactional by default, this check might not be needed ensure!( current_pool.evaluations.len() < (MAX_EVALUATIONS_PER_SESSION as usize), Error::<T>::QueueFull ); - T::Currency::reserve(&who, <T as Config>::EvaluationPrice::get())?; + T::Currency::reserve(who, <T as Config>::EvaluationPrice::get())?; current_pool .evaluations .try_push((idty_index, median::MedianAcc::new())) .map_err(|_| Error::<T>::QueueFull)?; - IdentityDistanceStatus::<T>::insert( - idty_index, - (&who, DistanceStatus::Pending), - ); + PendingEvaluationRequest::<T>::insert(idty_index, who); - DistanceStatusExpireOn::<T>::mutate( - pallet_session::CurrentIndex::<T>::get() + T::ResultExpiration::get(), - move |identities| identities.try_push(idty_index).ok(), - ); - Self::deposit_event(Event::EvaluationRequested { idty_index, who }); + Self::deposit_event(Event::EvaluationRequested { + idty_index, + who: who.clone(), + }); Ok(()) }, ) } + /// update distance evaluation in next pool fn do_update_evaluation( evaluator: <T as frame_system::Config>::AccountId, computation_result: ComputationResult, ) -> DispatchResult { Pallet::<T>::mutate_next_pool(pallet_session::CurrentIndex::<T>::get(), |result_pool| { + // evaluation must be provided for all identities (no more, no less) ensure!( computation_result.distances.len() == result_pool.evaluations.len(), Error::<T>::WrongResultLength ); + // insert the evaluator if not already there if result_pool .evaluators .try_insert(evaluator.clone()) .map_err(|_| Error::<T>::TooManyEvaluators)? { + // update the median accumulator with the new result for (distance_value, (_identity, median_acc)) in computation_result .distances .into_iter() @@ -454,23 +491,28 @@ pub mod pallet { { median_acc.push(distance_value); } - - Self::deposit_event(Event::EvaluationUpdated { evaluator }); Ok(()) } else { + // one author can only submit one evaluation Err(Error::<T>::TooManyEvaluationsByAuthor.into()) } }) } + + /// Set the distance status using IdtyIndex and AccountId + pub fn do_valid_distance_status(idty: <T as pallet_identity::Config>::IdtyIndex) { + // callback + T::OnValidDistanceStatus::on_valid_distance_status(idty); + // deposit event + Self::deposit_event(Event::EvaluatedValid { idty_index: idty }); + } } impl<T: Config> pallet_authority_members::OnNewSession for Pallet<T> { fn on_new_session(index: SessionIndex) { + // set evaluation block EvaluationBlock::<T>::set(frame_system::Pallet::<T>::parent_hash()); - // Make results expire - DistanceStatusExpireOn::<T>::remove(index); - // Apply the results from the current pool (which was previous session's result pool) // We take the results so the pool is left empty for the new session. #[allow(clippy::type_complexity)] @@ -479,35 +521,48 @@ pub mod pallet { <T as pallet_identity::Config>::IdtyIndex, > = Pallet::<T>::take_current_pool(index); for (idty, median_acc) in current_pool.evaluations.into_iter() { + // distance result + let mut distance_result: Option<bool> = None; + + // get result of the computation if let Some(median_result) = median_acc.get_median() { let median = match median_result { MedianResult::One(m) => m, MedianResult::Two(m1, m2) => m1 + (m2 - m1) / 2, // Avoid overflow (since max is 1) }; - IdentityDistanceStatus::<T>::mutate(idty, |entry| { - if let Some((account_id, status)) = entry.as_mut() { - if median >= T::MinAccessibleReferees::get() { - T::Currency::unreserve( - account_id, - <T as Config>::EvaluationPrice::get(), - ); - *status = DistanceStatus::Valid; - } else { - T::Currency::slash_reserved( - account_id, - <T as Config>::EvaluationPrice::get(), - ); - *status = DistanceStatus::Invalid; - } + // update distance result + distance_result = Some(median >= T::MinAccessibleReferees::get()); + } + + // take requester and perform unreserve or slash + if let Some(requester) = PendingEvaluationRequest::<T>::take(idty) { + match distance_result { + None => { + // no result, unreserve + T::Currency::unreserve( + &requester, + <T as Config>::EvaluationPrice::get(), + ); } - }); - } else if let Some((account_id, _status)) = IdentityDistanceStatus::<T>::take(idty) - { - <T as Config>::Currency::unreserve( - &account_id, - <T as Config>::EvaluationPrice::get(), - ); + Some(true) => { + // positive result, unreserve and apply + T::Currency::unreserve( + &requester, + <T as Config>::EvaluationPrice::get(), + ); + Self::do_valid_distance_status(idty); + } + Some(false) => { + // negative result, slash and deposit event + T::Currency::slash_reserved( + &requester, + <T as Config>::EvaluationPrice::get(), + ); + Self::deposit_event(Event::EvaluatedInvalid { idty_index: idty }); + } + } } + // if evaluation happened without request, it's ok to do nothing } } } diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs index ef477a8c8..6861b3c43 100644 --- a/pallets/distance/src/mock.rs +++ b/pallets/distance/src/mock.rs @@ -19,7 +19,7 @@ use crate::{self as pallet_distance}; use core::marker::PhantomData; use frame_support::{ parameter_types, - traits::{Everything, GenesisBuild}, + traits::{Everything, GenesisBuild, OnFinalize, OnInitialize}, }; use frame_system as system; use pallet_balances::AccountData; @@ -256,9 +256,10 @@ impl pallet_distance::Config for Test { type EvaluationPrice = frame_support::traits::ConstU64<1000>; type MaxRefereeDistance = frame_support::traits::ConstU32<5>; type MinAccessibleReferees = MinAccessibleReferees; - type ResultExpiration = frame_support::traits::ConstU32<720>; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); + type OnValidDistanceStatus = (); + type CheckRequestDistanceEvaluation = (); } // Build genesis storage according to the mock runtime. @@ -290,3 +291,14 @@ pub fn new_test_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::new(t) } + +pub fn run_to_block(n: u64) { + while System::block_number() < n { + Session::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::reset_events(); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Session::on_initialize(System::block_number()); + } +} diff --git a/pallets/distance/src/tests.rs b/pallets/distance/src/tests.rs new file mode 100644 index 000000000..18302be75 --- /dev/null +++ b/pallets/distance/src/tests.rs @@ -0,0 +1,108 @@ +// Copyright 2023 Axiom-Team +// +// This file is part of Duniter-v2S. +// +// Duniter-v2S is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// Duniter-v2S is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// 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/>. + +use crate::mock::*; +use crate::*; +use frame_support::traits::Currency; +use frame_support::{assert_noop, assert_ok}; + +// allow request distance evaluation for oneself +#[test] +fn test_request_distance_evaluation() { + new_test_ext().execute_with(|| { + run_to_block(1); + // give enough for reserve + Balances::make_free_balance_be(&1, 10_000); + + // call request + assert_ok!(Distance::request_distance_evaluation( + RuntimeOrigin::signed(1) + )); + System::assert_has_event(RuntimeEvent::Distance(Event::EvaluationRequested { + idty_index: 1, + who: 1, + })); + + // currency was reserved + assert_eq!(Balances::reserved_balance(1), 1000); + }); +} + +// allow request distance evaluation for an unvalidated identity +#[test] +fn test_request_distance_evaluation_for() { + new_test_ext().execute_with(|| { + run_to_block(1); + // give enough for reserve + Balances::make_free_balance_be(&1, 10_000); + assert_ok!(Identity::create_identity(RuntimeOrigin::signed(1), 5)); + assert_ok!(Identity::confirm_identity( + RuntimeOrigin::signed(5), + "Eeeve".into() + )); + + // call request + assert_ok!(Distance::request_distance_evaluation_for( + RuntimeOrigin::signed(1), + 5 + )); + System::assert_has_event(RuntimeEvent::Distance(Event::EvaluationRequested { + idty_index: 5, + who: 1, + })); + + // currency was reserved + assert_eq!(Balances::reserved_balance(1), 1000); + }); +} + +// non member can not request distance evaluation +#[test] +fn test_request_distance_evaluation_non_member() { + new_test_ext().execute_with(|| { + run_to_block(1); + // give enough for reserve + Balances::make_free_balance_be(&5, 10_000); + + assert_noop!( + Distance::request_distance_evaluation_for(RuntimeOrigin::signed(5), 1), + Error::<Test>::CallerHasNoIdentity + ); + assert_ok!(Identity::create_identity(RuntimeOrigin::signed(1), 5)); + assert_noop!( + Distance::request_distance_evaluation_for(RuntimeOrigin::signed(5), 1), + Error::<Test>::CallerNotMember + ); + }); +} + +// can not request distance eval if already in evaluation +#[test] +fn test_request_distance_evaluation_twice() { + new_test_ext().execute_with(|| { + run_to_block(1); + // give enough for reserve + Balances::make_free_balance_be(&1, 10_000); + + assert_ok!(Distance::request_distance_evaluation( + RuntimeOrigin::signed(1) + )); + assert_noop!( + Distance::request_distance_evaluation(RuntimeOrigin::signed(1)), + Error::<Test>::AlreadyInEvaluation + ); + }); +} diff --git a/pallets/distance/src/traits.rs b/pallets/distance/src/traits.rs index de9d532d6..6849d9c25 100644 --- a/pallets/distance/src/traits.rs +++ b/pallets/distance/src/traits.rs @@ -14,8 +14,24 @@ // 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/>. -pub trait HandleNegativeEvaluation<T: crate::Config> { - /// Do something with the reserved amount on the account, - /// when distance evaluation result is negative. - fn handle_negative_evaluation(account_id: T::AccountId); +use crate::*; +use frame_support::pallet_prelude::*; + +pub trait OnValidDistanceStatus<T: Config> { + /// Handler for valid distance evaluation + fn on_valid_distance_status(idty_index: T::IdtyIndex); +} + +impl<T: Config> OnValidDistanceStatus<T> for () { + fn on_valid_distance_status(_idty_index: T::IdtyIndex) {} +} + +pub trait CheckRequestDistanceEvaluation<T: Config> { + fn check_request_distance_evaluation(idty_index: T::IdtyIndex) -> Result<(), DispatchError>; +} + +impl<T: Config> CheckRequestDistanceEvaluation<T> for () { + fn check_request_distance_evaluation(_idty_index: T::IdtyIndex) -> Result<(), DispatchError> { + Ok(()) + } } diff --git a/pallets/distance/src/weights.rs b/pallets/distance/src/weights.rs index b0808361a..0e8f34c45 100644 --- a/pallets/distance/src/weights.rs +++ b/pallets/distance/src/weights.rs @@ -20,102 +20,82 @@ use frame_support::weights::{constants::RocksDbWeight, Weight}; pub trait WeightInfo { fn request_distance_evaluation() -> Weight; + fn request_distance_evaluation_for() -> Weight; fn update_evaluation(i: u32) -> Weight; fn force_update_evaluation(i: u32) -> Weight; - fn force_set_distance_status() -> Weight; + fn force_valid_distance_status() -> Weight; fn on_finalize() -> Weight; } // Insecure weights implementation, use it for tests only! impl WeightInfo for () { - /// Storage: Identity IdentityIndexOf (r:1 w:0) - /// Proof Skipped: Identity IdentityIndexOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Distance IdentityDistanceStatus (r:1 w:1) - /// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured) - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Distance EvaluationPool2 (r:1 w:1) - /// Proof Skipped: Distance EvaluationPool2 (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(121), added: 2596, mode: MaxEncodedLen) - /// Storage: Distance DistanceStatusExpireOn (r:1 w:1) - /// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured) fn request_distance_evaluation() -> Weight { // Proof Size summary in bytes: - // Measured: `935` - // Estimated: `4400` - // Minimum execution time: 28_469_000 picoseconds. - Weight::from_parts(30_905_000, 0) - .saturating_add(Weight::from_parts(0, 4400)) - .saturating_add(RocksDbWeight::get().reads(6)) - .saturating_add(RocksDbWeight::get().writes(4)) + // Measured: `1280` + // Estimated: `4745` + // Minimum execution time: 876_053_000 picoseconds. + Weight::from_parts(898_445_000, 0) + .saturating_add(Weight::from_parts(0, 4745)) + .saturating_add(RocksDbWeight::get().reads(8)) + .saturating_add(RocksDbWeight::get().writes(3)) + } + + fn request_distance_evaluation_for() -> Weight { + // Proof Size summary in bytes: + // Measured: `1485` + // Estimated: `7425` + // Minimum execution time: 1_118_982_000 picoseconds. + Weight::from_parts(1_292_782_000, 0) + .saturating_add(Weight::from_parts(0, 7425)) + .saturating_add(RocksDbWeight::get().reads(10)) + .saturating_add(RocksDbWeight::get().writes(3)) } - /// Storage: Distance DidUpdate (r:1 w:1) - /// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Authorship Author (r:1 w:1) - /// Proof: Authorship Author (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: System Digest (r:1 w:0) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Distance EvaluationPool0 (r:1 w:1) - /// Proof Skipped: Distance EvaluationPool0 (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `i` is `[1, 600]`. + fn update_evaluation(i: u32) -> Weight { // Proof Size summary in bytes: - // Measured: `744 + i * (10 ±0)` - // Estimated: `2228 + i * (10 ±0)` - // Minimum execution time: 13_870_000 picoseconds. - Weight::from_parts(17_116_748, 0) - .saturating_add(Weight::from_parts(0, 2228)) - // Standard Error: 684 - .saturating_add(Weight::from_parts(128_989, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(5)) + // Measured: `773 + i * (10 ±0)` + // Estimated: `2256 + i * (10 ±0)` + // Minimum execution time: 463_878_000 picoseconds. + Weight::from_parts(743_823_548, 0) + .saturating_add(Weight::from_parts(0, 2256)) + // Standard Error: 292_144 + .saturating_add(Weight::from_parts(1_326_639, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(6)) .saturating_add(RocksDbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into())) } - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Distance EvaluationPool0 (r:1 w:1) - /// Proof Skipped: Distance EvaluationPool0 (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `i` is `[1, 600]`. + fn force_update_evaluation(i: u32) -> Weight { // Proof Size summary in bytes: // Measured: `612 + i * (10 ±0)` - // Estimated: `2096 + i * (10 ±0)` - // Minimum execution time: 8_392_000 picoseconds. - Weight::from_parts(10_825_908, 0) - .saturating_add(Weight::from_parts(0, 2096)) - // Standard Error: 326 - .saturating_add(Weight::from_parts(123_200, 0).saturating_mul(i.into())) + // Estimated: `2095 + i * (10 ±0)` + // Minimum execution time: 208_812_000 picoseconds. + Weight::from_parts(257_150_521, 0) + .saturating_add(Weight::from_parts(0, 2095)) + // Standard Error: 53_366 + .saturating_add(Weight::from_parts(1_841_329, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into())) } - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Distance DistanceStatusExpireOn (r:1 w:1) - /// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured) - /// Storage: Distance IdentityDistanceStatus (r:0 w:1) - /// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured) - fn force_set_distance_status() -> Weight { + + fn force_valid_distance_status() -> Weight { // Proof Size summary in bytes: - // Measured: `586` - // Estimated: `4051` - // Minimum execution time: 8_099_000 picoseconds. - Weight::from_parts(8_786_000, 0) - .saturating_add(Weight::from_parts(0, 4051)) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(2)) + // Measured: `1181` + // Estimated: `7121` + // Minimum execution time: 873_892_000 picoseconds. + Weight::from_parts(1_081_510_000, 0) + .saturating_add(Weight::from_parts(0, 7121)) + .saturating_add(RocksDbWeight::get().reads(7)) + .saturating_add(RocksDbWeight::get().writes(5)) } - /// Storage: Distance DidUpdate (r:1 w:1) - /// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured) + fn on_finalize() -> Weight { // Proof Size summary in bytes: // Measured: `170` // Estimated: `1655` - // Minimum execution time: 3_904_000 picoseconds. - Weight::from_parts(4_132_000, 0) + // Minimum execution time: 93_595_000 picoseconds. + Weight::from_parts(109_467_000, 0) .saturating_add(Weight::from_parts(0, 1655)) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) diff --git a/pallets/duniter-test-parameters/src/lib.rs b/pallets/duniter-test-parameters/src/lib.rs index 4da26974b..55ca1c612 100644 --- a/pallets/duniter-test-parameters/src/lib.rs +++ b/pallets/duniter-test-parameters/src/lib.rs @@ -45,7 +45,7 @@ pub mod types { pub idty_confirm_period: BlockNumber, pub idty_creation_period: BlockNumber, pub membership_period: BlockNumber, - pub pending_membership_period: BlockNumber, + pub membership_renewal_period: BlockNumber, pub ud_creation_period: PeriodCount, pub ud_reeval_period: PeriodCount, pub smith_cert_max_by_issuer: CertCount, diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs index eb127c0cc..7051c922c 100644 --- a/pallets/duniter-wot/src/lib.rs +++ b/pallets/duniter-wot/src/lib.rs @@ -30,8 +30,6 @@ mod benchmarking;*/ pub use pallet::*; -use traits::*; - use frame_support::pallet_prelude::*; use pallet_certification::traits::SetNextIssuableOn; use pallet_identity::{IdtyEvent, IdtyStatus}; @@ -61,8 +59,6 @@ pub mod pallet { + pallet_identity::Config<IdtyIndex = IdtyIndex> + pallet_membership::Config<IdtyId = IdtyIndex> { - /// Distance evaluation provider - type IsDistanceOk: IsDistanceOk<IdtyIndex>; #[pallet::constant] type FirstIssuableOn: Get<Self::BlockNumber>; #[pallet::constant] @@ -87,38 +83,24 @@ pub mod pallet { #[pallet::error] pub enum Error<T> { - /// Insufficient certifications received to claim membership. - NotEnoughCertsToClaimMembership, - /// Distance is invalid. - DistanceIsInvalid, - /// Distance is not evaluated. - DistanceNotEvaluated, - /// Distance evaluation has been requested but is still pending - DistanceEvaluationPending, - /// Distance evaluation has not been requested - DistanceEvaluationNotRequested, - /// Identity is not allowed to request membership. - IdtyNotAllowedToRequestMembership, - /// Identity not allowed to renew membership. - IdtyNotAllowedToRenewMembership, + /// Insufficient certifications received. + NotEnoughCerts, + /// Target status is incompatible with this operation. + // - Membership can not be added/renewed with this status + // - Certification can not be added to identity with this status + TargetStatusInvalid, /// Identity creation period not respected. IdtyCreationPeriodNotRespected, /// Insufficient received certifications to create identity. NotEnoughReceivedCertsToCreateIdty, /// Maximum number of emitted certifications reached. MaxEmittedCertsReached, - /// Not allowed to change identity address. - NotAllowedToChangeIdtyAddress, - /// Not allowed to remove identity. - NotAllowedToRemoveIdty, /// Issuer cannot emit a certification because it is not member. IssuerNotMember, - /// Cannot issue a certification to an unconfirmed identity - CertToUnconfirmed, - /// Cannot issue a certification to a revoked identity - CertToRevoked, /// Issuer or receiver not found. IdtyNotFound, + /// Membership can only be renewed after an antispam delay. + MembershipRenewalPeriodNotRespected, } } @@ -162,55 +144,53 @@ impl<T: Config> pallet_certification::traits::CheckCertAllowed<IdtyIndex> for Pa fn check_cert_allowed(issuer: IdtyIndex, receiver: IdtyIndex) -> Result<(), DispatchError> { // issuer checks // ensure issuer is member - if let Some(issuer_data) = pallet_identity::Pallet::<T>::identity(issuer) { - ensure!( - issuer_data.status == IdtyStatus::Member, - Error::<T>::IssuerNotMember - ); - } else { - return Err(Error::<T>::IdtyNotFound.into()); - } + let issuer_data = + pallet_identity::Pallet::<T>::identity(issuer).ok_or(Error::<T>::IdtyNotFound)?; + ensure!( + issuer_data.status == IdtyStatus::Member, + Error::<T>::IssuerNotMember + ); // receiver checks // ensure receiver identity is confirmed and not revoked - if let Some(receiver_data) = pallet_identity::Pallet::<T>::identity(receiver) { - match receiver_data.status { - // able to receive cert - IdtyStatus::Unvalidated | IdtyStatus::Member | IdtyStatus::NotMember => {} - IdtyStatus::Unconfirmed => return Err(Error::<T>::CertToUnconfirmed.into()), - IdtyStatus::Revoked => return Err(Error::<T>::CertToRevoked.into()), - }; - } else { - return Err(Error::<T>::IdtyNotFound.into()); - } + let receiver_data = + pallet_identity::Pallet::<T>::identity(receiver).ok_or(Error::<T>::IdtyNotFound)?; + ensure!( + receiver_data.status == IdtyStatus::Unvalidated + || receiver_data.status == IdtyStatus::Member + || receiver_data.status == IdtyStatus::NotMember, + Error::<T>::TargetStatusInvalid + ); Ok(()) } } // implement membership call checks -impl<T: Config> sp_membership::traits::CheckMembershipCallAllowed<IdtyIndex> for Pallet<T> { - // membership claim is only possible when enough certs are received (both wots) and distance is ok - fn check_idty_allowed_to_claim_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> { - let idty_cert_meta = pallet_certification::Pallet::<T>::idty_cert_meta(idty_index); +impl<T: Config> sp_membership::traits::CheckMembershipOpAllowed<IdtyIndex> for Pallet<T> { + fn check_add_membership(idty_index: IdtyIndex) -> Result<(), DispatchError> { + // check identity status + let idty_value = + pallet_identity::Pallet::<T>::identity(idty_index).ok_or(Error::<T>::IdtyNotFound)?; ensure!( - idty_cert_meta.received_count >= T::MinCertForMembership::get(), - Error::<T>::NotEnoughCertsToClaimMembership + idty_value.status == IdtyStatus::Unvalidated + || idty_value.status == IdtyStatus::NotMember, + Error::<T>::TargetStatusInvalid ); - T::IsDistanceOk::is_distance_ok(idty_index)?; + // check cert count + check_cert_count::<T>(idty_index)?; Ok(()) } // membership renewal is only possible when identity is member (otherwise it should claim again) - 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!( - idty_value.status == IdtyStatus::Member, - Error::<T>::IdtyNotAllowedToRenewMembership - ); - T::IsDistanceOk::is_distance_ok(idty_index)?; - } else { - return Err(Error::<T>::IdtyNotFound.into()); - } + fn check_renew_membership(idty_index: IdtyIndex) -> Result<(), DispatchError> { + // check identity status + let idty_value = + pallet_identity::Pallet::<T>::identity(idty_index).ok_or(Error::<T>::IdtyNotFound)?; + ensure!( + idty_value.status == IdtyStatus::Member, + Error::<T>::TargetStatusInvalid + ); + // no need to check certification count since loosing certifications make membership expire Ok(()) } } @@ -250,7 +230,7 @@ impl<T: Config> pallet_identity::traits::OnIdtyChange<T> for Pallet<T> { } } } - // TODO split in removed / revoked in two events: + // we could split this event in removed / revoked: // if identity is revoked keep it // if identity is removed also remove certs IdtyEvent::Removed { status } => { @@ -321,3 +301,91 @@ impl<T: Config> pallet_certification::traits::OnRemovedCert<IdtyIndex> for Palle } } } + +/// valid distance status handler +impl<T: Config + pallet_distance::Config> pallet_distance::traits::OnValidDistanceStatus<T> + for Pallet<T> +{ + fn on_valid_distance_status(idty_index: IdtyIndex) { + if let Some(identity) = pallet_identity::Identities::<T>::get(idty_index) { + match identity.status { + IdtyStatus::Unconfirmed | IdtyStatus::Revoked => { + // IdtyStatus::Unconfirmed + // distance evaluation request should never happen for unconfirmed identity + // IdtyStatus::Revoked + // the identity can have been revoked during distance evaluation by the oracle + } + + IdtyStatus::Unvalidated | IdtyStatus::NotMember => { + // IdtyStatus::Unvalidated + // normal scenario for first entry + // IdtyStatus::NotMember + // normal scenario for re-entry + // the following can fail if a certification expired during distance evaluation + // otherwise it should succeed + let _ = pallet_membership::Pallet::<T>::try_add_membership(idty_index); + // sp_std::if_std! { + // if let Err(e) = r { + // print!("failed to claim identity when distance status was found ok: "); + // println!("{:?}", idty_index); + // println!("reason: {:?}", e); + // } + // } + } + IdtyStatus::Member => { + // IdtyStatus::Member + // normal scenario for renewal + // should succeed + let _ = pallet_membership::Pallet::<T>::try_renew_membership(idty_index); + // sp_std::if_std! { + // if let Err(e) = r { + // print!("failed to renew identity when distance status was found ok: "); + // println!("{:?}", idty_index); + // println!("reason: {:?}", e); + // } + // } + } + } + } else { + // identity was removed before distance status was found + // so it's ok to do nothing + sp_std::if_std! { + println!("identity was removed before distance status was found: {:?}", idty_index); + } + } + } +} + +/// distance evaluation request allowed check +impl<T: Config + pallet_distance::Config> pallet_distance::traits::CheckRequestDistanceEvaluation<T> + for Pallet<T> +{ + fn check_request_distance_evaluation(idty_index: IdtyIndex) -> Result<(), DispatchError> { + // check membership renewal antispam + let maybe_membership_data = pallet_membership::Pallet::<T>::membership(idty_index); + if let Some(membership_data) = maybe_membership_data { + // if membership data exists, this is for a renewal, apply antispam + ensure!( + // current_block > expiration block - membership period + renewal period + membership_data.expire_on + + <T as pallet_membership::Config>::MembershipRenewalPeriod::get() + < frame_system::Pallet::<T>::block_number() + + <T as pallet_membership::Config>::MembershipPeriod::get(), + Error::<T>::MembershipRenewalPeriodNotRespected + ); + }; + // check cert count + check_cert_count::<T>(idty_index)?; + Ok(()) + } +} + +/// check certification count +fn check_cert_count<T: Config>(idty_index: IdtyIndex) -> Result<(), DispatchError> { + let idty_cert_meta = pallet_certification::Pallet::<T>::idty_cert_meta(idty_index); + ensure!( + idty_cert_meta.received_count >= T::MinCertForMembership::get(), + Error::<T>::NotEnoughCerts + ); + Ok(()) +} diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs index c043ef764..3ecb6c233 100644 --- a/pallets/duniter-wot/src/mock.rs +++ b/pallets/duniter-wot/src/mock.rs @@ -48,7 +48,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, DuniterWot: pallet_duniter_wot::{Pallet}, Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>}, - Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>}, + Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>}, Cert: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>}, } ); @@ -97,7 +97,6 @@ impl pallet_duniter_wot::Config for Test { type MinCertForMembership = MinCertForMembership; type MinCertForCreateIdtyRight = MinCertForCreateIdtyRight; type FirstIssuableOn = FirstIssuableOn; - type IsDistanceOk = crate::traits::DistanceAlwaysOk; } // Identity @@ -139,14 +138,16 @@ impl pallet_identity::Config for Test { // Membership parameter_types! { pub const MembershipPeriod: u64 = 8; + pub const MembershipRenewalPeriod: u64 = 2; } impl pallet_membership::Config for Test { - type CheckMembershipCallAllowed = DuniterWot; + type CheckMembershipOpAllowed = DuniterWot; type IdtyId = IdtyIndex; type IdtyIdOf = IdentityIndexOf<Self>; type AccountIdOf = (); type MembershipPeriod = MembershipPeriod; + type MembershipRenewalPeriod = MembershipRenewalPeriod; type OnEvent = DuniterWot; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs index 2b86ee97f..f1718d140 100644 --- a/pallets/duniter-wot/src/tests.rs +++ b/pallets/duniter-wot/src/tests.rs @@ -123,7 +123,7 @@ fn test_new_idty_validation() { // Ferdie should be able to claim membership run_to_block(5); - assert_ok!(Membership::claim_membership(RuntimeOrigin::signed(6)),); + assert_ok!(Membership::try_add_membership(6)); System::assert_has_event(RuntimeEvent::Membership( pallet_membership::Event::MembershipAdded { member: 6, @@ -242,9 +242,9 @@ fn test_idty_membership_expire() { run_to_block(4); // Alice renews her membership - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); + assert_ok!(Membership::try_renew_membership(1)); // Bob renews his membership - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); + assert_ok!(Membership::try_renew_membership(2)); run_to_block(5); // renew certifications so that Alice can still issue cert at block 22 @@ -253,7 +253,7 @@ fn test_idty_membership_expire() { // Charlie's membership should expire at block #8 run_to_block(8); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); + assert_ok!(Membership::try_renew_membership(1)); assert!(Membership::membership(3).is_none()); System::assert_has_event(RuntimeEvent::Membership( @@ -276,7 +276,7 @@ fn test_idty_membership_expire() { // check that identity is added to auto-revoke list (currently IdentityChangeSchedule) assert_eq!(Identity::next_scheduled(14), vec!(3)); run_to_block(14); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); + assert_ok!(Membership::try_renew_membership(1)); // Charlie's identity should be auto-revoked at block #11 (8 + 3) System::assert_has_event(RuntimeEvent::Identity( pallet_identity::Event::IdtyRevoked { @@ -298,7 +298,7 @@ fn test_idty_membership_expire() { // Alice can't certify revoked identity assert_noop!( Cert::add_cert(RuntimeOrigin::signed(1), 1, 3), - pallet_duniter_wot::Error::<Test>::CertToRevoked + pallet_duniter_wot::Error::<Test>::TargetStatusInvalid ); run_to_block(21); @@ -356,15 +356,15 @@ fn test_certification_expire() { assert_ok!(Cert::add_cert(RuntimeOrigin::signed(3), 3, 2)); // --- BLOCK 7 --- run_to_block(7); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); + assert_ok!(Membership::try_renew_membership(1)); + assert_ok!(Membership::try_renew_membership(2)); + assert_ok!(Membership::try_renew_membership(3)); // --- BLOCK 14 --- run_to_block(14); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); + assert_ok!(Membership::try_renew_membership(1)); + assert_ok!(Membership::try_renew_membership(2)); + assert_ok!(Membership::try_renew_membership(3)); // normal cert Bob → Alice expires at block 20 run_to_block(20); @@ -387,19 +387,19 @@ fn test_certification_expire() { // --- BLOCK 21 --- // Bob and Charlie can renew their membership run_to_block(21); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); + assert_ok!(Membership::try_renew_membership(2)); + assert_ok!(Membership::try_renew_membership(3)); // Alice can not renew her membership which does not exist assert_noop!( - Membership::renew_membership(RuntimeOrigin::signed(1)), + Membership::try_renew_membership(1), pallet_membership::Error::<Test>::MembershipNotFound ); // Alice can not claim her membership because she does not have enough certifications assert_noop!( - Membership::claim_membership(RuntimeOrigin::signed(1)), - pallet_duniter_wot::Error::<Test>::NotEnoughCertsToClaimMembership + Membership::try_add_membership(1), + pallet_duniter_wot::Error::<Test>::NotEnoughCerts ); // --- BLOCK 23 --- @@ -436,16 +436,16 @@ fn test_cert_can_not_be_issued() { assert_ok!(Cert::add_cert(RuntimeOrigin::signed(4), 4, 3)); // +20 // --- BLOCK 7 --- run_to_block(7); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); // + 8 - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); // + 8 - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); // + 8 - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(4))); // + 8 + assert_ok!(Membership::try_renew_membership(1)); // + 8 + assert_ok!(Membership::try_renew_membership(2)); // + 8 + assert_ok!(Membership::try_renew_membership(3)); // + 8 + assert_ok!(Membership::try_renew_membership(4)); // + 8 run_to_block(14); - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); // + 8 - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); // + 8 - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); // + 8 - assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(4))); // + 8 + assert_ok!(Membership::try_renew_membership(1)); // + 8 + assert_ok!(Membership::try_renew_membership(2)); // + 8 + assert_ok!(Membership::try_renew_membership(3)); // + 8 + assert_ok!(Membership::try_renew_membership(4)); // + 8 run_to_block(20); // println!("{:?}", System::events()); diff --git a/pallets/duniter-wot/src/traits.rs b/pallets/duniter-wot/src/traits.rs index 6934ce454..f3474822b 100644 --- a/pallets/duniter-wot/src/traits.rs +++ b/pallets/duniter-wot/src/traits.rs @@ -13,17 +13,3 @@ // // 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/>. - -use crate::DispatchError; - -pub trait IsDistanceOk<IdtyId> { - fn is_distance_ok(idty_id: &IdtyId) -> Result<(), DispatchError>; -} - -pub struct DistanceAlwaysOk; - -impl<IdtyId> IsDistanceOk<IdtyId> for DistanceAlwaysOk { - fn is_distance_ok(_idty_id: &IdtyId) -> Result<(), DispatchError> { - Ok(()) - } -} diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 01998e0c5..dbe7167c5 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -82,18 +82,18 @@ pub mod pallet { /// Period after which a revoked identity is removed and the keys are freed. #[pallet::constant] type DeletionPeriod: Get<Self::BlockNumber>; - /// Minimum duration between two owner key changes + /// Minimum duration between two owner key changes. // to avoid stealing the identity without means to revoke #[pallet::constant] type ChangeOwnerKeyPeriod: Get<Self::BlockNumber>; - /// Minimum duration between the creation of 2 identities by the same creator + /// Minimum duration between the creation of 2 identities by the same creator. // it should be greater or equal than the certification period in certification pallet #[pallet::constant] type IdtyCreationPeriod: Get<Self::BlockNumber>; /// Management of the authorizations of the different calls. /// The default implementation allows everything. type CheckIdtyCallAllowed: CheckIdtyCallAllowed<Self>; - /// Custom data to store in each identity + /// Custom data to store in each identity. type IdtyData: Clone + Codec + Default diff --git a/pallets/membership/README.md b/pallets/membership/README.md index 4bcd6020f..c6dc0da83 100644 --- a/pallets/membership/README.md +++ b/pallets/membership/README.md @@ -4,20 +4,4 @@ Duniter membership is related to duniter Web of Trust and more specific than [pa ## Main Web of Trust -When used in conjunction with the main Web of Trust, the membership pallet is combined with the Duniter-WoT pallet and the identity pallet, resulting in the following functionality: - -- `claim_membership` requires enough certifications, and a valid distance -- `renew_membership` requires a valid membership and a valid distance status to extend the validity period of a membership. -- `revoke_membership` (to be removed?) - -In practice, a new user creates an account, confirms identity using the `confirm_identity` call from the identity pallet. The user then validates its identity using `claim_membership`. If the certification and distance requirements are met, the identity is granted membership. - -## Sub Web of Trust Smith - -Functionality related to the Smith Web of Trust involves the following: - -- `claim_membership` requires to be member of the main wot and have enough smith certifications -- `renew_membership` needs a valid membership to extend the membership's validity period. -- `revoke_membership` requires a valid origin to revoke the membership. - -In practice, a user must complete all steps to gain membership in the main Web Of Trust. They can call `claim_membership` to be added to the authority members. +Membership pallet manages all events related to web of trust membership of an identity. It exposes no calls to the user and its features are only available trough distance evaluation provided by distance oracle. diff --git a/pallets/membership/src/benchmarking.rs b/pallets/membership/src/benchmarking.rs index 1bdf5edf6..84216da89 100644 --- a/pallets/membership/src/benchmarking.rs +++ b/pallets/membership/src/benchmarking.rs @@ -20,17 +20,15 @@ use super::*; use frame_benchmarking::benchmarks; use frame_system::pallet_prelude::BlockNumberFor; -use frame_system::RawOrigin; -use sp_runtime::traits::{Convert, One}; #[cfg(test)] use maplit::btreemap; use crate::Pallet; -fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { - frame_system::Pallet::<T>::assert_has_event(generic_event.into()); -} +// fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { +// frame_system::Pallet::<T>::assert_has_event(generic_event.into()); +// } benchmarks! { where_clause { @@ -39,39 +37,7 @@ benchmarks! { <T as frame_system::Config>::BlockNumber: From<u32>, } - // claim membership - claim_membership { - let idty: T::IdtyId = 3.into(); - Membership::<T>::take(idty); - let caller: T::AccountId = T::AccountIdOf::convert(idty).unwrap(); - let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); - T::BenchmarkSetupHandler::force_status_ok(&idty, &caller); - }: _<T::RuntimeOrigin>(caller_origin) - verify { - assert_has_event::<T>(Event::<T>::MembershipAdded{member: idty, expire_on: BlockNumberFor::<T>::one() + T::MembershipPeriod::get()}.into()); - } - - // renew membership - renew_membership { - let idty: T::IdtyId = 3.into(); - let caller: T::AccountId = T::AccountIdOf::convert(idty).unwrap(); - let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); - T::BenchmarkSetupHandler::force_status_ok(&idty, &caller); - }: _<T::RuntimeOrigin>(caller_origin) - verify { - assert_has_event::<T>(Event::<T>::MembershipAdded{member: idty, expire_on: BlockNumberFor::<T>::one() + T::MembershipPeriod::get()}.into()); - } - - // revoke membership - revoke_membership { - let idty: T::IdtyId = 3.into(); - let caller: T::AccountId = T::AccountIdOf::convert(idty).unwrap(); - let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); - frame_system::pallet::Pallet::<T>::set_block_number(10_000_000.into()); // Arbitrarily high, to be in the worst case of wot instance. - }: _<T::RuntimeOrigin>(caller_origin) - verify { - assert_has_event::<T>(Event::<T>::MembershipRemoved{member: idty, reason: MembershipRemovalReason::Revoked}.into()); - } + // TODO membership add and renewal should be included to distance on_new_session as worst case scenario // Base weight of an empty initialize on_initialize { @@ -98,7 +64,7 @@ benchmarks! { impl_benchmark_test_suite!( Pallet, - crate::mock::new_test_ext(crate::mock::DefaultMembershipConfig { + crate::mock::new_test_ext(crate::mock::MembershipConfig { memberships: btreemap![ 3 => MembershipData { expire_on: 3, diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs index cba39e37e..c23c07a86 100644 --- a/pallets/membership/src/lib.rs +++ b/pallets/membership/src/lib.rs @@ -32,9 +32,7 @@ pub use pallet::*; pub use weights::WeightInfo; use frame_support::dispatch::Weight; -use frame_support::error::BadOrigin; use frame_support::pallet_prelude::*; -use frame_system::RawOrigin; use sp_membership::traits::*; use sp_membership::MembershipData; use sp_runtime::traits::Zero; @@ -44,13 +42,13 @@ use std::collections::BTreeMap; #[cfg(feature = "runtime-benchmarks")] pub trait SetupBenchmark<IdtyId, AccountId> { - fn force_status_ok(idty_index: &IdtyId, account: &AccountId); + fn force_valid_distance_status(idty_index: &IdtyId); fn add_cert(_issuer: &IdtyId, _receiver: &IdtyId); } #[cfg(feature = "runtime-benchmarks")] impl<IdtyId, AccountId> SetupBenchmark<IdtyId, AccountId> for () { - fn force_status_ok(_idty_id: &IdtyId, _account: &AccountId) {} + fn force_valid_distance_status(_idty_id: &IdtyId) {} fn add_cert(_issuer: &IdtyId, _receiver: &IdtyId) {} } @@ -86,16 +84,21 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { /// Ask the runtime whether the identity can perform membership operations - type CheckMembershipCallAllowed: CheckMembershipCallAllowed<Self::IdtyId>; + type CheckMembershipOpAllowed: CheckMembershipOpAllowed<Self::IdtyId>; /// Something that identifies an identity type IdtyId: Copy + MaybeSerializeDeserialize + Parameter + Ord; /// Something that gives the IdtyId of an AccountId type IdtyIdOf: Convert<Self::AccountId, Option<Self::IdtyId>>; /// Something that gives the AccountId of an IdtyId type AccountIdOf: Convert<Self::IdtyId, Option<Self::AccountId>>; + /// Maximum life span of a single membership (in number of blocks) + // (this could be renamed "validity" or "duration") #[pallet::constant] - /// Maximum life span of a non-renewable membership (in number of blocks) type MembershipPeriod: Get<Self::BlockNumber>; + /// Minimum delay to wait before renewing membership + // i.e. asking for distance evaluation + #[pallet::constant] + type MembershipRenewalPeriod: Get<Self::BlockNumber>; /// On event handler type OnEvent: OnEvent<Self::IdtyId>; /// Because this pallet emits events, it depends on the runtime's definition of an event. @@ -156,6 +159,11 @@ pub mod pallet { member: T::IdtyId, expire_on: BlockNumberFor<T>, }, + /// A membership was renewed. + MembershipRenewed { + member: T::IdtyId, + expire_on: BlockNumberFor<T>, + }, /// A membership was removed. MembershipRemoved { member: T::IdtyId, @@ -173,6 +181,8 @@ pub mod pallet { MembershipAlreadyAcquired, /// Membership not found. MembershipNotFound, + /// Already member, can not claim membership. + AlreadyMember, } // HOOKS // @@ -188,61 +198,13 @@ pub mod pallet { } } - // CALLS // - - #[pallet::call] - impl<T: Config> Pallet<T> { - /// claim membership - /// it must fullfill the requirements (certs, distance) - /// TODO #159 for main wot claim_membership is called automatically when distance is evaluated positively - /// for smith wot, it means joining the authority members - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::claim_membership())] - 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_add_membership(idty_id); - Ok(().into()) - } - - /// extend the validity period of an active membership - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::renew_membership())] - pub fn renew_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo { - // Verify phase - let idty_id = Self::get_idty_id(origin)?; - let membership_data = - Membership::<T>::get(idty_id).ok_or(Error::<T>::MembershipNotFound)?; - - T::CheckMembershipCallAllowed::check_idty_allowed_to_renew_membership(&idty_id)?; - - // apply phase - Self::unschedule_membership_expiry(idty_id, membership_data.expire_on); - Self::insert_membership_and_schedule_expiry(idty_id); - T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id)); - - Ok(().into()) - } - - /// revoke an active membership - /// (only available for sub wot, automatic for main wot) - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::revoke_membership())] - pub fn revoke_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo { - // Verify phase - let idty_id = Self::get_idty_id(origin)?; - - // Apply phase - Self::do_remove_membership(idty_id, MembershipRemovalReason::Revoked); - - Ok(().into()) - } - } + // // CALLS // + // #[pallet::call] + // impl<T: Config> Pallet<T> { + // // no calls for membership pallet + // } // INTERNAL FUNCTIONS // - impl<T: Config> Pallet<T> { /// unschedule membership expiry fn unschedule_membership_expiry(idty_id: T::IdtyId, block_number: T::BlockNumber) { @@ -254,31 +216,78 @@ pub mod pallet { } } /// schedule membership expiry - fn insert_membership_and_schedule_expiry(idty_id: T::IdtyId) { + fn insert_membership_and_schedule_expiry(idty_id: T::IdtyId) -> T::BlockNumber { let block_number = frame_system::pallet::Pallet::<T>::block_number(); let expire_on = block_number + T::MembershipPeriod::get(); Membership::<T>::insert(idty_id, MembershipData { expire_on }); MembershipsExpireOn::<T>::append(expire_on, idty_id); - Self::deposit_event(Event::MembershipAdded { - member: idty_id, - expire_on, - }); + expire_on } /// check that membership can be claimed - pub fn check_allowed_to_claim(idty_id: T::IdtyId) -> Result<(), DispatchError> { + pub fn check_add_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> { + // no-op is error + ensure!( + Membership::<T>::get(idty_id).is_none(), + Error::<T>::AlreadyMember + ); + // enough certifications and distance rule for example - T::CheckMembershipCallAllowed::check_idty_allowed_to_claim_membership(&idty_id)?; + T::CheckMembershipOpAllowed::check_add_membership(idty_id)?; + Ok(()) + } + + /// check that membership can be renewed + pub fn check_renew_membership( + idty_id: T::IdtyId, + ) -> Result<MembershipData<T::BlockNumber>, DispatchError> { + let membership_data = + Membership::<T>::get(idty_id).ok_or(Error::<T>::MembershipNotFound)?; + + // enough certifications and distance rule for example + T::CheckMembershipOpAllowed::check_renew_membership(idty_id)?; + Ok(membership_data) + } + + /// try claim membership + pub fn try_add_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> { + Self::check_add_membership(idty_id)?; + Self::do_add_membership(idty_id); + Ok(()) + } + + /// try renew membership + pub fn try_renew_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> { + let membership_data = Self::check_renew_membership(idty_id)?; + Self::do_renew_membership(idty_id, membership_data); Ok(()) } /// perform membership addition fn do_add_membership(idty_id: T::IdtyId) { - Self::insert_membership_and_schedule_expiry(idty_id); + let expire_on = Self::insert_membership_and_schedule_expiry(idty_id); + Self::deposit_event(Event::MembershipAdded { + member: idty_id, + expire_on, + }); T::OnEvent::on_event(&sp_membership::Event::MembershipAdded(idty_id)); } + /// perform membership renewal + fn do_renew_membership( + idty_id: T::IdtyId, + membership_data: MembershipData<T::BlockNumber>, + ) { + Self::unschedule_membership_expiry(idty_id, membership_data.expire_on); + let expire_on = Self::insert_membership_and_schedule_expiry(idty_id); + Self::deposit_event(Event::MembershipRenewed { + member: idty_id, + expire_on, + }); + T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id)); + } + /// perform membership removal pub fn do_remove_membership(idty_id: T::IdtyId, reason: MembershipRemovalReason) { if let Some(membership_data) = Membership::<T>::take(idty_id) { @@ -291,15 +300,6 @@ pub mod pallet { } } - /// check the origin and get identity id if valid - 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>::IdtyIdNotFound.into()) - } else { - Err(BadOrigin.into()) - } - } - /// perform the membership expiry scheduled at given block pub fn expire_memberships(block_number: T::BlockNumber) -> Weight { let mut expired_idty_count = 0u32; diff --git a/pallets/membership/src/mock.rs b/pallets/membership/src/mock.rs index 5173ff716..f19769127 100644 --- a/pallets/membership/src/mock.rs +++ b/pallets/membership/src/mock.rs @@ -41,7 +41,7 @@ frame_support::construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, - DefaultMembership: pallet_membership::{Pallet, Call, Event<T>, Storage, Config<T>}, + Membership: pallet_membership::{Pallet, Event<T>, Storage, Config<T>}, } ); @@ -79,15 +79,16 @@ impl system::Config for Test { parameter_types! { pub const MembershipPeriod: BlockNumber = 5; - pub const PendingMembershipPeriod: BlockNumber = 3; + pub const MembershipRenewalPeriod: BlockNumber = 2; } impl pallet_membership::Config for Test { - type CheckMembershipCallAllowed = (); + type CheckMembershipOpAllowed = (); type IdtyId = IdtyId; type IdtyIdOf = ConvertInto; type AccountIdOf = ConvertInto; type MembershipPeriod = MembershipPeriod; + type MembershipRenewalPeriod = MembershipRenewalPeriod; type OnEvent = (); type RuntimeEvent = RuntimeEvent; type WeightInfo = (); @@ -99,7 +100,7 @@ impl pallet_membership::Config for Test { pub fn new_test_ext(gen_conf: pallet_membership::GenesisConfig<Test>) -> sp_io::TestExternalities { GenesisConfig { system: SystemConfig::default(), - default_membership: gen_conf, + membership: gen_conf, } .build_storage() .unwrap() @@ -108,11 +109,11 @@ pub fn new_test_ext(gen_conf: pallet_membership::GenesisConfig<Test>) -> sp_io:: pub fn run_to_block(n: u64) { while System::block_number() < n { - DefaultMembership::on_finalize(System::block_number()); + Membership::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::reset_events(); System::set_block_number(System::block_number() + 1); System::on_initialize(System::block_number()); - DefaultMembership::on_initialize(System::block_number()); + Membership::on_initialize(System::block_number()); } } diff --git a/pallets/membership/src/tests.rs b/pallets/membership/src/tests.rs index 53da60336..4f22c3213 100644 --- a/pallets/membership/src/tests.rs +++ b/pallets/membership/src/tests.rs @@ -22,11 +22,8 @@ use maplit::btreemap; use sp_membership::traits::*; use sp_membership::MembershipData; -// alias -type RtEvent = RuntimeEvent; - -fn default_gen_conf() -> DefaultMembershipConfig { - DefaultMembershipConfig { +fn default_gen_conf() -> MembershipConfig { + MembershipConfig { memberships: btreemap![ 0 => MembershipData { expire_on: 3, @@ -41,10 +38,10 @@ fn test_genesis_build() { run_to_block(1); // Verify state assert_eq!( - DefaultMembership::membership(0), + Membership::membership(0), Some(MembershipData { expire_on: 3 }) ); - assert_eq!(DefaultMembership::members_count(), 1); + assert_eq!(Membership::members_count(), 1); }); } @@ -55,42 +52,39 @@ fn test_membership_expiration() { new_test_ext(default_gen_conf()).execute_with(|| { // Membership 0 should not expired on block #2 run_to_block(2); - assert!(DefaultMembership::is_member(&0)); + assert!(Membership::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::MembershipRemoved { + assert!(!Membership::is_member(&0)); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved { member: 0, reason: MembershipRemovalReason::Expired, })); }); } -/// test membership renewal -// there is no limit for membership renewal outside wot rules (number of certs, distance rule) +/// test membership renewal (triggered automatically after distance evaluation) #[test] fn test_membership_renewal() { new_test_ext(default_gen_conf()).execute_with(|| { // membership still valid at block 2 run_to_block(2); - assert!(DefaultMembership::is_member(&0)); + assert!(Membership::is_member(&0)); // Membership 0 can be renewed - assert_ok!(DefaultMembership::renew_membership(RuntimeOrigin::signed( - 0 - ),)); - System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAdded { + assert_ok!(Membership::try_renew_membership(0)); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRenewed { member: 0, expire_on: 2 + <Test as crate::Config>::MembershipPeriod::get(), })); // membership should not expire at block 3 to 6 because it has been renewed run_to_block(3); - assert!(DefaultMembership::is_member(&0)); + assert!(Membership::is_member(&0)); run_to_block(6); - assert!(DefaultMembership::is_member(&0)); + assert!(Membership::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::MembershipRemoved { + assert!(!Membership::is_member(&0)); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved { member: 0, reason: MembershipRemovalReason::Expired, })); @@ -102,14 +96,14 @@ fn test_membership_renewal() { fn test_membership_renewal_nope() { new_test_ext(default_gen_conf()).execute_with(|| { run_to_block(2); - assert!(!DefaultMembership::is_member(&1)); + assert!(!Membership::is_member(&1)); // Membership 1 can not be renewed assert_noop!( - DefaultMembership::renew_membership(RuntimeOrigin::signed(1)), + Membership::try_renew_membership(1), Error::<Test>::MembershipNotFound, ); run_to_block(3); - assert!(!DefaultMembership::is_member(&1)); + assert!(!Membership::is_member(&1)); }); } @@ -119,21 +113,17 @@ fn test_membership_revocation() { new_test_ext(default_gen_conf()).execute_with(|| { run_to_block(1); // Membership 0 can be revocable on block #1 - assert_ok!(DefaultMembership::revoke_membership(RuntimeOrigin::signed( - 0 - ),)); - System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRemoved { + Membership::do_remove_membership(0, MembershipRemovalReason::Revoked); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved { member: 0, reason: MembershipRemovalReason::Revoked, })); - assert_eq!(DefaultMembership::membership(0), None); + assert_eq!(Membership::membership(0), None); // Membership 0 can re-claim membership run_to_block(5); - assert_ok!(DefaultMembership::claim_membership(RuntimeOrigin::signed( - 0 - ),)); - System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAdded { + assert_ok!(Membership::try_add_membership(0)); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipAdded { member: 0, expire_on: 5 + <Test as crate::Config>::MembershipPeriod::get(), })); @@ -149,28 +139,24 @@ fn test_membership_workflow() { new_test_ext(Default::default()).execute_with(|| { // - Then, idty 0 claim membership run_to_block(2); - assert_ok!(DefaultMembership::claim_membership(RuntimeOrigin::signed( - 0 - ),)); - System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAdded { + assert_ok!(Membership::try_add_membership(0)); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipAdded { member: 0, expire_on: 2 + <Test as crate::Config>::MembershipPeriod::get(), })); // - Then, idty 0 claim renewal, should success run_to_block(2); - assert_ok!(DefaultMembership::renew_membership(RuntimeOrigin::signed( - 0 - ),)); + assert_ok!(Membership::try_renew_membership(0)); // idty 0 should still be member until membership period ended run_to_block(6); // 2 + 5 - 1 - assert!(DefaultMembership::is_member(&0)); + assert!(Membership::is_member(&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::MembershipRemoved { + assert!(!Membership::is_member(&0)); + System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved { member: 0, reason: MembershipRemovalReason::Expired, })); diff --git a/pallets/smith-members/src/lib.rs b/pallets/smith-members/src/lib.rs index 95dd31177..8d4df3fa6 100644 --- a/pallets/smith-members/src/lib.rs +++ b/pallets/smith-members/src/lib.rs @@ -128,12 +128,12 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { - /// An identity is being inivited to become a smith + /// An identity is being inivited to become a smith. InvitationSent { idty_index: T::IdtyIndex, invited_by: T::IdtyIndex, }, - /// The invitation has been accepted + /// The invitation has been accepted. InvitationAccepted { idty_index: T::IdtyIndex }, /// Certification received CertificationReceived { @@ -142,7 +142,7 @@ pub mod pallet { }, /// A smith gathered enough certifications to become an authority (can call `go_online()`). PromotedToSmith { idty_index: T::IdtyIndex }, - /// A smith has been removed from the smiths set + /// A smith has been removed from the smiths set. SmithExcluded { idty_index: T::IdtyIndex }, } diff --git a/pallets/universal-dividend/src/lib.rs b/pallets/universal-dividend/src/lib.rs index 25de8fb02..b52482709 100644 --- a/pallets/universal-dividend/src/lib.rs +++ b/pallets/universal-dividend/src/lib.rs @@ -61,8 +61,8 @@ pub mod pallet { type MomentIntoBalance: Convert<Self::Moment, BalanceOf<Self>>; // The currency type Currency: Currency<Self::AccountId>; - #[pallet::constant] /// Maximum number of past UD revaluations to keep in storage. + #[pallet::constant] type MaxPastReeval: Get<u32>; /// Somethings that must provide the number of accounts allowed to create the universal dividend type MembersCount: Get<BalanceOf<Self>>; @@ -70,19 +70,19 @@ pub mod pallet { type MembersStorage: frame_support::traits::StoredMap<Self::AccountId, FirstEligibleUd>; /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; - #[pallet::constant] /// Square of the money growth rate per ud reevaluation period - type SquareMoneyGrowthRate: Get<Perbill>; #[pallet::constant] + type SquareMoneyGrowthRate: Get<Perbill>; /// Universal dividend creation period (ms) - type UdCreationPeriod: Get<Self::Moment>; #[pallet::constant] + type UdCreationPeriod: Get<Self::Moment>; /// Universal dividend reevaluation period (ms) - type UdReevalPeriod: Get<Self::Moment>; #[pallet::constant] + type UdReevalPeriod: Get<Self::Moment>; /// The number of units to divide the amounts expressed in number of UDs /// Example: If you wish to express the UD amounts with a maximum precision of the order /// of the milliUD, choose 1000 + #[pallet::constant] type UnitsPerUd: Get<BalanceOf<Self>>; /// Pallet weights info type WeightInfo: WeightInfo; @@ -97,12 +97,14 @@ pub mod pallet { #[pallet::getter(fn current_ud)] pub type CurrentUd<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>; + // default value for number of the next UD #[pallet::type_value] pub fn DefaultForCurrentUdIndex() -> UdIndex { 1 } /// Current UD index + // (more like the index of the ongoing UD = the next one) #[pallet::storage] #[pallet::getter(fn ud_index)] pub type CurrentUdIndex<T: Config> = @@ -110,6 +112,9 @@ pub mod pallet { #[cfg(test)] #[pallet::storage] + // UD should be linked to idtyid instead of accountid + // if it is convenient in test, why not have it in runtime also? + // storing it in idty_value.data is strange pub type TestMembers<T: Config> = StorageMap< _, Blake2_128Concat, diff --git a/primitives/membership/src/traits.rs b/primitives/membership/src/traits.rs index abb5961cd..9e54c148b 100644 --- a/primitives/membership/src/traits.rs +++ b/primitives/membership/src/traits.rs @@ -16,16 +16,16 @@ use frame_support::pallet_prelude::*; -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>; +pub trait CheckMembershipOpAllowed<IdtyId> { + fn check_add_membership(idty_id: IdtyId) -> Result<(), DispatchError>; + fn check_renew_membership(idty_id: IdtyId) -> Result<(), DispatchError>; } -impl<IdtyId> CheckMembershipCallAllowed<IdtyId> for () { - fn check_idty_allowed_to_claim_membership(_: &IdtyId) -> Result<(), DispatchError> { +impl<IdtyId> CheckMembershipOpAllowed<IdtyId> for () { + fn check_add_membership(_: IdtyId) -> Result<(), DispatchError> { Ok(()) } - fn check_idty_allowed_to_renew_membership(_: &IdtyId) -> Result<(), DispatchError> { + fn check_renew_membership(_: IdtyId) -> Result<(), DispatchError> { Ok(()) } } diff --git a/resources/gdev.yaml b/resources/gdev.yaml index a90145753..fcfd98034 100644 --- a/resources/gdev.yaml +++ b/resources/gdev.yaml @@ -27,8 +27,8 @@ parameters: cert_validity_period: 2102400 # Validity duration of a membership. 1051200 blocks = 73 days. membership_period: 1051200 - # Validity duration of a pending membership. 172800 blocks = 12 days. - pending_membership_period: 172800 + # Period to wait before membership renewal. 1051200 blocks = 1 days. + membership_renewal_period: 14400 # Delay a new member must observe before being able to emit a certification wot_first_cert_issuable_on: 0 # Number of required received certs to become a member @@ -68,4 +68,5 @@ sudo_key: "5CfodrEFe64MJtWvfhTHYBuUySr4WXLv2B41mZFucTXSGMFA" treasury_funder_address: "5E6q47RRGZU15LjUiBTm2DZjpqFKAjRNafYS8YV8AzTQZtLG" # The technical committee members, to act as sudo -technical_committee: ["Pini", "moul", "HugoTrentesaux", "tuxmain", "1000i100", "vit", "cgeek"] \ No newline at end of file +technical_committee: + ["Pini", "moul", "HugoTrentesaux", "tuxmain", "1000i100", "vit", "cgeek"] diff --git a/resources/metadata.scale b/resources/metadata.scale index 15ad56874e147a9b94c30468250afd196b8504ec..820374cd2073ee49d2ba67359255742635e037a9 100644 GIT binary patch delta 8781 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 delta 9718 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 Wwox-ZmL=7?os*MGB+tA((fZ#=nIKvK diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 6bae43eb5..d70ece043 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -453,7 +453,6 @@ macro_rules! pallets_config { impl pallet_duniter_wot::Config for Runtime { type FirstIssuableOn = WotFirstCertIssuableOn; - type IsDistanceOk = common_runtime::providers::MainWotIsDistanceOk<Runtime>; type MinCertForMembership = WotMinCertForMembership; type MinCertForCreateIdtyRight = WotMinCertForCreateIdtyRight; } @@ -483,11 +482,12 @@ macro_rules! pallets_config { } impl pallet_membership::Config for Runtime { - type CheckMembershipCallAllowed = Wot; + type CheckMembershipOpAllowed = Wot; type IdtyId = IdtyIndex; type IdtyIdOf = common_runtime::providers::IdentityIndexOf<Self>; type AccountIdOf = common_runtime::providers::IdentityAccountIdProvider<Self>; type MembershipPeriod = MembershipPeriod; + type MembershipRenewalPeriod = MembershipRenewalPeriod; type OnEvent = (OnMembershipEventHandler<Wot, Runtime>, Wot); type RuntimeEvent = RuntimeEvent; type WeightInfo = common_runtime::weights::pallet_membership::WeightInfo<Runtime>; @@ -516,9 +516,10 @@ macro_rules! pallets_config { type EvaluationPrice = frame_support::traits::ConstU64<1000>; type MaxRefereeDistance = frame_support::traits::ConstU32<5>; type MinAccessibleReferees = MinAccessibleReferees; - type ResultExpiration = frame_support::traits::ConstU32<720>; type RuntimeEvent = RuntimeEvent; type WeightInfo = common_runtime::weights::pallet_distance::WeightInfo<Runtime>; + type OnValidDistanceStatus = Wot; + type CheckRequestDistanceEvaluation = Wot; } // SMITH-MEMBERS diff --git a/runtime/common/src/providers.rs b/runtime/common/src/providers.rs index ca631988e..504cf92f8 100644 --- a/runtime/common/src/providers.rs +++ b/runtime/common/src/providers.rs @@ -17,7 +17,6 @@ use crate::{entities::IdtyData, AccountId, IdtyIndex}; use core::marker::PhantomData; use pallet_universal_dividend::FirstEligibleUd; -use sp_runtime::DispatchError; pub struct IdentityAccountIdProvider<Runtime>(PhantomData<Runtime>); @@ -71,31 +70,6 @@ where } } -pub struct MainWotIsDistanceOk<T>(PhantomData<T>); - -impl<T> pallet_duniter_wot::traits::IsDistanceOk<<T as pallet_identity::Config>::IdtyIndex> - for MainWotIsDistanceOk<T> -where - T: pallet_distance::Config + pallet_duniter_wot::Config, -{ - fn is_distance_ok( - idty_id: &<T as pallet_identity::Config>::IdtyIndex, - ) -> Result<(), DispatchError> { - match pallet_distance::Pallet::<T>::identity_distance_status(idty_id) { - Some((_, status)) => match status { - pallet_distance::DistanceStatus::Valid => Ok(()), - pallet_distance::DistanceStatus::Invalid => { - Err(pallet_duniter_wot::Error::<T>::DistanceIsInvalid.into()) - } - pallet_distance::DistanceStatus::Pending => { - Err(pallet_duniter_wot::Error::<T>::DistanceEvaluationPending.into()) - } - }, - None => Err(pallet_duniter_wot::Error::<T>::DistanceEvaluationNotRequested.into()), - } - } -} - pub struct IsWoTMemberProvider<T>(PhantomData<T>); impl<T: pallet_smith_members::Config> sp_runtime::traits::IsMember<<T as pallet_membership::Config>::IdtyId> @@ -121,14 +95,8 @@ macro_rules! impl_benchmark_setup_handler { T: pallet_certification::Config, <T as pallet_certification::Config>::IdtyIndex: From<u32>, { - fn force_status_ok( - idty_id: &IdtyIndex, - account: &<T as frame_system::Config>::AccountId, - ) -> () { - let _ = pallet_distance::Pallet::<T>::set_distance_status( - *idty_id, - Some((account.clone(), pallet_distance::DistanceStatus::Valid)), - ); + fn force_valid_distance_status(idty_id: &IdtyIndex) -> () { + let _ = pallet_distance::Pallet::<T>::do_valid_distance_status(*idty_id); } fn add_cert(issuer: &IdtyIndex, receiver: &IdtyIndex) { let _ = pallet_certification::Pallet::<T>::do_add_cert_checked( diff --git a/runtime/common/src/weights/pallet_certification.rs b/runtime/common/src/weights/pallet_certification.rs index 3e98b656d..4252be460 100644 --- a/runtime/common/src/weights/pallet_certification.rs +++ b/runtime/common/src/weights/pallet_certification.rs @@ -54,8 +54,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> /// Proof Skipped: Cert StorageIdtyCertMeta (max_values: None, max_size: None, mode: Measured) /// Storage: Parameters ParametersStorage (r:1 w:0) /// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Cert StorageCertsRemovableOn (r:1 w:1) - /// Proof Skipped: Cert StorageCertsRemovableOn (max_values: None, max_size: None, mode: Measured) + /// Storage: Cert CertsRemovableOn (r:1 w:1) + /// Proof Skipped: Cert CertsRemovableOn (max_values: None, max_size: None, mode: Measured) /// Storage: Cert CertsByReceiver (r:1 w:1) /// Proof Skipped: Cert CertsByReceiver (max_values: None, max_size: None, mode: Measured) fn add_cert() -> Weight { @@ -110,8 +110,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(i.into())) } - /// Storage: Cert StorageCertsRemovableOn (r:1 w:0) - /// Proof Skipped: Cert StorageCertsRemovableOn (max_values: None, max_size: None, mode: Measured) + /// Storage: Cert CertsRemovableOn (r:1 w:0) + /// Proof Skipped: Cert CertsRemovableOn (max_values: None, max_size: None, mode: Measured) fn on_initialize() -> Weight { // Proof Size summary in bytes: // Measured: `181` diff --git a/runtime/common/src/weights/pallet_distance.rs b/runtime/common/src/weights/pallet_distance.rs index 27aa94475..c1c3c8c55 100644 --- a/runtime/common/src/weights/pallet_distance.rs +++ b/runtime/common/src/weights/pallet_distance.rs @@ -17,19 +17,19 @@ //! Autogenerated weights for `pallet_distance` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-12-20, STEPS: `8`, REPEAT: `4`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bgallois-ms7d43`, CPU: `12th Gen Intel(R) Core(TM) i3-12100F` +//! HOSTNAME: `squirrel`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// target/release/duniter +// ./target/debug/duniter // benchmark // pallet // --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=* +// --steps=8 +// --repeat=4 +// --pallet=pallet-distance // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -50,25 +50,57 @@ pub struct WeightInfo<T>(PhantomData<T>); impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> { /// Storage: Identity IdentityIndexOf (r:1 w:0) /// Proof Skipped: Identity IdentityIndexOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Distance IdentityDistanceStatus (r:1 w:1) - /// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured) + /// Storage: Distance PendingEvaluationRequest (r:1 w:1) + /// Proof Skipped: Distance PendingEvaluationRequest (max_values: None, max_size: None, mode: Measured) + /// Storage: Distance ValidEvaluationResult (r:1 w:0) + /// Proof Skipped: Distance ValidEvaluationResult (max_values: None, max_size: None, mode: Measured) + /// Storage: Cert StorageIdtyCertMeta (r:1 w:0) + /// Proof Skipped: Cert StorageIdtyCertMeta (max_values: None, max_size: None, mode: Measured) + /// Storage: Parameters ParametersStorage (r:1 w:0) + /// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Session CurrentIndex (r:1 w:0) /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Distance EvaluationPool2 (r:1 w:1) /// Proof Skipped: Distance EvaluationPool2 (max_values: Some(1), max_size: None, mode: Measured) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(126), added: 2601, mode: MaxEncodedLen) - /// Storage: Distance DistanceStatusExpireOn (r:1 w:1) - /// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured) fn request_distance_evaluation() -> Weight { // Proof Size summary in bytes: - // Measured: `939` - // Estimated: `4404` - // Minimum execution time: 33_043_000 picoseconds. - Weight::from_parts(33_977_000, 0) - .saturating_add(Weight::from_parts(0, 4404)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `1280` + // Estimated: `4745` + // Minimum execution time: 876_053_000 picoseconds. + Weight::from_parts(898_445_000, 0) + .saturating_add(Weight::from_parts(0, 4745)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: Identity IdentityIndexOf (r:1 w:0) + /// Proof Skipped: Identity IdentityIndexOf (max_values: None, max_size: None, mode: Measured) + /// Storage: Identity Identities (r:2 w:0) + /// Proof Skipped: Identity Identities (max_values: None, max_size: None, mode: Measured) + /// Storage: Distance PendingEvaluationRequest (r:1 w:1) + /// Proof Skipped: Distance PendingEvaluationRequest (max_values: None, max_size: None, mode: Measured) + /// Storage: Distance ValidEvaluationResult (r:1 w:0) + /// Proof Skipped: Distance ValidEvaluationResult (max_values: None, max_size: None, mode: Measured) + /// Storage: Cert StorageIdtyCertMeta (r:1 w:0) + /// Proof Skipped: Cert StorageIdtyCertMeta (max_values: None, max_size: None, mode: Measured) + /// Storage: Parameters ParametersStorage (r:1 w:0) + /// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Session CurrentIndex (r:1 w:0) + /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Distance EvaluationPool2 (r:1 w:1) + /// Proof Skipped: Distance EvaluationPool2 (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(126), added: 2601, mode: MaxEncodedLen) + fn request_distance_evaluation_for() -> Weight { + // Proof Size summary in bytes: + // Measured: `1485` + // Estimated: `7425` + // Minimum execution time: 1_118_982_000 picoseconds. + Weight::from_parts(1_292_782_000, 0) + .saturating_add(Weight::from_parts(0, 7425)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: Distance DidUpdate (r:1 w:1) /// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured) @@ -85,13 +117,13 @@ impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> { /// The range of component `i` is `[1, 600]`. fn update_evaluation(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `772 + i * (10 ±0)` + // Measured: `773 + i * (10 ±0)` // Estimated: `2256 + i * (10 ±0)` - // Minimum execution time: 22_135_000 picoseconds. - Weight::from_parts(27_043_883, 0) + // Minimum execution time: 463_878_000 picoseconds. + Weight::from_parts(743_823_548, 0) .saturating_add(Weight::from_parts(0, 2256)) - // Standard Error: 1_421 - .saturating_add(Weight::from_parts(125_435, 0).saturating_mul(i.into())) + // Standard Error: 292_144 + .saturating_add(Weight::from_parts(1_326_639, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into())) @@ -103,32 +135,40 @@ impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> { /// The range of component `i` is `[1, 600]`. fn force_update_evaluation(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `611 + i * (10 ±0)` + // Measured: `612 + i * (10 ±0)` // Estimated: `2095 + i * (10 ±0)` - // Minimum execution time: 13_033_000 picoseconds. - Weight::from_parts(15_741_933, 0) + // Minimum execution time: 208_812_000 picoseconds. + Weight::from_parts(257_150_521, 0) .saturating_add(Weight::from_parts(0, 2095)) - // Standard Error: 693 - .saturating_add(Weight::from_parts(121_989, 0).saturating_mul(i.into())) + // Standard Error: 53_366 + .saturating_add(Weight::from_parts(1_841_329, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into())) } /// Storage: Session CurrentIndex (r:1 w:0) /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Distance DistanceStatusExpireOn (r:1 w:1) - /// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured) - /// Storage: Distance IdentityDistanceStatus (r:0 w:1) - /// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured) - fn force_set_distance_status() -> Weight { + /// Storage: Distance ValidEvaluationExpireOn (r:1 w:1) + /// Proof Skipped: Distance ValidEvaluationExpireOn (max_values: None, max_size: None, mode: Measured) + /// Storage: Identity Identities (r:1 w:0) + /// Proof Skipped: Identity Identities (max_values: None, max_size: None, mode: Measured) + /// Storage: Membership Membership (r:1 w:1) + /// Proof Skipped: Membership Membership (max_values: None, max_size: None, mode: Measured) + /// Storage: Membership MembershipsExpireOn (r:2 w:2) + /// Proof Skipped: Membership MembershipsExpireOn (max_values: None, max_size: None, mode: Measured) + /// Storage: Parameters ParametersStorage (r:1 w:0) + /// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Distance ValidEvaluationResult (r:0 w:1) + /// Proof Skipped: Distance ValidEvaluationResult (max_values: None, max_size: None, mode: Measured) + fn force_valid_distance_status() -> Weight { // Proof Size summary in bytes: - // Measured: `585` - // Estimated: `4050` - // Minimum execution time: 12_886_000 picoseconds. - Weight::from_parts(13_465_000, 0) - .saturating_add(Weight::from_parts(0, 4050)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `1181` + // Estimated: `7121` + // Minimum execution time: 873_892_000 picoseconds. + Weight::from_parts(1_081_510_000, 0) + .saturating_add(Weight::from_parts(0, 7121)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: Distance DidUpdate (r:1 w:1) /// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured) @@ -136,8 +176,8 @@ impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `170` // Estimated: `1655` - // Minimum execution time: 3_830_000 picoseconds. - Weight::from_parts(4_065_000, 0) + // Minimum execution time: 93_595_000 picoseconds. + Weight::from_parts(109_467_000, 0) .saturating_add(Weight::from_parts(0, 1655)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/runtime/common/src/weights/pallet_identity.rs b/runtime/common/src/weights/pallet_identity.rs index 159094ef9..16189190a 100644 --- a/runtime/common/src/weights/pallet_identity.rs +++ b/runtime/common/src/weights/pallet_identity.rs @@ -64,8 +64,8 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> { /// Proof Skipped: Identity IdentityChangeSchedule (max_values: None, max_size: None, mode: Measured) /// Storage: Identity CounterForIdentities (r:1 w:1) /// Proof: Identity CounterForIdentities (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Cert StorageCertsRemovableOn (r:1 w:1) - /// Proof Skipped: Cert StorageCertsRemovableOn (max_values: None, max_size: None, mode: Measured) + /// Storage: Cert CertsRemovableOn (r:1 w:1) + /// Proof Skipped: Cert CertsRemovableOn (max_values: None, max_size: None, mode: Measured) /// Storage: Cert CertsByReceiver (r:1 w:1) /// Proof Skipped: Cert CertsByReceiver (max_values: None, max_size: None, mode: Measured) /// Storage: Quota IdtyQuota (r:0 w:1) diff --git a/runtime/g1/src/lib.rs b/runtime/g1/src/lib.rs index 40a3025ac..8c9bca67c 100644 --- a/runtime/g1/src/lib.rs +++ b/runtime/g1/src/lib.rs @@ -174,9 +174,6 @@ impl Contains<RuntimeCall> for BaseCallFilter { call, RuntimeCall::System( frame_system::Call::remark { .. } | frame_system::Call::remark_with_event { .. } - ) | RuntimeCall::Membership( - pallet_membership::Call::claim_membership { .. } - | pallet_membership::Call::revoke_membership { .. } ) | RuntimeCall::Session(_) ) } @@ -287,7 +284,7 @@ construct_runtime!( // Web Of Trust Wot: pallet_duniter_wot::{Pallet} = 40, Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41, - Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>} = 42, + Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>} = 42, Certification: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>} = 43, Distance: pallet_distance::{Pallet, Call, Storage, Inherent, Event<T>} = 44, diff --git a/runtime/g1/src/parameters.rs b/runtime/g1/src/parameters.rs index 22ef4efa4..adfc0183a 100644 --- a/runtime/g1/src/parameters.rs +++ b/runtime/g1/src/parameters.rs @@ -105,7 +105,7 @@ parameter_types! { // Membership parameter_types! { pub const MembershipPeriod: BlockNumber = YEARS; - pub const PendingMembershipPeriod: BlockNumber = 2 * MONTHS; + pub const MembershipRenewalPeriod: BlockNumber = 2 * MONTHS; } // Certification diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs index 04366d5db..d64153d64 100644 --- a/runtime/gdev/src/lib.rs +++ b/runtime/gdev/src/lib.rs @@ -177,10 +177,9 @@ 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::revoke_membership { .. }) - | RuntimeCall::Session(_) + // session calls can not be called directly + // it should be done through authority-members pallet + RuntimeCall::Session(_) ) } } @@ -257,6 +256,7 @@ common_runtime::pallets_config! { pub type ConfirmPeriod = pallet_duniter_test_parameters::IdtyConfirmPeriod<Runtime>; pub type IdtyCreationPeriod = pallet_duniter_test_parameters::IdtyCreationPeriod<Runtime>; pub type MembershipPeriod = pallet_duniter_test_parameters::MembershipPeriod<Runtime>; + pub type MembershipRenewalPeriod = pallet_duniter_test_parameters::MembershipRenewalPeriod<Runtime>; pub type UdCreationPeriod = pallet_duniter_test_parameters::UdCreationPeriod<Runtime>; pub type UdReevalPeriod = pallet_duniter_test_parameters::UdReevalPeriod<Runtime>; pub type WotFirstCertIssuableOn = pallet_duniter_test_parameters::WotFirstCertIssuableOn<Runtime>; @@ -329,7 +329,7 @@ construct_runtime!( // Web Of Trust Wot: pallet_duniter_wot::{Pallet} = 40, Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41, - Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>} = 42, + Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>} = 42, Certification: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>} = 43, Distance: pallet_distance::{Pallet, Call, Storage, Inherent, Event<T>} = 44, diff --git a/runtime/gdev/src/parameters.rs b/runtime/gdev/src/parameters.rs index 7dc4e8177..abd164fe6 100644 --- a/runtime/gdev/src/parameters.rs +++ b/runtime/gdev/src/parameters.rs @@ -90,6 +90,11 @@ frame_support::parameter_types! { pub const ChangeOwnerKeyPeriod: BlockNumber = 7 * DAYS; } +// Membership +frame_support::parameter_types! { + pub const SmithMembershipRenewalPeriod: BlockNumber = MONTHS; +} + /*************/ /* UTILITIES */ /*************/ diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs index 261755f75..0ca8aa74c 100644 --- a/runtime/gdev/tests/common/mod.rs +++ b/runtime/gdev/tests/common/mod.rs @@ -105,7 +105,7 @@ impl ExtBuilder { idty_confirm_period: 40, idty_creation_period: 50, membership_period: 100, - pending_membership_period: 500, + membership_renewal_period: 10, ud_creation_period: 60_000, ud_reeval_period: 60_000 * 20, smith_cert_max_by_issuer: 8, diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs index 49755e0c4..cfc3cb05d 100644 --- a/runtime/gdev/tests/integration_tests.rs +++ b/runtime/gdev/tests/integration_tests.rs @@ -311,7 +311,7 @@ fn test_remove_identity() { }); } -/// test identity is validated when membership is claimed +/// test identity is "validated" (= membership is claimed) when distance is evaluated positively #[test] fn test_validate_identity_when_claim() { ExtBuilder::new(1, 3, 4) @@ -346,11 +346,14 @@ fn test_validate_identity_when_claim() { 5 )); + // eve request distance evaluation for herself assert_ok!(Distance::request_distance_evaluation( frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), )); - run_to_block(51); // Pass 2 sessions + // Pass 2 sessions + run_to_block(51); + // simulate an evaluation published by smith Alice assert_ok!(Distance::force_update_evaluation( frame_system::RawOrigin::Root.into(), AccountKeyring::Alice.to_account_id(), @@ -358,20 +361,161 @@ fn test_validate_identity_when_claim() { distances: vec![Perbill::one()], } )); - run_to_block(76); // Pass 1 session + run_to_block(75); // Pass 1 session + System::assert_has_event(RuntimeEvent::Distance( + pallet_distance::Event::EvaluatedValid { idty_index: 5 }, + )); + + // eve can not claim her membership manually because it is done automatically + // the following call does not exist anymore + // assert_noop!( + // Membership::claim_membership( + // frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + // ), + // pallet_membership::Error::<Runtime>::AlreadyMember + // ); - // eve can claim her membership - assert_ok!(Membership::claim_membership( + // println!("{:?}", System::events()); + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipAdded { + member: 5, + expire_on: 75 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(), + }, + )); + }); +} + +/// test identity creation workflow +// with distance requested by last certifier +#[test] +fn test_identity_creation_workflow() { + ExtBuilder::new(1, 3, 4) + .with_initial_balances(vec![ + (AccountKeyring::Charlie.to_account_id(), 10_000), // necessary for evalation distance reserve + (AccountKeyring::Eve.to_account_id(), 2_000), + (AccountKeyring::Ferdie.to_account_id(), 1_000), + ]) + .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(), + )); + assert_eq!( + Identity::identity(5), + Some(pallet_identity::IdtyValue { + data: Default::default(), + next_creatable_identity_on: 0u32, + old_owner_key: None, + owner_key: AccountKeyring::Eve.to_account_id(), + next_scheduled: 1 + 40, + status: pallet_identity::IdtyStatus::Unconfirmed, + }) + ); + 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(), + )); + assert_eq!( + Identity::identity(5), + Some(pallet_identity::IdtyValue { + data: Default::default(), + next_creatable_identity_on: 0u32, + old_owner_key: None, + owner_key: AccountKeyring::Eve.to_account_id(), + next_scheduled: 2 + 876600, + status: pallet_identity::IdtyStatus::Unvalidated, + }) + ); + run_to_block(3); + // eve gets certified by bob and charlie + assert_ok!(Certification::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(), + 2, + 5 + )); + assert_ok!(Certification::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(), + 3, + 5 + )); + // charlie also request distance evaluation for eve + // (could be done in batch) + assert_ok!(Distance::request_distance_evaluation_for( + frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(), + 5 + )); + // then the evaluation is pending + assert_eq!( + Distance::pending_evaluation_request(5), + Some(AccountKeyring::Charlie.to_account_id(),) + ); + + // Pass 2 sessions + run_to_block(51); + // simulate evaluation published by smith Alice + assert_ok!(Distance::force_update_evaluation( + frame_system::RawOrigin::Root.into(), + AccountKeyring::Alice.to_account_id(), + pallet_distance::ComputationResult { + distances: vec![Perbill::one()], + } )); + // Pass 1 session + run_to_block(75); + // eve should not even have to claim her membership System::assert_has_event(RuntimeEvent::Membership( pallet_membership::Event::MembershipAdded { member: 5, - expire_on: 76 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(), + expire_on: 75 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(), + }, + )); + + // test state coherence + assert_eq!( + Identity::identity(5), + Some(pallet_identity::IdtyValue { + data: IdtyData { + // block time is 6_000 ms + // ud creation period is 60_000 ms ~ 10 blocks + // first ud is at 24_000 ms ~ 4 blocks + // at block 75 this is UD number 8, so next is 9 + first_eligible_ud: pallet_universal_dividend::FirstEligibleUd(Some( + sp_std::num::NonZeroU16::new(9).unwrap() + )) + }, + next_creatable_identity_on: 0u32, + old_owner_key: None, + owner_key: AccountKeyring::Eve.to_account_id(), + next_scheduled: 0, + status: pallet_identity::IdtyStatus::Member, + }) + ); + + run_to_block(84); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::NewUdCreated { + amount: 1000, + index: 9, + monetary_mass: 50_000, // 13_000 (initial) + 4 * 1000 * 8 (produced) + 5000 + members_count: 5, + }, + )); + assert_ok!(UniversalDividend::claim_uds( + frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + )); + System::assert_has_event(RuntimeEvent::UniversalDividend( + pallet_universal_dividend::Event::UdsClaimed { + count: 1, + total: 1000, + who: AccountKeyring::Eve.to_account_id(), }, )); - // not possible anymore to validate identity of someone else }); } @@ -424,6 +568,16 @@ fn test_membership_renewal() { .with_initial_balances(vec![(AccountKeyring::Alice.to_account_id(), 2000)]) .build() .execute_with(|| { + // can not renew membership immediately + assert_noop!( + Distance::request_distance_evaluation( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + ), + pallet_duniter_wot::Error::<Runtime>::MembershipRenewalPeriodNotRespected, + ); + + // but ok after waiting 10 blocks delay + run_to_block(11); assert_ok!(Distance::request_distance_evaluation( frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), )); @@ -436,33 +590,27 @@ fn test_membership_renewal() { distances: vec![Perbill::one()], } )); - run_to_block(76); // Pass 1 session - - // renew at block 76 - assert_ok!(Membership::renew_membership( - frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), - )); + // Pass 1 session, membership is renewed automatically + run_to_block(75); System::assert_has_event(RuntimeEvent::Membership( - pallet_membership::Event::MembershipAdded { + pallet_membership::Event::MembershipRenewed { member: 1, - expire_on: 76 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(), + expire_on: 75 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(), }, )); - // renew at block 77 - run_to_block(77); - assert_ok!(Membership::renew_membership( - frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), - )); - System::assert_has_event(RuntimeEvent::Membership( - pallet_membership::Event::MembershipAdded { - member: 1, - expire_on: 77 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(), - }, - )); + run_to_block(76); + // not possible to renew manually + // can not ask renewal when period is not respected + assert_noop!( + Distance::request_distance_evaluation( + frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + ), + pallet_duniter_wot::Error::<Runtime>::MembershipRenewalPeriodNotRespected, + ); - // should expire at block 177 = 77+100 - run_to_block(177); + // should expire at block 175 = 75+100 + run_to_block(175); System::assert_has_event(RuntimeEvent::Membership( pallet_membership::Event::MembershipRemoved { member: 1, @@ -583,18 +731,19 @@ fn test_ud_claimed_membership_on_and_off() { }, )); - // alice claims back her membership - assert_ok!(Distance::force_set_distance_status( + // alice claims back her membership through distance evaluation + assert_ok!(Distance::force_valid_distance_status( frame_system::RawOrigin::Root.into(), 1, - Some(( - AccountKeyring::Alice.to_account_id(), - pallet_distance::DistanceStatus::Valid - )) - )); - assert_ok!(Membership::claim_membership( - frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into() )); + // it can not be done manually + // because the call does not exist anymore + // assert_noop!( + // Membership::claim_membership( + // frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), + // ), + // pallet_membership::Error::<Runtime>::AlreadyMember + // ); System::assert_has_event(RuntimeEvent::Membership( pallet_membership::Event::MembershipAdded { member: 1, @@ -963,12 +1112,22 @@ fn test_create_new_idty_without_founds() { .build() .execute_with(|| { run_to_block(2); + assert_eq!( + Balances::free_balance(AccountKeyring::Eve.to_account_id()), + 0 + ); // Should be able to create an identity without founds assert_ok!(Identity::create_identity( frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(), AccountKeyring::Eve.to_account_id(), )); + System::assert_has_event(RuntimeEvent::Identity( + pallet_identity::Event::IdtyCreated { + idty_index: 5, + owner_key: AccountKeyring::Eve.to_account_id(), + }, + )); // At next block, nothing should be preleved run_to_block(3); @@ -1029,24 +1188,26 @@ fn test_validate_new_idty_after_few_uds() { pallet_identity::IdtyName::from("Eve"), )); - // At next block, Bob should be able to certify and validate the new identity + // At next block, Bob should be able to certify the new identity run_to_block(23); assert_ok!(Certification::add_cert( frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(), 2, 5, )); - assert_ok!(Distance::force_set_distance_status( + // valid distance status should trigger identity validation + assert_ok!(Distance::force_valid_distance_status( frame_system::RawOrigin::Root.into(), 5, - Some(( - AccountKeyring::Bob.to_account_id(), - pallet_distance::DistanceStatus::Valid - )) - )); - assert_ok!(Membership::claim_membership( - frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), )); + // and it is not possible to call it manually + // because the call does not exist anymore + // assert_noop!( + // Membership::claim_membership( + // frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + // ), + // pallet_membership::Error::<Runtime>::AlreadyMember + // ); // The new member should have first_eligible_ud equal to three assert!(Identity::identity(5).is_some()); @@ -1095,18 +1256,19 @@ fn test_claim_memberhsip_after_few_uds() { 5, )); - // eve should be able to claim her membership - assert_ok!(Distance::force_set_distance_status( + // eve membership should be able to be claimed through distance evaluation + assert_ok!(Distance::force_valid_distance_status( frame_system::RawOrigin::Root.into(), 5, - Some(( - AccountKeyring::Eve.to_account_id(), - pallet_distance::DistanceStatus::Valid - )) - )); - assert_ok!(Membership::claim_membership( - frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), )); + // but not manually + // because the call does not exist + // assert_noop!( + // Membership::claim_membership( + // frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(), + // ), + // pallet_membership::Error::<Runtime>::AlreadyMember + // ); // The new member should have first_eligible_ud equal to three assert!(Identity::identity(5).is_some()); @@ -1451,16 +1613,6 @@ fn test_new_account_linked() { ); }) } -#[test] -#[ignore = "what was this test supposed to do?"] -fn smith_data_problem() { - ExtBuilder::new(1, 3, 4) - .change_parameters(|_parameters| {}) - .build() - .execute_with(|| { - run_to_block(4); - }); -} /// test killed account // The only way to kill an account is to kill the identity diff --git a/runtime/gdev/tests/xt_tests.rs b/runtime/gdev/tests/xt_tests.rs index 1663d6221..4635571e3 100644 --- a/runtime/gdev/tests/xt_tests.rs +++ b/runtime/gdev/tests/xt_tests.rs @@ -15,6 +15,7 @@ // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. // these integration tests aim to test fees and extrinsic-related externalities +// they need constant-fees feature to work mod common; diff --git a/runtime/gtest/src/lib.rs b/runtime/gtest/src/lib.rs index a4b6e010a..7fe0cfeba 100644 --- a/runtime/gtest/src/lib.rs +++ b/runtime/gtest/src/lib.rs @@ -292,7 +292,7 @@ construct_runtime!( // Web Of Trust Wot: pallet_duniter_wot::{Pallet} = 40, Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41, - Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>} = 42, + Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>} = 42, Certification: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>} = 43, Distance: pallet_distance::{Pallet, Call, Storage, Inherent, Event<T>} = 44, diff --git a/runtime/gtest/src/parameters.rs b/runtime/gtest/src/parameters.rs index 252ae211a..c7731b0a9 100644 --- a/runtime/gtest/src/parameters.rs +++ b/runtime/gtest/src/parameters.rs @@ -107,7 +107,7 @@ parameter_types! { // Membership parameter_types! { pub const MembershipPeriod: BlockNumber = 73 * DAYS; - pub const PendingMembershipPeriod: BlockNumber = 12 * DAYS; + pub const MembershipRenewalPeriod: BlockNumber = 56 * DAYS; } // Certification -- GitLab