Skip to content

Commit 366e6d0

Browse files
committed
refac(torus0/namespace): require agent prefix
This change makes the "agent.<name>" prefix required in order to facilitate future integrations. This change, though, makes the agent name immutable. Now, upon registration, the agent name will be automatically registered to the namespace. Closes CHAIN-102.
1 parent 10b4a0f commit 366e6d0

File tree

12 files changed

+536
-359
lines changed

12 files changed

+536
-359
lines changed

pallets/permission0/src/ext/namespace_impl.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ pub fn grant_namespace_permission_impl<T: Config>(
4545
let paths = paths
4646
.into_iter()
4747
.map(|path| {
48-
let path = NamespacePath::new(&path).map_err(|_| Error::<T>::NamespacePathIsInvalid)?;
48+
let path =
49+
NamespacePath::new_agent(&path).map_err(|_| Error::<T>::NamespacePathIsInvalid)?;
4950
ensure!(
5051
T::Torus::namespace_exists(&grantor, &path),
5152
Error::<T>::NamespaceDoesNotExist

pallets/torus0/api/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ try-runtime = ["polkadot-sdk/try-runtime"]
1818
[dependencies]
1919
codec = { workspace = true, features = ["derive"] }
2020
scale-info = { workspace = true, features = ["derive"] }
21-
polkadot-sdk = { workspace = true, features = ["sp-runtime"] }
21+
polkadot-sdk = { workspace = true, features = [
22+
"sp-api",
23+
"sp-core",
24+
"sp-runtime",
25+
"sp-std",
26+
] }

pallets/torus0/api/src/lib.rs

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,21 @@ pub const MAX_SEGMENT_LENGTH: usize = 64;
6868
pub const MAX_NAMESPACE_SEGMENTS: usize = 10;
6969

7070
pub const NAMESPACE_SEPARATOR: u8 = b'.';
71+
pub const NAMESPACE_AGENT_PREFIX: &[u8] = b"agent.";
7172

7273
pub type NamespacePathInner = BoundedVec<u8, ConstU32<{ MAX_NAMESPACE_PATH_LENGTH as u32 }>>;
7374

7475
#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, TypeInfo, MaxEncodedLen)]
7576
pub struct NamespacePath(NamespacePathInner);
7677

7778
impl NamespacePath {
79+
/// The root agent namespace entry.
80+
pub fn agent_root() -> NamespacePath {
81+
NamespacePath(b"agent".to_vec().try_into().unwrap())
82+
}
83+
7884
/// Create a new namespace path from bytes with validation
79-
pub fn new(bytes: &[u8]) -> Result<Self, &'static str> {
85+
pub fn new_agent(bytes: &[u8]) -> Result<Self, &'static str> {
8086
if bytes.is_empty() {
8187
return Err("empty namespace path");
8288
}
@@ -85,6 +91,10 @@ impl NamespacePath {
8591
return Err("path too long");
8692
}
8793

94+
if !bytes.starts_with(NAMESPACE_AGENT_PREFIX) {
95+
return Err("path must begin with agent prefix");
96+
}
97+
8898
let segments: Vec<&[u8]> = bytes.split(|&b| b == NAMESPACE_SEPARATOR).collect();
8999
if segments.len() > MAX_NAMESPACE_SEGMENTS {
90100
return Err("too many namespace segments");
@@ -128,10 +138,17 @@ impl NamespacePath {
128138
}
129139

130140
/// Parse a namespace path into segments
131-
pub fn segments(&self) -> Vec<&[u8]> {
132-
self.as_bytes()
133-
.split(|&b| b == NAMESPACE_SEPARATOR)
134-
.collect()
141+
pub fn segments(&self) -> impl Iterator<Item = &[u8]> {
142+
self.as_bytes().split(|&b| b == NAMESPACE_SEPARATOR)
143+
}
144+
145+
/// Returns the first segment of the path.
146+
pub fn root(&self) -> Option<Self> {
147+
let Some(root) = self.as_bytes().split(|&b| b == NAMESPACE_SEPARATOR).next() else {
148+
return Some(self.clone());
149+
};
150+
151+
root.to_vec().try_into().ok().map(Self)
135152
}
136153

137154
/// Get the parent path of this namespace
@@ -147,7 +164,7 @@ impl NamespacePath {
147164

148165
/// Get the depth of this namespace (number of segments)
149166
pub fn depth(&self) -> u32 {
150-
self.segments().len() as u32
167+
self.segments().count() as u32
151168
}
152169

153170
/// Check if this path is a parent of another path
@@ -194,7 +211,7 @@ impl FromStr for NamespacePath {
194211
type Err = &'static str;
195212

196213
fn from_str(s: &str) -> Result<Self, Self::Err> {
197-
Self::new(s.as_bytes())
214+
Self::new_agent(s.as_bytes())
198215
}
199216
}
200217

@@ -206,54 +223,54 @@ mod tests {
206223

207224
#[test]
208225
fn namespace_creation_validates_paths() {
209-
assert!(NamespacePath::new(b"agent").is_ok());
210-
assert!(NamespacePath::new(b"agent.alice").is_ok());
211-
assert!(NamespacePath::new(b"agent.alice.memory").is_ok());
212-
assert!(NamespacePath::new(b"agent-1.alice_2.key=val+1").is_ok());
213-
214-
assert!(NamespacePath::new(b"").is_err());
215-
assert!(NamespacePath::new(b".agent").is_err());
216-
assert!(NamespacePath::new(b"agent.").is_err());
217-
assert!(NamespacePath::new(b"agent..alice").is_err());
218-
assert!(NamespacePath::new(b"agent.-alice").is_err());
219-
assert!(NamespacePath::new(b"agent.alice!").is_err());
220-
assert!(NamespacePath::new(b"agent.alice memory").is_err());
226+
assert!(NamespacePath::new_agent(b"agent.alice").is_ok());
227+
assert!(NamespacePath::new_agent("agent.alice.tørûs".as_bytes()).is_ok());
228+
assert!(NamespacePath::new_agent(b"agent.alice_2.memory-1.key=val+1").is_ok());
229+
230+
assert!(NamespacePath::new_agent(b"").is_err());
231+
assert!(NamespacePath::new_agent(b"agent").is_err());
232+
assert!(NamespacePath::new_agent(b".agent").is_err());
233+
assert!(NamespacePath::new_agent(b"agent.").is_err());
234+
assert!(NamespacePath::new_agent(b"agent..alice").is_err());
235+
assert!(NamespacePath::new_agent(b"agent.-alice").is_err());
236+
assert!(NamespacePath::new_agent(b"agent.alice!").is_err());
237+
assert!(NamespacePath::new_agent(b"agent.alice memory").is_err());
221238
}
222239

223240
#[test]
224241
fn namespace_segment_listing() {
225-
let path = NamespacePath::new(b"agent.alice.memory").unwrap();
226-
let segments = path.segments();
227-
assert_eq!(segments.len(), 3);
228-
assert_eq!(segments[0], b"agent");
229-
assert_eq!(segments[1], b"alice");
230-
assert_eq!(segments[2], b"memory");
242+
let path = NamespacePath::new_agent(b"agent.alice.memory").unwrap();
243+
let mut segments = path.segments();
244+
assert_eq!(segments.next(), Some(b"agent".as_slice()));
245+
assert_eq!(segments.next(), Some(b"alice".as_slice()));
246+
assert_eq!(segments.next(), Some(b"memory".as_slice()));
247+
assert_eq!(segments.next(), None);
231248
}
232249

233250
#[test]
234251
fn namespace_parent_returns_correctly() {
235-
let path = NamespacePath::new(b"agent.alice.memory").unwrap();
252+
let path = NamespacePath::new_agent(b"agent.alice.memory").unwrap();
236253
let parent = path.parent().unwrap();
237254
assert_eq!(parent.as_bytes(), b"agent.alice");
238255

239-
let root = NamespacePath::new(b"agent").unwrap();
256+
let root = NamespacePath::agent_root();
240257
assert!(root.parent().is_none());
241258
}
242259

243260
#[test]
244261
fn namespace_depth_calculation() {
245-
let path1 = NamespacePath::new(b"agent").unwrap();
246-
assert_eq!(path1.depth(), 1);
262+
let path1 = NamespacePath::new_agent(b"agent.alice").unwrap();
263+
assert_eq!(path1.depth(), 2);
247264

248-
let path2 = NamespacePath::new(b"agent.alice.memory.twitter").unwrap();
265+
let path2 = NamespacePath::new_agent(b"agent.alice.memory.twitter").unwrap();
249266
assert_eq!(path2.depth(), 4);
250267
}
251268

252269
#[test]
253270
fn test_is_parent_of() {
254-
let parent = NamespacePath::new(b"agent.alice").unwrap();
255-
let child = NamespacePath::new(b"agent.alice.memory").unwrap();
256-
let other = NamespacePath::new(b"agent.bob").unwrap();
271+
let parent = NamespacePath::new_agent(b"agent.alice").unwrap();
272+
let child = NamespacePath::new_agent(b"agent.alice.memory").unwrap();
273+
let other = NamespacePath::new_agent(b"agent.bob").unwrap();
257274

258275
assert!(parent.is_parent_of(&child));
259276
assert!(!parent.is_parent_of(&other));
@@ -262,7 +279,7 @@ mod tests {
262279

263280
#[test]
264281
fn test_parents() {
265-
let path = NamespacePath::new(b"agent.alice.memory.twitter").unwrap();
282+
let path = NamespacePath::new_agent(b"agent.alice.memory.twitter").unwrap();
266283
let parents = path.parents();
267284
assert_eq!(parents.len(), 3);
268285
assert_eq!(parents[0].as_bytes(), b"agent.alice.memory");

pallets/torus0/src/agent.rs

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use codec::{Decode, Encode, MaxEncodedLen};
22
use pallet_emission0_api::Emission0Api;
33
use pallet_governance_api::GovernanceApi;
4+
use pallet_torus0_api::{NamespacePath, NAMESPACE_AGENT_PREFIX};
45
use polkadot_sdk::{
56
frame_election_provider_support::Get,
67
frame_support::{
@@ -11,7 +12,7 @@ use polkadot_sdk::{
1112
},
1213
polkadot_sdk_frame::prelude::BlockNumberFor,
1314
sp_runtime::{traits::Saturating, BoundedVec, DispatchError, Percent},
14-
sp_tracing::debug_span,
15+
sp_tracing::{debug_span, warn},
1516
};
1617
use scale_info::{prelude::vec::Vec, TypeInfo};
1718

@@ -77,7 +78,12 @@ pub fn register<T: crate::Config>(
7778
crate::Error::<T>::TooManyAgentRegistrationsThisInterval
7879
);
7980

80-
validate_agent_name::<T>(&name[..])?;
81+
let namespace_path: Vec<_> = [NAMESPACE_AGENT_PREFIX, &name].concat();
82+
let namespace_path = NamespacePath::new_agent(&namespace_path).map_err(|err| {
83+
warn!("{agent_key:?} tried using invalid name: {err:?}");
84+
crate::Error::<T>::InvalidNamespacePath
85+
})?;
86+
8187
validate_agent_url::<T>(&url[..])?;
8288
validate_agent_metadata::<T>(&metadata[..])?;
8389

@@ -107,6 +113,11 @@ pub fn register<T: crate::Config>(
107113
},
108114
);
109115

116+
crate::namespace::create_namespace::<T>(
117+
crate::namespace::NamespaceOwnership::Account(agent_key.clone()),
118+
namespace_path,
119+
)?;
120+
110121
crate::RegistrationsThisBlock::<T>::mutate(|value| value.saturating_add(1));
111122
crate::RegistrationsThisInterval::<T>::mutate(|value| value.saturating_add(1));
112123

@@ -127,10 +138,15 @@ pub fn unregister<T: crate::Config>(agent_key: AccountIdOf<T>) -> DispatchResult
127138
let span = debug_span!("unregister", agent.key = ?agent_key);
128139
let _guard = span.enter();
129140

130-
ensure!(
131-
exists::<T>(&agent_key),
132-
crate::Error::<T>::AgentDoesNotExist
133-
);
141+
let agent = crate::Agents::<T>::get(&agent_key).ok_or(crate::Error::<T>::AgentDoesNotExist)?;
142+
143+
let namespace_path: Vec<_> = [NAMESPACE_AGENT_PREFIX, &agent.name].concat();
144+
let namespace_path = NamespacePath::new_agent(&namespace_path)
145+
.map_err(|_| crate::Error::<T>::InvalidNamespacePath)?;
146+
crate::namespace::delete_namespace::<T>(
147+
crate::namespace::NamespaceOwnership::Account(agent_key.clone()),
148+
namespace_path,
149+
)?;
134150

135151
crate::Agents::<T>::remove(&agent_key);
136152
crate::stake::clear_key::<T>(&agent_key)?;
@@ -143,7 +159,6 @@ pub fn unregister<T: crate::Config>(agent_key: AccountIdOf<T>) -> DispatchResult
143159
/// Updates the metadata of an existing agent.
144160
pub fn update<T: crate::Config>(
145161
agent_key: AccountIdOf<T>,
146-
name: Vec<u8>,
147162
url: Vec<u8>,
148163
metadata: Option<Vec<u8>>,
149164
staking_fee: Option<Percent>,
@@ -161,9 +176,6 @@ pub fn update<T: crate::Config>(
161176
return Err(crate::Error::<T>::AgentUpdateOnCooldown.into());
162177
}
163178

164-
validate_agent_name::<T>(&name[..])?;
165-
agent.name = BoundedVec::truncate_from(name);
166-
167179
validate_agent_url::<T>(&url[..])?;
168180
agent.url = BoundedVec::truncate_from(url);
169181

@@ -228,31 +240,6 @@ pub fn exists<T: crate::Config>(key: &AccountIdOf<T>) -> bool {
228240
crate::Agents::<T>::contains_key(key)
229241
}
230242

231-
fn validate_agent_name<T: crate::Config>(bytes: &[u8]) -> DispatchResult {
232-
let len: u32 = bytes
233-
.len()
234-
.try_into()
235-
.map_err(|_| crate::Error::<T>::AgentNameTooLong)?;
236-
237-
ensure!(
238-
len >= crate::MinNameLength::<T>::get() as u32,
239-
crate::Error::<T>::AgentNameTooShort
240-
);
241-
242-
ensure!(
243-
len <= (crate::MaxNameLength::<T>::get() as u32)
244-
.min(T::MaxAgentNameLengthConstraint::get()),
245-
crate::Error::<T>::AgentNameTooLong
246-
);
247-
248-
ensure!(
249-
core::str::from_utf8(bytes).is_ok(),
250-
crate::Error::<T>::InvalidAgentName
251-
);
252-
253-
Ok(())
254-
}
255-
256243
fn validate_agent_url<T: crate::Config>(bytes: &[u8]) -> DispatchResult {
257244
let len: u32 = bytes
258245
.len()

pallets/torus0/src/benchmarking.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ mod benchmarks {
109109

110110
AgentUpdateCooldown::<T>::set(Default::default());
111111

112-
let name = vec![4, 5, 6];
113112
let url = vec![4, 5, 6];
114113
let metadata = Some(vec![4, 5, 6]);
115114
let staking_fee = Some(Percent::from_percent(10));
@@ -118,7 +117,6 @@ mod benchmarks {
118117
#[extrinsic_call]
119118
update_agent(
120119
RawOrigin::Signed(agent),
121-
name,
122120
url,
123121
metadata,
124122
staking_fee,

0 commit comments

Comments
 (0)