diff --git a/pallets/namespace/src/lib.rs b/pallets/namespace/src/lib.rs index e13b8543..de4780c5 100644 --- a/pallets/namespace/src/lib.rs +++ b/pallets/namespace/src/lib.rs @@ -266,6 +266,8 @@ pub mod pallet { AuthorizationNotFound, /// Delegate not found. DelegateNotFound, + /// Namespace Registry list limit exceeded. + NameSpaceRegistryListLimitExceeded, } #[pallet::call] @@ -571,7 +573,7 @@ pub mod pallet { digest, creator: creator.clone(), archive: false, - registry_id: Some(BoundedVec::default()), + registry_ids: Some(BoundedVec::default()), }, ); @@ -943,6 +945,57 @@ impl Pallet { Ok(()) } + /// Adds a registry ID to the list of registry IDs associated with a namespace. + /// + /// This function updates the namespace's `registry_ids` list by appending the specified + /// `registry_id` if it is not already present. If the list is uninitialized (i.e., `None`), + /// it initializes the list and adds the `registry_id`. The function ensures that the + /// `BoundedVec` does not exceed its capacity, returning an error if the limit is reached. + /// + /// # Parameters + /// - `namespace_id`: A reference to the ID of the namespace where the registry ID should be + /// added. + /// - `registry_id`: A reference to the registry ID to be added to the namespace's list of + /// registry IDs. + /// + /// # Returns + /// - `Ok(())`: If the `registry_id` was successfully added or was already present in the list. + /// - `Err(Error::NameSpaceNotFound)`: If the specified namespace does not exist. + /// - `Err(Error::NameSpaceRegistryListLimitExceeded)`: If the `registry_ids` list exceeds + /// its maximum capacity while attempting to add the `registry_id`. + /// + /// # Errors + /// - Returns `NameSpaceNotFound` if the `namespace_id` does not exist in the storage. + /// - Returns `NameSpaceRegistryListLimitExceeded` if the `registry_ids` list cannot accommodate + /// any more entries. + pub fn add_registry_id_to_namespace_details( + namespace_id: &NameSpaceIdOf, + registry_id: &RegistryIdOf, + ) -> Result<(), Error> { + NameSpaces::::try_mutate(namespace_id, |space_opt| { + if let Some(space_details) = space_opt { + if let Some(ref mut registry_ids) = space_details.registry_ids { + if !registry_ids.contains(registry_id) { + registry_ids + .try_push(registry_id.clone()) + .map_err(|_| Error::::NameSpaceRegistryListLimitExceeded)?; + } + } else { + // Below is required to avoid runtime panic when intialized with None. + let mut new_registry_ids = BoundedVec::default(); + new_registry_ids + .try_push(registry_id.clone()) + .map_err(|_| Error::::NameSpaceRegistryListLimitExceeded)?; + space_details.registry_ids = Some(new_registry_ids); + } + + Ok(()) + } else { + Err(Error::::NameSpaceNotFound) + } + }) + } + /// Updates the global timeline with a new activity event for a namespace. /// /// This function is an internal mechanism that logs each significant change diff --git a/pallets/namespace/src/types.rs b/pallets/namespace/src/types.rs index a727f1ad..ae9339a2 100644 --- a/pallets/namespace/src/types.rs +++ b/pallets/namespace/src/types.rs @@ -78,7 +78,7 @@ pub struct NameSpaceDetails, + pub registry_ids: Option, } /// Authorization details for a namespace delegate. diff --git a/pallets/registries/src/lib.rs b/pallets/registries/src/lib.rs index e077ecf0..45d46459 100644 --- a/pallets/registries/src/lib.rs +++ b/pallets/registries/src/lib.rs @@ -112,6 +112,8 @@ use sp_runtime::traits::{Hash, UniqueSaturatedInto}; pub type RegistryAuthorizationIdOf = Ss58Identifier; /// Namespace Authorization Identifier pub type NamespaceAuthorizationIdOf = Ss58Identifier; +/// Type of the Namespace Id +pub type NameSpaceIdOf = Ss58Identifier; /// Type of the Registry Id pub type RegistryIdOf = Ss58Identifier; /// Tyoe of the Registry Digest @@ -131,7 +133,7 @@ pub type RegistryAuthorizationOf = RegistryAuthorization, Permissions>; /// Type of Registry Details pub type RegistryDetailsOf = - RegistryDetails, StatusOf, RegistryHashOf, SchemaIdOf>; + RegistryDetails, StatusOf, RegistryHashOf, NameSpaceIdOf, SchemaIdOf>; #[frame_support::pallet] pub mod pallet { @@ -607,7 +609,7 @@ pub mod pallet { ) -> DispatchResult { let creator = ensure_signed(origin)?; - let _namespace_id = pallet_namespace::Pallet::::ensure_authorization_origin( + let namespace_id = pallet_namespace::Pallet::::ensure_authorization_origin( &namespace_authorization, &creator, ) @@ -669,10 +671,18 @@ pub mod pallet { revoked: false, archived: false, digest, + namespace_id: namespace_id.clone(), schema_id, }, ); + // Update the namespace with the newly added registry. + pallet_namespace::Pallet::::add_registry_id_to_namespace_details( + &namespace_id, + &identifier, + ) + .map_err(>::from)?; + Self::update_activity(&identifier, IdentifierTypeOf::Registries, CallTypeOf::Genesis) .map_err(Error::::from)?; diff --git a/pallets/registries/src/tests.rs b/pallets/registries/src/tests.rs index 8836383f..451e05f6 100644 --- a/pallets/registries/src/tests.rs +++ b/pallets/registries/src/tests.rs @@ -2035,3 +2035,119 @@ fn add_delegator_should_fail_if_registry_delegates_limit_exceeded() { ); }); } + +#[test] +fn registry_id_should_be_updated_on_namespace_chainstorage_on_create() { + let creator = ACCOUNT_00; + + let namespace = [2u8; 256].to_vec(); + let namespace_digest = ::Hashing::hash(&namespace.encode()[..]); + + let id_digest = ::Hashing::hash( + &[&namespace_digest.encode()[..], &creator.encode()[..]].concat()[..], + ); + let namespace_id: NameSpaceIdOf = generate_namespace_id::(&id_digest); + + let namespace_auth_id_digest = ::Hashing::hash( + &[&namespace_id.encode()[..], &creator.encode()[..], &creator.encode()[..]].concat()[..], + ); + let namespace_authorization_id: NamespaceAuthorizationIdOf = + generate_namespace_authorization_id::(&namespace_auth_id_digest); + + let registry = [2u8; 256].to_vec(); + + let raw_blob = [2u8; 256].to_vec(); + let blob: RegistryBlobOf = BoundedVec::try_from(raw_blob) + .expect("Test blob should fit into the expected input length of for the test runtime."); + + let registry_digest = ::Hashing::hash(®istry.encode()[..]); + + let id_digest = ::Hashing::hash( + &[®istry_digest.encode()[..], &creator.encode()[..]].concat()[..], + ); + + let registry_id: RegistryIdOf = generate_registry_id::(&id_digest); + + let registry_2 = [3u8; 256].to_vec(); + + let raw_blob_2 = [3u8; 256].to_vec(); + let blob_2: RegistryBlobOf = BoundedVec::try_from(raw_blob_2) + .expect("Test blob should fit into the expected input length of for the test runtime."); + + let registry_digest_2 = ::Hashing::hash(®istry_2.encode()[..]); + + let id_digest_2 = ::Hashing::hash( + &[®istry_digest_2.encode()[..], &creator.encode()[..]].concat()[..], + ); + + let registry_id_2: RegistryIdOf = generate_registry_id::(&id_digest_2); + + let raw_schema = [2u8; 256].to_vec(); + let schema: InputSchemaOf = BoundedVec::try_from(raw_schema) + .expect("Test Schema should fit into the expected input length of for the test runtime."); + let _digest: SchemaHashOf = ::Hashing::hash(&schema[..]); + let schema_id_digest = ::Hashing::hash(&schema.encode()[..]); + let schema_id: SchemaIdOf = generate_schema_id::(&schema_id_digest); + + new_test_ext().execute_with(|| { + assert_ok!(NameSpace::create( + frame_system::RawOrigin::Signed(creator.clone()).into(), + namespace_digest, + None, + )); + + // Create Registry 1 + assert_ok!(Registries::create( + frame_system::RawOrigin::Signed(creator.clone()).into(), + registry_digest, + namespace_authorization_id.clone(), + Some(schema_id.clone()), + Some(blob) + )); + + // Create Registry 2 + assert_ok!(Registries::create( + frame_system::RawOrigin::Signed(creator.clone()).into(), + registry_digest_2, + namespace_authorization_id.clone(), + Some(schema_id), + Some(blob_2) + )); + + // Verify if the newly created registry-id is added as a list in the Namespace Chain + // Storage. + let name_space_details = pallet_namespace::NameSpaces::::get(namespace_id.clone()) + .ok_or(pallet_namespace::pallet::Error::::NameSpaceNotFound) + .unwrap(); + assert!( + name_space_details + .registry_ids + .clone() + .unwrap_or_default() + .contains(®istry_id), + "Registry ID 1 not found in the Namespace Chain Storage." + ); + assert!( + name_space_details.registry_ids.unwrap_or_default().contains(®istry_id_2), + "Registry ID 2 not found in the Namespace Chain Storage." + ); + + // Verify if the newly created registry-id 1 is present in the Registry Chain Storage. + let registry_info = RegistryInfo::::get(®istry_id) + .ok_or(Error::::RegistryNotFound) + .unwrap(); + assert_eq!( + registry_info.namespace_id, namespace_id, + "Namespace ID not found in the Registry 1 Chain Storage." + ); + + // Verify if the newly created registry-id 2 is present in the Registry Chain Storage. + let registry_info = RegistryInfo::::get(®istry_id_2) + .ok_or(Error::::RegistryNotFound) + .unwrap(); + assert_eq!( + registry_info.namespace_id, namespace_id, + "Namespace ID not found in the Registry 2 Chain Storage." + ); + }); +} diff --git a/pallets/registries/src/types.rs b/pallets/registries/src/types.rs index 37c66a9e..fdcb25be 100644 --- a/pallets/registries/src/types.rs +++ b/pallets/registries/src/types.rs @@ -75,11 +75,12 @@ impl Default for Permissions { /// - `digest`: A hash representing unique content or metadata of the registry. /// - `schema_id`: (Optional) Identifier linking the registry to a specific schema. #[derive(Encode, Decode, Clone, MaxEncodedLen, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct RegistryDetails { +pub struct RegistryDetails { pub creator: RegistryCreatorOf, pub revoked: StatusOf, pub archived: StatusOf, pub digest: RegistryHashOf, + pub namespace_id: NameSpaceIdOf, pub schema_id: Option, }