diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs index 255b6c48c8efd628e782530947304177bd938a30..b64aebf2233bbe18aea9dada4ffb488849f1b81f 100644 --- a/pallets/certification/src/lib.rs +++ b/pallets/certification/src/lib.rs @@ -221,7 +221,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn certs_by_receiver)] pub type StorageCertsByReceiver<T: Config<I>, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::IdtyIndex, Vec<T::IdtyIndex>, OptionQuery>; + StorageMap<_, Blake2_128Concat, T::IdtyIndex, Vec<T::IdtyIndex>, ValueQuery>; /// Certifications removable on #[pallet::storage] diff --git a/pallets/duniter-wot/Cargo.toml b/pallets/duniter-wot/Cargo.toml index e23031d7e3d9224681b513320308250685db5a4c..43a8cb644f4de425c13bd2f6526d3b37ca1eb475 100644 --- a/pallets/duniter-wot/Cargo.toml +++ b/pallets/duniter-wot/Cargo.toml @@ -22,6 +22,7 @@ std = [ 'pallet-membership/std', 'serde', 'sp-core/std', + 'sp-io/std', 'sp-membership/std', 'sp-runtime/std', 'sp-std/std', @@ -69,6 +70,11 @@ default-features = false git = 'https://github.com/librelois/substrate.git' branch = 'duniter-monthly-2022-01' +[dependencies.sp-io] +default-features = false +git = 'https://github.com/librelois/substrate.git' +branch = 'duniter-monthly-2022-01' + [dependencies.sp-runtime] default-features = false git = 'https://github.com/librelois/substrate.git' diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs index 42f25c2acd66a0b98c7a79ecf1ad60e48b6274c8..66750e68545460b545460fc3ad59b1c420f33f53 100644 --- a/pallets/duniter-wot/src/lib.rs +++ b/pallets/duniter-wot/src/lib.rs @@ -33,6 +33,7 @@ pub use pallet::*; pub use types::*; use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::BlockNumberFor; use frame_system::RawOrigin; use pallet_certification::traits::SetNextIssuableOn; use pallet_identity::{IdtyEvent, IdtyStatus}; @@ -46,6 +47,7 @@ pub mod pallet { use super::*; use frame_support::dispatch::UnfilteredDispatchable; use frame_support::traits::StorageVersion; + use sp_std::vec::Vec; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -70,6 +72,34 @@ pub mod pallet { type MinCertForCreateIdtyRight: Get<u32>; } + // STORAGE // + + #[pallet::storage] + #[pallet::getter(fn wot_diffs)] + pub(super) type WotDiffs<T: Config<I>, I: 'static = ()> = + StorageValue<_, Vec<WotDiff>, ValueQuery>; + + // HOOKS // + + #[pallet::hooks] + impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> { + fn on_initialize(_: T::BlockNumber) -> Weight { + 0 + } + fn on_finalize(block_number: T::BlockNumber) { + const OFFCHAIN_WOT_DIFFS_KEY: &[u8] = b"ocw/wot-diffs/"; + let diffs = WotDiffs::<T, I>::take(); + let key = block_number.using_encoded(|encoded_block_number| { + OFFCHAIN_WOT_DIFFS_KEY + .iter() + .chain(encoded_block_number) + .copied() + .collect::<Vec<u8>>() + }); + sp_io::offchain_index::set(&key, &diffs.encode()); + } + } + // INTERNAL FUNCTIONS // impl<T: Config<I>, I: 'static> Pallet<T, I> { @@ -197,11 +227,19 @@ where { fn on_event(membership_event: &sp_membership::Event<IdtyIndex, MetaData>) -> Weight { match membership_event { - sp_membership::Event::<IdtyIndex, MetaData>::MembershipAcquired(_, _) => {} - sp_membership::Event::<IdtyIndex, MetaData>::MembershipExpired(idty_index) => { + sp_membership::Event::<IdtyIndex, MetaData>::MembershipAcquired(idty_index, _) => { + if !T::IsSubWot::get() { + WotDiffs::<T, I>::append(WotDiff::AddNode(*idty_index)); + } + } + sp_membership::Event::<IdtyIndex, MetaData>::MembershipExpired(idty_index) + | sp_membership::Event::<IdtyIndex, MetaData>::MembershipRevoked(idty_index) => { Self::dispath_idty_call(pallet_identity::Call::disable_identity { idty_index: *idty_index, }); + if !T::IsSubWot::get() { + WotDiffs::<T, I>::append(WotDiff::DisableNode(*idty_index)); + } } sp_membership::Event::<IdtyIndex, MetaData>::MembershipRenewed(_) => {} sp_membership::Event::<IdtyIndex, MetaData>::MembershipRequested(idty_index) => { @@ -222,7 +260,6 @@ where } } } - sp_membership::Event::<IdtyIndex, MetaData>::MembershipRevoked(_) => {} sp_membership::Event::<IdtyIndex, MetaData>::PendingMembershipExpired(idty_index) => { Self::dispath_idty_call(pallet_identity::Call::remove_identity { idty_index: *idty_index, @@ -257,7 +294,7 @@ impl<T: Config<I>, I: 'static> pallet_identity::traits::OnIdtyChange<T> for Pall impl<T: Config<I>, I: 'static> pallet_certification::traits::OnNewcert<IdtyIndex> for Pallet<T, I> { fn on_new_cert( - _issuer: IdtyIndex, + issuer: IdtyIndex, _issuer_issued_count: u32, receiver: IdtyIndex, receiver_received_count: u32, @@ -266,6 +303,9 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnNewcert<IdtyIndex if receiver_received_count == T::MinReceivedCertToBeAbleToIssueCert::get() { Self::do_apply_first_issuable_on(receiver); } + if T::IsSubWot::get() { + WotDiffs::<T, I>::append(WotDiff::AddLink(issuer, receiver)); + } } else if pallet_membership::Pallet::<T, I>::is_in_pending_memberships(receiver) && receiver_received_count >= T::MinCertForMembership::get() { @@ -283,6 +323,9 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnNewcert<IdtyIndex Self::dispath_idty_call(pallet_identity::Call::validate_identity { idty_index: receiver, }); + for issuer in pallet_certification::Pallet::<T, I>::certs_by_receiver(receiver) { + WotDiffs::<T, I>::append(WotDiff::AddLink(issuer, receiver)); + } } if receiver_received_count == T::MinReceivedCertToBeAbleToIssueCert::get() { @@ -297,13 +340,15 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnRemovedCert<IdtyI for Pallet<T, I> { fn on_removed_cert( - _issuer: IdtyIndex, + issuer: IdtyIndex, _issuer_issued_count: u32, receiver: IdtyIndex, receiver_received_count: u32, _expiration: bool, ) -> Weight { - if receiver_received_count < T::MinCertForMembership::get() { + if receiver_received_count < T::MinCertForMembership::get() + && pallet_membership::Pallet::<T, I>::is_member(&receiver) + { // Revoke receiver membership and disable his identity if let Err(e) = pallet_membership::Pallet::<T, I>::revoke_membership( RawOrigin::Root.into(), @@ -312,12 +357,11 @@ impl<T: Config<I>, I: 'static> pallet_certification::traits::OnRemovedCert<IdtyI sp_std::if_std! { println!("{:?}", e) } - } else { - Self::dispath_idty_call(pallet_identity::Call::disable_identity { - idty_index: receiver, - }); } } + if !T::IsSubWot::get() { + WotDiffs::<T, I>::append(WotDiff::DelLink(issuer, receiver)); + } 0 } } diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs index a688a06d7dc80edce5e06ea0ba285b2c7f94b78c..7c001e96abf2414cad86749a58dfe3bc7b063693 100644 --- a/pallets/duniter-wot/src/tests.rs +++ b/pallets/duniter-wot/src/tests.rs @@ -16,6 +16,7 @@ use crate::mock::Identity; use crate::mock::*; +use crate::WotDiff; use frame_support::assert_err; use frame_support::assert_ok; use frame_support::error::BadOrigin; @@ -94,11 +95,12 @@ fn test_create_idty_ok() { ); assert_eq!(Identity::identity(6).unwrap().status, IdtyStatus::Created); assert_eq!(Identity::identity(6).unwrap().removable_on, 4); + assert!(DuniterWot::wot_diffs().is_empty()); }); } #[test] -fn test_ud_right_achievement_ok() { +fn test_new_idty_validation() { new_test_ext(5).execute_with(|| { // Alice create Ferdie identity run_to_block(2); @@ -117,14 +119,16 @@ fn test_ud_right_achievement_ok() { 6 )); + // Ferdie is not yet validated, so there should be no wot diff + assert!(DuniterWot::wot_diffs().is_empty()); + // Bob should be able to certify Ferdie run_to_block(4); assert_ok!(Cert::add_cert(Origin::signed(2), 2, 6)); let events = System::events(); - // 3 events should have occurred: NewCert, MembershipAcquired, IdtyValidated and IdtyAcquireRight + // 3 events should have occurred: NewCert, MembershipAcquired and IdtyValidated assert_eq!(events.len(), 3); - //println!("{:?}", events[2]); assert_eq!( events[0], EventRecord { @@ -156,6 +160,17 @@ fn test_ud_right_achievement_ok() { topics: vec![], } ); + + // Ferdie has just been validated, so the wot diff should contain her entry and all her + // certifications + assert_eq!( + DuniterWot::wot_diffs(), + vec![ + WotDiff::AddNode(6), + WotDiff::AddLink(1, 6), + WotDiff::AddLink(2, 6) + ] + ); }); } @@ -228,6 +243,7 @@ fn test_idty_membership_expire_them_requested() { topics: vec![], } ); + assert_eq!(DuniterWot::wot_diffs(), vec![WotDiff::DisableNode(3),]); // Charlie's identity should be disabled at block #5 assert_eq!(Identity::identity(3).unwrap().status, IdtyStatus::Disabled); @@ -268,5 +284,7 @@ fn test_idty_membership_expire_them_requested() { topics: vec![], } ); + + assert_eq!(DuniterWot::wot_diffs(), vec![WotDiff::AddNode(3),]); }); } diff --git a/pallets/duniter-wot/src/types.rs b/pallets/duniter-wot/src/types.rs index b6b0675ae5c2e52b83a2923f8ad8f138dc796119..fc72ef21a5b4821325bdac30de0a0a2c9011bb7e 100644 --- a/pallets/duniter-wot/src/types.rs +++ b/pallets/duniter-wot/src/types.rs @@ -91,3 +91,19 @@ impl<T: Config<I>, I: 'static> EnsureOrigin<(T::Origin, IdtyIndex, IdtyIndex)> } } } + +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(codec::Decode, codec::Encode, Eq, PartialEq, TypeInfo)] +pub enum WotDiff { + AddNode(IdtyIndex), + AddPendingLink(IdtyIndex, IdtyIndex), + AddLink(IdtyIndex, IdtyIndex), + DelLink(IdtyIndex, IdtyIndex), + DisableNode(IdtyIndex), +} + +impl Default for WotDiff { + fn default() -> Self { + unreachable!() + } +}