diff --git a/pallets/distance/src/lib.rs b/pallets/distance/src/lib.rs index 23277e89b1bda102ccab6ba2b94d2caac5c42d7b..4acdb34ef6a194534a3432422690780bb08ce1dd 100644 --- a/pallets/distance/src/lib.rs +++ b/pallets/distance/src/lib.rs @@ -190,6 +190,8 @@ pub mod pallet { TooManyEvaluators, /// Evaluation result has a wrong length. WrongResultLength, + /// Targeted distance evaluation request is only possible for an unvalidated identity + DistanceRequestNotAllowed, } #[pallet::hooks] @@ -254,10 +256,10 @@ pub mod pallet { let target_idty = pallet_identity::Identities::<T>::get(target).ok_or(Error::<T>::NoIdentity)?; - // check that target is unconfirmed + // check that target is unvalidated ensure!( - target_idty.status == pallet_identity::IdtyStatus::Unconfirmed, - Error::<T>::NoIdentity + target_idty.status == pallet_identity::IdtyStatus::Unvalidated, + Error::<T>::DistanceRequestNotAllowed ); // check that no distance status is already there @@ -488,29 +490,45 @@ pub mod pallet { > = Pallet::<T>::take_current_pool(index); for (idty, median_acc) in current_pool.evaluations.into_iter() { if let Some(median_result) = median_acc.get_median() { + // get result of the comutation let median = match median_result { MedianResult::One(m) => m, MedianResult::Two(m1, m2) => m1 + (m2 - m1) / 2, // Avoid overflow (since max is 1) }; + // flag to capture distance status + 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() { - if median >= T::MinAccessibleReferees::get() { + // 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; - } else { + } + // 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 + if is_distance_status_valid { + T::OnValidDistanceStatus::on_valid_distance_status(idty); + } } else if let Some((account_id, _status)) = IdentityDistanceStatus::<T>::take(idty) { + // when no result is published, all funds are unreserved <T as Config>::Currency::unreserve( &account_id, <T as Config>::EvaluationPrice::get(), diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs index 04a02e808c373dc24b1c407abac3dc15da46297a..a33bec74339e7d07cd80ccd27d3101246535e024 100644 --- a/pallets/duniter-wot/src/lib.rs +++ b/pallets/duniter-wot/src/lib.rs @@ -367,7 +367,14 @@ impl<T: Config<I> + pallet_distance::Config, I: 'static> IdtyStatus::Unconfirmed => { /* should not happen */ } IdtyStatus::Unvalidated | IdtyStatus::NotMember => { // ok to fail - let _ = pallet_membership::Pallet::<T, I>::try_claim_membership(idty_index); + let r = pallet_membership::Pallet::<T, I>::try_claim_membership(idty_index); + sp_std::if_std! { + if let Err(e) = r { + print!("failed to claim identity when distance status was ok: "); + println!("{:?}", idty_index); + println!("reason: {:?}", e); + } + } } IdtyStatus::Member => { // ok to fail @@ -378,6 +385,9 @@ impl<T: Config<I> + pallet_distance::Config, I: 'static> } 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); + } } } } diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs index 442b635d69063afb821babbde3305bb9625accef..c465ff75c9a98a16d419ff53461e8baa8fb9746e 100644 --- a/runtime/gdev/tests/integration_tests.rs +++ b/runtime/gdev/tests/integration_tests.rs @@ -376,6 +376,99 @@ fn test_validate_identity_when_claim() { }); } +/// 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.into(), + 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.into(), + 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!(Cert::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(), + 2, + 5 + )); + assert_ok!(Cert::add_cert( + frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(), + 3, + 5 + )); + // charlie also request distance evaluation for eve + assert_ok!(Distance::request_distance_evaluation_for( + frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(), + 5 + )); + assert_eq!( + Distance::identity_distance_status(5), + Some(( + AccountKeyring::Charlie.to_account_id(), + pallet_distance::DistanceStatus::Pending + )) + ); + + run_to_block(51); // Pass 2 sessions + assert_ok!(Distance::force_update_evaluation( + frame_system::RawOrigin::Root.into(), + AccountKeyring::Alice.to_account_id(), + pallet_distance::ComputationResult { + distances: vec![Perbill::one()], + } + )); + run_to_block(75); // Pass 1 session + + // eve should not even have to claim her membership + System::assert_has_event(RuntimeEvent::Membership( + pallet_membership::Event::MembershipAdded { + member: 5, + expire_on: 75 + + <Runtime as pallet_membership::Config<Instance1>>::MembershipPeriod::get( + ), + }, + )); + }); +} + /// test membership expiry #[test] fn test_membership_expiry() {