From e14d80749896993605593a4f48a2b0e89bc89b24 Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo@trentesaux.fr>
Date: Mon, 18 Dec 2023 18:17:02 +0100
Subject: [PATCH] cargo check ok
---
pallets/distance/src/lib.rs | 122 +++++++++++++++------------
pallets/distance/src/mock.rs | 1 +
pallets/distance/src/traits.rs | 13 ++-
pallets/duniter-wot/src/lib.rs | 25 ++++++
pallets/membership/src/lib.rs | 49 ++++++++---
runtime/common/src/pallets_config.rs | 1 +
6 files changed, 141 insertions(+), 70 deletions(-)
diff --git a/pallets/distance/src/lib.rs b/pallets/distance/src/lib.rs
index d7d131cd8..0cc101401 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;
@@ -66,6 +66,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]
@@ -85,6 +86,8 @@ pub mod pallet {
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>;
}
// STORAGE //
@@ -143,20 +146,6 @@ pub mod pallet {
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>;
@@ -228,7 +217,9 @@ 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 {
@@ -237,6 +228,8 @@ pub mod pallet {
let idty =
pallet_identity::IdentityIndexOf::<T>::get(&who).ok_or(Error::<T>::NoIdentity)?;
+ // TODO is it necessary to check that the same account performed the request?
+ // TODO what if the distance status is existing but valid?
ensure!(
IdentityDistanceStatus::<T>::get(idty)
!= Some((who.clone(), DistanceStatus::Pending)),
@@ -247,13 +240,49 @@ pub mod pallet {
Ok(().into())
}
+ /// Request target identity to be evaluated
+ /// only possible for unconfirmed identity
+ #[pallet::call_index(4)]
+ #[pallet::weight(<T as pallet::Config>::WeightInfo::request_distance_evaluation())]
+ pub fn request_distance_evaluation_for(
+ origin: OriginFor<T>,
+ target: T::IdtyIndex,
+ ) -> DispatchResultWithPostInfo {
+ let who = ensure_signed(origin)?;
+
+ // check that the caller has an identity (TODO is this necessary ?)
+ // let _ =
+ // pallet_identity::IdentityIndexOf::<T>::get(&who).ok_or(Error::<T>::NoIdentity)?;
+
+ // get the identity value of the target
+ let target_idty =
+ pallet_identity::Identities::<T>::get(target).ok_or(Error::<T>::NoIdentity)?;
+
+ // check that target is unconfirmed
+ ensure!(
+ target_idty.status == pallet_identity::IdtyStatus::Unconfirmed,
+ Error::<T>::NoIdentity
+ );
+
+ // check that no distance status is already there
+ ensure!(
+ IdentityDistanceStatus::<T>::get(target).is_none(),
+ Error::<T>::AlreadyInEvaluation
+ );
+
+ 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 +296,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,7 +310,8 @@ pub mod pallet {
Pallet::<T>::do_update_evaluation(evaluator, computation_result)
}
- /// Set the distance evaluation status of an identity
+ /// Force set the distance evaluation status of an identity
+ // (it is convenient to have this in test network)
///
/// Removes the status if `status` is `None`.
///
@@ -296,15 +327,7 @@ pub mod pallet {
) -> 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::do_set_distance_status(identity, status.clone());
Self::deposit_event(Event::EvaluationStatusForced {
idty_index: identity,
status,
@@ -313,27 +336,6 @@ pub mod pallet {
}
}
- // 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,6 +400,7 @@ pub mod pallet {
}
}
+ /// request distance evaluation in current pool
fn do_request_distance_evaluation(
who: T::AccountId,
idty_index: <T as pallet_identity::Config>::IdtyIndex,
@@ -422,31 +425,31 @@ pub mod pallet {
(&who, DistanceStatus::Pending),
);
- 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 });
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()
@@ -458,19 +461,28 @@ pub mod pallet {
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_set_distance_status(
+ identity: <T as pallet_identity::Config>::IdtyIndex,
+ status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>,
+ ) {
+ IdentityDistanceStatus::<T>::set(identity, status.clone());
+ if let Some((_, DistanceStatus::Valid)) = status {
+ T::OnValidDistanceStatus::on_valid_distance_status(identity);
+ }
+ }
}
impl<T: Config> pallet_authority_members::OnNewSession for Pallet<T> {
fn on_new_session(index: SessionIndex) {
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)]
diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs
index bcdbd095d..537d5bce0 100644
--- a/pallets/distance/src/mock.rs
+++ b/pallets/distance/src/mock.rs
@@ -258,6 +258,7 @@ impl pallet_distance::Config for Test {
type ResultExpiration = frame_support::traits::ConstU32<720>;
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
+ type OnValidDistanceStatus = ();
}
// Build genesis storage according to the mock runtime.
diff --git a/pallets/distance/src/traits.rs b/pallets/distance/src/traits.rs
index de9d532d6..eafb5451c 100644
--- a/pallets/distance/src/traits.rs
+++ b/pallets/distance/src/traits.rs
@@ -14,8 +14,13 @@
// 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::*;
+
+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) {}
}
diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs
index 41ad8db13..04a02e808 100644
--- a/pallets/duniter-wot/src/lib.rs
+++ b/pallets/duniter-wot/src/lib.rs
@@ -356,3 +356,28 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnRemovedCert<IdtyI
}
}
}
+
+/// valid distance status handler
+impl<T: Config<I> + pallet_distance::Config, I: 'static>
+ pallet_distance::traits::OnValidDistanceStatus<T> for Pallet<T, I>
+{
+ fn on_valid_distance_status(idty_index: IdtyIndex) {
+ if let Some(identity) = pallet_identity::Identities::<T>::get(idty_index) {
+ match identity.status {
+ IdtyStatus::Unconfirmed => { /* should not happen */ }
+ IdtyStatus::Unvalidated | IdtyStatus::NotMember => {
+ // ok to fail
+ let _ = pallet_membership::Pallet::<T, I>::try_claim_membership(idty_index);
+ }
+ IdtyStatus::Member => {
+ // ok to fail
+ let _ = pallet_membership::Pallet::<T, I>::try_renew_membership(idty_index);
+ }
+ IdtyStatus::Revoked => { /* should not happen */ }
+ }
+ } else {
+ // identity was removed before distance status was found
+ // so it's ok to do nothing
+ }
+ }
+}
diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs
index 08bbb81ad..435d6b42d 100644
--- a/pallets/membership/src/lib.rs
+++ b/pallets/membership/src/lib.rs
@@ -203,8 +203,7 @@ pub mod pallet {
// get identity
let idty_id = Self::get_idty_id(origin)?;
- Self::check_allowed_to_claim(idty_id)?;
- Self::do_add_membership(idty_id);
+ Self::try_claim_membership(idty_id)?;
Ok(().into())
}
@@ -214,16 +213,8 @@ pub mod pallet {
pub fn renew_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
// Verify phase
let idty_id = Self::get_idty_id(origin)?;
- let membership_data =
- Membership::<T, I>::get(idty_id).ok_or(Error::<T, I>::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));
+ Self::try_renew_membership(idty_id)?;
Ok(().into())
}
@@ -274,12 +265,48 @@ pub mod pallet {
Ok(())
}
+ /// check that membership can be renewed
+ pub fn check_allowed_to_renew(
+ idty_id: T::IdtyId,
+ ) -> Result<MembershipData<T::BlockNumber>, DispatchError> {
+ let membership_data =
+ Membership::<T, I>::get(idty_id).ok_or(Error::<T, I>::MembershipNotFound)?;
+
+ // enough certifications and distance rule for example
+ T::CheckMembershipCallAllowed::check_idty_allowed_to_renew_membership(&idty_id)?;
+ Ok(membership_data)
+ }
+
+ /// try claim membership
+ pub fn try_claim_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> {
+ Self::check_allowed_to_claim(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_allowed_to_renew(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);
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);
+ Self::insert_membership_and_schedule_expiry(idty_id);
+ 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, I>::take(idty_id) {
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index e61c1996d..b20283e14 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -523,6 +523,7 @@ macro_rules! pallets_config {
type ResultExpiration = frame_support::traits::ConstU32<720>;
type RuntimeEvent = RuntimeEvent;
type WeightInfo = common_runtime::weights::pallet_distance::WeightInfo<Runtime>;
+ type OnValidDistanceStatus = Wot;
}
// SMITHS SUB-WOT //
--
GitLab