pallet_permission0/
ext.rs

1use crate::permission::EnforcementReferendum;
2use crate::{
3    pallet, Config, EnforcementAuthority, EnforcementTracking, Error, Event, Pallet,
4    PermissionDuration, PermissionId, PermissionScope, Permissions, RevocationTerms,
5};
6use pallet_permission0_api::{
7    EnforcementAuthority as ApiEnforcementAuthority, Permission0Api,
8    PermissionDuration as ApiPermissionDuration, RevocationTerms as ApiRevocationTerms,
9};
10use polkadot_sdk::{
11    frame_support::ensure,
12    frame_system::ensure_signed_or_root,
13    polkadot_sdk_frame::prelude::{BlockNumberFor, OriginFor},
14    sp_runtime::{DispatchError, DispatchResult},
15};
16
17pub mod curator_impl;
18pub mod emission_impl;
19pub mod namespace_impl;
20
21/// Implementation of the Permission0Api trait to be used externally
22impl<T: Config> Permission0Api<OriginFor<T>> for pallet::Pallet<T> {
23    fn permission_exists(id: &PermissionId) -> bool {
24        Permissions::<T>::contains_key(id)
25    }
26
27    fn revoke_permission(who: OriginFor<T>, permission_id: &PermissionId) -> DispatchResult {
28        revoke_permission_impl::<T>(who, permission_id)
29    }
30
31    fn execute_permission(who: OriginFor<T>, permission_id: &PermissionId) -> DispatchResult {
32        execute_permission_impl::<T>(who, permission_id)
33    }
34}
35
36fn translate_duration<T: Config>(
37    duration: ApiPermissionDuration<BlockNumberFor<T>>,
38) -> Result<PermissionDuration<T>, DispatchError> {
39    Ok(match duration {
40        ApiPermissionDuration::UntilBlock(block) => {
41            let current_block = <polkadot_sdk::frame_system::Pallet<T>>::block_number();
42            ensure!(block > current_block, Error::<T>::InvalidInterval);
43
44            PermissionDuration::UntilBlock(block)
45        }
46        ApiPermissionDuration::Indefinite => PermissionDuration::Indefinite,
47    })
48}
49
50fn translate_revocation_terms<T: Config>(
51    revocation: ApiRevocationTerms<T::AccountId, BlockNumberFor<T>>,
52) -> Result<RevocationTerms<T>, DispatchError> {
53    let revocation = match revocation {
54        ApiRevocationTerms::Irrevocable => RevocationTerms::Irrevocable,
55        ApiRevocationTerms::RevocableByGrantor => RevocationTerms::RevocableByGrantor,
56        ApiRevocationTerms::RevocableByArbiters {
57            accounts,
58            required_votes,
59        } => {
60            ensure!(required_votes > 0, Error::<T>::InvalidNumberOfRevokers);
61            ensure!(!accounts.is_empty(), Error::<T>::InvalidNumberOfRevokers);
62
63            ensure!(
64                required_votes as usize <= accounts.len(),
65                Error::<T>::InvalidNumberOfRevokers
66            );
67
68            RevocationTerms::RevocableByArbiters {
69                accounts: accounts
70                    .try_into()
71                    .map_err(|_| crate::Error::<T>::TooManyRevokers)?,
72                required_votes,
73            }
74        }
75        ApiRevocationTerms::RevocableAfter(block) => {
76            let current_block = <polkadot_sdk::frame_system::Pallet<T>>::block_number();
77            ensure!(block > current_block, Error::<T>::InvalidInterval);
78
79            RevocationTerms::RevocableAfter(block)
80        }
81    };
82
83    Ok(revocation)
84}
85
86fn translate_enforcement_authority<T: Config>(
87    enforcement: ApiEnforcementAuthority<T::AccountId>,
88) -> Result<EnforcementAuthority<T>, DispatchError> {
89    let enforcement = match enforcement {
90        ApiEnforcementAuthority::None => EnforcementAuthority::None,
91        ApiEnforcementAuthority::ControlledBy {
92            controllers,
93            required_votes,
94        } => {
95            ensure!(required_votes > 0, Error::<T>::InvalidNumberOfControllers);
96            ensure!(
97                !controllers.is_empty(),
98                Error::<T>::InvalidNumberOfControllers
99            );
100
101            ensure!(
102                required_votes as usize <= controllers.len(),
103                Error::<T>::InvalidNumberOfControllers
104            );
105
106            EnforcementAuthority::ControlledBy {
107                controllers: controllers
108                    .try_into()
109                    .map_err(|_| crate::Error::<T>::TooManyControllers)?,
110                required_votes,
111            }
112        }
113    };
114
115    Ok(enforcement)
116}
117
118/// Revoke a permission implementation
119pub(crate) fn revoke_permission_impl<T: Config>(
120    origin: OriginFor<T>,
121    permission_id: &PermissionId,
122) -> DispatchResult {
123    let contract = Permissions::<T>::get(permission_id).ok_or(Error::<T>::PermissionNotFound)?;
124    contract.revoke(origin, *permission_id)
125}
126
127/// Execute a permission implementation
128pub(crate) fn execute_permission_impl<T: Config>(
129    who: OriginFor<T>,
130    permission_id: &PermissionId,
131) -> DispatchResult {
132    let who = ensure_signed_or_root(who)?;
133
134    let contract = Permissions::<T>::get(permission_id).ok_or(Error::<T>::PermissionNotFound)?;
135
136    let grantor = contract.grantor.clone();
137
138    ensure!(
139        who.is_none() || who.as_ref() == Some(&grantor),
140        Error::<T>::NotPermissionGrantor
141    );
142
143    match &contract.scope {
144        PermissionScope::Emission(emission_scope) => {
145            emission_impl::execute_permission_impl(permission_id, &contract, emission_scope)
146        }
147        PermissionScope::Curator(_) => curator_impl::execute_permission_impl::<T>(permission_id),
148        PermissionScope::Namespace(_) => Ok(()),
149    }
150}
151
152/// Execute a permission through enforcement authority
153pub fn enforcement_execute_permission_impl<T: Config>(
154    origin: OriginFor<T>,
155    permission_id: PermissionId,
156) -> DispatchResult {
157    let who = ensure_signed_or_root(origin)?;
158
159    let contract = Permissions::<T>::get(permission_id).ok_or(Error::<T>::PermissionNotFound)?;
160
161    // If not root, check enforcement authority
162    if let Some(who) = &who {
163        match &contract.enforcement {
164            EnforcementAuthority::None => {
165                return Err(Error::<T>::NotAuthorizedToToggle.into());
166            }
167            EnforcementAuthority::ControlledBy {
168                controllers,
169                required_votes,
170            } => {
171                ensure!(controllers.contains(who), Error::<T>::NotAuthorizedToToggle);
172
173                let referendum = EnforcementReferendum::Execution;
174                let votes = EnforcementTracking::<T>::get(permission_id, &referendum)
175                    .into_iter()
176                    .filter(|id| id != who)
177                    .filter(|id| controllers.contains(id))
178                    .count();
179
180                if votes.saturating_add(1) < *required_votes as usize {
181                    return EnforcementTracking::<T>::mutate(
182                        permission_id,
183                        referendum.clone(),
184                        |votes| {
185                            votes
186                                .try_insert(who.clone())
187                                .map_err(|_| Error::<T>::TooManyControllers)?;
188
189                            <Pallet<T>>::deposit_event(Event::EnforcementVoteCast {
190                                permission_id,
191                                voter: who.clone(),
192                                referendum,
193                            });
194
195                            Ok(())
196                        },
197                    );
198                }
199            }
200        }
201    }
202
203    match &contract.scope {
204        PermissionScope::Emission(emission_scope) => {
205            emission_impl::execute_permission_impl(&permission_id, &contract, emission_scope)?
206        }
207        PermissionScope::Curator(_) => {
208            return curator_impl::execute_permission_impl::<T>(&permission_id);
209        }
210        PermissionScope::Namespace(_) => return Ok(()),
211    }
212
213    EnforcementTracking::<T>::remove(permission_id, EnforcementReferendum::Execution);
214
215    <Pallet<T>>::deposit_event(Event::PermissionEnforcementExecuted {
216        permission_id,
217        executed_by: who,
218    });
219
220    Ok(())
221}