pallet_permission0/
ext.rs1use 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
21impl<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
118pub(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
127pub(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
152pub 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 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}