pallet_permission0/ext/
curator_impl.rs

1use 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        // Root permission is not delegateable
110        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            // We do not check for the ROOT curator permission at the moment.
146            // This is mainly due to our use of a SUDO key at the moment.
147            // Once we move away from centralized chain management, a ROOT curator
148            // will be appointed by the system.
149
150            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}