pallet_permission0/ext/
curator_impl.rs1use crate::{
2 Config, CuratorPermissions, CuratorScope, Error, Event, Pallet, PermissionContract,
3 PermissionDuration, PermissionScope, Permissions, PermissionsByRecipient, RevocationTerms,
4 generate_permission_id, pallet, update_permission_indices,
5};
6
7use pallet_permission0_api::{
8 CuratorPermissions as ApiCuratorPermissions, Permission0CuratorApi, PermissionId,
9};
10use polkadot_sdk::frame_system::ensure_signed_or_root;
11use polkadot_sdk::sp_core::Get;
12use polkadot_sdk::sp_runtime::BoundedBTreeMap;
13use polkadot_sdk::sp_runtime::traits::{AccountIdConversion, Saturating};
14use polkadot_sdk::{
15 frame_support::ensure,
16 frame_system,
17 polkadot_sdk_frame::prelude::{BlockNumberFor, OriginFor},
18 sp_runtime::{DispatchError, DispatchResult},
19};
20
21impl<T: Config> Permission0CuratorApi<T::AccountId, OriginFor<T>, BlockNumberFor<T>>
22 for pallet::Pallet<T>
23{
24 fn ensure_curator_permission(
25 recipient: OriginFor<T>,
26 flags: ApiCuratorPermissions,
27 ) -> Result<T::AccountId, DispatchError> {
28 let Some(recipient) = ensure_signed_or_root(recipient)? else {
29 return Ok(T::PalletId::get().into_account_truncating());
30 };
31
32 let flags = CuratorPermissions::from_bits_truncate(flags.bits());
33 let permissions = PermissionsByRecipient::<T>::get(&recipient);
34 let now = <frame_system::Pallet<T>>::block_number();
35
36 let mut cur_error = Error::<T>::PermissionNotFound;
37 for permission_id in permissions {
38 let Some(contract) = Permissions::<T>::get(permission_id) else {
39 continue;
40 };
41
42 let PermissionScope::Curator(scope) = &contract.scope else {
43 continue;
44 };
45
46 if !scope.has_permission(flags) {
47 continue;
48 }
49
50 if contract.available_instances() < 1 {
51 if !matches!(cur_error, Error::<T>::PermissionInCooldown) {
52 cur_error = Error::<T>::NotEnoughInstances;
53 }
54
55 continue;
56 }
57
58 if let Some(cooldown) = scope.cooldown
59 && let Some(last_execution) = contract.last_execution()
60 && last_execution.saturating_add(cooldown) > now
61 {
62 cur_error = Error::<T>::PermissionInCooldown;
63 continue;
64 }
65
66 return Ok(contract.recipient);
67 }
68
69 Err(cur_error.into())
70 }
71
72 fn get_curator_permission(recipient: &T::AccountId) -> Option<PermissionId> {
73 PermissionsByRecipient::<T>::get(recipient)
74 .into_iter()
75 .find_map(|permission_id| {
76 let contract = Permissions::<T>::get(permission_id)?;
77
78 if matches!(&contract.scope, PermissionScope::Curator(_)) {
79 Some(permission_id)
80 } else {
81 None
82 }
83 })
84 }
85}
86
87pub fn delegate_curator_permission_impl<T: Config>(
88 delegator: OriginFor<T>,
89 recipient: T::AccountId,
90 mut flags: BoundedBTreeMap<
91 Option<PermissionId>,
92 CuratorPermissions,
93 T::MaxCuratorSubpermissionsPerPermission,
94 >,
95 cooldown: Option<BlockNumberFor<T>>,
96 duration: PermissionDuration<T>,
97 revocation: RevocationTerms<T>,
98 instances: u32,
99) -> Result<PermissionId, DispatchError> {
100 let signer = ensure_signed_or_root(delegator)?;
101
102 let is_root = signer.is_none();
103 let delegator =
104 signer.unwrap_or_else(|| <T as Config>::PalletId::get().into_account_truncating());
105
106 let mut parents = polkadot_sdk::sp_std::vec::Vec::with_capacity(flags.len());
107
108 for (parent_pid, flags) in &mut flags {
109 flags.remove(CuratorPermissions::ROOT);
111
112 ensure!(!flags.is_empty(), Error::<T>::InvalidCuratorPermissions);
113
114 if let Some(parent_pid) = parent_pid {
115 let Some(parent) = Permissions::<T>::get(parent_pid) else {
116 return Err(Error::<T>::ParentPermissionNotFound.into());
117 };
118
119 ensure!(
120 parent.recipient == delegator,
121 Error::<T>::NotPermissionRecipient
122 );
123
124 let PermissionScope::Curator(scope) = &parent.scope else {
125 return Err(Error::<T>::UnsupportedPermissionType.into());
126 };
127
128 ensure!(
129 scope.has_permission(*flags),
130 Error::<T>::InvalidCuratorPermissions
131 );
132
133 ensure!(
134 instances <= parent.available_instances(),
135 Error::<T>::NotEnoughInstances
136 );
137
138 ensure!(
139 RevocationTerms::<T>::is_weaker(&parent.revocation, &revocation),
140 Error::<T>::RevocationTermsTooStrong
141 );
142
143 parents.push(*parent_pid);
144 } else {
145 ensure!(is_root, Error::<T>::NotPermissionRecipient);
151 }
152 }
153
154 let scope = PermissionScope::Curator(CuratorScope { flags, cooldown });
155 let permission_id = generate_permission_id::<T>(&delegator, &recipient, &scope)?;
156
157 for parent in parents {
158 Permissions::<T>::mutate_extant(parent, |parent| {
159 parent.children.try_insert(permission_id).ok()
160 })
161 .ok_or(Error::<T>::TooManyChildren)?;
162 }
163
164 let contract = PermissionContract::<T>::new(
165 delegator,
166 recipient,
167 scope,
168 duration,
169 revocation,
170 crate::EnforcementAuthority::None,
171 1,
172 );
173
174 Permissions::<T>::insert(permission_id, &contract);
175 update_permission_indices::<T>(&contract.delegator, &contract.recipient, permission_id)?;
176
177 <Pallet<T>>::deposit_event(Event::PermissionDelegated {
178 delegator: contract.delegator,
179 recipient: contract.recipient,
180 permission_id,
181 });
182
183 Ok(permission_id)
184}
185
186pub fn execute_permission_impl<T: Config>(permission_id: &PermissionId) -> DispatchResult {
187 if let Some(mut contract) = Permissions::<T>::get(permission_id) {
188 contract.tick_execution(<frame_system::Pallet<T>>::block_number())?;
189 Permissions::<T>::insert(permission_id, &contract);
190 }
191
192 Ok(())
193}