pallet_permission0/
ext.rs1use 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
22impl<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
119pub(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
128pub(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
154pub 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 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}