1use codec::{Decode, Encode, MaxEncodedLen};
2use pallet_torus0_api::NamespacePath;
3use polkadot_sdk::{
4 frame_support::{
5 dispatch::DispatchResult, ensure, CloneNoBound, DebugNoBound, DefaultNoBound, EqNoBound,
6 PartialEqNoBound,
7 },
8 frame_system::{self, ensure_signed_or_root},
9 polkadot_sdk_frame::prelude::{BlockNumberFor, OriginFor},
10 sp_core::{H256, U256},
11 sp_runtime::{
12 traits::{BlakeTwo256, Hash},
13 BoundedBTreeMap, BoundedVec, DispatchError, Percent,
14 },
15 sp_std::vec::Vec,
16};
17use scale_info::TypeInfo;
18
19use crate::*;
20
21pub use curator::{CuratorPermissions, CuratorScope};
22pub use emission::{DistributionControl, EmissionAllocation, EmissionScope};
23
24pub mod curator;
25pub mod emission;
26
27pub type PermissionId = H256;
29
30pub fn generate_permission_id<T: Config>(
33 grantor: &T::AccountId,
34 grantee: &T::AccountId,
35 scope: &PermissionScope<T>,
36) -> Result<PermissionId, DispatchError> {
37 let mut data = grantor.encode();
38 data.extend(grantee.encode());
39 data.extend(scope.encode());
40
41 data.extend(<frame_system::Pallet<T>>::block_number().encode());
42
43 if let Some(extrinsic_index) = <frame_system::Pallet<T>>::extrinsic_index() {
44 data.extend(extrinsic_index.encode());
45 }
46
47 let id = BlakeTwo256::hash(&data);
48 ensure!(
49 !Permissions::<T>::contains_key(id),
50 Error::<T>::DuplicatePermissionInBlock
51 );
52
53 Ok(id)
54}
55
56#[derive(Encode, Decode, CloneNoBound, TypeInfo, MaxEncodedLen, DebugNoBound)]
57#[scale_info(skip_type_params(T))]
58pub struct PermissionContract<T: Config> {
59 pub grantor: T::AccountId,
60 pub grantee: T::AccountId,
61 pub scope: PermissionScope<T>,
62 pub duration: PermissionDuration<T>,
63 pub revocation: RevocationTerms<T>,
64 pub enforcement: EnforcementAuthority<T>,
66 pub last_execution: Option<BlockNumberFor<T>>,
68 pub execution_count: u32,
70 pub parent: Option<PermissionId>,
72 pub created_at: BlockNumberFor<T>,
73}
74
75impl<T: Config> PermissionContract<T> {
76 pub fn is_expired(&self, current_block: BlockNumberFor<T>) -> bool {
77 match self.duration {
78 PermissionDuration::UntilBlock(block) => current_block > block,
79 PermissionDuration::Indefinite => false,
80 }
81 }
82
83 pub fn revoke(self, origin: OriginFor<T>, permission_id: H256) -> DispatchResult {
84 let who = ensure_signed_or_root(origin)?.filter(|who| who != &self.grantee);
86
87 let grantor = self.grantor.clone();
88 let grantee = self.grantee.clone();
89
90 if let Some(who) = &who {
92 match &self.revocation {
93 RevocationTerms::RevocableByGrantor => {
94 ensure!(who == &grantor, Error::<T>::NotAuthorizedToRevoke)
95 }
96 RevocationTerms::RevocableByArbiters {
97 accounts,
98 required_votes,
99 } if accounts.contains(who) => {
100 let votes = RevocationTracking::<T>::get(permission_id)
101 .into_iter()
102 .filter(|id| id != who)
103 .filter(|id| accounts.contains(id))
104 .count();
105 if votes.saturating_add(1) < *required_votes as usize {
106 return RevocationTracking::<T>::mutate(permission_id, |votes| {
107 votes
108 .try_insert(who.clone())
109 .map_err(|_| Error::<T>::TooManyRevokers)?;
110 Ok(())
111 });
112 }
113 }
114 RevocationTerms::RevocableByArbiters { .. } => {
115 return Err(Error::<T>::NotAuthorizedToRevoke.into())
116 }
117 RevocationTerms::RevocableAfter(block) if who == &grantor => ensure!(
118 <frame_system::Pallet<T>>::block_number() >= *block,
119 Error::<T>::NotAuthorizedToRevoke
120 ),
121 RevocationTerms::RevocableAfter(_) => {
122 return Err(Error::<T>::NotAuthorizedToRevoke.into())
123 }
124 RevocationTerms::Irrevocable => {
125 return Err(Error::<T>::NotAuthorizedToRevoke.into())
126 }
127 };
128 }
129
130 self.cleanup(permission_id);
131
132 <Pallet<T>>::deposit_event(Event::PermissionRevoked {
133 grantor,
134 grantee,
135 revoked_by: who,
136 permission_id,
137 });
138
139 Ok(())
140 }
141
142 pub fn update_enforcement(
147 mut self,
148 permission_id: H256,
149 enforcement: EnforcementAuthority<T>,
150 ) -> DispatchResult {
151 let (controllers, required_votes) = match enforcement {
152 EnforcementAuthority::None => {
153 self.enforcement = EnforcementAuthority::None;
154 Permissions::<T>::insert(permission_id, self);
155
156 let _ = EnforcementTracking::<T>::clear_prefix(permission_id, u32::MAX, None);
157
158 Pallet::<T>::deposit_event(Event::EnforcementAuthoritySet {
159 permission_id,
160 controllers_count: 0,
161 required_votes: 0,
162 });
163
164 return Ok(());
165 }
166 EnforcementAuthority::ControlledBy {
167 controllers,
168 required_votes,
169 } => (controllers, required_votes),
170 };
171
172 ensure!(
173 !controllers.is_empty(),
174 Error::<T>::InvalidNumberOfControllers
175 );
176 ensure!(required_votes > 0, Error::<T>::InvalidNumberOfControllers);
177 ensure!(
178 required_votes as usize <= controllers.len(),
179 Error::<T>::InvalidNumberOfControllers
180 );
181
182 let event = Event::EnforcementAuthoritySet {
183 permission_id,
184 controllers_count: controllers.len() as u32,
185 required_votes,
186 };
187
188 self.enforcement = EnforcementAuthority::ControlledBy {
189 controllers,
190 required_votes,
191 };
192 Permissions::<T>::insert(permission_id, self);
193
194 let _ = EnforcementTracking::<T>::clear_prefix(permission_id, u32::MAX, None);
195
196 <Pallet<T>>::deposit_event(event);
197
198 Ok(())
199 }
200
201 fn cleanup(self, permission_id: H256) {
202 crate::remove_permission_from_indices::<T>(&self.grantor, &self.grantee, permission_id);
203
204 Permissions::<T>::remove(permission_id);
205 RevocationTracking::<T>::remove(permission_id);
206 let _ = EnforcementTracking::<T>::clear_prefix(permission_id, u32::MAX, None);
207
208 match self.scope {
209 PermissionScope::Emission(emission) => {
210 emission.cleanup(permission_id, &self.last_execution, &self.grantor)
211 }
212 PermissionScope::Curator(curator) => {
213 curator.cleanup(permission_id, &self.last_execution, &self.grantor)
214 }
215 PermissionScope::Namespace(_) => {
216 }
218 }
219 }
220
221 pub fn is_updatable(&self) -> bool {
222 let current_block = frame_system::Pallet::<T>::block_number();
223
224 match &self.revocation {
225 RevocationTerms::RevocableByGrantor => true,
226 RevocationTerms::RevocableAfter(block) => ¤t_block > block,
227 _ => false,
228 }
229 }
230}
231
232#[derive(Encode, Decode, CloneNoBound, TypeInfo, MaxEncodedLen, DebugNoBound)]
234#[scale_info(skip_type_params(T))]
235pub enum PermissionScope<T: Config> {
236 Emission(EmissionScope<T>),
237 Curator(CuratorScope<T>),
238 Namespace(NamespaceScope<T>),
239}
240
241#[derive(Encode, Decode, CloneNoBound, TypeInfo, MaxEncodedLen, DebugNoBound)]
243#[scale_info(skip_type_params(T))]
244pub struct NamespaceScope<T: Config> {
245 pub paths: BoundedBTreeSet<NamespacePath, T::MaxNamespacesPerPermission>,
247}
248
249#[derive(
250 Encode, Decode, CloneNoBound, PartialEqNoBound, EqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
251)]
252#[scale_info(skip_type_params(T))]
253pub enum PermissionDuration<T: Config> {
254 UntilBlock(BlockNumberFor<T>),
256 Indefinite,
258}
259
260#[derive(
261 Encode, Decode, CloneNoBound, PartialEqNoBound, EqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
262)]
263#[scale_info(skip_type_params(T))]
264pub enum RevocationTerms<T: Config> {
265 Irrevocable,
267 RevocableByGrantor,
269 RevocableByArbiters {
271 accounts: BoundedVec<T::AccountId, T::MaxRevokersPerPermission>,
272 required_votes: u32,
273 },
274 RevocableAfter(BlockNumberFor<T>),
276}
277
278#[derive(
280 Encode, Decode, CloneNoBound, PartialEqNoBound, EqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
281)]
282#[scale_info(skip_type_params(T))]
283pub enum EnforcementReferendum {
284 EmissionAccumulation(bool),
286 Execution,
288}
289
290#[derive(
292 Encode,
293 Decode,
294 CloneNoBound,
295 PartialEqNoBound,
296 EqNoBound,
297 TypeInfo,
298 MaxEncodedLen,
299 DebugNoBound,
300 DefaultNoBound,
301)]
302#[scale_info(skip_type_params(T))]
303pub enum EnforcementAuthority<T: Config> {
304 #[default]
306 None,
307 ControlledBy {
309 controllers: BoundedVec<T::AccountId, T::MaxControllersPerPermission>,
310 required_votes: u32,
311 },
312}
313
314pub(crate) fn do_auto_permission_execution<T: Config>(current_block: BlockNumberFor<T>) {
316 if <BlockNumberFor<T> as Into<U256>>::into(current_block)
318 .checked_rem(10.into())
319 .unwrap_or_default()
320 != U256::zero()
321 {
322 return;
323 }
324
325 let permissions: Vec<_> = Permissions::<T>::iter().collect();
326 let mut expired = Vec::with_capacity(permissions.len());
327
328 for (permission_id, contract) in Permissions::<T>::iter() {
329 #[allow(clippy::single_match)]
330 match &contract.scope {
331 PermissionScope::Emission(emission_scope) => {
332 emission::do_auto_distribution(
333 emission_scope,
334 permission_id,
335 current_block,
336 &contract,
337 );
338 }
339 _ => (),
340 }
341
342 if contract.is_expired(current_block) {
343 expired.push((permission_id, contract));
344 }
345 }
346
347 for (permission_id, contract) in expired {
348 let grantor = contract.grantor.clone();
349 let grantee = contract.grantee.clone();
350
351 contract.cleanup(permission_id);
352
353 <Pallet<T>>::deposit_event(Event::PermissionExpired {
354 grantor,
355 grantee,
356 permission_id,
357 });
358 }
359}