pallet_encointer_reputation_commitments/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
18
19use core::marker::PhantomData;
20use encointer_primitives::{
21 communities::CommunityIdentifier,
22 reputation_commitments::{DescriptorType, PurposeIdType},
23 scheduler::{CeremonyIndexType, CeremonyPhaseType},
24};
25use frame_system::{self as frame_system, ensure_signed, pallet_prelude::OriginFor};
26use log::info;
27pub use pallet::*;
28use pallet_encointer_scheduler::OnCeremonyPhaseChange;
29use sp_core::H256;
30use sp_std::convert::TryInto;
31pub use weights::WeightInfo;
32
33const LOG: &str = "encointer";
35
36#[cfg(feature = "runtime-benchmarks")]
37mod benchmarking;
38#[cfg(test)]
39mod mock;
40#[cfg(test)]
41mod tests;
42
43mod weights;
44#[frame_support::pallet]
45pub mod pallet {
46 use super::*;
47 use frame_support::pallet_prelude::*;
48
49 #[pallet::pallet]
50 pub struct Pallet<T>(PhantomData<T>);
51
52 #[pallet::config]
53 pub trait Config:
54 frame_system::Config
55 + pallet_encointer_ceremonies::Config
56 + pallet_encointer_communities::Config
57 {
58 #[allow(deprecated)]
59 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
60 type WeightInfo: WeightInfo;
61 }
62
63 #[pallet::call]
64 impl<T: Config> Pallet<T> {
65 #[pallet::call_index(0)]
66 #[pallet::weight((<T as Config>::WeightInfo::register_purpose(), DispatchClass::Normal, Pays::Yes))]
67 pub fn register_purpose(
68 origin: OriginFor<T>,
69 descriptor: DescriptorType,
70 ) -> DispatchResultWithPostInfo {
71 ensure_signed(origin)?;
72 Self::do_register_purpose(descriptor)?;
73 Ok(().into())
74 }
75
76 #[pallet::call_index(1)]
77 #[pallet::weight((<T as Config>::WeightInfo::commit_reputation(), DispatchClass::Normal, Pays::Yes))]
78 pub fn commit_reputation(
79 origin: OriginFor<T>,
80 cid: CommunityIdentifier,
81 cindex: CeremonyIndexType,
82 purpose: PurposeIdType,
83 commitment_hash: Option<H256>,
84 ) -> DispatchResultWithPostInfo {
85 let from = ensure_signed(origin)?;
86 Self::do_commit_reputation(&from, cid, cindex, purpose, commitment_hash)?;
87 Ok(().into())
88 }
89 }
90
91 impl<T: Config> Pallet<T> {
92 pub fn do_register_purpose(descriptor: DescriptorType) -> Result<PurposeIdType, Error<T>> {
93 let current_id = Self::current_purpose_id();
94 let next_id = current_id.checked_add(1).ok_or(<Error<T>>::PurposeRegistryOverflow)?;
95
96 <CurrentPurposeId<T>>::put(next_id);
97 <Purposes<T>>::insert(current_id, descriptor.clone());
98 info!(target: LOG, "commitment purpose registered: {current_id:?}, {descriptor:?}");
99 Self::deposit_event(Event::RegisteredCommitmentPurpose(current_id, descriptor));
100 Ok(current_id)
101 }
102
103 pub fn do_commit_reputation(
104 account: &T::AccountId,
105 cid: CommunityIdentifier,
106 cindex: CeremonyIndexType,
107 purpose: PurposeIdType,
108 commitment_hash: Option<H256>,
109 ) -> Result<(), Error<T>> {
110 if !<Purposes<T>>::contains_key(purpose) {
111 return Err(<Error<T>>::InexistentPurpose);
112 }
113
114 if !<pallet_encointer_ceremonies::Pallet<T>>::participant_reputation(
115 (cid, cindex),
116 account,
117 )
118 .is_verified()
119 {
120 return Err(<Error<T>>::NoReputation);
121 }
122
123 if <Commitments<T>>::contains_key((cid, cindex), (purpose, &account)) {
124 return Err(<Error<T>>::AlreadyCommitted);
125 }
126
127 <Commitments<T>>::insert((cid, cindex), (purpose, &account), commitment_hash);
128 info!(
129 target: LOG,
130 "{:?} commited reputation for cid {:?} at cindex {:?} for purposed id {:?}",
131 account.clone(),
132 cid,
133 cindex,
134 purpose
135 );
136 Self::deposit_event(Event::CommitedReputation(
137 cid,
138 cindex,
139 purpose,
140 account.clone(),
141 commitment_hash,
142 ));
143 Ok(())
144 }
145
146 #[allow(deprecated)]
147 pub fn purge_registry(cindex: CeremonyIndexType) {
148 let cids = <pallet_encointer_communities::Pallet<T>>::community_identifiers();
149 for cid in cids.into_iter() {
150 <Commitments<T>>::remove_prefix((cid, cindex), None);
151 }
152 info!(target: LOG, "commitment registry purged at cindex {cindex:?}");
153 Self::deposit_event(Event::CommitmentRegistryPurged(cindex));
154 }
155 }
156
157 #[pallet::event]
158 #[pallet::generate_deposit(pub(super) fn deposit_event)]
159 pub enum Event<T: Config> {
160 RegisteredCommitmentPurpose(PurposeIdType, DescriptorType),
162 CommitedReputation(
164 CommunityIdentifier,
165 CeremonyIndexType,
166 PurposeIdType,
167 T::AccountId,
168 Option<H256>,
169 ),
170 CommitmentRegistryPurged(CeremonyIndexType),
172 }
173
174 #[pallet::error]
175 #[derive(PartialEq)]
176 pub enum Error<T> {
177 AlreadyCommitted,
179 NoReputation,
181 PurposeRegistryOverflow,
183 InexistentPurpose,
185 }
186
187 #[pallet::storage]
188 #[pallet::getter(fn current_purpose_id)]
189 pub(super) type CurrentPurposeId<T: Config> = StorageValue<_, PurposeIdType, ValueQuery>;
190
191 #[pallet::storage]
192 #[pallet::getter(fn purposes)]
193 pub(super) type Purposes<T: Config> =
194 StorageMap<_, Identity, PurposeIdType, DescriptorType, ValueQuery>;
195
196 #[pallet::storage]
197 #[pallet::getter(fn commitments)]
198 pub(super) type Commitments<T: Config> = StorageDoubleMap<
199 _,
200 Blake2_128Concat,
201 (CommunityIdentifier, CeremonyIndexType),
202 Identity,
203 (PurposeIdType, T::AccountId),
204 Option<H256>,
205 ValueQuery,
206 >;
207}
208
209impl<T: Config> OnCeremonyPhaseChange for Pallet<T> {
210 fn on_ceremony_phase_change(new_phase: CeremonyPhaseType) {
211 match new_phase {
212 CeremonyPhaseType::Assigning => {},
213 CeremonyPhaseType::Attesting => {},
214 CeremonyPhaseType::Registering => {
215 let reputation_lifetime =
216 <pallet_encointer_ceremonies::Pallet<T>>::reputation_lifetime();
217 let cindex = <pallet_encointer_scheduler::Pallet<T>>::current_ceremony_index();
218 if cindex > reputation_lifetime {
221 Self::purge_registry(
222 cindex.saturating_sub(reputation_lifetime).saturating_sub(1),
223 );
224 }
225 },
226 }
227 }
228}