pallet_permission0/
ext.rs

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