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, permission::add_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().unwrap_or_default() < 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(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 #[cfg(feature = "runtime-benchmarks")]
87 fn force_curator(recipient: &T::AccountId, flags: ApiCuratorPermissions) {
88 use polkadot_sdk::frame_system::RawOrigin;
89
90 let mut map: BoundedBTreeMap<
91 Option<PermissionId>,
92 CuratorPermissions,
93 T::MaxCuratorSubpermissionsPerPermission,
94 > = BoundedBTreeMap::new();
95 map.try_insert(None, CuratorPermissions::from_bits_truncate(flags.bits()))
96 .unwrap();
97
98 delegate_curator_permission_impl(
99 RawOrigin::Root.into(),
100 recipient.clone(),
101 map,
102 None,
103 PermissionDuration::<T>::Indefinite,
104 RevocationTerms::<T>::Irrevocable,
105 1,
106 )
107 .unwrap();
108 }
109}
110
111pub fn delegate_curator_permission_impl<T: Config>(
112 delegator: OriginFor<T>,
113 recipient: T::AccountId,
114 mut flags: BoundedBTreeMap<
115 Option<PermissionId>,
116 CuratorPermissions,
117 T::MaxCuratorSubpermissionsPerPermission,
118 >,
119 cooldown: Option<BlockNumberFor<T>>,
120 duration: PermissionDuration<T>,
121 revocation: RevocationTerms<T>,
122 instances: u32,
123) -> Result<PermissionId, DispatchError> {
124 let signer = ensure_signed_or_root(delegator)?;
125
126 let is_root = signer.is_none();
127 let delegator =
128 signer.unwrap_or_else(|| <T as Config>::PalletId::get().into_account_truncating());
129
130 let mut parents = polkadot_sdk::sp_std::vec::Vec::with_capacity(flags.len());
131
132 for (parent_pid, flags) in &mut flags {
133 flags.remove(CuratorPermissions::ROOT);
135
136 ensure!(!flags.is_empty(), Error::<T>::InvalidCuratorPermissions);
137
138 if let Some(parent_pid) = parent_pid {
139 let Some(parent) = Permissions::<T>::get(parent_pid) else {
140 return Err(Error::<T>::ParentPermissionNotFound.into());
141 };
142
143 let PermissionScope::Curator(scope) = &parent.scope else {
144 return Err(Error::<T>::UnsupportedPermissionType.into());
145 };
146
147 ensure!(
148 scope.recipient == delegator,
149 Error::<T>::NotPermissionRecipient
150 );
151
152 ensure!(
153 scope.has_permission(*flags),
154 Error::<T>::InvalidCuratorPermissions
155 );
156
157 ensure!(
158 instances <= parent.available_instances().unwrap_or_default(),
159 Error::<T>::NotEnoughInstances
160 );
161
162 ensure!(
163 RevocationTerms::<T>::is_weaker(&parent.revocation, &revocation),
164 Error::<T>::RevocationTermsTooStrong
165 );
166
167 parents.push(*parent_pid);
168 } else {
169 ensure!(is_root, Error::<T>::NotPermissionRecipient);
175 }
176 }
177
178 let scope = PermissionScope::Curator(CuratorScope {
179 recipient: recipient.clone(),
180 flags,
181 cooldown,
182 children: Default::default(),
183 max_instances: 1,
184 });
185 let permission_id = generate_permission_id::<T>(&delegator, &scope)?;
186
187 for parent in parents {
188 Permissions::<T>::mutate_extant(parent, |parent| {
189 if let Some(children) = parent.children_mut() {
190 children.try_insert(permission_id).ok()
191 } else {
192 Some(false)
193 }
194 })
195 .ok_or(Error::<T>::TooManyChildren)?;
196 }
197
198 let contract = PermissionContract::<T>::new(
199 delegator,
200 scope,
201 duration,
202 revocation,
203 crate::EnforcementAuthority::None,
204 );
205
206 Permissions::<T>::insert(permission_id, &contract);
207
208 add_permission_indices::<T>(
209 &contract.delegator,
210 core::iter::once(&recipient),
211 permission_id,
212 )?;
213
214 <Pallet<T>>::deposit_event(Event::PermissionDelegated {
215 delegator: contract.delegator,
216 permission_id,
217 });
218
219 Ok(permission_id)
220}
221
222pub fn execute_permission_impl<T: Config>(permission_id: &PermissionId) -> DispatchResult {
223 if let Some(mut contract) = Permissions::<T>::get(permission_id) {
224 contract.tick_execution(<frame_system::Pallet<T>>::block_number())?;
225 Permissions::<T>::insert(permission_id, &contract);
226 }
227
228 Ok(())
229}