1use crate::{
2 Config, Error, Event, Pallet, PermissionContract, PermissionDuration, PermissionId,
3 PermissionScope, Permissions, PermissionsByDelegator, RevocationTerms, generate_permission_id,
4 permission::NamespaceScope, update_permission_indices,
5};
6use pallet_permission0_api::Permission0NamespacesApi;
7use pallet_torus0_api::{NamespacePath, NamespacePathInner, Torus0Api};
8use polkadot_sdk::{
9 frame_support::ensure,
10 frame_system::ensure_signed,
11 polkadot_sdk_frame::prelude::OriginFor,
12 sp_core::Get,
13 sp_runtime::{BoundedBTreeMap, BoundedBTreeSet, DispatchError},
14 sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet},
15};
16
17impl<T: Config> Permission0NamespacesApi<T::AccountId, NamespacePath> for Pallet<T> {
18 fn is_delegating_namespace(delegator: &T::AccountId, path: &NamespacePath) -> bool {
19 PermissionsByDelegator::<T>::get(delegator)
20 .iter()
21 .any(|id| {
22 Permissions::<T>::get(id)
23 .filter(|permission| {
24 if let PermissionScope::Namespace(scope) = &permission.scope {
25 for other in scope.paths.values().flat_map(|k| k.iter()) {
26 if other == path
27 || path.is_parent_of(other)
28 || other.is_parent_of(path)
29 {
30 return true;
31 }
32 }
33 }
34
35 false
36 })
37 .is_some()
38 })
39 }
40}
41
42pub fn delegate_namespace_permission_impl<T: Config>(
43 delegator: OriginFor<T>,
44 recipient: T::AccountId,
45 paths: BoundedBTreeMap<
46 Option<PermissionId>,
47 BoundedBTreeSet<NamespacePathInner, T::MaxNamespacesPerPermission>,
48 T::MaxNamespacesPerPermission,
49 >,
50 duration: PermissionDuration<T>,
51 revocation: RevocationTerms<T>,
52 instances: u32,
53) -> Result<PermissionId, DispatchError> {
54 let delegator = ensure_signed(delegator)?;
55
56 ensure!(
57 T::Torus::is_agent_registered(&delegator),
58 Error::<T>::NotRegisteredAgent
59 );
60 ensure!(
61 T::Torus::is_agent_registered(&recipient),
62 Error::<T>::NotRegisteredAgent
63 );
64
65 ensure!(instances > 0, Error::<T>::NotEnoughInstances);
66
67 let total_multi_parent = paths.keys().filter(|p| p.is_some()).count();
68 ensure!(total_multi_parent <= 1, Error::<T>::MultiParentForbidden);
69
70 let mut total_paths = 0usize;
71 let mut parents = polkadot_sdk::sp_std::vec::Vec::with_capacity(paths.len());
72
73 let paths = paths
74 .iter()
75 .map(|(pid, paths)| {
76 total_paths = total_paths.saturating_add(paths.len());
77 ensure!(
78 total_paths as u32 <= T::MaxNamespacesPerPermission::get(),
79 Error::<T>::TooManyNamespaces
80 );
81
82 let namespaces = if let Some(pid) = pid {
83 let Some(parent) = Permissions::<T>::get(pid) else {
84 return Err(Error::<T>::ParentPermissionNotFound.into());
85 };
86
87 ensure!(
88 parent.recipient == delegator,
89 Error::<T>::NotPermissionRecipient
90 );
91
92 let PermissionScope::Namespace(namespace) = &parent.scope else {
93 return Err(Error::<T>::UnsupportedPermissionType.into());
94 };
95
96 ensure!(
97 instances <= parent.available_instances(),
98 Error::<T>::NotEnoughInstances
99 );
100
101 ensure!(
102 RevocationTerms::<T>::is_weaker(&parent.revocation, &revocation),
103 Error::<T>::RevocationTermsTooStrong
104 );
105
106 parents.push(pid);
107
108 resolve_paths::<T>(&delegator, Some((*pid, namespace)), paths)
109 } else {
110 resolve_paths::<T>(&delegator, None, paths)
111 }?;
112
113 Ok((*pid, namespaces))
114 })
115 .collect::<Result<BTreeMap<_, _>, DispatchError>>()?;
116
117 let paths = paths
118 .try_into()
119 .map_err(|_| Error::<T>::TooManyNamespaces)?;
120
121 let scope = PermissionScope::Namespace(NamespaceScope { paths });
122 let permission_id = generate_permission_id::<T>(&delegator, &recipient, &scope)?;
123
124 for parent in parents {
125 Permissions::<T>::mutate_extant(parent, |parent| {
126 parent.children.try_insert(permission_id).ok()
127 })
128 .ok_or(Error::<T>::TooManyChildren)?;
129 }
130
131 let contract = PermissionContract::<T>::new(
132 delegator,
133 recipient,
134 scope,
135 duration,
136 revocation,
137 crate::EnforcementAuthority::None,
138 instances,
139 );
140
141 Permissions::<T>::insert(permission_id, &contract);
142 update_permission_indices::<T>(&contract.delegator, &contract.recipient, permission_id)?;
143
144 <Pallet<T>>::deposit_event(Event::PermissionDelegated {
145 delegator: contract.delegator,
146 recipient: contract.recipient,
147 permission_id,
148 });
149
150 Ok(permission_id)
151}
152
153const MAX_DELEGATION_DEPTH: u32 = 5;
155
156fn check_namespace_delegation_depth<T: Config>(
158 namespace_path: &NamespacePath,
159 parent_permission_id: Option<PermissionId>,
160 current_depth: u32,
161) -> Result<(), DispatchError> {
162 if current_depth > MAX_DELEGATION_DEPTH {
163 return Err(Error::<T>::DelegationDepthExceeded.into());
164 }
165
166 let Some(parent_id) = parent_permission_id else {
167 return Ok(());
168 };
169
170 let Some(parent_permission) = Permissions::<T>::get(parent_id) else {
171 return Err(Error::<T>::ParentPermissionNotFound.into());
172 };
173
174 let PermissionScope::Namespace(parent_scope) = &parent_permission.scope else {
175 return Err(Error::<T>::UnsupportedPermissionType.into());
176 };
177
178 for (grandparent_id, namespace_set) in parent_scope.paths.iter() {
179 if namespace_set.contains(namespace_path) {
180 return check_namespace_delegation_depth::<T>(
181 namespace_path,
182 *grandparent_id,
183 current_depth.saturating_add(1),
184 );
185 }
186
187 for parent_namespace in namespace_set.iter() {
188 if parent_namespace.is_parent_of(namespace_path) {
189 return check_namespace_delegation_depth::<T>(
190 parent_namespace,
191 *grandparent_id,
192 current_depth.saturating_add(1),
193 );
194 }
195 }
196 }
197
198 Ok(())
199}
200
201fn resolve_paths<T: Config>(
202 delegator: &T::AccountId,
203 parent: Option<(PermissionId, &NamespaceScope<T>)>,
204 paths: &BoundedBTreeSet<NamespacePathInner, T::MaxNamespacesPerPermission>,
205) -> Result<BoundedBTreeSet<NamespacePath, T::MaxNamespacesPerPermission>, DispatchError> {
206 let children = paths.iter().map(|path| {
207 NamespacePath::new_agent(path).map_err(|_| Error::<T>::NamespacePathIsInvalid.into())
208 });
209
210 let paths = if let Some((parent_pid, parent)) = parent {
211 let children = children.collect::<Result<BTreeSet<_>, DispatchError>>()?;
212 let matched_paths = parent
213 .paths
214 .values()
215 .flat_map(|p_path| p_path.iter())
216 .filter_map(|parent_path| {
217 if children.contains(parent_path) {
218 Some(())
219 } else {
220 let agent_name = parent_path.agent_name()?;
221 let child_path = children
222 .iter()
223 .find(|child| parent_path.is_parent_of(child))?;
224
225 let agent = T::Torus::find_agent_by_name(agent_name)?;
226 if !T::Torus::namespace_exists(&agent, child_path) {
227 return None;
228 }
229
230 Some(())
231 }
232 })
233 .count();
234
235 ensure!(
236 matched_paths == children.len(),
237 Error::<T>::ParentPermissionNotFound
238 );
239
240 for child_path in &children {
241 check_namespace_delegation_depth::<T>(child_path, Some(parent_pid), 1)?;
242 }
243
244 children
245 } else {
246 children
247 .map(|path| {
248 path.and_then(|path| {
249 if T::Torus::namespace_exists(delegator, &path) {
250 Ok(path)
251 } else {
252 Err(Error::<T>::NamespaceDoesNotExist.into())
253 }
254 })
255 })
256 .collect::<Result<BTreeSet<_>, DispatchError>>()?
257 };
258
259 paths
260 .try_into()
261 .map_err(|_| Error::<T>::TooManyNamespaces.into())
262}