diff --git a/Cargo.lock b/Cargo.lock
index 257ac22dc..5a1d69bea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1271,6 +1271,7 @@ dependencies = [
"pallet-indices",
"pallet-insecure-randomness-collective-flip",
"pallet-multisig",
+ "pallet-namespace",
"pallet-network-membership",
"pallet-network-score",
"pallet-node-authorization",
@@ -1405,6 +1406,7 @@ dependencies = [
"pallet-insecure-randomness-collective-flip",
"pallet-membership",
"pallet-multisig",
+ "pallet-namespace",
"pallet-network-membership",
"pallet-network-score",
"pallet-node-authorization",
@@ -1931,6 +1933,7 @@ dependencies = [
"pallet-insecure-randomness-collective-flip",
"pallet-membership",
"pallet-multisig",
+ "pallet-namespace",
"pallet-network-membership",
"pallet-network-score",
"pallet-node-authorization",
@@ -6483,6 +6486,26 @@ dependencies = [
"sp-runtime",
]
+[[package]]
+name = "pallet-namespace"
+version = "0.9.4"
+dependencies = [
+ "bitflags 1.3.2",
+ "cord-identifier",
+ "cord-primitives",
+ "cord-utilities",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-keystore",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "pallet-network-membership"
version = "0.9.4"
diff --git a/Cargo.toml b/Cargo.toml
index 6b47e30d0..ad48ebe96 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ members = [
"node/testing",
"pallets/asset",
"pallets/chain-space",
+ "pallets/namespace",
"pallets/did",
"pallets/did-name",
"pallets/identity",
@@ -155,6 +156,7 @@ pallet-transaction-weight-runtime-api = { path = "runtimes/common/api/weight", d
pallet-registries = { path = "pallets/registries", default-features = false }
pallet-entries = { path = "pallets/entries", default-features = false }
pallet-schema-accounts = { path = "pallets/schema-accounts", default-features = false }
+pallet-namespace = { path = 'pallets/namespace', default-features = false }
# substrate dependencies
diff --git a/pallets/namespace/Cargo.toml b/pallets/namespace/Cargo.toml
new file mode 100644
index 000000000..006fec14c
--- /dev/null
+++ b/pallets/namespace/Cargo.toml
@@ -0,0 +1,73 @@
+[package]
+name = 'pallet-namespace'
+description = 'Manage Name-Spaces.'
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[lints]
+workspace = true
+
+[package.metadata.docs.rs]
+targets = ['x86_64-unknown-linux-gnu']
+
+[dev-dependencies]
+sp-core = { features = ["std"], workspace = true }
+sp-keystore = { features = ["std"], workspace = true }
+cord-utilities = { features = ["mock"], workspace = true }
+
+[dependencies]
+codec = { features = ["derive"], workspace = true }
+scale-info = { features = ["derive"], workspace = true }
+bitflags = { workspace = true }
+
+# Internal dependencies
+cord-primitives = { workspace = true }
+cord-utilities = { workspace = true }
+identifier = { workspace = true }
+
+# Substrate dependencies
+frame-benchmarking = { optional = true, workspace = true }
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+sp-core = { optional = true, workspace = true }
+sp-io = { optional = true, workspace = true }
+sp-keystore = { optional = true, workspace = true }
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+
+
+[features]
+default = ['std']
+runtime-benchmarks = [
+ "frame-benchmarking/runtime-benchmarks",
+ "sp-runtime/runtime-benchmarks",
+ "frame-support/runtime-benchmarks",
+ "frame-system/runtime-benchmarks",
+ "cord-utilities/runtime-benchmarks",
+]
+std = [
+ "codec/std",
+ "identifier/std",
+ "frame-benchmarking/std",
+ "frame-support/std",
+ "frame-system/std",
+ "cord-primitives/std",
+ "cord-utilities/std",
+ "scale-info/std",
+ "sp-core/std",
+ "sp-io/std",
+ "sp-keystore/std",
+ "sp-runtime/std",
+ "sp-std/std",
+]
+try-runtime = [
+ "frame-support/try-runtime",
+ "frame-system/try-runtime",
+ "identifier/try-runtime",
+ "cord-utilities/try-runtime",
+ "sp-runtime/try-runtime"
+]
diff --git a/pallets/namespace/src/lib.rs b/pallets/namespace/src/lib.rs
new file mode 100644
index 000000000..599a84201
--- /dev/null
+++ b/pallets/namespace/src/lib.rs
@@ -0,0 +1,1135 @@
+// This file is part of CORD – https://cord.network
+
+// Copyright (C) Dhiway Networks Pvt. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// CORD is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// CORD is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with CORD. If not, see .
+//
+
+//! # NameSpace Pallet
+//!
+//! The NameSpace pallet provides a framework for creating and managing
+//! isolated namespaces within the CORD blockchain that can be governed and
+//! moderated with a fine-grained permission system. It allows for the creation,
+//! approval, and archival of namespaces, as well as the management of delegates
+//! within these namespaces.
+//!
+//! ## Overview
+//!
+//! The NameSpace pallet allows for the creation of distinct namespaces on the CORD
+//! blockchain, each with its own set of rules and governance. These namespaces can
+//! be used to manage different ecosystems or communities within the larger
+//! blockchain environment. NameSpaces are created with a unique identifier and can
+//! be managed by appointed delegates.
+//!
+//! ## Interface
+//!
+//! The pallet provides dispatchable functions for namespace management:
+//!
+//! - `create`: Initializes a new namespace with a unique identifier.
+//! - `approve`: Approves a namespace for use, setting its capacity and governance status. (TODO:
+//! Remove approve)
+//! - `archive`: Marks a namespace as archived, effectively freezing its state.
+//! - `restore`: Unarchives a namespace, returning it to active status.
+//! - `add_delegate`: Adds a delegate to a namespace, granting them specific permissions.
+//! - `add_admin_delegate`: Adds an admin delegate to a namespace, granting them administrative
+//! permissions.
+//! - `add_audit_delegate`: Adds an audit delegate to a namespace, granting them audit permissions.
+//! - `remove_delegate`: Removes a delegate from a namespace, revoking their permissions.
+//!
+//! ## Permissions
+//!
+//! The pallet uses a permissions system to manage the actions that delegates
+//! can perform within a namespace. Permissions are granular and can be assigned to
+//! different roles, such as an admin or a regular delegate.
+//!
+//! ## Data Privacy
+//!
+//! The NameSpace pallet is designed with data privacy as a core consideration.
+//! It does not directly store any personal or sensitive information on-chain.
+//! Instead, it manages references to off-chain data, ensuring that the
+//! blockchain layer remains compliant with data privacy regulations. Users and
+//! developers are responsible for ensuring that the off-chain data handling
+//! processes adhere to the applicable laws and standards.
+//!
+//! ## Usage
+//!
+//! The NameSpace pallet can be used by other pallets to create
+//! compartmentalized and governed sections of the blockchain. This is
+//! particularly useful for applications that require distinct governance models
+//! or privacy settings within a shared ecosystem.
+//!
+//! ## Governance Integration
+//!
+//! The NameSpace pallet is integrated with on-chain governance pallets to
+//! allow namespace administrators and delegates to propose changes, vote on
+//! initiatives, or manage the namespace in accordance with the collective decisions
+//! of its members.
+//!
+//! ## Examples
+//!
+//! - Creating a new namespace for a community-driven project.
+//! - Approving a namespace for official use after meeting certain criteria. (TODO: Remove this
+//! line)
+//! - Archiving a namespace that is no longer active or has violated terms of use.
+//! - Adding delegates to a namespace to ensure ongoing compliance with governance standards.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+#![allow(clippy::unused_unit)]
+
+#[cfg(any(feature = "mock", test))]
+pub mod mock;
+
+#[cfg(test)]
+mod tests;
+
+use frame_support::{ensure, storage::types::StorageMap, BoundedVec};
+pub mod types;
+pub use crate::{pallet::*, types::*};
+use codec::Encode;
+use frame_system::WeightInfo;
+use identifier::{
+ types::{CallTypeOf, IdentifierTypeOf, Timepoint},
+ EventEntryOf,
+};
+use sp_runtime::traits::{Hash, UniqueSaturatedInto};
+
+/// Type of a namespace creator.
+pub type NameSpaceCreatorOf = ::AccountId;
+
+/// Namespace Identifier
+pub type NameSpaceIdOf = Ss58Identifier;
+
+/// Registry Identifier
+pub type RegistryIdOf = Ss58Identifier;
+
+/// Authorization Identifier
+pub type AuthorizationIdOf = Ss58Identifier;
+
+/// Namespace input code
+pub type NameSpaceCodeOf = ::Hash;
+
+/// Registry Identifier mapped to a Namespace.
+pub type MaxRegistriesOf = ::MaxNameSpaceDelegates;
+
+/// Type of on-chain Namespace details
+pub type NameSpaceDetailsOf = NameSpaceDetails<
+ NameSpaceCodeOf,
+ NameSpaceCreatorOf,
+ StatusOf,
+ BoundedVec>,
+>;
+
+/// Type of Namespace Authorization details
+pub type NameSpaceAuthorizationOf =
+ NameSpaceAuthorization, Permissions>;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use super::*;
+ pub use cord_primitives::{IsPermissioned, StatusOf};
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+ pub use identifier::{IdentifierCreator, IdentifierTimeline, IdentifierType, Ss58Identifier};
+
+ /// The current storage version.
+ const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config + identifier::Config {
+ type RuntimeEvent: From> + IsType<::RuntimeEvent>;
+
+ type ChainSpaceOrigin: EnsureOrigin;
+ type NetworkPermission: IsPermissioned;
+
+ #[pallet::constant]
+ type MaxNameSpaceDelegates: Get;
+
+ /// Weight information for extrinsics in this pallet.
+ type WeightInfo: WeightInfo;
+ }
+
+ #[pallet::pallet]
+ #[pallet::storage_version(STORAGE_VERSION)]
+ pub struct Pallet(_);
+
+ #[pallet::hooks]
+ impl Hooks> for Pallet {}
+
+ /// Namespace information stored on chain.
+ /// It maps from an identifier to its details.
+ #[pallet::storage]
+ pub type NameSpaces =
+ StorageMap<_, Blake2_128Concat, NameSpaceIdOf, NameSpaceDetailsOf, OptionQuery>;
+
+ /// Namespace authorizations stored on-chain.
+ /// It maps from an identifier to delegates.
+ #[pallet::storage]
+ pub type Authorizations = StorageMap<
+ _,
+ Blake2_128Concat,
+ AuthorizationIdOf,
+ NameSpaceAuthorizationOf,
+ OptionQuery,
+ >;
+
+ /// Namespace delegates stored on chain.
+ /// It maps from an identifier to a bounded vec of delegates and
+ /// permissions.
+ #[pallet::storage]
+ pub(super) type Delegates = StorageMap<
+ _,
+ Blake2_128Concat,
+ NameSpaceIdOf,
+ BoundedVec, T::MaxNameSpaceDelegates>,
+ ValueQuery,
+ >;
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ pub enum Event {
+ /// A new namespace authorization has been added.
+ /// \[namespace identifier, authorization, delegate\]
+ Authorization {
+ namespace: NameSpaceIdOf,
+ authorization: AuthorizationIdOf,
+ delegate: NameSpaceCreatorOf,
+ },
+ /// A namespace authorization has been removed.
+ /// \[namespace identifier, authorization, ]
+ Deauthorization { namespace: NameSpaceIdOf, authorization: AuthorizationIdOf },
+ /// A new namespace has been created.
+ /// \[namespace identifier, creator, authorization\]
+ Create {
+ namespace: NameSpaceIdOf,
+ creator: NameSpaceCreatorOf,
+ authorization: AuthorizationIdOf,
+ },
+ /// A new namespace has been approved.
+ /// \[namespace identifier \]
+ Approve { namespace: NameSpaceIdOf },
+ /// A namespace has been archived.
+ /// \[namespace identifier, authority\]
+ Archive { namespace: NameSpaceIdOf, authority: NameSpaceCreatorOf },
+ /// A namespace has been restored.
+ /// \[namespace identifier, authority\]
+ Restore { namespace: NameSpaceIdOf, authority: NameSpaceCreatorOf },
+ /// A namespace has been restored.
+ /// \[namespace identifier, \]
+ Revoke { namespace: NameSpaceIdOf },
+ /// A namespace approval has been revoked.
+ /// \[namespace identifier, \]
+ ApprovalRevoke { namespace: NameSpaceIdOf },
+ /// A namespace approval has been restored.
+ /// \[namespace identifier, \]
+ ApprovalRestore { namespace: NameSpaceIdOf },
+ }
+
+ #[pallet::error]
+ #[derive(PartialEq)]
+ pub enum Error {
+ /// NameSpace identifier is not unique
+ NameSpaceAlreadyAnchored,
+ /// NameSpace identifier not found
+ NameSpaceNotFound,
+ /// Only when the author is not the controller or delegate.
+ UnauthorizedOperation,
+ /// Invalid Identifier
+ InvalidIdentifier,
+ /// Invalid Identifier Length
+ InvalidIdentifierLength,
+ /// Invalid Identifier Prefix
+ InvalidIdentifierPrefix,
+ /// Archived NameSpace
+ ArchivedNameSpace,
+ /// NameSpace not Archived
+ NameSpaceNotArchived,
+ /// NameSpace delegation limit exceeded
+ NameSpaceDelegatesLimitExceeded,
+ /// Empty transaction.
+ EmptyTransaction,
+ /// Authority already added
+ DelegateAlreadyAdded,
+ /// Authorization Id not found
+ AuthorizationNotFound,
+ /// Delegate not found.
+ DelegateNotFound,
+ /// NameSpace already approved
+ NameSpaceAlreadyApproved,
+ /// NameSpace not approved.
+ NameSpaceNotApproved,
+ }
+
+ #[pallet::call]
+ impl Pallet {
+ /// Adds a delegate with the ability to assert new entries to a namespace.
+ ///
+ /// The `ASSERT` permission allows the delegate to sign and add new
+ /// entries within the namespace. This function is called to grant a
+ /// delegate this specific permission. It checks that the caller has the
+ /// necessary authorization (admin rights) to add a delegate to the
+ /// namespace. If the caller is authorized, the delegate is added with the
+ /// `ASSERT` permission using the `space_delegate_addition`
+ /// internal function.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the call, which must be signed by an admin of the namespace.
+ /// - `namespace_id`: The identifier of the namespace to which the delegate is being added.
+ /// - `delegate`: The identifier of the delegate being added to the namespace.
+ /// - `authorization`: The authorization ID used to validate the addition.
+ ///
+ /// # Returns
+ /// Returns `Ok(())` if the delegate was successfully added with
+ /// `ASSERT` permission, or an `Err` with an appropriate error if the
+ /// operation fails.
+ ///
+ /// # Errors
+ /// - `UnauthorizedOperation`: If the caller is not an admin of the namespace.
+ /// - Propagates errors from `space_delegate_addition` if it fails.
+ #[pallet::call_index(0)]
+ #[pallet::weight({0})]
+ pub fn add_delegate(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ delegate: NameSpaceCreatorOf,
+ authorization: AuthorizationIdOf,
+ ) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+ let auth_space_id =
+ Self::ensure_authorization_delegator_origin(&authorization, &creator)?;
+ ensure!(auth_space_id == namespace_id, Error::::UnauthorizedOperation);
+
+ let permissions = Permissions::ASSERT;
+ Self::space_delegate_addition(auth_space_id, delegate, creator, permissions)?;
+
+ Ok(())
+ }
+
+ /// Adds an administrative delegate to a namespace.
+ ///
+ /// The `ADMIN` permission grants the delegate extensive control over
+ /// the namespace, including the ability to manage other delegates and
+ /// change namespace configurations. This function is called to
+ /// grant a delegate these administrative privileges. It verifies that
+ /// the caller has the necessary authorization (admin rights) to add an
+ /// admin delegate to the namespace. If the caller is authorized,
+ /// the delegate is added with the `ADMIN` permission using the
+ /// `space_delegate_addition` internal function.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the call, which must be signed by an existing admin of the
+ /// namespace.
+ /// - `namespace_id`: The identifier of the namespace to which the admin delegate is being
+ /// added.
+ /// - `delegate`: The identifier of the delegate being granted admin permissions.
+ /// - `authorization`: The authorization ID used to validate the addition.
+ ///
+ /// # Returns
+ /// Returns `Ok(())` if the admin delegate was successfully added, or an
+ /// `Err` with an appropriate error if the operation fails.
+ ///
+ /// # Errors
+ /// - `UnauthorizedOperation`: If the caller is not an admin of the namespace.
+ /// - Propagates errors from `space_delegate_addition` if it fails.
+ #[pallet::call_index(1)]
+ #[pallet::weight({0})]
+ pub fn add_admin_delegate(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ delegate: NameSpaceCreatorOf,
+ authorization: AuthorizationIdOf,
+ ) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+ let auth_space_id = Self::ensure_authorization_admin_origin(&authorization, &creator)?;
+
+ ensure!(auth_space_id == namespace_id, Error::::UnauthorizedOperation);
+
+ let permissions = Permissions::ADMIN;
+ Self::space_delegate_addition(auth_space_id, delegate, creator, permissions)?;
+
+ Ok(())
+ }
+
+ /// Adds an audit delegate to a namespace.
+ ///
+ /// The `AUDIT` permission grants the delegate the ability to perform
+ /// oversight and compliance checks within the namespace. This function is
+ /// used to assign a delegate these audit privileges. It ensures that
+ /// the caller has the necessary authorization (admin rights) to add an
+ /// audit delegate to the namespace. If the caller is authorized, the
+ /// delegate is added with the `AUDIT` permission using the
+ /// `space_delegate_addition` internal function.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the call, which must be signed by an existing admin of the
+ /// namespace.
+ /// - `namespace_id`: The identifier of the namespace to which the audit delegate is being
+ /// added.
+ /// - `delegate`: The identifier of the delegate being granted audit permissions.
+ /// - `authorization`: The authorization ID used to validate the addition.
+ ///
+ /// # Returns
+ /// Returns `Ok(())` if the audit delegate was successfully added, or an
+ /// `Err` with an appropriate error if the operation fails.
+ #[pallet::call_index(2)]
+ #[pallet::weight({0})]
+ pub fn add_delegator(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ delegate: NameSpaceCreatorOf,
+ authorization: AuthorizationIdOf,
+ ) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+ let auth_space_id = Self::ensure_authorization_admin_origin(&authorization, &creator)?;
+
+ ensure!(auth_space_id == namespace_id, Error::::UnauthorizedOperation);
+
+ let permissions = Permissions::DELEGATE;
+ Self::space_delegate_addition(auth_space_id, delegate, creator, permissions)?;
+
+ Ok(())
+ }
+
+ /// Removes a delegate from a specified namespace.
+ ///
+ /// This function will remove an existing delegate from a namespace, given
+ /// the namespace ID and the delegate's authorization ID. It checks that the
+ /// namespace exists, is not archived, is approved, and that the provided
+ /// authorization corresponds to a delegate of the namespace. It also
+ /// verifies that the caller has the authority to remove a delegate.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the transaction, which must be signed by the creator or an
+ /// admin.
+ /// - `namespace_id`: The identifier of the namespace from which the delegate is being
+ /// removed.
+ /// - `remove_authorization`: The authorization ID of the delegate to be removed.
+ /// - `authorization`: An identifier for the authorization being used to validate the
+ /// removal.
+ ///
+ /// # Returns
+ /// - `DispatchResult`: This function returns `Ok(())` if the delegate is successfully
+ /// removed, or an error (`DispatchError`) if any of the checks fail.
+ ///
+ /// # Errors
+ /// - `AuthorizationNotFound`: If the provided `remove_authorization` does not exist.
+ /// - `UnauthorizedOperation`: If the origin is not authorized to remove a delegate from the
+ /// namespace.
+ /// - `NameSpaceNotFound`: If the specified namespace ID does not correspond to an existing
+ /// namespace.
+ /// - `ArchivedNameSpace`: If the namespace is archived and no longer active.
+ /// - `NameSpaceNotApproved`: If the namespace has not been approved for use.
+ /// - `DelegateNotFound`: If the delegate specified by `remove_authorization` is not found
+ /// in the namespace.
+ ///
+ /// # Events
+ ///
+ /// - `Deauthorization`: Emitted when a delegate is successfully removed from a namespace.
+ /// The event includes the namespace ID and the authorization ID of the removed delegate.
+ #[pallet::call_index(3)]
+ #[pallet::weight({0})]
+ pub fn remove_delegate(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ remove_authorization: AuthorizationIdOf,
+ authorization: AuthorizationIdOf,
+ ) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+ let auth_space_id =
+ Self::ensure_authorization_admin_remove_origin(&authorization, &creator)?;
+
+ ensure!(auth_space_id == namespace_id, Error::::UnauthorizedOperation);
+
+ // Ensure the authorization exists and retrieve its details.
+ let authorization_details = Authorizations::::get(&remove_authorization)
+ .ok_or(Error::::AuthorizationNotFound)?;
+
+ let mut delegates = Delegates::::get(&namespace_id);
+ if let Some(index) = delegates.iter().position(|d| d == &authorization_details.delegate)
+ {
+ delegates.remove(index);
+ Delegates::::insert(&namespace_id, delegates);
+
+ Authorizations::::remove(&remove_authorization);
+
+ Self::update_activity(
+ &namespace_id,
+ IdentifierTypeOf::Auth,
+ CallTypeOf::Deauthorization,
+ )?;
+
+ Self::deposit_event(Event::Deauthorization {
+ namespace: namespace_id,
+ authorization: remove_authorization,
+ });
+
+ Ok(())
+ } else {
+ Err(Error::::DelegateNotFound.into())
+ }
+ }
+
+ /// Creates a new namespace with a unique identifier based on the provided
+ /// namespace code and the creator's identity.
+ ///
+ /// This function generates a unique identifier for the namespace by hashing
+ /// the encoded namespace code and creator's identifier. It ensures that the
+ /// generated namespace identifier is not already in use. An authorization
+ /// ID is also created for the new namespace, which is used to manage
+ /// delegations. The creator is automatically added as a delegate with
+ /// all permissions.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the transaction, which must be signed by the creator.
+ /// - `space_code`: A unique code representing the namespace to be created.
+ ///
+ /// # Returns
+ /// - `DispatchResult`: Returns `Ok(())` if the namespace is successfully created, or an
+ /// error (`DispatchError`) if:
+ /// - The generated namespace identifier is already in use.
+ /// - The generated authorization ID is of invalid length.
+ /// - The namespace delegates limit is exceeded.
+ ///
+ /// # Errors
+ /// - `InvalidIdentifierLength`: If the generated identifiers for the namespace or
+ /// authorization are of invalid length.
+ /// - `NameSpaceAlreadyAnchored`: If the namespace identifier is already in use.
+ /// - `NameSpaceDelegatesLimitExceeded`: If the namespace exceeds the limit of allowed
+ /// delegates.
+ ///
+ /// # Events
+ /// - `Create`: Emitted when a new namespace is successfully created. It includes the
+ /// namespace identifier, the creator's identifier, and the authorization ID.
+ #[pallet::call_index(4)]
+ #[pallet::weight({0})]
+ pub fn create(origin: OriginFor, namespace_code: NameSpaceCodeOf) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+
+ // Id Digest = concat (H(,
+ // ))
+ let id_digest = ::Hashing::hash(
+ &[&namespace_code.encode()[..], &creator.encode()[..]].concat()[..],
+ );
+
+ let identifier =
+ Ss58Identifier::create_identifier(&id_digest.encode()[..], IdentifierType::Space)
+ .map_err(|_| Error::::InvalidIdentifierLength)?;
+
+ ensure!(
+ !>::contains_key(&identifier),
+ Error::::NameSpaceAlreadyAnchored
+ );
+
+ // Construct the authorization_id from the provided parameters.
+ // Id Digest = concat (H(,
+ // ))
+ let auth_id_digest = T::Hashing::hash(
+ &[&identifier.encode()[..], &creator.encode()[..], &creator.encode()[..]].concat()
+ [..],
+ );
+
+ let authorization_id = Ss58Identifier::create_identifier(
+ &auth_id_digest.encode(),
+ IdentifierType::Authorization,
+ )
+ .map_err(|_| Error::::InvalidIdentifierLength)?;
+
+ let mut delegates: BoundedVec, T::MaxNameSpaceDelegates> =
+ BoundedVec::default();
+ delegates
+ .try_push(creator.clone())
+ .map_err(|_| Error::::NameSpaceDelegatesLimitExceeded)?;
+
+ Delegates::::insert(&identifier, delegates);
+
+ Authorizations::::insert(
+ &authorization_id,
+ NameSpaceAuthorizationOf:: {
+ namespace_id: identifier.clone(),
+ delegate: creator.clone(),
+ permissions: Permissions::all(),
+ delegator: creator.clone(),
+ },
+ );
+
+ let approved = !T::NetworkPermission::is_permissioned();
+
+ >::insert(
+ &identifier,
+ NameSpaceDetailsOf:: {
+ code: namespace_code,
+ creator: creator.clone(),
+ approved,
+ archive: false,
+ registry_id: Some(BoundedVec::default()),
+ },
+ );
+
+ Self::update_activity(&identifier, IdentifierTypeOf::ChainSpace, CallTypeOf::Genesis)
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::Create {
+ namespace: identifier,
+ creator,
+ authorization: authorization_id,
+ });
+
+ Ok(())
+ }
+
+ /// Approves a namespace and sets its capacity.
+ ///
+ /// This function can only be called by a council or root origin,
+ /// reflecting its privileged nature. It is used to approve a namespace that
+ /// has been previously created, setting its transaction capacity and
+ /// marking it as approved. It ensures that the namespace exists, is not
+ /// archived, and has not already been approved.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the transaction, which must be a council or root origin.
+ /// - `namespace_id`: The identifier of the namespace to be approved.
+ /// - `txn_capacity`: The transaction capacity to be set for the namespace.
+ ///
+ /// # Returns
+ /// - `DispatchResult`: Returns `Ok(())` if the namespace is successfully approved, or an
+ /// error (`DispatchError`) if:
+ /// - The origin is not a council or root origin.
+ /// - The namespace does not exist.
+ /// - The namespace is archived.
+ /// - The namespace is already approved.
+ ///
+ /// # Errors
+ /// - `BadOrigin`: If the call does not come from a council or root origin.
+ /// - `NameSpaceNotFound`: If the specified namespace ID does not correspond to an existing
+ /// namespace.
+ /// - `ArchivedNameSpace`: If the namespace is archived and no longer active.
+ /// - `NameSpaceAlreadyApproved`: If the namespace has already been approved.
+ ///
+ /// # Events
+ /// - `Approve`: Emitted when a namespace is successfully approved. It includes the
+ /// namespace identifier.
+ ///
+ /// # Security Considerations
+ /// Due to the privileged nature of this function, callers must ensure
+ /// that they have the appropriate authority. Misuse can lead to
+ /// unauthorized approval of namespaces, which may have security
+ /// implications.
+ #[pallet::call_index(5)]
+ #[pallet::weight({0})]
+ pub fn approve(origin: OriginFor, namespace_id: NameSpaceIdOf) -> DispatchResult {
+ // TODO: Below should be root
+ let _creator = ensure_signed(origin)?;
+
+ let namespace_details =
+ NameSpaces::::get(&namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+ ensure!(!namespace_details.archive, Error::::ArchivedNameSpace);
+ ensure!(!namespace_details.approved, Error::::NameSpaceAlreadyApproved);
+
+ >::insert(
+ &namespace_id,
+ NameSpaceDetailsOf:: { approved: true, ..namespace_details },
+ );
+
+ // TODO: Add Namespace in Identifier types and update all activities.
+ Self::update_activity(
+ &namespace_id,
+ IdentifierTypeOf::ChainSpace,
+ CallTypeOf::Approved,
+ )
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::Approve { namespace: namespace_id });
+
+ Ok(())
+ }
+
+ /// Archives a namespace, rendering it inactive.
+ ///
+ /// This function marks a namespace as archived based on the provided namespace
+ /// ID. It checks that the namespace exists, is not already archived, and is
+ /// approved. Additionally, it verifies that the caller has the
+ /// authority to archive the namespace, as indicated by the provided
+ /// authorization ID.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the transaction, which must be signed by the creator or an
+ /// admin with the appropriate authority.
+ /// - `namespace_id`: The identifier of the namespace to be archived.
+ /// - `authorization`: An identifier for the authorization being used to validate the
+ /// archival.
+ ///
+ /// # Returns
+ /// - `DispatchResult`: Returns `Ok(())` if the namespace is successfully archived, or an
+ /// error (`DispatchError`) if:
+ /// - The namespace does not exist.
+ /// - `ArchivedNameSpace`: If the namespace is already archived.
+ /// - `NameSpaceNotApproved`: If the namespace has not been approved for use.
+ /// - `UnauthorizedOperation`: If the caller does not have the authority to archive the
+ /// namespace.
+ ///
+ /// # Errors
+ /// - `NameSpaceNotFound`: If the specified namespace ID does not correspond to an existing
+ /// namespace.
+ /// - `ArchivedNameSpace`: If the namespace is already archived.
+ /// - `NameSpaceNotApproved`: If the namespace has not been approved for use.
+ /// - `UnauthorizedOperation`: If the caller is not authorized to archive the namespace.
+ ///
+ /// # Events
+ /// - `Archive`: Emitted when a namespace is successfully archived. It includes the
+ /// namespace ID and the authority who performed the archival.
+ #[pallet::call_index(6)]
+ #[pallet::weight({0})]
+ pub fn archive(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ authorization: AuthorizationIdOf,
+ ) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+ let auth_space_id = Self::ensure_authorization_admin_origin(&authorization, &creator)?;
+
+ ensure!(auth_space_id == namespace_id, Error::::UnauthorizedOperation);
+
+ let namespace_details =
+ NameSpaces::::get(&namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+ ensure!(!namespace_details.archive, Error::::ArchivedNameSpace);
+ ensure!(namespace_details.approved, Error::::NameSpaceNotApproved);
+
+ >::insert(
+ &namespace_id,
+ NameSpaceDetailsOf:: { archive: true, ..namespace_details },
+ );
+
+ Self::update_activity(&namespace_id, IdentifierTypeOf::ChainSpace, CallTypeOf::Archive)
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::Archive { namespace: namespace_id, authority: creator });
+
+ Ok(())
+ }
+
+ /// Restores an archived namespace, making it active again.
+ ///
+ /// This function unarchives a namespace based on the provided namespace ID. It
+ /// checks that the namespace exists, is currently archived, and is
+ /// approved. It also verifies that the caller has the authority to
+ /// restore the namespace, as indicated by the provided authorization ID.
+ ///
+ /// # Parameters
+ /// - `origin`: The origin of the transaction, which must be signed by the creator or an
+ /// admin with the appropriate authority.
+ /// - `namespace_id`: The identifier of the namespace to be restored.
+ /// - `authorization`: An identifier for the authorization being used to validate the
+ /// restoration.
+ ///
+ /// # Returns
+ /// - `DispatchResult`: Returns `Ok(())` if the namespace is successfully restored, or an
+ /// error (`DispatchError`) if:
+ /// - The namespace does not exist.
+ /// - The namespace is not archived.
+ /// - The namespace is not approved.
+ /// - The caller does not have the authority to restore the namespace.
+ ///
+ /// # Errors
+ /// - `NameSpaceNotFound`: If the specified namespace ID does not correspond to an existing
+ /// namespace.
+ /// - `NameSpaceNotArchived`: If the namespace is not currently archived.
+ /// - `NameSpaceNotApproved`: If the namespace has not been approved for use.
+ /// - `UnauthorizedOperation`: If the caller is not authorized to restore the namespace.
+ ///
+ /// # Events
+ /// - `Restore`: Emitted when a namespace is successfully restored. It includes the
+ /// namespace ID and the authority who performed the restoration.
+ #[pallet::call_index(7)]
+ #[pallet::weight({0})]
+ pub fn restore(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ authorization: AuthorizationIdOf,
+ ) -> DispatchResult {
+ let creator = ensure_signed(origin)?;
+ let auth_space_id =
+ Self::ensure_authorization_restore_origin(&authorization, &creator)?;
+
+ ensure!(auth_space_id == namespace_id, Error::::UnauthorizedOperation);
+
+ let namespace_details =
+ NameSpaces::::get(&namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+ ensure!(namespace_details.archive, Error::::NameSpaceNotArchived);
+ ensure!(namespace_details.approved, Error::::NameSpaceNotApproved);
+
+ >::insert(
+ &namespace_id,
+ NameSpaceDetailsOf:: { archive: false, ..namespace_details },
+ );
+
+ Self::update_activity(&namespace_id, IdentifierTypeOf::ChainSpace, CallTypeOf::Restore)
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::Restore { namespace: namespace_id, authority: creator });
+
+ Ok(())
+ }
+
+ /// Revokes approval for a specified namespace.
+ ///
+ /// This function can be executed by an authorized origin, as determined
+ /// by `ChainSpaceOrigin`. It is designed to change the status of a
+ /// given namespace, referred to by `namespace_id`, to unapproved.
+ /// The revocation is only allowed if the namespace is currently approved,
+ /// and not archived.
+ ///
+ /// # Parameters
+ /// - `origin`: The transaction's origin, which must satisfy the `ChainSpaceOrigin` policy.
+ /// - `namespace_id`: The identifier of the namespace whose approval status is being
+ /// revoked.
+ ///
+ /// # Errors
+ /// - Returns `NameSpaceNotFound` if no namespace corresponds to the provided `space_id`.
+ /// - Returns `ArchivedNameSpace` if the namespace is archived, in which case its status
+ /// cannot be altered.
+ /// - Returns `NameSpaceNotApproved` if the namespace is already unapproved.
+ ///
+ /// # Events
+ /// - Emits `Revoke` when the namespace's approved status is successfully revoked.
+ #[pallet::call_index(10)]
+ #[pallet::weight({0})]
+ pub fn approval_revoke(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ ) -> DispatchResult {
+ let _creator = ensure_signed(origin)?;
+
+ let namespace_details =
+ NameSpaces::::get(&namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+ ensure!(!namespace_details.archive, Error::::ArchivedNameSpace);
+ ensure!(namespace_details.approved, Error::::NameSpaceNotApproved);
+
+ >::insert(
+ &namespace_id,
+ NameSpaceDetailsOf:: { approved: false, ..namespace_details },
+ );
+
+ Self::update_activity(
+ &namespace_id,
+ IdentifierTypeOf::ChainSpace,
+ CallTypeOf::CouncilRevoke,
+ )
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::ApprovalRevoke { namespace: namespace_id });
+
+ Ok(())
+ }
+
+ #[pallet::call_index(11)]
+ #[pallet::weight({0})]
+ pub fn approval_restore(
+ origin: OriginFor,
+ namespace_id: NameSpaceIdOf,
+ ) -> DispatchResult {
+ let _creator = ensure_signed(origin)?;
+
+ let namespace_details =
+ NameSpaces::::get(&namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+ ensure!(!namespace_details.archive, Error::::ArchivedNameSpace);
+ ensure!(!namespace_details.approved, Error::::NameSpaceAlreadyApproved);
+
+ >::insert(
+ &namespace_id,
+ NameSpaceDetailsOf:: { approved: true, ..namespace_details },
+ );
+
+ Self::update_activity(
+ &namespace_id,
+ IdentifierTypeOf::ChainSpace,
+ CallTypeOf::CouncilRestore,
+ )
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::ApprovalRestore { namespace: namespace_id });
+
+ Ok(())
+ }
+ }
+}
+
+impl Pallet {
+ /// Adds a delegate to a namespace with specified permissions.
+ ///
+ /// This function will add a new delegate to a namespace, given the namespace's ID,
+ /// the delegate's information, and the required permissions. It constructs
+ /// an authorization ID based on the namespace ID, delegate, and creator,
+ /// ensuring that the delegate is not already added. It also checks that the
+ /// namespace is not archived, is approved, and has not exceeded its capacity.
+ fn space_delegate_addition(
+ namespace_id: NameSpaceIdOf,
+ delegate: NameSpaceCreatorOf,
+ creator: NameSpaceCreatorOf,
+ permissions: Permissions,
+ ) -> Result<(), Error> {
+ // Id Digest = concat (H(,
+ // , ))
+ let id_digest = T::Hashing::hash(
+ &[&namespace_id.encode()[..], &delegate.encode()[..], &creator.encode()[..]].concat()[..],
+ );
+
+ let delegate_authorization_id =
+ Ss58Identifier::create_identifier(&id_digest.encode(), IdentifierType::Authorization)
+ .map_err(|_| Error::::InvalidIdentifierLength)?;
+
+ ensure!(
+ !Authorizations::::contains_key(&delegate_authorization_id),
+ Error::::DelegateAlreadyAdded
+ );
+
+ let mut delegates = Delegates::::get(&namespace_id);
+ delegates
+ .try_push(delegate.clone())
+ .map_err(|_| Error::::NameSpaceDelegatesLimitExceeded)?;
+ Delegates::::insert(&namespace_id, delegates);
+
+ Authorizations::::insert(
+ &delegate_authorization_id,
+ NameSpaceAuthorizationOf:: {
+ namespace_id: namespace_id.clone(),
+ delegate: delegate.clone(),
+ permissions,
+ delegator: creator,
+ },
+ );
+
+ Self::update_activity(&namespace_id, IdentifierTypeOf::Auth, CallTypeOf::Authorization)
+ .map_err(Error::::from)?;
+
+ Self::deposit_event(Event::Authorization {
+ namespace: namespace_id,
+ authorization: delegate_authorization_id,
+ delegate,
+ });
+
+ Ok(())
+ }
+
+ /// Checks if a given entity is a delegate for the specified namespace.
+ ///
+ /// This function retrieves the list of delegates for a namespace and determines
+ /// whether the specified delegate is among them. It is a read-only
+ /// operation and does not modify the state.
+ pub fn is_a_delegate(tx_id: &NameSpaceIdOf, delegate: NameSpaceCreatorOf) -> bool {
+ >::get(tx_id).iter().any(|d| d == &delegate)
+ }
+
+ /// Verifies if a given delegate has a specific authorization.
+ ///
+ /// This function checks if the provided delegate is associated with the
+ /// given authorization ID and has the 'ASSERT' permission.
+ pub fn ensure_authorization_origin(
+ authorization_id: &AuthorizationIdOf,
+ delegate: &NameSpaceCreatorOf,
+ ) -> Result> {
+ let d =
+ >::get(authorization_id).ok_or(Error::::AuthorizationNotFound)?;
+
+ ensure!(d.delegate == *delegate, Error::::UnauthorizedOperation);
+
+ // TODO: Update all similar function names.
+ Self::validate_space_for_transaction(&d.namespace_id)?;
+
+ ensure!(d.permissions.contains(Permissions::ASSERT), Error::::UnauthorizedOperation);
+
+ Ok(d.namespace_id)
+ }
+
+ pub fn ensure_authorization_restore_origin(
+ authorization_id: &AuthorizationIdOf,
+ delegate: &NameSpaceCreatorOf,
+ ) -> Result> {
+ let d =
+ >::get(authorization_id).ok_or(Error::::AuthorizationNotFound)?;
+
+ ensure!(d.delegate == *delegate, Error::::UnauthorizedOperation);
+
+ Self::validate_space_for_restore_transaction(&d.namespace_id)?;
+
+ ensure!(d.permissions.contains(Permissions::ADMIN), Error::::UnauthorizedOperation);
+
+ Ok(d.namespace_id)
+ }
+
+ /// Checks if a given delegate is an admin for the namespace associated with the
+ /// authorization ID.
+ ///
+ /// This function verifies whether the specified delegate is the admin of
+ /// the namespace by checking the 'ADMIN' permission within the authorization
+ /// tied to the provided authorization ID.
+ pub fn ensure_authorization_admin_origin(
+ authorization_id: &AuthorizationIdOf,
+ delegate: &NameSpaceCreatorOf,
+ ) -> Result> {
+ let d =
+ >::get(authorization_id).ok_or(Error::::AuthorizationNotFound)?;
+
+ ensure!(d.delegate == *delegate, Error::::UnauthorizedOperation);
+
+ Self::validate_space_for_transaction(&d.namespace_id)?;
+
+ ensure!(d.permissions.contains(Permissions::ADMIN), Error::::UnauthorizedOperation);
+
+ Ok(d.namespace_id)
+ }
+
+ /// Ensures that the given delegate is authorized to perform an audit
+ /// operation on a namespace.
+ ///
+ /// This function checks whether the provided `authorization_id` corresponds
+ /// to an existing authorization and whether the delegate associated with
+ /// that authorization is allowed to perform audit operations. It also
+ /// increments usage and validates the namespace for transactions.
+ pub fn ensure_authorization_delegator_origin(
+ authorization_id: &AuthorizationIdOf,
+ delegate: &NameSpaceCreatorOf,
+ ) -> Result> {
+ let d =
+ >::get(authorization_id).ok_or(Error::::AuthorizationNotFound)?;
+
+ ensure!(d.delegate == *delegate, Error::::UnauthorizedOperation);
+
+ Self::validate_space_for_transaction(&d.namespace_id)?;
+
+ ensure!(
+ d.permissions.contains(Permissions::DELEGATE | Permissions::ADMIN),
+ Error::::UnauthorizedOperation
+ );
+
+ Ok(d.namespace_id)
+ }
+
+ /// Checks if a given delegate is an admin for the namespace associated with the
+ /// authorization ID.
+ ///
+ /// This function verifies whether the specified delegate is the admin of
+ /// the namespace by checking the 'ADMIN' permission within the authorization
+ /// tied to the provided authorization ID.
+ pub fn ensure_authorization_admin_remove_origin(
+ authorization_id: &AuthorizationIdOf,
+ delegate: &NameSpaceCreatorOf,
+ ) -> Result> {
+ let d =
+ >::get(authorization_id).ok_or(Error::::AuthorizationNotFound)?;
+
+ ensure!(d.delegate == *delegate, Error::::UnauthorizedOperation);
+
+ Self::validate_space_for_transaction(&d.namespace_id)?;
+
+ ensure!(d.permissions.contains(Permissions::ADMIN), Error::::UnauthorizedOperation);
+
+ Ok(d.namespace_id)
+ }
+
+ /// Validates that a namespace is eligible for a new transaction.
+ ///
+ /// This function ensures that a namespace is not archived, is approved, and has
+ /// not exceeded its capacity limit before allowing a new transaction to be
+ /// recorded. It is a critical check that enforces the integrity and
+ /// constraints of namespace usage on the chain.
+ pub fn validate_space_for_transaction(namespace_id: &NameSpaceIdOf) -> Result<(), Error> {
+ let namespace_details =
+ NameSpaces::::get(namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+
+ // Ensure the namespace is not archived.
+ ensure!(!namespace_details.archive, Error::::ArchivedNameSpace);
+
+ // Ensure the namespace is approved for transactions.
+ ensure!(namespace_details.approved, Error::::NameSpaceNotApproved);
+
+ Ok(())
+ }
+
+ /// Validates a namespace for restore transactions.
+ ///
+ /// This function checks that the specified namespace is approved and has not
+ /// exceeded its capacity limit. It is designed to be called before
+ /// performing any administrative actions on a namespace to ensure
+ /// that the namespace is in a proper state for such transactions.
+ pub fn validate_space_for_restore_transaction(
+ namespace_id: &NameSpaceIdOf,
+ ) -> Result<(), Error> {
+ let namespace_details =
+ NameSpaces::::get(namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+
+ // Ensure the namespace is archived.
+ ensure!(namespace_details.archive, Error::::NameSpaceNotArchived);
+
+ // Ensure the namespace is approved for transactions.
+ ensure!(namespace_details.approved, Error::::NameSpaceNotApproved);
+
+ Ok(())
+ }
+
+ /// Validates that a namespace can accommodate a batch of new entries without
+ /// exceeding its capacity.
+ ///
+ /// This function ensures that a namespace is not archived, is approved, and has
+ /// enough remaining capacity to accommodate a specified number of new
+ /// entries. It is a critical check that enforces the integrity and
+ /// constraints of namespace usage on the chain, especially when dealing
+ /// with batch operations.
+ pub fn validate_space_for_transaction_entries(
+ namespace_id: &NameSpaceIdOf,
+ _entries: u16,
+ ) -> Result<(), Error> {
+ let namespace_details =
+ NameSpaces::::get(namespace_id).ok_or(Error::::NameSpaceNotFound)?;
+
+ // Ensure the namespace is not archived.
+ ensure!(!namespace_details.archive, Error::::ArchivedNameSpace);
+
+ // Ensure the namespace is approved for adding new entries.
+ ensure!(namespace_details.approved, Error::::NameSpaceNotApproved);
+
+ Ok(())
+ }
+
+ /// Updates the global timeline with a new activity event for a namespace.
+ ///
+ /// This function is an internal mechanism that logs each significant change
+ /// to a namespace on the global timeline. It is automatically called by the
+ /// system whenever an update to a namespace occurs, capturing the type of
+ /// activity and the precise time at which it happened. This automated
+ /// tracking is crucial for maintaining a consistent and auditable record of
+ /// all namespace-related activities.
+ pub fn update_activity(
+ tx_id: &NameSpaceIdOf,
+ tx_type: IdentifierTypeOf,
+ tx_action: CallTypeOf,
+ ) -> Result<(), Error> {
+ let tx_moment = Self::timepoint();
+
+ let tx_entry = EventEntryOf { action: tx_action, location: tx_moment };
+ let _ = IdentifierTimeline::update_timeline::(tx_id, tx_type, tx_entry);
+ Ok(())
+ }
+
+ /// Retrieves the current timepoint.
+ ///
+ /// This function returns a `Timepoint` structure containing the current
+ /// block number and extrinsic index. It is typically used in conjunction
+ /// with `update_activity` to record when an event occurred.
+ pub fn timepoint() -> Timepoint {
+ Timepoint {
+ height: frame_system::Pallet::::block_number().unique_saturated_into(),
+ index: frame_system::Pallet::::extrinsic_index().unwrap_or_default(),
+ }
+ }
+}
diff --git a/pallets/namespace/src/mock.rs b/pallets/namespace/src/mock.rs
new file mode 100644
index 000000000..42fce696e
--- /dev/null
+++ b/pallets/namespace/src/mock.rs
@@ -0,0 +1,119 @@
+// This file is part of CORD – https://cord.network
+
+// Copyright (C) Dhiway Networks Pvt. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// CORD is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// CORD is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with CORD. If not, see .
+
+use crate as pallet_namespace;
+use cord_utilities::mock::{mock_origin, SubjectId};
+use frame_support::{derive_impl, parameter_types};
+use pallet_namespace::IsPermissioned;
+
+use frame_system::EnsureRoot;
+use sp_runtime::{
+ traits::{IdentifyAccount, IdentityLookup, Verify},
+ BuildStorage, MultiSignature,
+};
+
+type Signature = MultiSignature;
+type AccountPublic = ::Signer;
+pub type AccountId = ::AccountId;
+pub(crate) type Block = frame_system::mocking::MockBlock;
+
+frame_support::construct_runtime!(
+ pub enum Test {
+ System: frame_system,
+ NameSpace: pallet_namespace,
+ Identifier: identifier,
+ MockOrigin: mock_origin,
+ }
+);
+
+parameter_types! {
+ pub const SS58Prefix: u8 = 29;
+}
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
+impl frame_system::Config for Test {
+ type RuntimeOrigin = RuntimeOrigin;
+ type RuntimeCall = RuntimeCall;
+ type Block = Block;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type SS58Prefix = SS58Prefix;
+}
+
+impl mock_origin::Config for Test {
+ type RuntimeOrigin = RuntimeOrigin;
+ type AccountId = AccountId;
+ type SubjectId = SubjectId;
+}
+
+pub struct NetworkPermission;
+impl IsPermissioned for NetworkPermission {
+ fn is_permissioned() -> bool {
+ true
+ }
+}
+
+parameter_types! {
+ #[derive(Debug, Clone)]
+ pub const MaxNameSpaceDelegates: u32 = 5u32;
+}
+
+impl pallet_namespace::Config for Test {
+ type RuntimeEvent = RuntimeEvent;
+ type ChainSpaceOrigin = EnsureRoot;
+ type NetworkPermission = NetworkPermission;
+ type MaxNameSpaceDelegates = MaxNameSpaceDelegates;
+ type WeightInfo = ();
+}
+
+parameter_types! {
+ pub const MaxEventsHistory: u32 = 6u32;
+}
+
+impl identifier::Config for Test {
+ type MaxEventsHistory = MaxEventsHistory;
+}
+
+parameter_types! {
+ storage NameSpaceEvents: u32 = 0;
+}
+
+/// All events of this pallet.
+pub fn space_events_since_last_call() -> Vec> {
+ let events = System::events()
+ .into_iter()
+ .map(|r| r.event)
+ .filter_map(|e| if let RuntimeEvent::NameSpace(inner) = e { Some(inner) } else { None })
+ .collect::>();
+ let already_seen = NameSpaceEvents::get();
+ NameSpaceEvents::set(&(events.len() as u32));
+ events.into_iter().skip(already_seen as usize).collect()
+}
+
+#[allow(dead_code)]
+pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
+ let t: sp_runtime::Storage =
+ frame_system::GenesisConfig::::default().build_storage().unwrap();
+ let mut ext = sp_io::TestExternalities::new(t);
+ #[cfg(feature = "runtime-benchmarks")]
+ let keystore = sp_keystore::testing::MemoryKeystore::new();
+ #[cfg(feature = "runtime-benchmarks")]
+ ext.register_extension(sp_keystore::KeystoreExt(sp_std::sync::Arc::new(keystore)));
+ ext.execute_with(|| System::set_block_number(1));
+ ext
+}
diff --git a/pallets/namespace/src/tests.rs b/pallets/namespace/src/tests.rs
new file mode 100644
index 000000000..42ac48e97
--- /dev/null
+++ b/pallets/namespace/src/tests.rs
@@ -0,0 +1,57 @@
+use super::*;
+use crate::mock::*;
+use codec::Encode;
+use frame_support::assert_ok;
+use sp_runtime::traits::Hash;
+use sp_std::prelude::*;
+
+pub fn generate_namespace_id(digest: &NameSpaceCodeOf) -> NameSpaceIdOf {
+ Ss58Identifier::create_identifier(&(digest).encode()[..], IdentifierType::Space).unwrap()
+}
+
+pub fn generate_authorization_id(digest: &NameSpaceCodeOf) -> AuthorizationIdOf {
+ Ss58Identifier::create_identifier(&(digest).encode()[..], IdentifierType::Authorization)
+ .unwrap()
+}
+
+pub(crate) const ACCOUNT_00: AccountId = AccountId::new([1u8; 32]);
+pub(crate) const ACCOUNT_01: AccountId = AccountId::new([2u8; 32]);
+
+//TEST FUNCTION FOR ADD DELEGATE
+#[test]
+fn add_delegate_should_succeed() {
+ let creator = ACCOUNT_00;
+ let delegate = ACCOUNT_01;
+ let space = [2u8; 256].to_vec();
+ let space_digest = ::Hashing::hash(&space.encode()[..]);
+
+ let id_digest = ::Hashing::hash(
+ &[&space_digest.encode()[..], &creator.encode()[..]].concat()[..],
+ );
+
+ let space_id: NameSpaceIdOf = generate_namespace_id::(&id_digest);
+
+ let auth_id_digest = ::Hashing::hash(
+ &[&space_id.encode()[..], &creator.encode()[..], &creator.encode()[..]].concat()[..],
+ );
+
+ let authorization_id: AuthorizationIdOf = generate_authorization_id::(&auth_id_digest);
+ new_test_ext().execute_with(|| {
+ assert_ok!(NameSpace::create(
+ frame_system::RawOrigin::Signed(creator.clone()).into(),
+ space_digest,
+ ));
+
+ assert_ok!(NameSpace::approve(
+ frame_system::RawOrigin::Signed(creator.clone()).into(),
+ space_id.clone(),
+ ));
+
+ assert_ok!(NameSpace::add_delegate(
+ frame_system::RawOrigin::Signed(creator.clone()).into(),
+ space_id,
+ delegate.clone(),
+ authorization_id,
+ ));
+ });
+}
diff --git a/pallets/namespace/src/types.rs b/pallets/namespace/src/types.rs
new file mode 100644
index 000000000..fe8381b3a
--- /dev/null
+++ b/pallets/namespace/src/types.rs
@@ -0,0 +1,109 @@
+// This file is part of CORD – https://cord.network
+
+// Copyright (C) Dhiway Networks Pvt. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// CORD is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// CORD is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with CORD. If not, see .
+//
+//! # Space Management Module Types
+//!
+//! This module defines types used for managing spaces within the blockchain,
+//! including permissions, space details, and space authorizations.
+
+use bitflags::bitflags;
+use codec::{Decode, Encode, MaxEncodedLen};
+use scale_info::TypeInfo;
+use sp_runtime::RuntimeDebug;
+
+bitflags! {
+ #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
+ pub struct Permissions: u32 {
+ const ASSERT = 0b0000_0001;
+ const DELEGATE = 0b0000_0010;
+ const ADMIN = 0b0000_0100;
+
+ }
+}
+
+impl Permissions {
+ // Encodes the permission bitflags into a 4-byte array.
+ ///
+ /// This method is useful for serialization and storage purposes, as it
+ /// converts the internal representation of the permissions into a format
+ /// that can be easily stored and transmitted.
+ ///
+ /// Returns a `[u8; 4]` representing the encoded permissions.
+ pub fn as_u8(self) -> [u8; 4] {
+ let x: u32 = self.bits;
+ let b1: u8 = ((x >> 24) & 0xff) as u8;
+ let b2: u8 = ((x >> 16) & 0xff) as u8;
+ let b3: u8 = ((x >> 8) & 0xff) as u8;
+ let b4: u8 = (x & 0xff) as u8;
+ [b4, b3, b2, b1]
+ }
+}
+
+impl Default for Permissions {
+ /// Provides a default for the Permissions struct.
+ ///
+ /// By default, the `ASSERT` permission is granted
+ fn default() -> Self {
+ Permissions::ASSERT
+ }
+}
+
+/// Details of an on-chain namespace.
+///
+/// This structure holds metadata about a namespace, including its identifier,
+/// creator, capacity, and current usage. It also tracks the approval and
+/// archival status of the namespace.
+///
+/// ## Fields
+///
+/// - `code`: The unique code or identifier for the namespace.
+/// - `creator`: The account or entity that created the namespace.
+/// - `txn_capacity`: The maximum allowed transactions within the namespace. A value of zero denotes
+/// unlimited capacity.
+/// - `txn_count`: The current usage of the namespace's capacity.
+/// - `approved`: Indicates whether the namespace has been approved by the appropriate governance
+/// body.
+/// - `archive`: Indicates whether the namespace is currently archived.
+#[derive(Encode, Decode, Clone, MaxEncodedLen, RuntimeDebug, PartialEq, Eq, TypeInfo)]
+pub struct NameSpaceDetails {
+ pub code: NameSpaceCodeOf,
+ pub creator: NameSpaceCreatorOf,
+ pub approved: StatusOf,
+ pub archive: StatusOf,
+ pub registry_id: Option,
+}
+
+/// Authorization details for a namespace delegate.
+///
+/// This structure defines the permissions granted to a delegate within a namespace,
+/// as well as the delegator who granted these permissions. It is used to manage
+/// and verify the actions that delegates are allowed to perform within a namespace.
+///
+/// ## Fields
+///
+/// - `namespace_id`: The identifier of the namespace to which the authorization applies.
+/// - `delegate`: The entity that has been granted permissions within the namespace.
+/// - `permissions`: The specific permissions granted to the delegate.
+/// - `delegator`: The entity that granted the permissions to the delegates
+#[derive(Encode, Decode, Clone, MaxEncodedLen, RuntimeDebug, PartialEq, Eq, TypeInfo)]
+pub struct NameSpaceAuthorization {
+ pub namespace_id: NameSpaceIdOf,
+ pub delegate: NameSpaceCreatorOf,
+ pub permissions: Permissions,
+ pub delegator: NameSpaceCreatorOf,
+}
diff --git a/runtimes/braid/Cargo.toml b/runtimes/braid/Cargo.toml
index 80d429ce7..c66a467c1 100644
--- a/runtimes/braid/Cargo.toml
+++ b/runtimes/braid/Cargo.toml
@@ -54,6 +54,7 @@ pallet-session-benchmarking = { workspace = true }
pallet-registries = { workspace = true }
pallet-entries = { workspace = true }
pallet-schema-accounts = { workspace = true }
+pallet-namespace = { workspace = true }
# Internal runtime API (with default disabled)
pallet-did-runtime-api = { workspace = true }
@@ -171,6 +172,7 @@ std = [
"pallet-registries/std",
"pallet-entries/std",
"pallet-schema-accounts/std",
+ "pallet-namespace/std",
"pallet-network-score/std",
"pallet-network-membership/std",
"pallet-runtime-upgrade/std",
@@ -222,6 +224,7 @@ runtime-benchmarks = [
"pallet-schema/runtime-benchmarks",
"pallet-statement/runtime-benchmarks",
"pallet-chain-space/runtime-benchmarks",
+ "pallet-namespace/runtime-benchmarks",
"pallet-network-membership/runtime-benchmarks",
"hex-literal",
"pallet-sudo/runtime-benchmarks",
@@ -262,6 +265,7 @@ try-runtime = [
"pallet-babe/try-runtime",
"pallet-schema/try-runtime",
"pallet-chain-space/try-runtime",
+ "pallet-namespace/try-runtime",
"pallet-statement/try-runtime",
"pallet-did/try-runtime",
"pallet-did-name/try-runtime",
diff --git a/runtimes/braid/src/lib.rs b/runtimes/braid/src/lib.rs
index 2300e90d0..194627f2f 100644
--- a/runtimes/braid/src/lib.rs
+++ b/runtimes/braid/src/lib.rs
@@ -762,6 +762,18 @@ parameter_types! {
pub const MaxRegistryDelegates: u32 = 10_000;
}
+parameter_types! {
+ pub const MaxNameSpaceDelegates: u32 = 10_000;
+}
+
+impl pallet_namespace::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type ChainSpaceOrigin = EnsureRoot;
+ type NetworkPermission = NetworkParameters;
+ type MaxNameSpaceDelegates = MaxNameSpaceDelegates;
+ type WeightInfo = ();
+}
+
impl pallet_registries::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type MaxRegistryDelegates = MaxRegistryDelegates;
@@ -998,6 +1010,9 @@ mod runtime {
#[runtime::pallet_index(63)]
pub type SchemaAccounts = pallet_schema_accounts;
+ #[runtime::pallet_index(64)]
+ pub type NameSpace = pallet_namespace;
+
#[runtime::pallet_index(255)]
pub type Sudo = pallet_sudo;
}
diff --git a/runtimes/loom/Cargo.toml b/runtimes/loom/Cargo.toml
index d304af2c7..594e98414 100644
--- a/runtimes/loom/Cargo.toml
+++ b/runtimes/loom/Cargo.toml
@@ -44,6 +44,7 @@ pallet-did-name = { workspace = true }
pallet-schema = { workspace = true }
pallet-config = { workspace = true }
pallet-chain-space = { workspace = true }
+pallet-namespace = { workspace = true }
pallet-statement = { workspace = true }
pallet-network-membership = { workspace = true }
pallet-runtime-upgrade = { workspace = true }
@@ -176,6 +177,7 @@ std = [
"pallet-did-name/std",
"pallet-schema/std",
"pallet-chain-space/std",
+ "pallet-namespace/std",
"pallet-statement/std",
"pallet-network-score/std",
"pallet-network-membership/std",
@@ -234,6 +236,7 @@ runtime-benchmarks = [
"pallet-schema/runtime-benchmarks",
"pallet-statement/runtime-benchmarks",
"pallet-chain-space/runtime-benchmarks",
+ "pallet-namespace/runtime-benchmarks",
"pallet-network-membership/runtime-benchmarks",
"hex-literal",
"pallet-sudo/runtime-benchmarks",
@@ -279,6 +282,7 @@ try-runtime = [
"pallet-babe/try-runtime",
"pallet-schema/try-runtime",
"pallet-chain-space/try-runtime",
+ "pallet-namespace/try-runtime",
"pallet-statement/try-runtime",
"pallet-did/try-runtime",
"pallet-did-name/try-runtime",
diff --git a/runtimes/loom/src/lib.rs b/runtimes/loom/src/lib.rs
index 776066f05..05b530a46 100644
--- a/runtimes/loom/src/lib.rs
+++ b/runtimes/loom/src/lib.rs
@@ -894,6 +894,18 @@ impl pallet_chain_space::Config for Runtime {
type WeightInfo = weights::pallet_chain_space::WeightInfo;
}
+parameter_types! {
+ pub const MaxNameSpaceDelegates: u32 = 10_000;
+}
+
+impl pallet_namespace::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type ChainSpaceOrigin = EnsureRoot;
+ type NetworkPermission = NetworkParameters;
+ type MaxNameSpaceDelegates = MaxNameSpaceDelegates;
+ type WeightInfo = ();
+}
+
parameter_types! {
pub const MaxRegistryBlobSize: u32 = 4 * 1024;
pub const MaxEncodedInputLength: u32 = 30;
@@ -1157,6 +1169,9 @@ mod runtime {
#[runtime::pallet_index(63)]
pub type SchemaAccounts = pallet_schema_accounts;
+ #[runtime::pallet_index(64)]
+ pub type NameSpace = pallet_namespace;
+
#[runtime::pallet_index(254)]
pub type RootTesting = pallet_root_testing;
diff --git a/runtimes/weave/Cargo.toml b/runtimes/weave/Cargo.toml
index 121db777e..edc309f8f 100644
--- a/runtimes/weave/Cargo.toml
+++ b/runtimes/weave/Cargo.toml
@@ -44,6 +44,7 @@ pallet-did-name = { workspace = true }
pallet-schema = { workspace = true }
pallet-config = { workspace = true }
pallet-chain-space = { workspace = true }
+pallet-namespace = { workspace = true }
pallet-statement = { workspace = true }
pallet-network-membership = { workspace = true }
pallet-runtime-upgrade = { workspace = true }
@@ -175,6 +176,7 @@ std = [
"pallet-did-name/std",
"pallet-schema/std",
"pallet-chain-space/std",
+ "pallet-namespace/std",
"pallet-statement/std",
"pallet-network-score/std",
"pallet-network-membership/std",
@@ -233,6 +235,7 @@ runtime-benchmarks = [
"pallet-schema/runtime-benchmarks",
"pallet-statement/runtime-benchmarks",
"pallet-chain-space/runtime-benchmarks",
+ "pallet-namespace/runtime-benchmarks",
"pallet-network-membership/runtime-benchmarks",
"hex-literal",
"pallet-sudo/runtime-benchmarks",
@@ -278,6 +281,7 @@ try-runtime = [
"pallet-babe/try-runtime",
"pallet-schema/try-runtime",
"pallet-chain-space/try-runtime",
+ "pallet-namespace/try-runtime",
"pallet-statement/try-runtime",
"pallet-did/try-runtime",
"pallet-did-name/try-runtime",
diff --git a/runtimes/weave/src/lib.rs b/runtimes/weave/src/lib.rs
index 6b0dcd09e..60a5b58b1 100644
--- a/runtimes/weave/src/lib.rs
+++ b/runtimes/weave/src/lib.rs
@@ -894,6 +894,18 @@ impl pallet_chain_space::Config for Runtime {
type WeightInfo = weights::pallet_chain_space::WeightInfo;
}
+parameter_types! {
+ pub const MaxNameSpaceDelegates: u32 = 10_000;
+}
+
+impl pallet_namespace::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type ChainSpaceOrigin = EnsureRoot;
+ type NetworkPermission = NetworkParameters;
+ type MaxNameSpaceDelegates = MaxNameSpaceDelegates;
+ type WeightInfo = ();
+}
+
parameter_types! {
pub const MaxRegistryBlobSize: u32 = 4 * 1024;
pub const MaxEncodedInputLength: u32 = 30;
@@ -1154,6 +1166,9 @@ mod runtime {
#[runtime::pallet_index(63)]
pub type SchemaAccounts = pallet_schema_accounts;
+ #[runtime::pallet_index(64)]
+ pub type NameSpace = pallet_namespace;
+
#[runtime::pallet_index(255)]
pub type Sudo = pallet_sudo;
}