Skip to content
Snippets Groups Projects
Commit 37b3578e authored by Hugo Trentesaux's avatar Hugo Trentesaux
Browse files

cargo check ok

parent 4184a627
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !219. Comments created here will be created in the context of that merge request.
......@@ -37,6 +37,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;
......@@ -77,6 +78,8 @@ pub mod pallet {
#[pallet::constant]
type MinAccessibleReferees: Get<Perbill>;
/// Number of session to keep a positive evaluation result
// (antispam mechanism)
#[pallet::constant]
type ResultExpiration: Get<u32>;
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
......@@ -127,18 +130,34 @@ pub mod pallet {
pub type EvaluationBlock<T: Config> =
StorageValue<_, <T as frame_system::Config>::Hash, ValueQuery>;
/// Distance evaluation status by identity
/// Pending evaluation requests
///
/// * `.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,
>;
/// Valid evaluation results
#[pallet::storage]
#[pallet::getter(fn valid_evaluation_result)]
pub type ValidEvaluationResult<T: Config> =
StorageMap<_, Twox64Concat, <T as pallet_identity::Config>::IdtyIndex, (), OptionQuery>;
/// Valid evaluation expiry blocks
#[pallet::storage]
#[pallet::getter(fn valid_evaluation_expire_on)]
pub type ValidEvaluationExpireOn<T: Config> = StorageMap<
_,
Twox64Concat,
u32,
Vec<<T as pallet_identity::Config>::IdtyIndex>,
OptionQuery,
>;
......@@ -161,9 +180,9 @@ pub mod pallet {
idty_index: T::IdtyIndex,
who: T::AccountId,
},
/// A distance evaluation was updated.
/// A distance evaluation was updated. // TODO refac
EvaluationUpdated { evaluator: T::AccountId },
/// A distance status was forced.
/// A distance status was forced. // TODO check if necessary to differenciate
EvaluationStatusForced {
idty_index: T::IdtyIndex,
status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>,
......@@ -183,7 +202,13 @@ pub mod pallet {
/// No author for this block.
NoAuthor,
/// Caller has no identity.
NoIdentity,
CallerHasNoIdentity,
/// Caller identity not found.
CallerIdentityNotFound,
/// Caller not member.
CallerNotMember,
/// Target identity not found.
TargetIdentityNotFound,
/// Evaluation queue is full.
QueueFull,
/// Too many evaluators in the current evaluation pool.
......@@ -191,7 +216,10 @@ pub mod pallet {
/// Evaluation result has a wrong length.
WrongResultLength,
/// Targeted distance evaluation request is only possible for an unvalidated identity
DistanceRequestNotAllowed,
DistanceRequestOnlyAllowedForUnvalidated,
/// Can not request distance evaluation when a valid result has already been published recently
// (antispam)
ValidDistanceResultAlreadyAvailable,
}
#[pallet::hooks]
......@@ -223,18 +251,9 @@ pub mod pallet {
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)?;
// 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)),
Error::<T>::AlreadyInEvaluation
);
Pallet::<T>::do_request_distance_evaluation(who, idty)?;
Pallet::<T>::do_request_distance_evaluation(&who, idty)?;
Ok(().into())
}
......@@ -248,27 +267,9 @@ pub mod pallet {
) -> 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 unvalidated
ensure!(
target_idty.status == pallet_identity::IdtyStatus::Unvalidated,
Error::<T>::DistanceRequestNotAllowed
);
// check that no distance status is already there
ensure!(
IdentityDistanceStatus::<T>::get(target).is_none(),
Error::<T>::AlreadyInEvaluation
);
Self::check_request_distance_evaluation_for(&who, target)?;
Pallet::<T>::do_request_distance_evaluation(who, target)?;
Pallet::<T>::do_request_distance_evaluation(&who, target)?;
Ok(().into())
}
......@@ -318,18 +319,13 @@ pub mod pallet {
/// * `status.1` is the status of the evaluation.
#[pallet::call_index(3)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::force_set_distance_status())]
pub fn force_set_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)?;
Self::do_set_distance_status(identity, status.clone());
Self::deposit_event(Event::EvaluationStatusForced {
idty_index: identity,
status,
});
Self::do_valid_distance_status(identity);
Ok(())
}
}
......@@ -398,9 +394,74 @@ 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)?;
// TODO some of the following can be moved to a "check idty call allowed" managed by wot
let idty = pallet_identity::Identities::<T>::get(idty_index)
.ok_or(Error::<T>::CallerIdentityNotFound)?;
// caller is member
ensure!(
idty.status == pallet_identity::IdtyStatus::NotMember
|| idty.status == pallet_identity::IdtyStatus::Member,
Error::<T>::CallerNotMember
);
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)?;
// TODO some of the following can be moved to a "check idty call allowed" managed by wot
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>::DistanceRequestOnlyAllowedForUnvalidated
);
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
);
// no valid status (antispam)
ensure!(
ValidEvaluationResult::<T>::get(target).is_none(),
Error::<T>::ValidDistanceResultAlreadyAvailable
);
Ok(())
}
/// 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(
......@@ -411,19 +472,19 @@ pub mod pallet {
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);
Self::deposit_event(Event::EvaluationRequested { idty_index, who });
Self::deposit_event(Event::EvaluationRequested {
idty_index,
who: who.clone(),
});
Ok(())
},
)
......@@ -466,14 +527,17 @@ pub mod pallet {
}
/// 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);
}
pub fn do_valid_distance_status(idty: <T as pallet_identity::Config>::IdtyIndex) {
// expiration session index
let expire_on =
pallet_session::Pallet::<T>::current_index() + T::ResultExpiration::get();
// index valid result
ValidEvaluationResult::<T>::set(idty, Some(()));
// schedule expiry
ValidEvaluationExpireOn::<T>::append(expire_on, idty);
// callback
T::OnValidDistanceStatus::on_valid_distance_status(idty);
// TODO event
}
}
......@@ -495,42 +559,37 @@ pub mod pallet {
MedianResult::One(m) => m,
MedianResult::Two(m1, m2) => m1 + (m2 - m1) / 2, // Avoid overflow (since max is 1)
};
// flag to capture distance status
// distance status criterion
let is_distance_status_valid = median >= T::MinAccessibleReferees::get();
// mutate the distance status accordingly
IdentityDistanceStatus::<T>::mutate(idty, |entry| {
if let Some((account_id, status)) = entry.as_mut() {
// result is ok
if is_distance_status_valid {
// unreserve price
T::Currency::unreserve(
account_id,
<T as Config>::EvaluationPrice::get(),
);
// update distance status
*status = DistanceStatus::Valid;
}
// result is ko
else {
// slash requester
T::Currency::slash_reserved(
account_id,
<T as Config>::EvaluationPrice::get(),
);
// update distance status
*status = DistanceStatus::Invalid;
}
} // each entry should be Some((_, DistanceStatus::Pending))
});
// consequences of valid status
// take requester and perform unreserve or slash
if let Some(requester) = PendingEvaluationRequest::<T>::take(idty) {
if is_distance_status_valid {
T::Currency::unreserve(
&requester,
<T as Config>::EvaluationPrice::get(),
);
} else {
T::Currency::slash_reserved(
&requester,
<T as Config>::EvaluationPrice::get(),
);
}
}
// if evaluation happened without request, it's ok to do nothing
// if evaluation is positive, remember it for antispam
// and perform callbacks
if is_distance_status_valid {
T::OnValidDistanceStatus::on_valid_distance_status(idty);
Self::do_valid_distance_status(idty);
} else {
// TODO event
}
} else if let Some((account_id, _status)) = IdentityDistanceStatus::<T>::take(idty)
{
// when no result is published, all funds are unreserved
}
// when no result is published, all funds are unreserved
else if let Some(requester) = PendingEvaluationRequest::<T>::take(idty) {
<T as Config>::Currency::unreserve(
&account_id,
&requester,
<T as Config>::EvaluationPrice::get(),
);
}
......
......@@ -118,13 +118,12 @@ where
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, frame_support::instances::Instance1>::DistanceIsInvalid.into()),
pallet_distance::DistanceStatus::Pending => Err(pallet_duniter_wot::Error::<T, frame_support::instances::Instance1>::DistanceEvaluationPending.into()),
},
None => Err(pallet_duniter_wot::Error::<T, frame_support::instances::Instance1>::DistanceEvaluationNotRequested.into()),
match pallet_distance::Pallet::<T>::valid_evaluation_result(idty_id) {
Some(()) => Ok(()),
None => match pallet_distance::Pallet::<T>::pending_evaluation_request(idty_id) {
Some(_) => Err(pallet_duniter_wot::Error::<T, frame_support::instances::Instance1>::DistanceEvaluationPending.into()),
None => Err(pallet_duniter_wot::Error::<T, frame_support::instances::Instance1>::DistanceEvaluationNotRequested.into()),
}
}
}
}
......@@ -146,9 +145,8 @@ macro_rules! impl_benchmark_setup_handler {
idty_id: &IdtyIndex,
account: &<T as frame_system::Config>::AccountId,
) -> () {
let _ = pallet_distance::Pallet::<T>::do_set_distance_status(
*idty_id,
Some((account.clone(), pallet_distance::DistanceStatus::Valid)),
let _ = pallet_distance::Pallet::<T>::do_valid_distance_status(
*idty_id
);
}
fn add_cert(issuer: &IdtyIndex, receiver: &IdtyIndex) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment