pallet_permission0/ext/
namespace_impl.rs

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
153/// Maximum allowed delegation depth for namespaces
154const MAX_DELEGATION_DEPTH: u32 = 5;
155
156/// Check the delegation depth of a namespace by traversing up the permission chain
157fn 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}