Browse Source

Merge pull request #396 from iorveth/content_dir_2_reset

Content dir 2
Bedeho Mender 4 years ago
parent
commit
fe3bdeec08

+ 18 - 0
Cargo.lock

@@ -4689,6 +4689,24 @@ dependencies = [
  "substrate-primitives",
 ]
 
+[[package]]
+name = "substrate-content-directory-module"
+version = "1.0.1"
+dependencies = [
+ "hex-literal 0.1.4",
+ "parity-scale-codec",
+ "quote 0.6.13",
+ "serde",
+ "sr-io",
+ "sr-primitives",
+ "sr-std",
+ "srml-support",
+ "srml-support-procedural",
+ "srml-system",
+ "srml-timestamp",
+ "substrate-primitives",
+]
+
 [[package]]
 name = "substrate-content-working-group-module"
 version = "1.0.0"

+ 2 - 1
Cargo.toml

@@ -19,10 +19,11 @@ members = [
 	"runtime-modules/token-minting",
 	"runtime-modules/versioned-store",
 	"runtime-modules/versioned-store-permissions",
+	"runtime-modules/content-directory",
 	"node",
 	"utils/chain-spec-builder/"
 ]
 
 [profile.release]
 # Substrate runtime requires unwinding.
-panic = "unwind"
+panic = "unwind"

+ 40 - 0
runtime-modules/content-directory/Cargo.toml

@@ -0,0 +1,40 @@
+[package]
+name = 'substrate-content-directory-module'
+version = '1.0.1'
+authors = ['Joystream contributors']
+edition = '2018'
+
+[dependencies]
+hex-literal = '0.1.0'
+codec = { package = 'parity-scale-codec', version = '1.0.0', default-features = false, features = ['derive'] }
+rstd = { package = 'sr-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+runtime-primitives = { package = 'sr-primitives', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+srml-support = { package = 'srml-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+srml-support-procedural = { package = 'srml-support-procedural', git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+system = { package = 'srml-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+timestamp = { package = 'srml-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+runtime-io = { package = 'sr-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+# https://users.rust-lang.org/t/failure-derive-compilation-error/39062
+quote = '<=1.0.2'
+
+[dependencies.serde]
+features = ['derive']
+optional = true
+version = '1.0.101'
+
+[dev-dependencies]
+runtime-io = { package = 'sr-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+primitives = { package = 'substrate-primitives', git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+
+[features]
+default = ['std']
+std = [
+	'serde',
+	'codec/std',
+	'rstd/std',
+	'runtime-io/std',
+	'runtime-primitives/std',
+	'srml-support/std',
+	'system/std',
+	'timestamp/std',
+]

+ 114 - 0
runtime-modules/content-directory/src/errors.rs

@@ -0,0 +1,114 @@
+// Validation errors
+// --------------------------------------
+
+pub const ERROR_PROPERTY_NAME_TOO_SHORT: &str = "Property name is too short";
+pub const ERROR_PROPERTY_NAME_TOO_LONG: &str = "Property name is too long";
+pub const ERROR_PROPERTY_DESCRIPTION_TOO_SHORT: &str = "Property description is too long";
+pub const ERROR_PROPERTY_DESCRIPTION_TOO_LONG: &str = "Property description is too long";
+
+pub const ERROR_CLASS_NAME_TOO_SHORT: &str = "Class name is too short";
+pub const ERROR_CLASS_NAME_TOO_LONG: &str = "Class name is too long";
+pub const ERROR_CLASS_DESCRIPTION_TOO_SHORT: &str = "Class description is too long";
+pub const ERROR_CLASS_DESCRIPTION_TOO_LONG: &str = "Class description is too long";
+
+pub const ERROR_CLASS_LIMIT_REACHED: &str = "Maximum number of classes limit reached";
+pub const ERROR_CLASS_SCHEMAS_LIMIT_REACHED: &str =
+    "Maximum number of given class schemas limit reached";
+pub const ERROR_CLASS_PROPERTIES_LIMIT_REACHED: &str =
+    "Maximum number of properties in schema limit reached";
+pub const ERROR_PER_CONTROLLER_ENTITIES_CREATION_LIMIT_EXCEEDS_OVERALL_LIMIT: &str =
+    "Entities creation limit per controller should be less than overall entities creation limit";
+pub const ERROR_ENTITIES_NUMBER_PER_CLASS_CONSTRAINT_VIOLATED: &str =
+    "Number of entities per class is to big";
+pub const ERROR_NUMBER_OF_CLASS_ENTITIES_PER_ACTOR_CONSTRAINT_VIOLATED: &str =
+    "Number of class entities per actor constraint violated";
+pub const ERROR_INDIVIDUAL_NUMBER_OF_CLASS_ENTITIES_PER_ACTOR_IS_TOO_BIG: &str =
+    "Individual number of class entities per actor is too big";
+pub const ERROR_NEW_ENTITIES_MAX_COUNT_IS_LESS_THAN_NUMBER_OF_ALREADY_CREATED: &str =
+    "Cannot set voucher entities count to be less than number of already created entities";
+pub const ERROR_MAX_NUMBER_OF_OPERATIONS_DURING_ATOMIC_BATCHING_LIMIT_REACHED: &str =
+    "Number of operations during atomic batching limit reached";
+pub const ERROR_TEXT_PROP_IS_TOO_LONG: &str = "Text property is too long";
+pub const ERROR_VEC_PROP_IS_TOO_LONG: &str = "Vector property is too long";
+pub const ERROR_ENTITY_PROP_VALUE_VECTOR_IS_TOO_LONG: &str =
+    "Propery value vector can`t contain more values";
+pub const ERROR_ENTITY_PROP_VALUE_VECTOR_INDEX_IS_OUT_OF_RANGE: &str =
+    "Given property value vector index is out of range";
+
+// Main logic errors
+// --------------------------------------
+
+pub const ERROR_CLASS_NOT_FOUND: &str = "Class was not found by id";
+pub const ERROR_UNKNOWN_CLASS_SCHEMA_ID: &str = "Unknown class schema id";
+pub const ERROR_CLASS_SCHEMA_NOT_ACTIVE: &str = "Given class schema is not active";
+pub const ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_PROP_INDEX: &str =
+    "New class schema refers to an unknown property index";
+pub const ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_CLASS: &str =
+    "New class schema refers to an unknown class id";
+pub const ERROR_NO_PROPS_IN_CLASS_SCHEMA: &str =
+    "Cannot add a class schema with an empty list of properties";
+pub const ERROR_ENTITY_NOT_FOUND: &str = "Entity was not found by id";
+pub const ERROR_SCHEMA_ALREADY_ADDED_TO_ENTITY: &str =
+    "Cannot add a schema that is already added to this entity";
+pub const ERROR_PROP_VALUE_DONT_MATCH_TYPE: &str =
+    "Some of the provided property values don't match the expected property type";
+pub const ERROR_PROP_VALUE_DONT_MATCH_VEC_TYPE: &str =
+    "Property value don't match the expected vector property type";
+pub const ERROR_PROP_VALUE_UNDER_GIVEN_INDEX_IS_NOT_A_VECTOR: &str =
+    "Property value under given index is not a vector";
+pub const ERROR_PROP_VALUE_VEC_NONCES_DOES_NOT_MATCH: &str =
+    "Current property value vector nonce does not equal to provided one";
+pub const ERROR_PROP_NAME_NOT_UNIQUE_IN_A_CLASS: &str =
+    "Property name is not unique within its class";
+pub const ERROR_MISSING_REQUIRED_PROP: &str =
+    "Some required property was not found when adding schema support to entity";
+pub const ERROR_UNKNOWN_ENTITY_PROP_ID: &str = "Some of the provided property ids cannot be found on the current list of propery values of this entity";
+pub const ERROR_PROP_VALUE_TYPE_DOESNT_MATCH_INTERNAL_ENTITY_VECTOR_TYPE: &str =
+    "Propery value type does not match internal entity vector type";
+pub const ERROR_PROP_DOES_NOT_MATCH_ITS_CLASS: &str = "Internal property does not match its class";
+pub const ERROR_ENTITY_RC_DOES_NOT_EQUAL_TO_ZERO: &str =
+    "Entity removal can`t be completed, as there are some property values pointing to given entity";
+pub const ERROR_ENTITY_INBOUND_SAME_OWNER_RC_DOES_NOT_EQUAL_TO_ZERO: &str =
+    "Entity ownership transfer can`t be completed, as there are some property values pointing to given entity with same owner flag set";
+pub const ERROR_CLASS_PROP_NOT_FOUND: &str = "Class property under given index not found";
+pub const ERROR_CURATOR_GROUP_REMOVAL_FORBIDDEN: &str =
+    "Curator group can`t be removed, as it currently maintains at least one class";
+
+// Permission errors
+
+pub const ERROR_ALL_PROP_WERE_LOCKED_ON_CLASS_LEVEL: &str =
+    "All property values, related to a given entity were locked on class level";
+pub const ERROR_CURATOR_IS_NOT_A_MEMBER_OF_A_GIVEN_CURATOR_GROUP: &str =
+    "Curator under provided curator id is not a member of curaror group under given id";
+pub const ERROR_CURATOR_GROUP_DOES_NOT_EXIST: &str = "Given curator group does not exist";
+pub const ERROR_SAME_CONTROLLER_CONSTRAINT_VIOLATION: &str =
+    "Entity should be referenced from the entity, owned by the same controller";
+pub const ERROR_MAINTAINER_DOES_NOT_EXIST: &str = "Given maintainer does not exist";
+pub const ERROR_MAINTAINER_ALREADY_EXISTS: &str = "Given maintainer already exist";
+pub const ERROR_ACTOR_CAN_NOT_CREATE_ENTITIES: &str =
+    "Provided actor can`t create entities of given class";
+pub const ERROR_MAX_NUMBER_OF_ENTITIES_PER_CLASS_LIMIT_REACHED: &str =
+    "Maximum numbers of entities per class limit reached";
+pub const ERROR_ENTITY_CREATION_BLOCKED: &str = "Current class entities creation blocked";
+pub const ERROR_VOUCHER_LIMIT_REACHED: &str = "Entities voucher limit reached";
+pub const ERROR_LEAD_AUTH_FAILED: &str = "Lead authentication failed";
+pub const ERROR_MEMBER_AUTH_FAILED: &str = "Member authentication failed";
+pub const ERROR_CURATOR_AUTH_FAILED: &str = "Curator authentication failed";
+pub const ERROR_BAD_ORIGIN: &str = "Expected root or signed origin";
+pub const ERROR_ENTITY_REMOVAL_ACCESS_DENIED: &str = "Entity removal access denied";
+pub const ERROR_ENTITY_ADD_SCHEMA_SUPPORT_ACCESS_DENIED: &str =
+    "Add entity schema support access denied";
+pub const ERROR_CLASS_ACCESS_DENIED: &str = "Class access denied";
+pub const ERROR_ENTITY_ACCESS_DENIED: &str = "Entity access denied";
+pub const ERROR_ENTITY_CAN_NOT_BE_REFRENCED: &str = "Given entity can`t be referenced";
+pub const ERROR_CLASS_PROPERTY_TYPE_IS_LOCKED_FOR_GIVEN_ACTOR: &str =
+    "Given class property type is locked for updating";
+pub const ERROR_NUMBER_OF_MAINTAINERS_PER_CLASS_LIMIT_REACHED: &str =
+    "Class maintainers limit reached";
+pub const ERROR_NUMBER_OF_CURATORS_PER_GROUP_LIMIT_REACHED: &str =
+    "Max number of curators per group limit reached";
+pub const ERROR_CURATOR_GROUP_IS_NOT_ACTIVE: &str = "Curator group is not active";
+pub const ERROR_ORIGIN_CANNOT_BE_MADE_INTO_RAW_ORIGIN: &str =
+    "Origin cannot be made into raw origin";
+pub const ERROR_PROPERTY_VALUE_SHOULD_BE_UNIQUE: &str =
+    "Provided property value should be unique across all entity property values";

+ 1999 - 0
runtime-modules/content-directory/src/lib.rs

@@ -0,0 +1,1999 @@
+// Ensure we're `no_std` when compiling for Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+#![recursion_limit = "256"]
+
+use codec::{Codec, Decode, Encode};
+use rstd::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
+use rstd::prelude::*;
+use runtime_primitives::traits::{MaybeSerializeDeserialize, Member, One, SimpleArithmetic, Zero};
+use srml_support::{
+    decl_event, decl_module, decl_storage, dispatch, ensure, traits::Get, Parameter,
+    StorageDoubleMap,
+};
+use std::hash::Hash;
+use system::ensure_signed;
+
+#[cfg(feature = "std")]
+pub use serde::{Deserialize, Serialize};
+
+mod errors;
+mod mock;
+mod operations;
+mod permissions;
+mod schema;
+mod tests;
+
+use core::fmt::Debug;
+pub use errors::*;
+pub use operations::*;
+pub use permissions::*;
+pub use schema::*;
+
+type MaxNumber = u32;
+
+/// Type, respresenting inbound entities rc for each entity
+type ReferenceCounter = u32;
+
+pub trait Trait: system::Trait + ActorAuthenticator + Debug + Clone {
+    /// The overarching event type.
+    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
+    /// Nonce type is used to avoid data race update conditions, when performing property value vector operations
+    type Nonce: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + Clone
+        + One
+        + Zero
+        + MaybeSerializeDeserialize
+        + Eq
+        + PartialEq
+        + Ord
+        + From<u32>;
+
+    /// Type of identifier for classes
+    type ClassId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + Clone
+        + One
+        + Zero
+        + MaybeSerializeDeserialize
+        + Eq
+        + PartialEq
+        + Ord;
+
+    /// Type of identifier for entities
+    type EntityId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + Clone
+        + Hash
+        + One
+        + Zero
+        + MaybeSerializeDeserialize
+        + Eq
+        + PartialEq
+        + Ord;
+
+    /// Security/configuration constraints
+
+    /// Type, representing min & max property name length constraints
+    type PropertyNameLengthConstraint: Get<InputValidationLengthConstraint>;
+
+    /// Type, representing min & max property description length constraints
+    type PropertyDescriptionLengthConstraint: Get<InputValidationLengthConstraint>;
+
+    /// Type, representing min & max class name length constraints
+    type ClassNameLengthConstraint: Get<InputValidationLengthConstraint>;
+
+    /// Type, representing min & max class description length constraints
+    type ClassDescriptionLengthConstraint: Get<InputValidationLengthConstraint>;
+
+    /// The maximum number of classes
+    type MaxNumberOfClasses: Get<MaxNumber>;
+
+    /// The maximum number of maintainers per class constraint
+    type MaxNumberOfMaintainersPerClass: Get<MaxNumber>;
+
+    /// The maximum number of curators per group constraint
+    type MaxNumberOfCuratorsPerGroup: Get<MaxNumber>;
+
+    /// The maximum number of schemas per class constraint
+    type NumberOfSchemasPerClass: Get<MaxNumber>;
+
+    /// The maximum number of properties per class constraint
+    type MaxNumberOfPropertiesPerClass: Get<MaxNumber>;
+
+    /// The maximum number of operations during single invocation of `transaction`
+    type MaxNumberOfOperationsDuringAtomicBatching: Get<MaxNumber>;
+
+    /// The maximum length of vector property value constarint
+    type VecMaxLengthConstraint: Get<VecMaxLength>;
+
+    /// The maximum length of text property value constarint
+    type TextMaxLengthConstraint: Get<TextMaxLength>;
+
+    /// Entities creation constraint per class
+    type MaxNumberOfEntitiesPerClass: Get<Self::EntityId>;
+
+    /// Entities creation constraint per individual
+    type IndividualEntitiesCreationLimit: Get<Self::EntityId>;
+}
+
+/// Length constraint for input validation
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, Copy, PartialEq, Eq, Debug)]
+pub struct InputValidationLengthConstraint {
+    /// Minimum length
+    pub min: u16,
+
+    /// Difference between minimum length and max length.
+    /// While having max would have been more direct, this
+    /// way makes max < min unrepresentable semantically,
+    /// which is safer.
+    pub max_min_diff: u16,
+}
+
+/// Structure, representing `inbound_entity_rcs` & `inbound_same_owner_entity_rcs` mappings to their respective count for each referenced entity id
+pub struct EntitiesRc<T: Trait> {
+    /// Entities, which inbound same owner rc should be changed
+    pub inbound_entity_rcs: BTreeMap<T::EntityId, ReferenceCounter>,
+
+    /// Entities, which rc should be changed (only includes entity ids, which are not in inbound_entity_rcs already)
+    pub inbound_same_owner_entity_rcs: BTreeMap<T::EntityId, ReferenceCounter>,
+}
+
+impl<T: Trait> Default for EntitiesRc<T> {
+    fn default() -> Self {
+        Self {
+            inbound_entity_rcs: BTreeMap::default(),
+            inbound_same_owner_entity_rcs: BTreeMap::default(),
+        }
+    }
+}
+
+impl<T: Trait> EntitiesRc<T> {
+    /// Fill in one of inbound entity rcs mappings, based on `same_owner` flag provided
+    fn fill_in_entity_rcs(&mut self, entity_ids: Vec<T::EntityId>, same_owner: bool) {
+        let inbound_entity_rcs = if same_owner {
+            &mut self.inbound_same_owner_entity_rcs
+        } else {
+            &mut self.inbound_entity_rcs
+        };
+
+        for entity_id in entity_ids {
+            *inbound_entity_rcs.entry(entity_id).or_insert(0) += 1;
+        }
+    }
+
+    /// Traverse `inbound_entity_rcs` & `inbound_same_owner_entity_rcs`,
+    /// increasing each `Entity` respective reference counters
+    fn increase_entity_rcs(self) {
+        self.inbound_same_owner_entity_rcs
+            .iter()
+            .for_each(|(entity_id, rc)| {
+                Module::<T>::increase_entity_rcs(entity_id, *rc, true);
+            });
+        self.inbound_entity_rcs.iter().for_each(|(entity_id, rc)| {
+            Module::<T>::increase_entity_rcs(entity_id, *rc, false);
+        });
+    }
+
+    /// Traverse `inbound_entity_rcs` & `inbound_same_owner_entity_rcs`,
+    /// decreasing each `Entity` respective reference counters
+    fn decrease_entity_rcs(self) {
+        self.inbound_same_owner_entity_rcs
+            .iter()
+            .for_each(|(entity_id, rc)| {
+                Module::<T>::decrease_entity_rcs(entity_id, *rc, true);
+            });
+        self.inbound_entity_rcs.iter().for_each(|(entity_id, rc)| {
+            Module::<T>::decrease_entity_rcs(entity_id, *rc, false);
+        });
+    }
+}
+
+impl InputValidationLengthConstraint {
+    pub fn new(min: u16, max_min_diff: u16) -> Self {
+        Self { min, max_min_diff }
+    }
+
+    /// Helper for computing max
+    pub fn max(self) -> u16 {
+        self.min + self.max_min_diff
+    }
+
+    pub fn ensure_valid(
+        self,
+        len: usize,
+        too_short_msg: &'static str,
+        too_long_msg: &'static str,
+    ) -> Result<(), &'static str> {
+        let length = len as u16;
+        if length < self.min {
+            Err(too_short_msg)
+        } else if length > self.max() {
+            Err(too_long_msg)
+        } else {
+            Ok(())
+        }
+    }
+}
+
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct Class<T: Trait> {
+    /// Permissions for an instance of a Class.
+    class_permissions: ClassPermissions<T>,
+    /// All properties that have been used on this class across different class schemas.
+    /// Unlikely to be more than roughly 20 properties per class, often less.
+    /// For Person, think "height", "weight", etc.
+    pub properties: Vec<Property<T>>,
+
+    /// All schemas that are available for this class, think v0.0 Person, v.1.0 Person, etc.
+    pub schemas: Vec<Schema>,
+
+    pub name: Vec<u8>,
+
+    pub description: Vec<u8>,
+
+    /// The maximum number of entities which can be created.
+    maximum_entities_count: T::EntityId,
+
+    /// The current number of entities which exist.
+    current_number_of_entities: T::EntityId,
+
+    /// How many entities a given controller may create at most.
+    default_entity_creation_voucher_upper_bound: T::EntityId,
+}
+
+impl<T: Trait> Default for Class<T> {
+    fn default() -> Self {
+        Self {
+            class_permissions: ClassPermissions::<T>::default(),
+            properties: vec![],
+            schemas: vec![],
+            name: vec![],
+            description: vec![],
+            maximum_entities_count: T::EntityId::default(),
+            current_number_of_entities: T::EntityId::default(),
+            default_entity_creation_voucher_upper_bound: T::EntityId::default(),
+        }
+    }
+}
+
+impl<T: Trait> Class<T> {
+    /// Create new `Class` with provided parameters
+    fn new(
+        class_permissions: ClassPermissions<T>,
+        name: Vec<u8>,
+        description: Vec<u8>,
+        maximum_entities_count: T::EntityId,
+        default_entity_creation_voucher_upper_bound: T::EntityId,
+    ) -> Self {
+        Self {
+            class_permissions,
+            properties: vec![],
+            schemas: vec![],
+            name,
+            description,
+            maximum_entities_count,
+            current_number_of_entities: T::EntityId::zero(),
+            default_entity_creation_voucher_upper_bound,
+        }
+    }
+
+    /// Used to update `Schema` status under given `schema_index`
+    fn update_schema_status(&mut self, schema_index: SchemaId, schema_status: bool) {
+        // Such indexing is safe, when length bounds were previously checked
+        self.schemas[schema_index as usize].set_status(schema_status);
+    }
+
+    /// Used to update `Class` permissions
+    fn update_permissions(&mut self, permissions: ClassPermissions<T>) {
+        self.class_permissions = permissions
+    }
+
+    /// Increment number of entities, associated with this class
+    fn increment_entities_count(&mut self) {
+        self.current_number_of_entities += T::EntityId::one();
+    }
+
+    /// Decrement number of entities, associated with this class
+    fn decrement_entities_count(&mut self) {
+        self.current_number_of_entities -= T::EntityId::one();
+    }
+
+    /// Retrieve `ClassPermissions` by mutable reference
+    fn get_permissions_mut(&mut self) -> &mut ClassPermissions<T> {
+        &mut self.class_permissions
+    }
+
+    /// Retrieve `ClassPermissions` by reference
+    fn get_permissions_ref(&self) -> &ClassPermissions<T> {
+        &self.class_permissions
+    }
+
+    /// Retrieve `ClassPermissions` by value
+    fn get_permissions(self) -> ClassPermissions<T> {
+        self.class_permissions
+    }
+
+    /// Retrieve `Class` properties by reference  
+    fn get_properties_ref(&self) -> &[Property<T>] {
+        &self.properties
+    }
+
+    /// Get per controller `Class`- specific limit
+    pub fn get_default_entity_creation_voucher_upper_bound(&self) -> T::EntityId {
+        self.default_entity_creation_voucher_upper_bound
+    }
+
+    /// Retrive the maximum entities count, which can be created for given `Class`
+    pub fn get_maximum_entities_count(&self) -> T::EntityId {
+        self.maximum_entities_count
+    }
+
+    /// Ensure `Class` `Schema` under given index exist, return corresponding `Schema`
+    fn ensure_schema_exists(&self, schema_index: SchemaId) -> Result<&Schema, &'static str> {
+        self.schemas
+            .get(schema_index as usize)
+            .map(|schema| schema)
+            .ok_or(ERROR_UNKNOWN_CLASS_SCHEMA_ID)
+    }
+
+    /// Ensure `schema_id` is a valid index of `Class` schemas vector
+    pub fn ensure_schema_id_exists(&self, schema_id: SchemaId) -> dispatch::Result {
+        ensure!(
+            schema_id < self.schemas.len() as SchemaId,
+            ERROR_UNKNOWN_CLASS_SCHEMA_ID
+        );
+        Ok(())
+    }
+
+    /// Ensure `Schema`s limit per `Class` not reached
+    pub fn ensure_schemas_limit_not_reached(&self) -> dispatch::Result {
+        ensure!(
+            T::NumberOfSchemasPerClass::get() < self.schemas.len() as MaxNumber,
+            ERROR_CLASS_SCHEMAS_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure properties limit per `Class` not reached
+    pub fn ensure_properties_limit_not_reached(
+        &self,
+        new_properties: &[Property<T>],
+    ) -> dispatch::Result {
+        ensure!(
+            T::MaxNumberOfPropertiesPerClass::get()
+                <= (self.properties.len() + new_properties.len()) as MaxNumber,
+            ERROR_CLASS_PROPERTIES_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure `Class` specific entities limit not reached
+    pub fn ensure_maximum_entities_count_limit_not_reached(&self) -> dispatch::Result {
+        ensure!(
+            self.current_number_of_entities < self.maximum_entities_count,
+            ERROR_MAX_NUMBER_OF_ENTITIES_PER_CLASS_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure `Property` under given `PropertyId` is unlocked from actor with given `EntityAccessLevel`
+    /// return corresponding `Property` by value
+    pub fn ensure_class_property_type_unlocked_from(
+        &self,
+        in_class_schema_property_id: PropertyId,
+        entity_access_level: EntityAccessLevel,
+    ) -> Result<Property<T>, &'static str> {
+        self.ensure_property_values_unlocked()?;
+
+        // Get class-level information about this `Property`
+        let class_property = self
+            .properties
+            .get(in_class_schema_property_id as usize)
+            // Throw an error if a property was not found on class
+            // by an in-class index of a property.
+            .ok_or(ERROR_CLASS_PROP_NOT_FOUND)?;
+
+        class_property.ensure_unlocked_from(entity_access_level)?;
+
+        Ok(class_property.to_owned())
+    }
+
+    /// Ensure property values were not locked on `Class` level
+    pub fn ensure_property_values_unlocked(&self) -> dispatch::Result {
+        ensure!(
+            !self
+                .get_permissions_ref()
+                .all_entity_property_values_locked(),
+            ERROR_ALL_PROP_WERE_LOCKED_ON_CLASS_LEVEL
+        );
+        Ok(())
+    }
+}
+
+/// Represents `Entity`, related to a specific `Class`
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct Entity<T: Trait> {
+    /// Permissions for an instance of an Entity.
+    pub entity_permissions: EntityPermissions<T>,
+
+    /// The class id of this entity.
+    pub class_id: T::ClassId,
+
+    /// What schemas under which this entity of a class is available, think
+    /// v.2.0 Person schema for John, v3.0 Person schema for John
+    /// Unlikely to be more than roughly 20ish, assuming schemas for a given class eventually stableize, or that very old schema are eventually removed.
+    pub supported_schemas: BTreeSet<SchemaId>, // indices of schema in corresponding class
+
+    /// Values for properties on class that are used by some schema used by this entity!
+    /// Length is no more than Class.properties.
+    pub values: BTreeMap<PropertyId, PropertyValue<T>>,
+
+    /// Number of property values referencing current entity
+    pub reference_count: ReferenceCounter,
+
+    /// Number of inbound references from another entities with `SameOwner`flag set
+    pub inbound_same_owner_references_from_other_entities_count: ReferenceCounter,
+}
+
+impl<T: Trait> Default for Entity<T> {
+    fn default() -> Self {
+        Self {
+            entity_permissions: EntityPermissions::<T>::default(),
+            class_id: T::ClassId::default(),
+            supported_schemas: BTreeSet::new(),
+            values: BTreeMap::new(),
+            reference_count: 0,
+            inbound_same_owner_references_from_other_entities_count: 0,
+        }
+    }
+}
+
+impl<T: Trait> Entity<T> {
+    /// Create new `Entity` instance, related to a given `class_id` with provided parameters,  
+    fn new(
+        controller: EntityController<T>,
+        class_id: T::ClassId,
+        supported_schemas: BTreeSet<SchemaId>,
+        values: BTreeMap<PropertyId, PropertyValue<T>>,
+    ) -> Self {
+        Self {
+            entity_permissions: EntityPermissions::<T>::default_with_controller(controller),
+            class_id,
+            supported_schemas,
+            values,
+            reference_count: 0,
+            inbound_same_owner_references_from_other_entities_count: 0,
+        }
+    }
+
+    /// Get `values` by reference
+    fn get_values_ref(&self) -> &BTreeMap<PropertyId, PropertyValue<T>> {
+        &self.values
+    }
+
+    /// Get mutable `EntityPermissions` reference, related to given `Entity`
+    fn get_permissions_mut(&mut self) -> &mut EntityPermissions<T> {
+        &mut self.entity_permissions
+    }
+
+    /// Get `EntityPermissions` reference, related to given `Entity`
+    fn get_permissions_ref(&self) -> &EntityPermissions<T> {
+        &self.entity_permissions
+    }
+
+    /// Get `EntityPermissions`, related to given `Entity` by value
+    fn get_permissions(self) -> EntityPermissions<T> {
+        self.entity_permissions
+    }
+
+    /// Update existing `EntityPermissions` with newly provided
+    pub fn update_permissions(&mut self, permissions: EntityPermissions<T>) {
+        self.entity_permissions = permissions
+    }
+
+    /// Ensure `Schema` under given id is not yet added to given `Entity`
+    pub fn ensure_schema_id_is_not_added(&self, schema_id: SchemaId) -> dispatch::Result {
+        let schema_not_added = !self.supported_schemas.contains(&schema_id);
+        ensure!(schema_not_added, ERROR_SCHEMA_ALREADY_ADDED_TO_ENTITY);
+        Ok(())
+    }
+
+    /// Ensure PropertyValue under given `in_class_schema_property_id` is Vector
+    fn ensure_property_value_is_vec(
+        &self,
+        in_class_schema_property_id: PropertyId,
+    ) -> Result<&VecPropertyValue<T>, &'static str> {
+        self.values
+            .get(&in_class_schema_property_id)
+            // Throw an error if a property was not found on entity
+            // by an in-class index of a property.
+            .ok_or(ERROR_UNKNOWN_ENTITY_PROP_ID)?
+            .as_vec_property_value()
+            // Ensure prop value under given class schema property id is vector
+            .ok_or(ERROR_PROP_VALUE_UNDER_GIVEN_INDEX_IS_NOT_A_VECTOR)
+    }
+
+    /// Ensure any `PropertyValue` from external entity does not point to the given `Entity`
+    pub fn ensure_rc_is_zero(&self) -> dispatch::Result {
+        ensure!(
+            self.reference_count == 0,
+            ERROR_ENTITY_RC_DOES_NOT_EQUAL_TO_ZERO
+        );
+        Ok(())
+    }
+
+    /// Ensure any inbound `PropertyValue` points to the given `Entity`
+    pub fn ensure_inbound_same_owner_rc_is_zero(&self) -> dispatch::Result {
+        ensure!(
+            self.inbound_same_owner_references_from_other_entities_count == 0,
+            ERROR_ENTITY_RC_DOES_NOT_EQUAL_TO_ZERO
+        );
+        Ok(())
+    }
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as ContentDirectory {
+
+        /// Map, representing  CuratorGroupId -> CuratorGroup relation
+        pub CuratorGroupById get(curator_group_by_id): map T::CuratorGroupId => CuratorGroup<T>;
+
+        /// Map, representing ClassId -> Class relation
+        pub ClassById get(class_by_id) config(): linked_map T::ClassId => Class<T>;
+
+        /// Map, representing EntityId -> Entity relation
+        pub EntityById get(entity_by_id) config(): map T::EntityId => Entity<T>;
+
+        /// Next runtime storage values used to maintain next id value, used on creation of respective curator groups, classes and entities
+
+        pub NextCuratorGroupId get(next_curator_group_id) config(): T::CuratorGroupId;
+
+        pub NextClassId get(next_class_id) config(): T::ClassId;
+
+        pub NextEntityId get(next_entity_id) config(): T::EntityId;
+
+        // The voucher associated with entity creation for a given class and controller.
+        // Is updated whenever an entity is created in a given class by a given controller.
+        // Constraint is updated by Root, an initial value comes from `ClassPermissions::default_entity_creation_voucher_upper_bound`.
+        pub EntityCreationVouchers get(entity_creation_vouchers): double_map hasher(blake2_128) T::ClassId, blake2_128(EntityController<T>) => EntityCreationVoucher<T>;
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+
+        // ======
+        // Next set of extrinsics can only be invoked by lead.
+        // ======
+
+        // Initializing events
+        fn deposit_event() = default;
+
+        /// Add new curator group to runtime storage
+        pub fn add_curator_group(
+            origin,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let curator_group_id = Self::next_curator_group_id();
+
+            // Insert empty curator group with `active` parameter set to false
+            <CuratorGroupById<T>>::insert(curator_group_id, CuratorGroup::<T>::default());
+
+            // Increment the next curator curator_group_id:
+            <NextCuratorGroupId<T>>::mutate(|n| *n += T::CuratorGroupId::one());
+
+            // Trigger event
+            Self::deposit_event(RawEvent::CuratorGroupAdded(curator_group_id));
+            Ok(())
+        }
+
+        /// Remove curator group under given `curator_group_id` from runtime storage
+        pub fn remove_curator_group(
+            origin,
+            curator_group_id: T::CuratorGroupId,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
+
+            curator_group.ensure_curator_group_maintains_no_classes()?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+
+            // Remove curator group under given curator group id from runtime storage
+            <CuratorGroupById<T>>::remove(curator_group_id);
+
+            // Trigger event
+            Self::deposit_event(RawEvent::CuratorGroupRemoved(curator_group_id));
+            Ok(())
+        }
+
+        /// Set `is_active` status for curator group under given `curator_group_id`
+        pub fn set_curator_group_status(
+            origin,
+            curator_group_id: T::CuratorGroupId,
+            is_active: bool,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            Self::ensure_curator_group_under_given_id_exists(&curator_group_id)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Mutate curator group status
+            <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
+                curator_group.set_status(is_active)
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::CuratorGroupStatusSet(is_active));
+            Ok(())
+        }
+
+        /// Add curator to curator group under given `curator_group_id`
+        pub fn add_curator_to_group(
+            origin,
+            curator_group_id: T::CuratorGroupId,
+            curator_id: T::CuratorId,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
+
+            curator_group.ensure_max_number_of_curators_limit_not_reached()?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Insert curator_id into curator_group under given curator_group_id
+            <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
+                curator_group.get_curators_mut().insert(curator_id);
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::CuratorAdded(curator_group_id, curator_id));
+            Ok(())
+        }
+
+        /// Remove curator from a given curator group
+        pub fn remove_curator_from_group(
+            origin,
+            curator_group_id: T::CuratorGroupId,
+            curator_id: T::CuratorId,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
+
+            curator_group.ensure_curator_in_group_exists(&curator_id)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Remove curator_id from curator_group under given curator_group_id
+            <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
+                curator_group.get_curators_mut().remove(&curator_id);
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::CuratorRemoved(curator_group_id, curator_id));
+            Ok(())
+        }
+
+        /// Add curator group under given `curator_group_id` as `Class` maintainer
+        pub fn add_maintainer_to_class(
+            origin,
+            class_id: T::ClassId,
+            curator_group_id: T::CuratorGroupId,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let class = Self::ensure_known_class_id(class_id)?;
+
+            Self::ensure_curator_group_under_given_id_exists(&curator_group_id)?;
+
+            let class_permissions = class.get_permissions_ref();
+
+            Self::ensure_maintainers_limit_not_reached(class_permissions.get_maintainers())?;
+
+            class_permissions.ensure_maintainer_does_not_exist(&curator_group_id)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Insert `curator_group_id` into `maintainers` set, associated with given `Class`
+            <ClassById<T>>::mutate(class_id, |class|
+                class.get_permissions_mut().get_maintainers_mut().insert(curator_group_id)
+            );
+
+            // Increment the number of classes, curator group under given `curator_group_id` maintains
+            <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
+                curator_group.increment_number_of_classes_maintained_count();
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::MaintainerAdded(class_id, curator_group_id));
+            Ok(())
+        }
+
+        /// Remove curator group under given `curator_group_id` from `Class` maintainers set
+        pub fn remove_maintainer_from_class(
+            origin,
+            class_id: T::ClassId,
+            curator_group_id: T::CuratorGroupId,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let class = Self::ensure_known_class_id(class_id)?;
+
+            class.get_permissions_ref().ensure_maintainer_exists(&curator_group_id)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Remove `curator_group_id` from `maintainers` set, associated with given `Class`
+            <ClassById<T>>::mutate(class_id, |class|
+                class.get_permissions_mut().get_maintainers_mut().remove(&curator_group_id)
+            );
+
+            // Decrement the number of classes, curator group under given `curator_group_id` maintains
+            <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
+                curator_group.decrement_number_of_classes_maintained_count();
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::MaintainerRemoved(class_id, curator_group_id));
+            Ok(())
+        }
+
+        /// Updates or creates new `EntityCreationVoucher` for given `EntityController` with individual limit
+        pub fn update_entity_creation_voucher(
+            origin,
+            class_id: T::ClassId,
+            controller: EntityController<T>,
+            maximum_entities_count: T::EntityId
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            Self::ensure_known_class_id(class_id)?;
+
+            // Check voucher existance
+            let voucher_exists = <EntityCreationVouchers<T>>::exists(class_id, &controller);
+
+
+            Self::ensure_valid_number_of_class_entities_per_actor_constraint(maximum_entities_count)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            if voucher_exists {
+                <EntityCreationVouchers<T>>::mutate(class_id, &controller, |entity_creation_voucher| {
+                    entity_creation_voucher.set_maximum_entities_count(maximum_entities_count);
+
+                    // Trigger event
+                    Self::deposit_event(RawEvent::EntityCreationVoucherUpdated(controller.clone(), entity_creation_voucher.to_owned()))
+                });
+            } else {
+                let entity_creation_voucher = EntityCreationVoucher::new(maximum_entities_count);
+
+                // Add newly created `EntityCreationVoucher` into `EntityCreationVouchers` runtime storage under given `class_id`, `controller` key
+                <EntityCreationVouchers<T>>::insert(class_id, controller.clone(), entity_creation_voucher.clone());
+
+                // Trigger event
+                Self::deposit_event(RawEvent::EntityCreationVoucherCreated(controller, entity_creation_voucher));
+            }
+
+            Ok(())
+        }
+
+        /// Create new `Class` with provided parameters
+        pub fn create_class(
+            origin,
+            name: Vec<u8>,
+            description: Vec<u8>,
+            class_permissions: ClassPermissions<T>,
+            maximum_entities_count: T::EntityId,
+            default_entity_creation_voucher_upper_bound: T::EntityId
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            Self::ensure_entities_creation_limits_are_valid(maximum_entities_count, default_entity_creation_voucher_upper_bound)?;
+
+            Self::ensure_class_limit_not_reached()?;
+
+            Self::ensure_class_name_is_valid(&name)?;
+            Self::ensure_class_description_is_valid(&description)?;
+            Self::ensure_class_permissions_are_valid(&class_permissions)?;
+
+            let class_id = Self::next_class_id();
+
+            let class = Class::new(class_permissions, name, description, maximum_entities_count, default_entity_creation_voucher_upper_bound);
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Add new `Class` to runtime storage
+            <ClassById<T>>::insert(&class_id, class);
+
+            // Increment the next class id:
+            <NextClassId<T>>::mutate(|n| *n += T::ClassId::one());
+
+            // Trigger event
+            Self::deposit_event(RawEvent::ClassCreated(class_id));
+            Ok(())
+        }
+
+        /// Update `ClassPermissions` under specific `class_id`
+        pub fn update_class_permissions(
+            origin,
+            class_id: T::ClassId,
+            updated_any_member: Option<bool>,
+            updated_entity_creation_blocked: Option<bool>,
+            updated_all_entity_property_values_locked: Option<bool>,
+            updated_maintainers: Option<BTreeSet<T::CuratorGroupId>>,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let class = Self::ensure_known_class_id(class_id)?;
+
+            let mut class_permissions = class.get_permissions();
+
+            if let Some(ref updated_maintainers) = updated_maintainers {
+                Self::ensure_curator_groups_exist(updated_maintainers)?;
+                Self::ensure_maintainers_limit_not_reached(updated_maintainers)?;
+            }
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // If no update performed, there is no purpose to emit event
+            let mut updated = false;
+
+            if let Some(updated_any_member) = updated_any_member {
+                class_permissions.set_any_member_status(updated_any_member);
+                updated = true;
+            }
+
+            if let Some(updated_entity_creation_blocked) = updated_entity_creation_blocked {
+                class_permissions.set_entity_creation_blocked(updated_entity_creation_blocked);
+                updated = true;
+            }
+
+            if let Some(updated_all_entity_property_values_locked) = updated_all_entity_property_values_locked {
+                class_permissions.set_all_entity_property_values_locked(updated_all_entity_property_values_locked);
+                updated = true;
+            }
+
+            if let Some(updated_maintainers) = updated_maintainers {
+                class_permissions.set_maintainers(updated_maintainers);
+                updated = true;
+            }
+
+            if updated  {
+
+                // Update `class_permissions` under given class id
+                <ClassById<T>>::mutate(class_id, |class| {
+                    class.update_permissions(class_permissions)
+                });
+
+                // Trigger event
+                Self::deposit_event(RawEvent::ClassPermissionsUpdated(class_id));
+            }
+
+            Ok(())
+        }
+
+        /// Create new class schema from existing property ids and new properties
+        pub fn add_class_schema(
+            origin,
+            class_id: T::ClassId,
+            existing_properties: BTreeSet<PropertyId>,
+            new_properties: Vec<Property<T>>
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let class = Self::ensure_known_class_id(class_id)?;
+
+            class.ensure_schemas_limit_not_reached()?;
+
+            Self::ensure_non_empty_schema(&existing_properties, &new_properties)?;
+
+            class.ensure_properties_limit_not_reached(&new_properties)?;
+
+            Self::ensure_all_properties_are_valid(&new_properties)?;
+
+            let class_properties = class.get_properties_ref();
+
+            Self::ensure_all_property_names_are_unique(class_properties, &new_properties)?;
+
+            // Create new Schema with existing properies provided
+            let mut schema = Schema::new(existing_properties);
+
+            schema.ensure_schema_properties_are_valid_indices(class_properties)?;
+
+            // Represents class properties after new `Schema` added
+            let mut updated_class_props = class.properties;
+
+            new_properties.into_iter().for_each(|prop| {
+
+                // Add new property ids to `Schema`
+                let prop_id = updated_class_props.len() as PropertyId;
+
+                schema.get_properties_mut().insert(prop_id);
+
+                // Update existing class properties
+                updated_class_props.push(prop);
+            });
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Update class properties and schemas
+            <ClassById<T>>::mutate(class_id, |class| {
+                class.properties = updated_class_props;
+                class.schemas.push(schema);
+
+                let schema_id = class.schemas.len() - 1;
+
+                // Trigger event
+                Self::deposit_event(RawEvent::ClassSchemaAdded(class_id, schema_id as SchemaId));
+            });
+
+            Ok(())
+        }
+
+        /// Update `schema_status` under specific `schema_id` in `Class`
+        pub fn update_class_schema_status(
+            origin,
+            class_id: T::ClassId,
+            schema_id: SchemaId,
+            schema_status: bool
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let class = Self::ensure_known_class_id(class_id)?;
+
+            class.ensure_schema_id_exists(schema_id)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Update class schema status
+            <ClassById<T>>::mutate(class_id, |class| {
+                class.update_schema_status(schema_id, schema_status)
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::ClassSchemaStatusUpdated(class_id, schema_id, schema_status));
+            Ok(())
+        }
+
+        /// Update entity permissions.
+        pub fn update_entity_permissions(
+            origin,
+            entity_id: T::EntityId,
+            updated_frozen_for_controller: Option<bool>,
+            updated_referenceable: Option<bool>
+        ) -> dispatch::Result {
+
+            // Ensure given origin is lead
+            ensure_is_lead::<T>(origin)?;
+
+            let entity = Self::ensure_known_entity_id(entity_id)?;
+
+            let mut entity_permissions = entity.get_permissions();
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // If no update performed, there is no purpose to emit event
+            let mut updated = false;
+
+            if let Some(updated_frozen_for_controller) = updated_frozen_for_controller {
+                entity_permissions.set_frozen(updated_frozen_for_controller);
+                updated = true;
+            }
+
+            if let Some(updated_referenceable) = updated_referenceable {
+                entity_permissions.set_referencable(updated_referenceable);
+                updated = true;
+            }
+
+            if updated {
+
+                // Update entity permissions under given entity id
+                <EntityById<T>>::mutate(entity_id, |entity| {
+                    entity.update_permissions(entity_permissions)
+                });
+
+                // Trigger event
+                Self::deposit_event(RawEvent::EntityPermissionsUpdated(entity_id));
+            }
+            Ok(())
+        }
+
+        /// Transfer ownership to new `EntityController` for `Entity` under given `entity_id`
+        /// If `Entity` has `PropertyValue` references with `SameOwner` flag activated, each `Entity` ownership
+        /// will be transfered to `new_controller`
+        pub fn transfer_entity_ownership(
+            origin,
+            entity_id: T::EntityId,
+            new_controller: EntityController<T>,
+        ) -> dispatch::Result {
+
+            ensure_is_lead::<T>(origin)?;
+
+            let (entity, class) = Self::ensure_entity_and_class(entity_id)?;
+
+            entity.ensure_inbound_same_owner_rc_is_zero()?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Set of all entities, which controller should be updated after ownership transfer performed
+            let mut entities = BTreeSet::new();
+
+            // Insert root entity_id into entities set
+            entities.insert(entity_id);
+
+            Self::retrieve_all_entities_to_perform_ownership_transfer(&class, entity, &mut entities);
+
+            // Perform ownership transfer of all involved entities
+            entities.into_iter().for_each(|involved_entity_id| {
+                <EntityById<T>>::mutate(involved_entity_id, |inner_entity|
+                    inner_entity.get_permissions_mut().set_conroller(new_controller.clone())
+                );
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::EntityOwnershipTransfered(entity_id, new_controller));
+
+            Ok(())
+        }
+
+        // ======
+        // The next set of extrinsics can be invoked by anyone who can properly sign for provided value of `Actor<T>`.
+        // ======
+
+        /// Create an entity.
+        /// If someone is making an entity of this class for first time,
+        /// then a voucher is also added with the class limit as the default limit value.
+        /// class limit default value.
+        pub fn create_entity(
+            origin,
+            class_id: T::ClassId,
+            actor: Actor<T>,
+        ) -> dispatch::Result {
+
+            let account_id = ensure_signed(origin)?;
+
+            let class = Self::ensure_class_exists(class_id)?;
+
+            // Ensure maximum entities limit per class not reached
+            class.ensure_maximum_entities_count_limit_not_reached()?;
+
+            let class_permissions = class.get_permissions_ref();
+
+            // Ensure actor can create entities
+
+            class_permissions.ensure_entity_creation_not_blocked()?;
+            class_permissions.ensure_can_create_entities(&account_id, &actor)?;
+
+            let entity_controller = EntityController::from_actor(&actor);
+
+            // Check if entity creation voucher exists
+            let voucher_exists = if <EntityCreationVouchers<T>>::exists(class_id, &entity_controller) {
+                // Ensure voucher limit not reached
+                Self::entity_creation_vouchers(class_id, &entity_controller).ensure_voucher_limit_not_reached()?;
+                true
+            } else {
+                false
+            };
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Create voucher, update if exists
+
+            if voucher_exists {
+                // Increment number of created entities count, if voucher already exist
+                <EntityCreationVouchers<T>>::mutate(class_id, &entity_controller, |entity_creation_voucher| {
+                    entity_creation_voucher.increment_created_entities_count()
+                });
+            } else {
+                // Create new voucher for given entity creator with default limit and increment created entities count
+                let mut entity_creation_voucher = EntityCreationVoucher::new(class.get_default_entity_creation_voucher_upper_bound());
+                entity_creation_voucher.increment_created_entities_count();
+                <EntityCreationVouchers<T>>::insert(class_id, entity_controller.clone(), entity_creation_voucher);
+            }
+
+            // Create new entity
+
+            let entity_id = Self::next_entity_id();
+
+            let new_entity = Entity::<T>::new(
+                entity_controller,
+                class_id,
+                BTreeSet::new(),
+                BTreeMap::new(),
+            );
+
+            // Save newly created entity:
+            EntityById::insert(entity_id, new_entity);
+
+            // Increment the next entity id:
+            <NextEntityId<T>>::mutate(|n| *n += T::EntityId::one());
+
+            <ClassById<T>>::mutate(class_id, |class| {
+                class.increment_entities_count();
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::EntityCreated(actor, entity_id));
+            Ok(())
+        }
+
+        /// Remove `Entity` under provided `entity_id`
+        pub fn remove_entity(
+            origin,
+            actor: Actor<T>,
+            entity_id: T::EntityId,
+        ) -> dispatch::Result {
+
+            let (_, entity, access_level) = Self::ensure_class_entity_and_access_level(origin, entity_id, &actor)?;
+
+            EntityPermissions::<T>::ensure_group_can_remove_entity(access_level)?;
+
+           entity.ensure_rc_is_zero()?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Remove entity
+            <EntityById<T>>::remove(entity_id);
+
+            // Decrement class entities counter
+            <ClassById<T>>::mutate(entity.class_id, |class| class.decrement_entities_count());
+
+            let entity_controller =  EntityController::<T>::from_actor(&actor);
+
+            // Decrement entity_creation_voucher after entity removal perfomed
+            <EntityCreationVouchers<T>>::mutate(entity.class_id, entity_controller, |entity_creation_voucher| {
+                entity_creation_voucher.decrement_created_entities_count();
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::EntityRemoved(actor, entity_id));
+            Ok(())
+        }
+
+        /// Add schema support to entity under given shema_id and provided `property_values`
+        pub fn add_schema_support_to_entity(
+            origin,
+            actor: Actor<T>,
+            entity_id: T::EntityId,
+            schema_id: SchemaId,
+            property_values: BTreeMap<PropertyId, PropertyValue<T>>
+        ) -> dispatch::Result {
+
+            let (class, entity, _) = Self::ensure_class_entity_and_access_level(origin, entity_id, &actor)?;
+
+            entity.ensure_schema_id_is_not_added(schema_id)?;
+
+            let schema = class.ensure_schema_exists(schema_id)?;
+
+            schema.ensure_is_active()?;
+
+            let entity_values = entity.get_values_ref();
+
+            // Updated entity values, after new schema support added
+            let mut entity_values_updated = entity.values.clone();
+
+            // Entities, which rc should be incremented
+            let mut entity_ids_to_increase_rcs = EntitiesRc::<T>::default();
+
+            for prop_id in schema.get_properties().iter() {
+                if entity_values.contains_key(prop_id) {
+                    // A property is already added to the entity and cannot be updated
+                    // while adding a schema support to this entity.
+                    continue;
+                }
+
+                // Indexing is safe, class shoud always maintain such constistency
+                let class_property = &class.properties[*prop_id as usize];
+
+                Self::add_new_property_value(
+                    class_property, &entity, *prop_id,
+                    &property_values, &mut entity_ids_to_increase_rcs, &mut entity_values_updated
+                )?;
+            }
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Add schema support to `Entity` under given `entity_id`
+            <EntityById<T>>::mutate(entity_id, |entity| {
+
+                // Add a new schema to the list of schemas supported by this entity.
+                entity.supported_schemas.insert(schema_id);
+
+                // Update entity values only if new properties have been added.
+                if entity_values_updated.len() > entity.values.len() {
+                    entity.values = entity_values_updated;
+                }
+            });
+
+            entity_ids_to_increase_rcs.increase_entity_rcs();
+
+            // Trigger event
+            Self::deposit_event(RawEvent::EntitySchemaSupportAdded(actor, entity_id, schema_id));
+            Ok(())
+        }
+
+        /// Update `Entity` `PropertyValue`'s with provided ones
+        pub fn update_entity_property_values(
+            origin,
+            actor: Actor<T>,
+            entity_id: T::EntityId,
+            new_property_values: BTreeMap<PropertyId, PropertyValue<T>>
+        ) -> dispatch::Result {
+
+            let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(origin, entity_id, &actor)?;
+
+            class.ensure_property_values_unlocked()?;
+
+            // Get current property values of an entity,
+            // so we can update them if new values provided present in new_property_values.
+            let mut updated_values = entity.values.clone();
+            let mut updated = false;
+
+            // Entities, which rc should be incremented
+            let mut entity_ids_to_increase_rcs = EntitiesRc::<T>::default();
+
+            // Entities, which rc should be decremented
+            let mut entity_ids_to_decrease_rcs = EntitiesRc::<T>::default();
+
+            // Iterate over a vector of new values and update corresponding properties
+            // of this entity if new values are valid.
+            for (id, new_value) in new_property_values.into_iter() {
+
+                // Try to find a current property value in the entity
+                // by matching its id to the id of a property with an updated value.
+                let current_prop_value = updated_values
+                    .get_mut(&id)
+
+                    // Throw an error if a property was not found on entity
+                    // by an in-class index of a property update.
+                    .ok_or(ERROR_UNKNOWN_ENTITY_PROP_ID)?;
+
+                // Skip update if new value is equal to the current one or class property type
+                // is locked for update from current actor
+                if new_value != *current_prop_value {
+
+                    // Get class-level information about this property
+                    if let Some(class_property) = class.properties.get(id as usize) {
+
+                        class_property.ensure_unlocked_from(access_level)?;
+
+                        class_property.ensure_property_value_to_update_is_valid(
+                            &new_value,
+                            entity.get_permissions_ref().get_controller(),
+                        )?;
+
+                        Self::fill_in_involved_entity_ids_rcs(
+                            &new_value, current_prop_value, class_property.property_type.same_controller_status(),
+                            &mut entity_ids_to_increase_rcs, &mut entity_ids_to_decrease_rcs
+                        );
+
+                        current_prop_value.update(new_value);
+
+                        updated = true;
+                    }
+                }
+            }
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // If property values should be updated
+            if updated {
+
+                <EntityById<T>>::mutate(entity_id, |entity| {
+                    entity.values = updated_values;
+                });
+
+                entity_ids_to_increase_rcs.increase_entity_rcs();
+
+                entity_ids_to_decrease_rcs.decrease_entity_rcs();
+
+                // Trigger event
+                Self::deposit_event(RawEvent::EntityPropertyValuesUpdated(actor, entity_id));
+            }
+
+            Ok(())
+        }
+
+        /// Clear `PropertyValueVec` under given `entity_id` & `in_class_schema_property_id`
+        pub fn clear_entity_property_vector(
+            origin,
+            actor: Actor<T>,
+            entity_id: T::EntityId,
+            in_class_schema_property_id: PropertyId
+        ) -> dispatch::Result {
+
+            let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(origin, entity_id, &actor)?;
+
+            let current_property_value_vec =
+                entity.ensure_property_value_is_vec(in_class_schema_property_id)?;
+
+            let property = class.ensure_class_property_type_unlocked_from(
+                in_class_schema_property_id,
+                access_level,
+            )?;
+
+            let entity_ids_to_decrease_rcs = current_property_value_vec
+                .get_vec_value()
+                .get_involved_entities();
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Clear property value vector
+            <EntityById<T>>::mutate(entity_id, |entity| {
+                if let Some(PropertyValue::Vector(current_property_value_vec)) =
+                    entity.values.get_mut(&in_class_schema_property_id)
+                {
+                    current_property_value_vec.vec_clear();
+                }
+
+                if let Some(entity_ids_to_decrease_rcs) = entity_ids_to_decrease_rcs {
+                    Self::count_entities(entity_ids_to_decrease_rcs).iter()
+                        .for_each(|(entity_id, rc)| Self::decrease_entity_rcs(
+                            entity_id, *rc, property.property_type.same_controller_status()
+                        )
+                    );
+                }
+            });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::EntityPropertyValueVectorCleared(actor, entity_id, in_class_schema_property_id));
+
+            Ok(())
+        }
+
+        /// Remove value at given `index_in_property_vec`
+        /// from `PropertyValueVec` under in_class_schema_property_id
+        pub fn remove_at_entity_property_vector(
+            origin,
+            actor: Actor<T>,
+            entity_id: T::EntityId,
+            in_class_schema_property_id: PropertyId,
+            index_in_property_vec: VecMaxLength,
+            nonce: T::Nonce
+        ) -> dispatch::Result {
+
+            let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(origin, entity_id, &actor)?;
+
+            let current_property_value_vec =
+                entity.ensure_property_value_is_vec(in_class_schema_property_id)?;
+
+            let property = class.ensure_class_property_type_unlocked_from(
+                in_class_schema_property_id,
+                access_level,
+            )?;
+
+            current_property_value_vec.ensure_nonce_equality(nonce)?;
+
+            current_property_value_vec
+                .ensure_index_in_property_vector_is_valid(index_in_property_vec)?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let involved_entity_id = current_property_value_vec
+                .get_vec_value()
+                .get_involved_entities()
+                .map(|involved_entities| involved_entities[index_in_property_vec as usize]);
+
+            // Remove value at in_class_schema_property_id in property value vector
+            <EntityById<T>>::mutate(entity_id, |entity| {
+                if let Some(PropertyValue::Vector(current_prop_value)) =
+                    entity.values.get_mut(&in_class_schema_property_id)
+                {
+                    current_prop_value.vec_remove_at(index_in_property_vec);
+
+                    // Trigger event
+                    Self::deposit_event(
+                        RawEvent::RemovedAtEntityPropertyValueVectorIndex(
+                            actor, entity_id, in_class_schema_property_id, index_in_property_vec, nonce
+                        )
+                    )
+                }
+            });
+
+            if let Some(involved_entity_id) = involved_entity_id {
+                Self::decrease_entity_rcs(&involved_entity_id, 1, property.property_type.same_controller_status());
+            }
+
+            Ok(())
+        }
+
+        /// Insert `SinglePropertyValue` at given `index_in_property_vec`
+        /// into `PropertyValueVec` under in_class_schema_property_id
+        pub fn insert_at_entity_property_vector(
+            origin,
+            actor: Actor<T>,
+            entity_id: T::EntityId,
+            in_class_schema_property_id: PropertyId,
+            index_in_property_vec: VecMaxLength,
+            property_value: SinglePropertyValue<T>,
+            nonce: T::Nonce
+        ) -> dispatch::Result {
+
+            let (class, entity, access_level) = Self::ensure_class_entity_and_access_level(origin, entity_id, &actor)?;
+
+            let current_property_value_vec =
+                entity.ensure_property_value_is_vec(in_class_schema_property_id)?;
+
+            let class_property = class.ensure_class_property_type_unlocked_from(
+                in_class_schema_property_id,
+                access_level,
+            )?;
+
+            current_property_value_vec.ensure_nonce_equality(nonce)?;
+
+            class_property.ensure_prop_value_can_be_inserted_at_prop_vec(
+                &property_value,
+                current_property_value_vec,
+                index_in_property_vec,
+                entity.get_permissions_ref().get_controller(),
+            )?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // Insert SinglePropertyValue at in_class_schema_property_id into property value vector
+            <EntityById<T>>::mutate(entity_id, |entity| {
+                let value = property_value.get_value();
+                if let Some(entity_rc_to_increment) = value.get_involved_entity() {
+                    Self::increase_entity_rcs(&entity_rc_to_increment, 1, class_property.property_type.same_controller_status());
+                }
+                if let Some(PropertyValue::Vector(current_prop_value)) =
+                    entity.values.get_mut(&in_class_schema_property_id)
+                {
+                    current_prop_value.vec_insert_at(index_in_property_vec, value);
+
+                    // Trigger event
+                    Self::deposit_event(
+                        RawEvent::InsertedAtEntityPropertyValueVectorIndex(
+                            actor, entity_id, in_class_schema_property_id, index_in_property_vec, nonce
+                        )
+                    )
+                }
+            });
+
+            Ok(())
+        }
+
+        pub fn transaction(origin, actor: Actor<T>, operations: Vec<OperationType<T>>) -> dispatch::Result {
+            Self::ensure_number_of_operations_during_atomic_batching_limit_not_reached(&operations)?;
+
+            // This Vec holds the T::EntityId of the entity created as a result of executing a `CreateEntity` `Operation`
+            let mut entity_created_in_operation = vec![];
+            let raw_origin = origin.into().map_err(|_| ERROR_ORIGIN_CANNOT_BE_MADE_INTO_RAW_ORIGIN)?;
+
+            for operation_type in operations.into_iter() {
+                let origin = T::Origin::from(raw_origin.clone());
+                let actor = actor.clone();
+                match operation_type {
+                    OperationType::CreateEntity(create_entity_operation) => {
+                        Self::create_entity(origin, create_entity_operation.class_id, actor)?;
+                        // entity id of newly created entity
+                        let entity_id = Self::next_entity_id() - T::EntityId::one();
+                        entity_created_in_operation.push(entity_id);
+                    },
+                    OperationType::UpdatePropertyValues(update_property_values_operation) => {
+                        let entity_id = operations::parametrized_entity_to_entity_id(
+                            &entity_created_in_operation, update_property_values_operation.entity_id
+                        )?;
+                        let property_values = operations::parametrized_property_values_to_property_values(
+                            &entity_created_in_operation, update_property_values_operation.new_parametrized_property_values
+                        )?;
+                        Self::update_entity_property_values(origin, actor, entity_id, property_values)?;
+                    },
+                    OperationType::AddSchemaSupportToEntity(add_schema_support_to_entity_operation) => {
+                        let entity_id = operations::parametrized_entity_to_entity_id(
+                            &entity_created_in_operation, add_schema_support_to_entity_operation.entity_id
+                        )?;
+                        let schema_id = add_schema_support_to_entity_operation.schema_id;
+                        let property_values = operations::parametrized_property_values_to_property_values(
+                            &entity_created_in_operation, add_schema_support_to_entity_operation.parametrized_property_values
+                        )?;
+                        Self::add_schema_support_to_entity(origin, actor, entity_id, schema_id, property_values)?;
+                    }
+                }
+            }
+
+            // Trigger event
+            Self::deposit_event(RawEvent::TransactionCompleted(actor));
+
+            Ok(())
+        }
+    }
+}
+
+impl<T: Trait> Module<T> {
+    /// Increases corresponding `Entity` references count by rc.
+    /// Depends on `same_owner` flag provided
+    fn increase_entity_rcs(entity_id: &T::EntityId, rc: u32, same_owner: bool) {
+        <EntityById<T>>::mutate(entity_id, |entity| {
+            if same_owner {
+                entity.inbound_same_owner_references_from_other_entities_count += rc;
+                entity.reference_count += rc
+            } else {
+                entity.reference_count += rc
+            }
+        })
+    }
+
+    /// Decreases corresponding `Entity` references count by rc.
+    /// Depends on `same_owner` flag provided
+    fn decrease_entity_rcs(entity_id: &T::EntityId, rc: u32, same_owner: bool) {
+        <EntityById<T>>::mutate(entity_id, |entity| {
+            if same_owner {
+                entity.inbound_same_owner_references_from_other_entities_count -= rc;
+                entity.reference_count -= rc
+            } else {
+                entity.reference_count -= rc
+            }
+        })
+    }
+
+    /// Returns the stored `Class` if exist, error otherwise.
+    fn ensure_class_exists(class_id: T::ClassId) -> Result<Class<T>, &'static str> {
+        ensure!(<ClassById<T>>::exists(class_id), ERROR_CLASS_NOT_FOUND);
+        Ok(Self::class_by_id(class_id))
+    }
+
+    /// Add new `PropertyValue`, if it was not already provided under `PropertyId` of this `Schema`
+    fn add_new_property_value(
+        class_property: &Property<T>,
+        entity: &Entity<T>,
+        prop_id: PropertyId,
+        property_values: &BTreeMap<PropertyId, PropertyValue<T>>,
+        entity_ids_to_increase_rcs: &mut EntitiesRc<T>,
+        entity_values_updated: &mut BTreeMap<PropertyId, PropertyValue<T>>,
+    ) -> Result<(), &'static str> {
+        if let Some(new_value) = property_values.get(&prop_id) {
+            class_property.ensure_property_value_to_update_is_valid(
+                new_value,
+                entity.get_permissions_ref().get_controller(),
+            )?;
+
+            class_property.ensure_unique_option_satisfied(new_value, entity_values_updated)?;
+
+            if let Some(entity_rcs_to_increment) = new_value.get_involved_entities() {
+                entity_ids_to_increase_rcs.fill_in_entity_rcs(
+                    entity_rcs_to_increment,
+                    class_property.property_type.same_controller_status(),
+                );
+            }
+
+            entity_values_updated.insert(prop_id, new_value.to_owned());
+        } else {
+            // All required prop values should be provided
+            ensure!(!class_property.required, ERROR_MISSING_REQUIRED_PROP);
+
+            // Add all missing non required schema prop values as PropertyValue::default()
+            entity_values_updated.insert(prop_id, PropertyValue::default());
+        }
+        Ok(())
+    }
+
+    /// Fill in `entity_ids_to_increase_rcs` & `entity_ids_to_decrease_rcs`,
+    /// based on entities involved into update process
+    pub fn fill_in_involved_entity_ids_rcs(
+        new_value: &PropertyValue<T>,
+        current_prop_value: &PropertyValue<T>,
+        same_controller: bool,
+        entity_ids_to_increase_rcs: &mut EntitiesRc<T>,
+        entity_ids_to_decrease_rcs: &mut EntitiesRc<T>,
+    ) {
+        // Retrieve unique entity ids to update rc
+        if let (Some(entities_rc_to_increment_vec), Some(entities_rc_to_decrement_vec)) = (
+            new_value.get_involved_entities(),
+            current_prop_value.get_involved_entities(),
+        ) {
+            let (entities_rc_to_decrement_vec, entities_rc_to_increment_vec): (
+                Vec<T::EntityId>,
+                Vec<T::EntityId>,
+            ) = entities_rc_to_decrement_vec
+                .into_iter()
+                .zip(entities_rc_to_increment_vec.into_iter())
+                // Do not count entity_ids, that should be incremented and decremented simultaneously
+                .filter(|(entity_rc_to_decrement, entity_rc_to_increment)| {
+                    entity_rc_to_decrement != entity_rc_to_increment
+                })
+                .unzip();
+
+            entity_ids_to_increase_rcs
+                .fill_in_entity_rcs(entities_rc_to_increment_vec, same_controller);
+
+            entity_ids_to_decrease_rcs
+                .fill_in_entity_rcs(entities_rc_to_decrement_vec, same_controller);
+        }
+    }
+
+    /// Returns class and entity under given id, if exists, and correspnding `origin` `EntityAccessLevel`, if permitted
+    fn ensure_class_entity_and_access_level(
+        origin: T::Origin,
+        entity_id: T::EntityId,
+        actor: &Actor<T>,
+    ) -> Result<(Class<T>, Entity<T>, EntityAccessLevel), &'static str> {
+        let account_id = ensure_signed(origin)?;
+
+        let entity = Self::ensure_known_entity_id(entity_id)?;
+
+        let class = Self::class_by_id(entity.class_id);
+
+        let access_level = EntityAccessLevel::derive(
+            &account_id,
+            entity.get_permissions_ref(),
+            class.get_permissions_ref(),
+            actor,
+        )?;
+        Ok((class, entity, access_level))
+    }
+
+    /// Ensure `Entity` under given `entity_id` exists, retrieve corresponding `Entity` & `Class`
+    pub fn ensure_entity_and_class(
+        entity_id: T::EntityId,
+    ) -> Result<(Entity<T>, Class<T>), &'static str> {
+        let entity = Self::ensure_known_entity_id(entity_id)?;
+
+        let class = ClassById::get(entity.class_id);
+        Ok((entity, class))
+    }
+
+    /// Retrieve all `entity_id`'s, depending on current `Entity` (the tree of referenced entities with `SameOwner` flag set)
+    pub fn retrieve_all_entities_to_perform_ownership_transfer(
+        class: &Class<T>,
+        entity: Entity<T>,
+        entities: &mut BTreeSet<T::EntityId>,
+    ) {
+        for (id, value) in entity.values.iter() {
+            // Check, that property_type of class_property under given index is reference with `SameOwner` flag set
+            match class.properties.get(*id as usize) {
+                Some(class_property) if class_property.property_type.same_controller_status() => {
+                    // Always safe
+                    let class_id = class_property
+                        .property_type
+                        .get_referenced_class_id()
+                        .unwrap();
+
+                    // If property class_id is not equal to current one, retrieve corresponding Class from runtime storage
+                    if class_id != entity.class_id {
+                        let new_class = Self::class_by_id(class_id);
+
+                        Self::get_all_same_owner_entities(&new_class, value, entities)
+                    } else {
+                        Self::get_all_same_owner_entities(&class, value, entities)
+                    }
+                }
+                _ => (),
+            }
+        }
+    }
+
+    /// Get all referenced entities from corresponding property with `SameOwner` flag set,
+    /// call `retrieve_all_entities_to_perform_ownership_transfer` recursively to complete tree traversal
+    pub fn get_all_same_owner_entities(
+        class: &Class<T>,
+        value: &PropertyValue<T>,
+        entities: &mut BTreeSet<T::EntityId>,
+    ) {
+        if let Some(entity_ids) = value.get_involved_entities() {
+            entity_ids.into_iter().for_each(|entity_id| {
+                // If new entity with `SameOwner` flag set found
+                if !entities.contains(&entity_id) {
+                    entities.insert(entity_id);
+                    let new_entity = Self::entity_by_id(entity_id);
+                    Self::retrieve_all_entities_to_perform_ownership_transfer(
+                        &class, new_entity, entities,
+                    );
+                }
+            })
+        }
+    }
+
+    /// Ensure `Class` under given id exists, return corresponding one
+    pub fn ensure_known_class_id(class_id: T::ClassId) -> Result<Class<T>, &'static str> {
+        ensure!(<ClassById<T>>::exists(class_id), ERROR_CLASS_NOT_FOUND);
+        Ok(Self::class_by_id(class_id))
+    }
+
+    /// Ensure `Entity` under given id exists, return corresponding one
+    pub fn ensure_known_entity_id(entity_id: T::EntityId) -> Result<Entity<T>, &'static str> {
+        ensure!(<EntityById<T>>::exists(entity_id), ERROR_ENTITY_NOT_FOUND);
+        Ok(Self::entity_by_id(entity_id))
+    }
+
+    /// Ensure `CuratorGroup` under given id exists
+    pub fn ensure_curator_group_under_given_id_exists(
+        curator_group_id: &T::CuratorGroupId,
+    ) -> dispatch::Result {
+        ensure!(
+            <CuratorGroupById<T>>::exists(curator_group_id),
+            ERROR_CURATOR_GROUP_DOES_NOT_EXIST
+        );
+        Ok(())
+    }
+
+    /// Ensure `CuratorGroup` under given id exists, return corresponding one
+    pub fn ensure_curator_group_exists(
+        curator_group_id: &T::CuratorGroupId,
+    ) -> Result<CuratorGroup<T>, &'static str> {
+        Self::ensure_curator_group_under_given_id_exists(curator_group_id)?;
+        Ok(Self::curator_group_by_id(curator_group_id))
+    }
+
+    /// Ensure `MaxNumberOfMaintainersPerClass` constraint satisfied
+    pub fn ensure_maintainers_limit_not_reached(
+        curator_groups: &BTreeSet<T::CuratorGroupId>,
+    ) -> Result<(), &'static str> {
+        ensure!(
+            curator_groups.len() < T::MaxNumberOfMaintainersPerClass::get() as usize,
+            ERROR_NUMBER_OF_MAINTAINERS_PER_CLASS_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure all curator groups under given id exist
+    pub fn ensure_curator_groups_exist(
+        curator_groups: &BTreeSet<T::CuratorGroupId>,
+    ) -> dispatch::Result {
+        for curator_group in curator_groups {
+            Self::ensure_curator_group_exists(curator_group)?;
+        }
+        Ok(())
+    }
+
+    /// Perform security checks to ensure provided `ClassPermissions` are valid
+    pub fn ensure_class_permissions_are_valid(
+        class_permissions: &ClassPermissions<T>,
+    ) -> dispatch::Result {
+        Self::ensure_maintainers_limit_not_reached(class_permissions.get_maintainers())?;
+        Self::ensure_curator_groups_exist(class_permissions.get_maintainers())?;
+        Ok(())
+    }
+
+    /// Ensure new `Schema` is not empty
+    pub fn ensure_non_empty_schema(
+        existing_properties: &BTreeSet<PropertyId>,
+        new_properties: &[Property<T>],
+    ) -> dispatch::Result {
+        let non_empty_schema = !existing_properties.is_empty() || !new_properties.is_empty();
+        ensure!(non_empty_schema, ERROR_NO_PROPS_IN_CLASS_SCHEMA);
+        Ok(())
+    }
+
+    /// Ensure `ClassNameLengthConstraint` conditions satisfied
+    pub fn ensure_class_name_is_valid(text: &[u8]) -> dispatch::Result {
+        T::ClassNameLengthConstraint::get().ensure_valid(
+            text.len(),
+            ERROR_CLASS_NAME_TOO_SHORT,
+            ERROR_CLASS_NAME_TOO_LONG,
+        )
+    }
+
+    /// Ensure `ClassDescriptionLengthConstraint` conditions satisfied
+    pub fn ensure_class_description_is_valid(text: &[u8]) -> dispatch::Result {
+        T::ClassDescriptionLengthConstraint::get().ensure_valid(
+            text.len(),
+            ERROR_CLASS_DESCRIPTION_TOO_SHORT,
+            ERROR_CLASS_DESCRIPTION_TOO_LONG,
+        )
+    }
+
+    /// Ensure `MaxNumberOfClasses` constraint satisfied
+    pub fn ensure_class_limit_not_reached() -> dispatch::Result {
+        ensure!(
+            T::MaxNumberOfClasses::get() < <ClassById<T>>::enumerate().count() as MaxNumber,
+            ERROR_CLASS_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure `MaxNumberOfEntitiesPerClass` constraint satisfied
+    pub fn ensure_valid_number_of_entities_per_class(
+        maximum_entities_count: T::EntityId,
+    ) -> dispatch::Result {
+        ensure!(
+            maximum_entities_count < T::MaxNumberOfEntitiesPerClass::get(),
+            ERROR_ENTITIES_NUMBER_PER_CLASS_CONSTRAINT_VIOLATED
+        );
+        Ok(())
+    }
+
+    /// Ensure `IndividualEntitiesCreationLimit` constraint satisfied
+    pub fn ensure_valid_number_of_class_entities_per_actor_constraint(
+        number_of_class_entities_per_actor: T::EntityId,
+    ) -> dispatch::Result {
+        ensure!(
+            number_of_class_entities_per_actor < T::IndividualEntitiesCreationLimit::get(),
+            ERROR_NUMBER_OF_CLASS_ENTITIES_PER_ACTOR_CONSTRAINT_VIOLATED
+        );
+        Ok(())
+    }
+
+    /// Ensure, that all entities creation limits, defined for a given `Class`, are valid
+    pub fn ensure_entities_creation_limits_are_valid(
+        maximum_entities_count: T::EntityId,
+        default_entity_creation_voucher_upper_bound: T::EntityId,
+    ) -> dispatch::Result {
+        // Ensure `per_controller_entities_creation_limit` does not exceed
+        ensure!(
+            default_entity_creation_voucher_upper_bound < maximum_entities_count,
+            ERROR_PER_CONTROLLER_ENTITIES_CREATION_LIMIT_EXCEEDS_OVERALL_LIMIT
+        );
+        Self::ensure_valid_number_of_entities_per_class(maximum_entities_count)?;
+        Self::ensure_valid_number_of_class_entities_per_actor_constraint(
+            default_entity_creation_voucher_upper_bound,
+        )
+    }
+
+    /// Ensure maximum number of operations during atomic batching constraint satisfied
+    pub fn ensure_number_of_operations_during_atomic_batching_limit_not_reached(
+        operations: &[OperationType<T>],
+    ) -> dispatch::Result {
+        ensure!(
+            operations.len() <= T::MaxNumberOfOperationsDuringAtomicBatching::get() as usize,
+            ERROR_MAX_NUMBER_OF_OPERATIONS_DURING_ATOMIC_BATCHING_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Complete all checks to ensure each `Property` is valid
+    pub fn ensure_all_properties_are_valid(new_properties: &[Property<T>]) -> dispatch::Result {
+        for new_property in new_properties.iter() {
+            new_property.ensure_name_is_valid()?;
+            new_property.ensure_description_is_valid()?;
+            new_property.ensure_property_type_size_is_valid()?;
+            new_property.ensure_property_type_reference_is_valid()?;
+        }
+        Ok(())
+    }
+
+    /// Ensure all `Property` names are  unique within `Class`
+    pub fn ensure_all_property_names_are_unique(
+        class_properties: &[Property<T>],
+        new_properties: &[Property<T>],
+    ) -> dispatch::Result {
+        // Used to ensure all property names are unique within class
+        let mut unique_prop_names = BTreeSet::new();
+
+        for property in class_properties.iter() {
+            unique_prop_names.insert(property.name.to_owned());
+        }
+
+        for new_property in new_properties {
+            // Ensure name of a new property is unique within its class.
+            ensure!(
+                !unique_prop_names.contains(&new_property.name),
+                ERROR_PROP_NAME_NOT_UNIQUE_IN_A_CLASS
+            );
+
+            unique_prop_names.insert(new_property.name.to_owned());
+        }
+
+        Ok(())
+    }
+
+    /// Counts the number of repetetive entities and returns `BTreeMap<T::EntityId, ReferenceCounter>`,
+    /// where T::EntityId - unique entity_id, ReferenceCounter - related counter
+    pub fn count_entities(entity_ids: Vec<T::EntityId>) -> BTreeMap<T::EntityId, ReferenceCounter> {
+        let mut result = BTreeMap::new();
+
+        for entity_id in entity_ids {
+            *result.entry(entity_id).or_insert(0) += 1;
+        }
+
+        result
+    }
+}
+
+decl_event!(
+    pub enum Event<T>
+    where
+        CuratorGroupId = <T as ActorAuthenticator>::CuratorGroupId,
+        CuratorId = <T as ActorAuthenticator>::CuratorId,
+        ClassId = <T as Trait>::ClassId,
+        EntityId = <T as Trait>::EntityId,
+        EntityController = EntityController<T>,
+        EntityCreationVoucher = EntityCreationVoucher<T>,
+        Status = bool,
+        Actor = Actor<T>,
+        Nonce = <T as Trait>::Nonce,
+    {
+        CuratorGroupAdded(CuratorGroupId),
+        CuratorGroupRemoved(CuratorGroupId),
+        CuratorGroupStatusSet(Status),
+        CuratorAdded(CuratorGroupId, CuratorId),
+        CuratorRemoved(CuratorGroupId, CuratorId),
+        MaintainerAdded(ClassId, CuratorGroupId),
+        MaintainerRemoved(ClassId, CuratorGroupId),
+        EntityCreationVoucherUpdated(EntityController, EntityCreationVoucher),
+        EntityCreationVoucherCreated(EntityController, EntityCreationVoucher),
+        ClassCreated(ClassId),
+        ClassPermissionsUpdated(ClassId),
+        ClassSchemaAdded(ClassId, SchemaId),
+        ClassSchemaStatusUpdated(ClassId, SchemaId, Status),
+        EntityPermissionsUpdated(EntityId),
+        EntityCreated(Actor, EntityId),
+        EntityRemoved(Actor, EntityId),
+        EntitySchemaSupportAdded(Actor, EntityId, SchemaId),
+        EntityPropertyValuesUpdated(Actor, EntityId),
+        EntityPropertyValueVectorCleared(Actor, EntityId, PropertyId),
+        RemovedAtEntityPropertyValueVectorIndex(Actor, EntityId, PropertyId, VecMaxLength, Nonce),
+        InsertedAtEntityPropertyValueVectorIndex(Actor, EntityId, PropertyId, VecMaxLength, Nonce),
+        TransactionCompleted(Actor),
+        EntityOwnershipTransfered(EntityId, EntityController),
+    }
+);

+ 466 - 0
runtime-modules/content-directory/src/mock.rs

@@ -0,0 +1,466 @@
+// #![cfg(test)]
+
+// use crate::*;
+
+// use crate::InputValidationLengthConstraint;
+// use primitives::H256;
+// use runtime_primitives::{
+//     testing::Header,
+//     traits::{BlakeTwo256, IdentityLookup},
+//     Perbill,
+// };
+// use srml_support::{assert_err, assert_ok, impl_outer_origin, parameter_types};
+// use std::cell::RefCell;
+
+// pub const MEMBER_ONE_WITH_CREDENTIAL_ZERO: u64 = 100;
+// pub const MEMBER_TWO_WITH_CREDENTIAL_ZERO: u64 = 101;
+// pub const MEMBER_ONE_WITH_CREDENTIAL_ONE: u64 = 102;
+// pub const MEMBER_TWO_WITH_CREDENTIAL_ONE: u64 = 103;
+
+// pub const UNKNOWN_CLASS_ID: <Runtime as Trait>::ClassId = 111;
+// pub const UNKNOWN_ENTITY_ID: <Runtime as Trait>::EntityId = 222;
+// pub const UNKNOWN_PROP_ID: PropertyId = 333;
+// pub const UNKNOWN_SCHEMA_ID: SchemaId = 444;
+
+// pub const SCHEMA_ID_0: SchemaId = 0;
+// pub const SCHEMA_ID_1: SchemaId = 1;
+
+// pub const ZERO_NONCE: <Runtime as Trait>::Nonce = 0;
+// pub const FIRST_NONCE: <Runtime as Trait>::Nonce = 1;
+// pub const SECOND_NONCE: <Runtime as Trait>::Nonce = 2;
+
+// pub const VALID_PROPERTY_VEC_INDEX: VecMaxLength = 0;
+// pub const INVALID_PROPERTY_VEC_INDEX: VecMaxLength = 5;
+
+// pub const PROP_ID_BOOL: PropertyId = 0;
+// pub const PROP_ID_REFERENCE_VEC: PropertyId = 1;
+// pub const PROP_ID_U32: PropertyId = 1;
+// pub const PROP_ID_REFERENCE: PropertyId = 2;
+// pub const PROP_ID_U32_VEC: PropertyId = 3;
+// pub const PROP_ID_U32_VEC_MAX_LEN: PropertyId = 20;
+
+// pub const PRINCIPAL_GROUP_MEMBERS: [[u64; 2]; 2] = [
+//     [
+//         MEMBER_ONE_WITH_CREDENTIAL_ZERO,
+//         MEMBER_TWO_WITH_CREDENTIAL_ZERO,
+//     ],
+//     [
+//         MEMBER_ONE_WITH_CREDENTIAL_ONE,
+//         MEMBER_TWO_WITH_CREDENTIAL_ONE,
+//     ],
+// ];
+
+// pub const CLASS_PERMISSIONS_CREATOR1: u64 = 200;
+// pub const CLASS_PERMISSIONS_CREATOR2: u64 = 300;
+// pub const UNAUTHORIZED_CLASS_PERMISSIONS_CREATOR: u64 = 50;
+
+// const CLASS_PERMISSIONS_CREATORS: [u64; 2] =
+//     [CLASS_PERMISSIONS_CREATOR1, CLASS_PERMISSIONS_CREATOR2];
+
+// impl_outer_origin! {
+//     pub enum Origin for Runtime {}
+// }
+
+// // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
+// #[derive(Clone, Default, PartialEq, Eq, Debug)]
+// pub struct Runtime;
+// parameter_types! {
+//     pub const BlockHashCount: u64 = 250;
+//     pub const MaximumBlockWeight: u32 = 1024;
+//     pub const MaximumBlockLength: u32 = 2 * 1024;
+//     pub const AvailableBlockRatio: Perbill = Perbill::one();
+//     pub const MinimumPeriod: u64 = 5;
+// }
+
+// impl system::Trait for Runtime {
+//     type Origin = Origin;
+//     type Index = u64;
+//     type BlockNumber = u64;
+//     type Call = ();
+//     type Hash = H256;
+//     type Hashing = BlakeTwo256;
+//     type AccountId = u64;
+//     type Lookup = IdentityLookup<Self::AccountId>;
+//     type Header = Header;
+//     type Event = ();
+//     type BlockHashCount = BlockHashCount;
+//     type MaximumBlockWeight = MaximumBlockWeight;
+//     type MaximumBlockLength = MaximumBlockLength;
+//     type AvailableBlockRatio = AvailableBlockRatio;
+//     type Version = ();
+// }
+
+// impl timestamp::Trait for Runtime {
+//     type Moment = u64;
+//     type OnTimestampSet = ();
+//     type MinimumPeriod = MinimumPeriod;
+// }
+
+// thread_local! {
+//     static PROPERTY_NAME_CONSTRAINT: RefCell<InputValidationLengthConstraint> = RefCell::new(InputValidationLengthConstraint::default());
+//     static PROPERTY_DESCRIPTION_CONSTRAINT: RefCell<InputValidationLengthConstraint> = RefCell::new(InputValidationLengthConstraint::default());
+//     static CLASS_NAME_CONSTRAINT: RefCell<InputValidationLengthConstraint> = RefCell::new(InputValidationLengthConstraint::default());
+//     static CLASS_DESCRIPTION_CONSTRAINT: RefCell<InputValidationLengthConstraint> = RefCell::new(InputValidationLengthConstraint::default());
+// }
+
+// pub struct PropertyNameConstraint;
+// impl Get<InputValidationLengthConstraint> for PropertyNameConstraint {
+//     fn get() -> InputValidationLengthConstraint {
+//         PROPERTY_NAME_CONSTRAINT.with(|v| *v.borrow())
+//     }
+// }
+
+// pub struct PropertyDescriptionConstraint;
+// impl Get<InputValidationLengthConstraint> for PropertyDescriptionConstraint {
+//     fn get() -> InputValidationLengthConstraint {
+//         PROPERTY_DESCRIPTION_CONSTRAINT.with(|v| *v.borrow())
+//     }
+// }
+
+// pub struct ClassNameConstraint;
+// impl Get<InputValidationLengthConstraint> for ClassNameConstraint {
+//     fn get() -> InputValidationLengthConstraint {
+//         CLASS_NAME_CONSTRAINT.with(|v| *v.borrow())
+//     }
+// }
+
+// pub struct ClassDescriptionConstraint;
+// impl Get<InputValidationLengthConstraint> for ClassDescriptionConstraint {
+//     fn get() -> InputValidationLengthConstraint {
+//         CLASS_DESCRIPTION_CONSTRAINT.with(|v| *v.borrow())
+//     }
+// }
+
+// impl Trait for Runtime {
+//     type Credential = u64;
+//     type Nonce = u64;
+//     type ClassId = u64;
+//     type EntityId = u64;
+//     type PropertyNameConstraint = PropertyNameConstraint;
+//     type PropertyDescriptionConstraint = PropertyDescriptionConstraint;
+//     type ClassNameConstraint = ClassNameConstraint;
+//     type ClassDescriptionConstraint = ClassDescriptionConstraint;
+// }
+
+// impl ActorAuthenticator for Runtime {
+//     type ActorId = u64;
+//     type GroupId = u64;
+
+//     fn authenticate_authority(account_id: &Self::AccountId) -> bool {
+//         true
+//     }
+
+//     fn authenticate_actor_in_group(
+//         account_id: &Self::AccountId,
+//         group_id: Self::GroupId,
+//         actor_id: Self::ActorId,
+//     ) -> bool {
+//         true
+//     }
+// }
+
+// pub struct ExtBuilder {
+//     property_name_constraint: InputValidationLengthConstraint,
+//     property_description_constraint: InputValidationLengthConstraint,
+//     class_name_constraint: InputValidationLengthConstraint,
+//     class_description_constraint: InputValidationLengthConstraint,
+// }
+
+// impl Default for ExtBuilder {
+//     fn default() -> Self {
+//         Self {
+//             property_name_constraint: InputValidationLengthConstraint::new(1, 49),
+//             property_description_constraint: InputValidationLengthConstraint::new(0, 500),
+//             class_name_constraint: InputValidationLengthConstraint::new(1, 49),
+//             class_description_constraint: InputValidationLengthConstraint::new(0, 500),
+//         }
+//     }
+// }
+
+// impl ExtBuilder {
+//     pub fn post_title_max_length(
+//         mut self,
+//         property_name_constraint: InputValidationLengthConstraint,
+//     ) -> Self {
+//         self.property_name_constraint = property_name_constraint;
+//         self
+//     }
+
+//     pub fn post_body_max_length(
+//         mut self,
+//         property_description_constraint: InputValidationLengthConstraint,
+//     ) -> Self {
+//         self.property_description_constraint = property_description_constraint;
+//         self
+//     }
+
+//     pub fn reply_max_length(
+//         mut self,
+//         class_name_constraint: InputValidationLengthConstraint,
+//     ) -> Self {
+//         self.class_name_constraint = class_name_constraint;
+//         self
+//     }
+
+//     pub fn posts_max_number(
+//         mut self,
+//         class_description_constraint: InputValidationLengthConstraint,
+//     ) -> Self {
+//         self.class_description_constraint = class_description_constraint;
+//         self
+//     }
+
+//     pub fn set_associated_consts(&self) {
+//         PROPERTY_NAME_CONSTRAINT.with(|v| *v.borrow_mut() = self.property_name_constraint);
+//         PROPERTY_DESCRIPTION_CONSTRAINT
+//             .with(|v| *v.borrow_mut() = self.property_description_constraint);
+//         CLASS_NAME_CONSTRAINT.with(|v| *v.borrow_mut() = self.class_name_constraint);
+//         CLASS_DESCRIPTION_CONSTRAINT.with(|v| *v.borrow_mut() = self.class_description_constraint);
+//     }
+
+//     pub fn build(self, config: GenesisConfig<Runtime>) -> runtime_io::TestExternalities {
+//         self.set_associated_consts();
+//         let mut t = system::GenesisConfig::default()
+//             .build_storage::<Runtime>()
+//             .unwrap();
+//         config.assimilate_storage(&mut t).unwrap();
+//         t.into()
+//     }
+// }
+
+// // This function basically just builds a genesis storage key/value store according to
+// // our desired mockup.
+
+// fn default_content_directory_genesis_config() -> GenesisConfig<Runtime> {
+//     GenesisConfig {
+//         class_by_id: vec![],
+//         entity_by_id: vec![],
+//         next_class_id: 1,
+//         next_entity_id: 1,
+//     }
+// }
+
+// pub fn with_test_externalities<R, F: FnOnce() -> R>(f: F) -> R {
+//     let default_genesis_config = default_content_directory_genesis_config();
+//     ExtBuilder::default()
+//         .build(default_genesis_config)
+//         .execute_with(f)
+// }
+
+// impl<T: Trait> Property<T> {
+//     pub fn required(mut self) -> Self {
+//         self.required = true;
+//         self
+//     }
+// }
+
+// pub fn assert_class_props(
+//     class_id: <Runtime as Trait>::ClassId,
+//     expected_props: Vec<Property<Runtime>>,
+// ) {
+//     let class = TestModule::class_by_id(class_id);
+//     assert_eq!(class.properties, expected_props);
+// }
+
+// pub fn assert_class_schemas(
+//     class_id: <Runtime as Trait>::ClassId,
+//     expected_schema_prop_ids: Vec<Vec<PropertyId>>,
+// ) {
+//     let class = TestModule::class_by_id(class_id);
+//     let schemas: Vec<_> = expected_schema_prop_ids
+//         .iter()
+//         .map(|prop_ids| Schema::new(prop_ids.to_owned()))
+//         .collect();
+//     assert_eq!(class.schemas, schemas);
+// }
+
+// pub fn assert_entity_not_found(result: dispatch::Result) {
+//     assert_err!(result, ERROR_ENTITY_NOT_FOUND);
+// }
+
+// pub fn simple_test_schema() -> Vec<Property<Runtime>> {
+//     vec![Property {
+//         property_type: PropertyType::Int64(PropertyLockingPolicy::default()),
+//         required: false,
+//         name: b"field1".to_vec(),
+//         description: b"Description field1".to_vec(),
+//     }]
+// }
+
+// pub fn simple_test_entity_property_values<T: Trait>() -> BTreeMap<PropertyId, PropertyValue<T>> {
+//     let mut property_values = BTreeMap::new();
+//     property_values.insert(0, PropertyValue::Int64(1337));
+//     property_values
+// }
+
+// pub fn create_simple_class(permissions: ClassPermissions) -> <Runtime as Trait>::ClassId {
+//     let class_id = TestModule::next_class_id();
+//     assert_ok!(TestModule::create_class(
+//         Origin::signed(CLASS_PERMISSIONS_CREATOR1),
+//         b"class_name_1".to_vec(),
+//         b"class_description_1".to_vec(),
+//         permissions
+//     ));
+//     class_id
+// }
+
+// pub fn create_simple_class_with_default_permissions() -> <Runtime as Trait>::ClassId {
+//     create_simple_class(Default::default())
+// }
+
+// pub fn class_minimal() -> ClassPermissions {
+//     ClassPermissions::default()
+// }
+
+// // pub fn class_minimal_with_admins(admins: Vec<<Runtime as Trait>::Credential>) -> ClassPermissions {
+// //     ClassPermissions { ..class_minimal() }
+// // }
+
+// pub fn next_entity_id() -> <Runtime as Trait>::EntityId {
+//     TestModule::next_entity_id()
+// }
+
+// // pub fn create_entity_of_class(
+// //     class_id: <Runtime as Trait>::ClassId,
+// // ) -> <Runtime as Trait>::EntityId {
+// //     let entity_id = TestModule::next_entity_id();
+// //     assert_eq!(TestModule::perform_entity_creation(class_id,), entity_id);
+// //     entity_id
+// // }
+
+// // pub fn create_entity_with_schema_support() -> <Runtime as Trait>::EntityId {
+// //     let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+// //     let mut property_values = BTreeMap::new();
+// //     property_values.insert(PROP_ID_BOOL, PropertyValue::Bool(true));
+// //     property_values.insert(
+// //         PROP_ID_U32_VEC,
+// //         PropertyValue::Uint32Vec(vec![123, 234, 44], <Runtime as Trait>::Nonce::default()),
+// //     );
+// //     assert_ok!(TestModule::add_entity_schema_support(
+// //         entity_id,
+// //         schema_id,
+// //         property_values
+// //     ));
+// //     entity_id
+// // }
+
+// // pub fn create_class_with_schema() -> (<Runtime as Trait>::ClassId, SchemaId) {
+// //     let class_id = create_simple_class_with_default_permissions();
+// //     let schema_id = TestModule::append_class_schema(
+// //         class_id,
+// //         vec![],
+// //         vec![
+// //             good_prop_bool().required(),
+// //             good_prop_u32(),
+// //             new_reference_class_prop(class_id),
+// //             good_prop_u32_vec(),
+// //         ],
+// //     )
+// //     .expect("This should not happen");
+// //     (class_id, schema_id)
+// // }
+
+// // pub fn create_class_with_schema_and_entity() -> (
+// //     <Runtime as Trait>::ClassId,
+// //     SchemaId,
+// //     <Runtime as Trait>::EntityId,
+// // ) {
+// //     let (class_id, schema_id) = create_class_with_schema();
+// //     let entity_id = create_entity_of_class(class_id);
+// //     (class_id, schema_id, entity_id)
+// // }
+
+// pub fn good_prop_bool() -> Property<Runtime> {
+//     Property {
+//         property_type: PropertyType::Bool(PropertyLockingPolicy::default()),
+//         required: false,
+//         name: b"Name of a bool property".to_vec(),
+//         description: b"Description of a bool property".to_vec(),
+//     }
+// }
+
+// pub fn good_prop_u32() -> Property<Runtime> {
+//     Property {
+//         property_type: PropertyType::Uint32(PropertyLockingPolicy::default()),
+//         required: false,
+//         name: b"Name of a u32 property".to_vec(),
+//         description: b"Description of a u32 property".to_vec(),
+//     }
+// }
+
+// pub fn good_prop_u32_vec() -> Property<Runtime> {
+//     Property {
+//         property_type: PropertyType::Uint32Vec(PROP_ID_U32_VEC_MAX_LEN, PropertyLockingPolicy::default()),
+//         required: false,
+//         name: b"Name of a u32 vec property".to_vec(),
+//         description: b"Description of a u32 vec property".to_vec(),
+//     }
+// }
+
+// pub fn good_prop_text() -> Property<Runtime> {
+//     Property {
+//         property_type: PropertyType::Text(20, PropertyLockingPolicy::default()),
+//         required: false,
+//         name: b"Name of a text property".to_vec(),
+//         description: b"Description of a text property".to_vec(),
+//     }
+// }
+
+// pub fn new_reference_class_prop(class_id: <Runtime as Trait>::ClassId) -> Property<Runtime> {
+//     Property {
+//         property_type: PropertyType::Reference(class_id, PropertyLockingPolicy::default(), false),
+//         required: false,
+//         name: b"Name of a internal property".to_vec(),
+//         description: b"Description of a internal property".to_vec(),
+//     }
+// }
+
+// pub fn new_reference_class_prop_vec(class_id: <Runtime as Trait>::ClassId) -> Property<Runtime> {
+//     Property {
+//         property_type: PropertyType::ReferenceVec(
+//             PROP_ID_U32_VEC_MAX_LEN,
+//             class_id,
+//             PropertyLockingPolicy::default(),
+//             false,
+//         ),
+//         required: false,
+//         name: b"Name of a internal property".to_vec(),
+//         description: b"Description of a internal property".to_vec(),
+//     }
+// }
+
+// pub fn good_class_name() -> Vec<u8> {
+//     b"Name of a class".to_vec()
+// }
+
+// pub fn good_class_description() -> Vec<u8> {
+//     b"Description of a class".to_vec()
+// }
+
+// pub fn good_props() -> Vec<Property<Runtime>> {
+//     vec![good_prop_bool(), good_prop_u32()]
+// }
+
+// pub fn good_prop_ids() -> Vec<PropertyId> {
+//     vec![0, 1]
+// }
+
+// pub fn bool_prop_value<T: Trait>() -> BTreeMap<PropertyId, PropertyValue<T>> {
+//     let mut property_values = BTreeMap::new();
+//     property_values.insert(0, PropertyValue::Bool(true));
+//     property_values
+// }
+
+// pub fn prop_value<T: Trait>(
+//     index: PropertyId,
+//     value: PropertyValue<T>,
+// ) -> BTreeMap<PropertyId, PropertyValue<T>> {
+//     let mut property_values = BTreeMap::new();
+//     property_values.insert(index, value);
+//     property_values
+// }
+
+// // pub type System = system::Module;
+
+// /// Export module on a test runtime
+// pub type TestModule = Module<Runtime>;

+ 126 - 0
runtime-modules/content-directory/src/operations.rs

@@ -0,0 +1,126 @@
+use crate::{
+    PropertyId, PropertyValue, SchemaId, SinglePropertyValue, Trait, Value, VecPropertyValue,
+    VecValue,
+};
+use codec::{Decode, Encode};
+use rstd::collections::btree_map::BTreeMap;
+use rstd::prelude::*;
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub enum ParametrizedPropertyValue<T: Trait> {
+    /// Same fields as normal PropertyValue
+    PropertyValue(PropertyValue<T>),
+
+    /// This is the index of an operation creating an entity in the transaction/batch operations
+    InternalEntityJustAdded(u32), // should really be usize but it doesn't have Encode/Decode support
+
+    /// Vector of mix of Entities already existing and just added in a recent operation
+    InternalEntityVec(Vec<ParameterizedEntity<T>>),
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub enum ParameterizedEntity<T: Trait> {
+    InternalEntityJustAdded(u32),
+    ExistingEntity(T::EntityId),
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct ParametrizedClassPropertyValue<T: Trait> {
+    /// Index is into properties vector of class.
+    pub in_class_index: PropertyId,
+
+    /// Value of property with index `in_class_index` in a given class.
+    pub value: ParametrizedPropertyValue<T>,
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct CreateEntityOperation<T: Trait> {
+    pub class_id: T::ClassId,
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct UpdatePropertyValuesOperation<T: Trait> {
+    pub entity_id: ParameterizedEntity<T>,
+    pub new_parametrized_property_values: Vec<ParametrizedClassPropertyValue<T>>,
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct AddSchemaSupportToEntityOperation<T: Trait> {
+    pub entity_id: ParameterizedEntity<T>,
+    pub schema_id: SchemaId,
+    pub parametrized_property_values: Vec<ParametrizedClassPropertyValue<T>>,
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub enum OperationType<T: Trait> {
+    CreateEntity(CreateEntityOperation<T>),
+    UpdatePropertyValues(UpdatePropertyValuesOperation<T>),
+    AddSchemaSupportToEntity(AddSchemaSupportToEntityOperation<T>),
+}
+
+pub fn parametrized_entity_to_entity_id<T: Trait>(
+    created_entities: &[T::EntityId],
+    entity: ParameterizedEntity<T>,
+) -> Result<T::EntityId, &'static str> {
+    match entity {
+        ParameterizedEntity::ExistingEntity(entity_id) => Ok(entity_id),
+        ParameterizedEntity::InternalEntityJustAdded(op_index_u32) => {
+            let op_index = op_index_u32 as usize;
+            Ok(*created_entities
+                .get(op_index)
+                .ok_or("EntityNotCreatedByOperation")?)
+        }
+    }
+}
+
+pub fn parametrized_property_values_to_property_values<T: Trait>(
+    created_entities: &[T::EntityId],
+    parametrized_property_values: Vec<ParametrizedClassPropertyValue<T>>,
+) -> Result<BTreeMap<PropertyId, PropertyValue<T>>, &'static str> {
+    let mut class_property_values = BTreeMap::new();
+
+    for parametrized_class_property_value in parametrized_property_values.into_iter() {
+        let property_value = match parametrized_class_property_value.value {
+            ParametrizedPropertyValue::PropertyValue(value) => value,
+            ParametrizedPropertyValue::InternalEntityJustAdded(
+                entity_created_in_operation_index,
+            ) => {
+                // Verify that referenced entity was indeed created created
+                let op_index = entity_created_in_operation_index as usize;
+                let entity_id = created_entities
+                    .get(op_index)
+                    .ok_or("EntityNotCreatedByOperation")?;
+                PropertyValue::Single(SinglePropertyValue::new(Value::Reference(*entity_id)))
+            }
+            ParametrizedPropertyValue::InternalEntityVec(parametrized_entities) => {
+                let mut entities: Vec<T::EntityId> = vec![];
+
+                for parametrized_entity in parametrized_entities.into_iter() {
+                    match parametrized_entity {
+                        ParameterizedEntity::ExistingEntity(id) => entities.push(id),
+                        ParameterizedEntity::InternalEntityJustAdded(
+                            entity_created_in_operation_index,
+                        ) => {
+                            let op_index = entity_created_in_operation_index as usize;
+                            let entity_id = created_entities
+                                .get(op_index)
+                                .ok_or("EntityNotCreatedByOperation")?;
+                            entities.push(*entity_id);
+                        }
+                    }
+                }
+                PropertyValue::Vector(VecPropertyValue::new(
+                    VecValue::Reference(entities),
+                    T::Nonce::default(),
+                ))
+            }
+        };
+
+        class_property_values.insert(
+            parametrized_class_property_value.in_class_index,
+            property_value,
+        );
+    }
+
+    Ok(class_property_values)
+}

+ 569 - 0
runtime-modules/content-directory/src/permissions.rs

@@ -0,0 +1,569 @@
+use crate::errors::*;
+use crate::*;
+use codec::{Codec, Decode, Encode};
+use core::fmt::Debug;
+use runtime_primitives::traits::{MaybeSerializeDeserialize, Member, SimpleArithmetic};
+
+#[cfg(feature = "std")]
+pub use serde::{Deserialize, Serialize};
+use srml_support::{dispatch, ensure, Parameter};
+
+/// Model of authentication manager.
+pub trait ActorAuthenticator: system::Trait + Debug {
+    /// Curator identifier
+    type CuratorId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + Clone
+        + MaybeSerializeDeserialize
+        + Eq
+        + PartialEq
+        + Ord;
+
+    /// Member identifier
+    type MemberId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + Clone
+        + MaybeSerializeDeserialize
+        + Eq
+        + PartialEq
+        + Ord;
+
+    /// Curator group identifier
+    type CuratorGroupId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + One
+        + Default
+        + Copy
+        + Clone
+        + MaybeSerializeDeserialize
+        + Eq
+        + PartialEq
+        + Ord;
+
+    /// Authorize actor as lead
+    fn is_lead(account_id: &Self::AccountId) -> bool;
+
+    /// Authorize actor as curator
+    fn is_curator(curator_id: &Self::CuratorId, account_id: &Self::AccountId) -> bool;
+
+    /// Authorize actor as member
+    fn is_member(member_id: &Self::MemberId, account_id: &Self::AccountId) -> bool;
+}
+
+pub fn ensure_curator_auth_success<T: ActorAuthenticator>(
+    curator_id: &T::CuratorId,
+    account_id: &T::AccountId,
+) -> dispatch::Result {
+    ensure!(
+        T::is_curator(curator_id, account_id),
+        ERROR_CURATOR_AUTH_FAILED
+    );
+    Ok(())
+}
+
+pub fn ensure_member_auth_success<T: ActorAuthenticator>(
+    member_id: &T::MemberId,
+    account_id: &T::AccountId,
+) -> dispatch::Result {
+    ensure!(
+        T::is_member(member_id, account_id),
+        ERROR_MEMBER_AUTH_FAILED
+    );
+    Ok(())
+}
+
+pub fn ensure_lead_auth_success<T: ActorAuthenticator>(
+    account_id: &T::AccountId,
+) -> dispatch::Result {
+    ensure!(T::is_lead(account_id), ERROR_LEAD_AUTH_FAILED);
+    Ok(())
+}
+
+/// Ensure given `Origin` is lead
+pub fn ensure_is_lead<T: ActorAuthenticator>(origin: T::Origin) -> dispatch::Result {
+    let account_id = ensure_signed(origin)?;
+    ensure_lead_auth_success::<T>(&account_id)
+}
+
+/// Authorize curator, performing all checks to ensure curator can act
+pub fn perform_curator_in_group_auth<T: Trait>(
+    curator_id: &T::CuratorId,
+    curator_group_id: &T::CuratorGroupId,
+    account_id: &T::AccountId,
+) -> dispatch::Result {
+    ensure_curator_auth_success::<T>(curator_id, account_id)?;
+
+    let curator_group = Module::<T>::ensure_curator_group_exists(curator_group_id)?;
+
+    ensure!(curator_group.is_active(), ERROR_CURATOR_GROUP_IS_NOT_ACTIVE);
+    CuratorGroup::<T>::ensure_curator_in_group_exists(&curator_group, curator_id)?;
+    Ok(())
+}
+
+/// A group, that consists of `curators` set
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct CuratorGroup<T: Trait> {
+    /// Curators set, associated with a iven curator group
+    curators: BTreeSet<T::CuratorId>,
+
+    /// When `false`, curator in a given group is forbidden to act
+    active: bool,
+
+    /// Used to count the number of `Class`(es), given curator group maintains
+    number_of_classes_maintained: ReferenceCounter,
+}
+
+impl<T: Trait> Default for CuratorGroup<T> {
+    fn default() -> Self {
+        Self {
+            curators: BTreeSet::new(),
+            active: false,
+            number_of_classes_maintained: 0,
+        }
+    }
+}
+
+impl<T: Trait> CuratorGroup<T> {
+    /// Check if `CuratorGroup` contains curator under given `curator_id`
+    pub fn is_curator(&self, curator_id: &T::CuratorId) -> bool {
+        self.curators.contains(curator_id)
+    }
+
+    /// Check if `CuratorGroup` is active
+    pub fn is_active(&self) -> bool {
+        self.active
+    }
+
+    /// Set `CuratorGroup` status as provided
+    pub fn set_status(&mut self, is_active: bool) {
+        self.active = is_active
+    }
+
+    /// Retrieve set of all curator_ids related to `CuratorGroup` by reference
+    pub fn get_curators(&self) -> &BTreeSet<T::CuratorId> {
+        &self.curators
+    }
+
+    /// Retrieve set of all curator_ids related to `CuratorGroup` by mutable  reference
+    pub fn get_curators_mut(&mut self) -> &mut BTreeSet<T::CuratorId> {
+        &mut self.curators
+    }
+
+    /// Increment number of classes `CuratorGroup` maintains
+    pub fn increment_number_of_classes_maintained_count(&mut self) {
+        self.number_of_classes_maintained += 1;
+    }
+
+    /// Decrement number of classes `CuratorGroup` maintains
+    pub fn decrement_number_of_classes_maintained_count(&mut self) {
+        self.number_of_classes_maintained -= 1;
+    }
+
+    /// Ensure curator group does not maintain any class
+    pub fn ensure_curator_group_maintains_no_classes(&self) -> dispatch::Result {
+        ensure!(
+            self.number_of_classes_maintained == 0,
+            ERROR_CURATOR_GROUP_REMOVAL_FORBIDDEN
+        );
+        Ok(())
+    }
+
+    /// Ensure `MaxNumberOfCuratorsPerGroup` constraint satisfied
+    pub fn ensure_max_number_of_curators_limit_not_reached(&self) -> dispatch::Result {
+        ensure!(
+            self.curators.len() < T::MaxNumberOfCuratorsPerGroup::get() as usize,
+            ERROR_NUMBER_OF_CURATORS_PER_GROUP_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure curator under given `curator_id` exists in `CuratorGroup`
+    pub fn ensure_curator_in_group_exists(&self, curator_id: &T::CuratorId) -> dispatch::Result {
+        ensure!(
+            self.get_curators().contains(curator_id),
+            ERROR_CURATOR_IS_NOT_A_MEMBER_OF_A_GIVEN_CURATOR_GROUP
+        );
+        Ok(())
+    }
+}
+
+/// A voucher for `Entity` creation
+#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq)]
+pub struct EntityCreationVoucher<T: Trait> {
+    /// How many are allowed in total
+    pub maximum_entities_count: T::EntityId,
+
+    /// How many have currently been created
+    pub entities_created: T::EntityId,
+}
+
+impl<T: Trait> Default for EntityCreationVoucher<T> {
+    fn default() -> Self {
+        Self {
+            maximum_entities_count: T::EntityId::zero(),
+            entities_created: T::EntityId::zero(),
+        }
+    }
+}
+
+impl<T: Trait> EntityCreationVoucher<T> {
+    /// Create a new instance of `EntityCreationVoucher` with specified limit
+    pub fn new(maximum_entities_count: T::EntityId) -> Self {
+        Self {
+            maximum_entities_count,
+            entities_created: T::EntityId::zero(),
+        }
+    }
+
+    /// Set new `maximum_entities_count` limit
+    pub fn set_maximum_entities_count(&mut self, maximum_entities_count: T::EntityId) {
+        self.maximum_entities_count = maximum_entities_count
+    }
+
+    /// Increase `entities_created` by 1
+    pub fn increment_created_entities_count(&mut self) {
+        self.entities_created += T::EntityId::one();
+    }
+
+    /// Decrease `entities_created` by 1
+    pub fn decrement_created_entities_count(&mut self) {
+        self.entities_created -= T::EntityId::one();
+    }
+
+    /// Check if `entities_created` is less than `maximum_entities_count` limit set to this `EntityCreationVoucher`
+    pub fn limit_not_reached(&self) -> bool {
+        self.entities_created < self.maximum_entities_count
+    }
+
+    /// Ensure new voucher`s max entities count is less than number of already created entities in this `EntityCreationVoucher`
+    pub fn ensure_new_max_entities_count_is_valid(
+        self,
+        maximum_entities_count: T::EntityId,
+    ) -> dispatch::Result {
+        ensure!(
+            maximum_entities_count >= self.entities_created,
+            ERROR_NEW_ENTITIES_MAX_COUNT_IS_LESS_THAN_NUMBER_OF_ALREADY_CREATED
+        );
+        Ok(())
+    }
+
+    /// Ensure voucher limit not reached
+    pub fn ensure_voucher_limit_not_reached(&self) -> dispatch::Result {
+        ensure!(self.limit_not_reached(), ERROR_VOUCHER_LIMIT_REACHED);
+        Ok(())
+    }
+}
+
+/// Enum, representing all possible `Actor`s
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, Debug)]
+pub enum Actor<T: Trait> {
+    Curator(T::CuratorGroupId, T::CuratorId),
+    Member(T::MemberId),
+    Lead,
+}
+
+impl<T: Trait> Default for Actor<T> {
+    fn default() -> Self {
+        Self::Lead
+    }
+}
+
+/// Permissions for an instance of a `Class` in the versioned store.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct ClassPermissions<T: Trait> {
+    /// For this permission, the individual member is allowed to create the entity and become controller.
+    any_member: bool,
+
+    /// Whether to prevent everyone from creating an entity.
+    ///
+    /// This could be useful in order to quickly, and possibly temporarily, block new entity creation, without
+    /// having to tear down `can_create_entities`.
+    entity_creation_blocked: bool,
+
+    /// Whether to prevent everyone from updating entity properties.
+    ///
+    /// This could be useful in order to quickly, and probably temporarily, block any editing of entities,
+    /// rather than for example having to set, and later clear.
+    all_entity_property_values_locked: bool,
+
+    /// Current class maintainer curator groups
+    maintainers: BTreeSet<T::CuratorGroupId>,
+}
+
+impl<T: Trait> Default for ClassPermissions<T> {
+    fn default() -> Self {
+        Self {
+            any_member: false,
+            entity_creation_blocked: false,
+            all_entity_property_values_locked: false,
+            maintainers: BTreeSet::new(),
+        }
+    }
+}
+
+impl<T: Trait> ClassPermissions<T> {
+    /// Retieve `all_entity_property_values_locked` status
+    pub fn all_entity_property_values_locked(&self) -> bool {
+        self.all_entity_property_values_locked
+    }
+
+    /// Retieve `any_member` status
+    pub fn any_member_status(&self) -> bool {
+        self.any_member
+    }
+
+    /// Check if given `curator_group_id` is maintainer of current `Class`
+    pub fn is_maintainer(&self, curator_group_id: &T::CuratorGroupId) -> bool {
+        self.maintainers.contains(curator_group_id)
+    }
+
+    /// Get `Class` maintainers by reference
+    pub fn get_maintainers(&self) -> &BTreeSet<T::CuratorGroupId> {
+        &self.maintainers
+    }
+
+    /// Get `Class` maintainers by mutable reference
+    pub fn get_maintainers_mut(&mut self) -> &mut BTreeSet<T::CuratorGroupId> {
+        &mut self.maintainers
+    }
+
+    /// Set `entity_creation_blocked` flag, as provided
+    pub fn set_entity_creation_blocked(&mut self, entity_creation_blocked: bool) {
+        self.entity_creation_blocked = entity_creation_blocked
+    }
+
+    /// Set `all_entity_property_values_locked` flag, as provided
+    pub fn set_all_entity_property_values_locked(
+        &mut self,
+        all_entity_property_values_locked: bool,
+    ) {
+        self.all_entity_property_values_locked = all_entity_property_values_locked
+    }
+
+    /// Set `any_member` flag, as provided
+    pub fn set_any_member_status(&mut self, any_member: bool) {
+        self.any_member = any_member;
+    }
+
+    /// Update `maintainers` set with provided one
+    pub fn set_maintainers(&mut self, maintainers: BTreeSet<T::CuratorGroupId>) {
+        self.maintainers = maintainers
+    }
+
+    /// Ensure provided actor can create entities of current `Class`
+    pub fn ensure_can_create_entities(
+        &self,
+        account_id: &T::AccountId,
+        actor: &Actor<T>,
+    ) -> Result<(), &'static str> {
+        let can_create = match &actor {
+            Actor::Lead => {
+                ensure_lead_auth_success::<T>(account_id)?;
+                true
+            }
+            Actor::Member(member_id) if self.any_member => {
+                ensure_member_auth_success::<T>(member_id, account_id)?;
+                true
+            }
+            Actor::Curator(curator_group_id, curator_id)
+                if self.maintainers.contains(curator_group_id) =>
+            {
+                perform_curator_in_group_auth::<T>(curator_id, curator_group_id, account_id)?;
+                true
+            }
+            _ => false,
+        };
+        ensure!(can_create, ERROR_ACTOR_CAN_NOT_CREATE_ENTITIES);
+        Ok(())
+    }
+
+    /// Ensure entities creation is not blocked on `Class` level
+    pub fn ensure_entity_creation_not_blocked(&self) -> dispatch::Result {
+        ensure!(self.entity_creation_blocked, ERROR_ENTITY_CREATION_BLOCKED);
+        Ok(())
+    }
+
+    /// Ensure maintainer, associated with given `group_id` is already added to `maintainers` set
+    pub fn ensure_maintainer_exists(&self, group_id: &T::CuratorGroupId) -> dispatch::Result {
+        ensure!(
+            self.maintainers.contains(group_id),
+            ERROR_MAINTAINER_DOES_NOT_EXIST
+        );
+        Ok(())
+    }
+
+    /// Ensure maintainer, associated with given `group_id` is not yet added to `maintainers` set
+    pub fn ensure_maintainer_does_not_exist(
+        &self,
+        group_id: &T::CuratorGroupId,
+    ) -> dispatch::Result {
+        ensure!(
+            !self.maintainers.contains(group_id),
+            ERROR_MAINTAINER_ALREADY_EXISTS
+        );
+        Ok(())
+    }
+}
+
+/// Owner of an `Entity`.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub enum EntityController<T: Trait> {
+    Maintainers,
+    Member(T::MemberId),
+    Lead,
+}
+
+impl<T: Trait> EntityController<T> {
+    /// Create `EntityController` enum representation, using provided `Actor`
+    pub fn from_actor(actor: &Actor<T>) -> Self {
+        match &actor {
+            Actor::Lead => Self::Lead,
+            Actor::Member(member_id) => Self::Member(*member_id),
+            Actor::Curator(_, _) => Self::Maintainers,
+        }
+    }
+}
+
+impl<T: Trait> Default for EntityController<T> {
+    fn default() -> Self {
+        Self::Lead
+    }
+}
+
+/// Permissions for a given entity.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct EntityPermissions<T: Trait> {
+    /// Current controller, which is initially set based on who created entity
+    pub controller: EntityController<T>,
+
+    /// Forbid groups to mutate any property value.
+    /// Can be useful to use in concert with some curation censorship policy
+    pub frozen: bool,
+
+    /// Prevent from being referenced by any entity (including self-references).
+    /// Can be useful to use in concert with some curation censorship policy,
+    /// e.g. to block content from being included in some public playlist.
+    pub referenceable: bool,
+}
+
+impl<T: Trait> Default for EntityPermissions<T> {
+    fn default() -> Self {
+        Self {
+            controller: EntityController::<T>::default(),
+            frozen: false,
+            referenceable: true,
+        }
+    }
+}
+
+impl<T: Trait> EntityPermissions<T> {
+    /// Create an instance of `EntityPermissions` with `EntityController` equal to provided one
+    pub fn default_with_controller(controller: EntityController<T>) -> Self {
+        Self {
+            controller,
+            ..EntityPermissions::default()
+        }
+    }
+
+    /// Set current `controller` as provided
+    pub fn set_conroller(&mut self, controller: EntityController<T>) {
+        self.controller = controller
+    }
+
+    /// Check if inner `controller` is equal to provided one
+    pub fn controller_is_equal_to(&self, entity_controller: &EntityController<T>) -> bool {
+        self.controller == *entity_controller
+    }
+
+    /// Set `frozen` flag as provided
+    pub fn set_frozen(&mut self, frozen: bool) {
+        self.frozen = frozen
+    }
+
+    /// Set `referenceable` flag as provided
+    pub fn set_referencable(&mut self, referenceable: bool) {
+        self.referenceable = referenceable;
+    }
+
+    /// Retrieve `referenceable` flag
+    pub fn is_referancable(&self) -> bool {
+        self.referenceable
+    }
+
+    /// Get current `controller` by reference
+    pub fn get_controller(&self) -> &EntityController<T> {
+        &self.controller
+    }
+
+    /// Ensure actor with given `EntityAccessLevel` can remove entity
+    pub fn ensure_group_can_remove_entity(access_level: EntityAccessLevel) -> dispatch::Result {
+        match access_level {
+            EntityAccessLevel::EntityController => Ok(()),
+            EntityAccessLevel::EntityControllerAndMaintainer => Ok(()),
+            _ => Err(ERROR_ENTITY_REMOVAL_ACCESS_DENIED),
+        }
+    }
+}
+
+/// Type, derived from dispatchable call, identifies the caller
+#[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
+pub enum EntityAccessLevel {
+    /// Caller identified as the entity maintainer
+    EntityMaintainer,
+
+    /// Caller identified as the entity controller
+    EntityController,
+
+    /// Caller, that can act as controller and maintainer simultaneously
+    /// (can be useful, when controller and maintainer have features, that do not intersect)
+    EntityControllerAndMaintainer,
+}
+
+impl EntityAccessLevel {
+    /// Derives the `EntityAccessLevel` for the actor, attempting to act.
+    pub fn derive<T: Trait>(
+        account_id: &T::AccountId,
+        entity_permissions: &EntityPermissions<T>,
+        class_permissions: &ClassPermissions<T>,
+        actor: &Actor<T>,
+    ) -> Result<Self, &'static str> {
+        let controller = EntityController::<T>::from_actor(actor);
+        match actor {
+            Actor::Lead if entity_permissions.controller_is_equal_to(&controller) => {
+                ensure_lead_auth_success::<T>(account_id).map(|_| Self::EntityController)
+            }
+            Actor::Member(member_id) if entity_permissions.controller_is_equal_to(&controller) => {
+                ensure_member_auth_success::<T>(member_id, account_id)
+                    .map(|_| Self::EntityController)
+            }
+            Actor::Curator(curator_group_id, curator_id) => {
+                perform_curator_in_group_auth::<T>(curator_id, curator_group_id, account_id)?;
+                match (
+                    entity_permissions.controller_is_equal_to(&controller),
+                    class_permissions.is_maintainer(curator_group_id),
+                ) {
+                    (true, true) => Ok(Self::EntityControllerAndMaintainer),
+                    (false, true) => Ok(Self::EntityMaintainer),
+                    // Curator cannot be controller, but not maintainer simultaneously
+                    _ => Err(ERROR_ENTITY_ACCESS_DENIED),
+                }
+            }
+            _ => Err(ERROR_ENTITY_ACCESS_DENIED),
+        }
+    }
+}

+ 955 - 0
runtime-modules/content-directory/src/schema.rs

@@ -0,0 +1,955 @@
+use crate::{permissions::EntityAccessLevel, *};
+use codec::{Decode, Encode};
+use core::ops::Deref;
+#[cfg(feature = "std")]
+pub use serde::{Deserialize, Serialize};
+
+/// Type representing max length of vector property type
+pub type VecMaxLength = u16;
+
+/// Type representing max length of text property type
+pub type TextMaxLength = u16;
+
+/// Type identificator for property id
+pub type PropertyId = u16;
+
+/// Type identificator for schema id
+pub type SchemaId = u16;
+
+/// Used to force property values to only reference entities, owned by the same controller
+pub type SameController = bool;
+
+/// Locking policy, representing `Property` locking status for both controller and maintainer
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Default, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub struct PropertyLockingPolicy {
+    is_locked_from_maintainer: bool,
+    is_locked_from_controller: bool,
+}
+
+/// Enum, used for `PropertyType` representation
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub enum Type<T: Trait> {
+    Bool,
+    Uint16,
+    Uint32,
+    Uint64,
+    Int16,
+    Int32,
+    Int64,
+    /// Max length of text item.
+    Text(TextMaxLength),
+    /// Can reference only specific class id entities
+    Reference(T::ClassId, SameController),
+}
+
+impl<T: Trait> Default for Type<T> {
+    fn default() -> Self {
+        Self::Bool
+    }
+}
+
+/// Vector property type representation
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub struct VecPropertyType<T: Trait> {
+    vec_type: Type<T>,
+    /// Max length of vector, corresponding to a given type
+    max_length: VecMaxLength,
+}
+
+impl<T: Trait> Default for VecPropertyType<T> {
+    fn default() -> Self {
+        Self {
+            vec_type: Type::default(),
+            max_length: 0,
+        }
+    }
+}
+
+impl<T: Trait> VecPropertyType<T> {
+    pub fn new(vec_type: Type<T>, max_length: VecMaxLength) -> Self {
+        Self {
+            vec_type,
+            max_length,
+        }
+    }
+
+    /// Ensure `Type` spcific `TextMaxLengthConstraint` & `VecMaxLengthConstraint` satisfied
+    fn ensure_property_type_size_is_valid(&self) -> dispatch::Result {
+        if let Type::Text(text_max_len) = self.vec_type {
+            ensure!(
+                text_max_len <= T::TextMaxLengthConstraint::get(),
+                ERROR_TEXT_PROP_IS_TOO_LONG
+            );
+        }
+        ensure!(
+            self.max_length <= T::VecMaxLengthConstraint::get(),
+            ERROR_VEC_PROP_IS_TOO_LONG
+        );
+        Ok(())
+    }
+
+    pub fn get_vec_type(&self) -> &Type<T> {
+        &self.vec_type
+    }
+
+    pub fn get_max_len(&self) -> VecMaxLength {
+        self.max_length
+    }
+}
+
+/// `Type` enum wrapper
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub struct SingleValuePropertyType<T: Trait>(Type<T>);
+
+impl<T: Trait> Default for SingleValuePropertyType<T> {
+    fn default() -> Self {
+        Self(Type::default())
+    }
+}
+
+impl<T: Trait> SingleValuePropertyType<T> {
+    /// Ensure `Type` specific `TextMaxLengthConstraint` satisfied
+    fn ensure_property_type_size_is_valid(&self) -> dispatch::Result {
+        if let Type::Text(text_max_len) = self.0 {
+            ensure!(
+                text_max_len <= T::TextMaxLengthConstraint::get(),
+                ERROR_TEXT_PROP_IS_TOO_LONG
+            );
+        }
+        Ok(())
+    }
+}
+
+impl<T: Trait> Deref for SingleValuePropertyType<T> {
+    type Target = Type<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+/// Enum, representing either `SingleValuePropertyType` or `VecPropertyType`
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub enum PropertyType<T: Trait> {
+    Single(SingleValuePropertyType<T>),
+    Vector(VecPropertyType<T>),
+}
+
+impl<T: Trait> Default for PropertyType<T> {
+    fn default() -> Self {
+        Self::Single(SingleValuePropertyType::default())
+    }
+}
+
+impl<T: Trait> PropertyType<T> {
+    pub fn as_single_value_type(&self) -> Option<&Type<T>> {
+        if let PropertyType::Single(single_value_property_type) = self {
+            Some(single_value_property_type)
+        } else {
+            None
+        }
+    }
+
+    pub fn as_vec_type(&self) -> Option<&VecPropertyType<T>> {
+        if let PropertyType::Vector(single_value_property_type) = self {
+            Some(single_value_property_type)
+        } else {
+            None
+        }
+    }
+
+    pub fn get_inner_type(&self) -> &Type<T> {
+        match self {
+            PropertyType::Single(single_property_type) => single_property_type,
+            PropertyType::Vector(vec_property_type) => vec_property_type.get_vec_type(),
+        }
+    }
+
+    /// Retrives same_controller.
+    /// Always returns false if `Type` is not a reference,
+    pub fn same_controller_status(&self) -> SameController {
+        if let Type::Reference(_, same_controller) = self.get_inner_type() {
+            *same_controller
+        } else {
+            false
+        }
+    }
+
+    pub fn get_referenced_class_id(&self) -> Option<T::ClassId> {
+        if let Type::Reference(class_id, _) = self.get_inner_type() {
+            Some(*class_id)
+        } else {
+            None
+        }
+    }
+}
+
+/// Value enum representation, related to corresponding `SinglePropertyValue` structure
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub enum Value<T: Trait> {
+    Bool(bool),
+    Uint16(u16),
+    Uint32(u32),
+    Uint64(u64),
+    Int16(i16),
+    Int32(i32),
+    Int64(i64),
+    Text(Vec<u8>),
+    Reference(T::EntityId),
+}
+
+impl<T: Trait> Default for Value<T> {
+    fn default() -> Value<T> {
+        Self::Bool(false)
+    }
+}
+
+impl<T: Trait> Value<T> {
+    /// Retrieve involved `entity_id`, if current `Value` is reference
+    pub fn get_involved_entity(&self) -> Option<T::EntityId> {
+        if let Value::Reference(entity_id) = self {
+            Some(*entity_id)
+        } else {
+            None
+        }
+    }
+}
+
+/// `Value` enum wrapper
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct SinglePropertyValue<T: Trait> {
+    value: Value<T>,
+}
+
+impl<T: Trait> Default for SinglePropertyValue<T> {
+    fn default() -> Self {
+        Self {
+            value: Value::default(),
+        }
+    }
+}
+
+impl<T: Trait> SinglePropertyValue<T> {
+    pub fn new(value: Value<T>) -> Self {
+        Self { value }
+    }
+
+    pub fn get_value_ref(&self) -> &Value<T> {
+        &self.value
+    }
+
+    pub fn get_value(self) -> Value<T> {
+        self.value
+    }
+}
+
+/// Vector value enum representation, related to corresponding `VecPropertyValue` structure
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub enum VecValue<T: Trait> {
+    Bool(Vec<bool>),
+    Uint16(Vec<u16>),
+    Uint32(Vec<u32>),
+    Uint64(Vec<u64>),
+    Int16(Vec<i16>),
+    Int32(Vec<i32>),
+    Int64(Vec<i64>),
+    Text(Vec<Vec<u8>>),
+    Reference(Vec<T::EntityId>),
+}
+
+impl<T: Trait> Default for VecValue<T> {
+    fn default() -> Self {
+        Self::Bool(vec![])
+    }
+}
+
+impl<T: Trait> VecValue<T> {
+    /// Retrieve all involved `entity_id`'s, if current `VecValue` is reference
+    pub fn get_involved_entities(&self) -> Option<Vec<T::EntityId>> {
+        if let Self::Reference(entity_ids) = self {
+            Some(entity_ids.to_owned())
+        } else {
+            None
+        }
+    }
+}
+
+/// Consists of `VecPropertyValue` enum representation and `nonce`, used to avoid vector data race update conditions
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
+pub struct VecPropertyValue<T: Trait> {
+    vec_value: VecValue<T>,
+    nonce: T::Nonce,
+}
+
+impl<T: Trait> VecPropertyValue<T> {
+    fn increment_nonce(&mut self) -> T::Nonce {
+        self.nonce += T::Nonce::one();
+        self.nonce
+    }
+
+    pub fn new(vec_value: VecValue<T>, nonce: T::Nonce) -> Self {
+        Self { vec_value, nonce }
+    }
+
+    /// Retrieve `VecValue` by reference
+    pub fn get_vec_value(&self) -> &VecValue<T> {
+        &self.vec_value
+    }
+
+    fn len(&self) -> usize {
+        match &self.vec_value {
+            VecValue::Bool(vec) => vec.len(),
+            VecValue::Uint16(vec) => vec.len(),
+            VecValue::Uint32(vec) => vec.len(),
+            VecValue::Uint64(vec) => vec.len(),
+            VecValue::Int16(vec) => vec.len(),
+            VecValue::Int32(vec) => vec.len(),
+            VecValue::Int64(vec) => vec.len(),
+            VecValue::Text(vec) => vec.len(),
+            VecValue::Reference(vec) => vec.len(),
+        }
+    }
+
+    /// Clear current `vec_value`, increment `nonce`
+    pub fn vec_clear(&mut self) {
+        match &mut self.vec_value {
+            VecValue::Bool(vec) => *vec = vec![],
+            VecValue::Uint16(vec) => *vec = vec![],
+            VecValue::Uint32(vec) => *vec = vec![],
+            VecValue::Uint64(vec) => *vec = vec![],
+            VecValue::Int16(vec) => *vec = vec![],
+            VecValue::Int32(vec) => *vec = vec![],
+            VecValue::Int64(vec) => *vec = vec![],
+            VecValue::Text(vec) => *vec = vec![],
+            VecValue::Reference(vec) => *vec = vec![],
+        }
+
+        self.increment_nonce();
+    }
+
+    /// Perform removal at given `index_in_property_vec`, increment `nonce`
+    pub fn vec_remove_at(&mut self, index_in_property_vec: VecMaxLength) {
+        fn remove_at_checked<T>(vec: &mut Vec<T>, index_in_property_vec: VecMaxLength) {
+            if (index_in_property_vec as usize) < vec.len() {
+                vec.remove(index_in_property_vec as usize);
+            }
+        }
+
+        match &mut self.vec_value {
+            VecValue::Bool(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Uint16(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Uint32(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Uint64(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Int16(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Int32(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Int64(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Text(vec) => remove_at_checked(vec, index_in_property_vec),
+            VecValue::Reference(vec) => remove_at_checked(vec, index_in_property_vec),
+        }
+
+        self.increment_nonce();
+    }
+
+    /// Insert provided `Value` at given `index_in_property_vec`, increment `nonce`
+    pub fn vec_insert_at(&mut self, index_in_property_vec: VecMaxLength, single_value: Value<T>) {
+        fn insert_at<T>(vec: &mut Vec<T>, index_in_property_vec: VecMaxLength, value: T) {
+            if (index_in_property_vec as usize) < vec.len() {
+                vec.insert(index_in_property_vec as usize, value);
+            }
+        }
+
+        match (&mut self.vec_value, single_value) {
+            (VecValue::Bool(vec), Value::Bool(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            (VecValue::Uint16(vec), Value::Uint16(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            (VecValue::Uint32(vec), Value::Uint32(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            (VecValue::Uint64(vec), Value::Uint64(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            (VecValue::Int16(vec), Value::Int16(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            (VecValue::Int32(vec), Value::Int32(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            (VecValue::Int64(vec), Value::Int64(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+
+            // Match by move, when https://github.com/rust-lang/rust/issues/68354 stableize
+            (VecValue::Text(vec), Value::Text(ref value)) => {
+                insert_at(vec, index_in_property_vec, value.to_owned())
+            }
+            (VecValue::Reference(vec), Value::Reference(value)) => {
+                insert_at(vec, index_in_property_vec, value)
+            }
+            _ => return,
+        }
+
+        self.increment_nonce();
+    }
+
+    /// Ensure `VecPropertyValue` nonce is equal to provided one.
+    /// Used to to avoid possible data races, when performing vector specific operations
+    pub fn ensure_nonce_equality(&self, new_nonce: T::Nonce) -> dispatch::Result {
+        ensure!(
+            self.nonce == new_nonce,
+            ERROR_PROP_VALUE_VEC_NONCES_DOES_NOT_MATCH
+        );
+        Ok(())
+    }
+
+    /// Ensure, provided index is valid index of `VecValue`
+    pub fn ensure_index_in_property_vector_is_valid(
+        &self,
+        index_in_property_vec: VecMaxLength,
+    ) -> dispatch::Result {
+        ensure!(
+            (index_in_property_vec as usize) < self.len(),
+            ERROR_ENTITY_PROP_VALUE_VECTOR_INDEX_IS_OUT_OF_RANGE
+        );
+
+        Ok(())
+    }
+}
+
+/// Enum, representing either `SinglePropertyValue` or `VecPropertyValue`
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub enum PropertyValue<T: Trait> {
+    Single(SinglePropertyValue<T>),
+    Vector(VecPropertyValue<T>),
+}
+
+impl<T: Trait> PropertyValue<T> {
+    pub fn as_single_property_value(&self) -> Option<&SinglePropertyValue<T>> {
+        if let PropertyValue::Single(single_property_value) = self {
+            Some(single_property_value)
+        } else {
+            None
+        }
+    }
+
+    pub fn as_vec_property_value(&self) -> Option<&VecPropertyValue<T>> {
+        if let PropertyValue::Vector(vec_property_value) = self {
+            Some(vec_property_value)
+        } else {
+            None
+        }
+    }
+
+    pub fn as_vec_property_value_mut(&mut self) -> Option<&mut VecPropertyValue<T>> {
+        if let PropertyValue::Vector(vec_property_value) = self {
+            Some(vec_property_value)
+        } else {
+            None
+        }
+    }
+
+    /// Update `Self` with provided `PropertyValue`
+    pub fn update(&mut self, mut new_value: Self) {
+        if let (Some(vec_property_value), Some(new_vec_property_value)) = (
+            self.as_vec_property_value_mut(),
+            new_value.as_vec_property_value_mut(),
+        ) {
+            new_vec_property_value.nonce = vec_property_value.increment_nonce();
+        }
+        *self = new_value;
+    }
+
+    /// Retrieve all involved `entity_id`'s, if current `PropertyValue` is reference
+    pub fn get_involved_entities(&self) -> Option<Vec<T::EntityId>> {
+        match self {
+            PropertyValue::Single(single_property_value) => {
+                if let Some(entity_id) = single_property_value.get_value_ref().get_involved_entity()
+                {
+                    Some(vec![entity_id])
+                } else {
+                    None
+                }
+            }
+            PropertyValue::Vector(vector_property_value) => vector_property_value
+                .get_vec_value()
+                .get_involved_entities(),
+        }
+    }
+}
+
+impl<T: Trait> Default for PropertyValue<T> {
+    fn default() -> Self {
+        PropertyValue::Single(SinglePropertyValue::default())
+    }
+}
+
+/// A schema defines what properties describe an entity
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct Schema {
+    /// Indices into properties vector for the corresponding class.
+    properties: BTreeSet<PropertyId>,
+    /// If schema can be added to an entity
+    is_active: bool,
+}
+
+impl Default for Schema {
+    fn default() -> Self {
+        Self {
+            properties: BTreeSet::new(),
+            // Default schema status
+            is_active: true,
+        }
+    }
+}
+
+impl Schema {
+    /// Create new schema with provided properties
+    pub fn new(properties: BTreeSet<PropertyId>) -> Self {
+        Self {
+            properties,
+            // Default schema status
+            is_active: true,
+        }
+    }
+
+    /// If `Schema` can be added to an entity
+    pub fn is_active(&self) -> bool {
+        self.is_active
+    }
+
+    /// Ensure schema in `active` status
+    pub fn ensure_is_active(&self) -> dispatch::Result {
+        ensure!(self.is_active, ERROR_CLASS_SCHEMA_NOT_ACTIVE);
+        Ok(())
+    }
+
+    /// Ensure `Schema` properties are valid indices of `Class` properties
+    pub fn ensure_schema_properties_are_valid_indices<T: Trait>(
+        &self,
+        class_properties: &[Property<T>],
+    ) -> dispatch::Result {
+        let has_unknown_properties = self
+            .get_properties()
+            .iter()
+            .any(|&prop_id| prop_id >= class_properties.len() as PropertyId);
+        ensure!(
+            !has_unknown_properties,
+            ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_PROP_INDEX
+        );
+        Ok(())
+    }
+
+    /// Get `Schema` `properties` by reference
+    pub fn get_properties(&self) -> &BTreeSet<PropertyId> {
+        &self.properties
+    }
+
+    /// Get `Schema` `properties` by mutable reference
+    pub fn get_properties_mut(&mut self) -> &mut BTreeSet<PropertyId> {
+        &mut self.properties
+    }
+
+    /// Set `Schema`'s `is_active` flag as provided
+    pub fn set_status(&mut self, is_active: bool) {
+        self.is_active = is_active;
+    }
+}
+
+/// `Property` representation, related to a given `Class`
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
+pub struct Property<T: Trait> {
+    pub property_type: PropertyType<T>,
+    /// If property value can be skipped, when adding entity schema support
+    pub required: bool,
+    /// Used to enforce uniquness of a property across all entities that have this property
+    pub unique: bool,
+    pub name: Vec<u8>,
+    pub description: Vec<u8>,
+    pub locking_policy: PropertyLockingPolicy,
+}
+
+impl<T: Trait> Property<T> {
+    pub fn is_locked_from(&self, access_level: EntityAccessLevel) -> bool {
+        let is_locked_from_controller = self.locking_policy.is_locked_from_controller;
+        let is_locked_from_maintainer = self.locking_policy.is_locked_from_maintainer;
+        match access_level {
+            EntityAccessLevel::EntityControllerAndMaintainer => {
+                is_locked_from_controller && is_locked_from_maintainer
+            }
+            EntityAccessLevel::EntityController => is_locked_from_controller,
+            EntityAccessLevel::EntityMaintainer => is_locked_from_maintainer,
+        }
+    }
+
+    /// Ensure `Property` is unlocked from `Actor` with given `EntityAccessLevel`
+    pub fn ensure_unlocked_from(&self, access_level: EntityAccessLevel) -> dispatch::Result {
+        ensure!(
+            self.is_locked_from(access_level),
+            ERROR_CLASS_PROPERTY_TYPE_IS_LOCKED_FOR_GIVEN_ACTOR
+        );
+        Ok(())
+    }
+
+    /// Ensure all values are unique except of null non required values
+    pub fn ensure_unique_option_satisfied(
+        &self,
+        new_value: &PropertyValue<T>,
+        entity_values_updated: &BTreeMap<PropertyId, PropertyValue<T>>,
+    ) -> dispatch::Result {
+        if self.unique && (*new_value != PropertyValue::default() || self.required) {
+            ensure!(
+                entity_values_updated
+                    .iter()
+                    .all(|(_, prop_value)| *prop_value != *new_value),
+                ERROR_PROPERTY_VALUE_SHOULD_BE_UNIQUE
+            );
+        }
+        Ok(())
+    }
+
+    /// Validate new `PropertyValue` against the type of this property
+    /// and check any additional constraints
+    pub fn ensure_property_value_to_update_is_valid(
+        &self,
+        value: &PropertyValue<T>,
+        current_entity_controller: &EntityController<T>,
+    ) -> dispatch::Result {
+        self.ensure_prop_value_matches_its_type(value)?;
+        self.ensure_valid_reference_prop(value, current_entity_controller)?;
+        self.validate_max_len_if_text_prop(value)?;
+        self.validate_max_len_if_vec_prop(value)?;
+        Ok(())
+    }
+
+    /// Ensure `SinglePropertyValue` type is equal to the `VecPropertyValue` type
+    /// and check all constraints
+    pub fn ensure_prop_value_can_be_inserted_at_prop_vec(
+        &self,
+        single_value: &SinglePropertyValue<T>,
+        vec_value: &VecPropertyValue<T>,
+        index_in_property_vec: VecMaxLength,
+        current_entity_controller: &EntityController<T>,
+    ) -> dispatch::Result {
+        vec_value.ensure_index_in_property_vector_is_valid(index_in_property_vec)?;
+
+        fn validate_prop_vec_len_after_value_insert<T>(
+            vec: &[T],
+            max_len: VecMaxLength,
+        ) -> dispatch::Result {
+            ensure!(
+                vec.len() < max_len as usize,
+                ERROR_ENTITY_PROP_VALUE_VECTOR_IS_TOO_LONG
+            );
+            Ok(())
+        }
+
+        let property_type_vec = self
+            .property_type
+            .as_vec_type()
+            .ok_or(ERROR_PROP_VALUE_TYPE_DOESNT_MATCH_INTERNAL_ENTITY_VECTOR_TYPE)?;
+
+        let max_vec_len = property_type_vec.get_max_len();
+
+        match (
+            single_value.get_value_ref(),
+            vec_value.get_vec_value(),
+            property_type_vec.get_vec_type(),
+        ) {
+            // Single values
+            (Value::Bool(_), VecValue::Bool(vec), Type::Bool) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Uint16(_), VecValue::Uint16(vec), Type::Uint16) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Uint32(_), VecValue::Uint32(vec), Type::Uint32) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Uint64(_), VecValue::Uint64(vec), Type::Uint64) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Int16(_), VecValue::Int16(vec), Type::Int16) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Int32(_), VecValue::Int32(vec), Type::Int32) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Int64(_), VecValue::Int64(vec), Type::Int64) => {
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (Value::Text(text_item), VecValue::Text(vec), Type::Text(text_max_len)) => {
+                Self::validate_max_len_of_text(text_item, *text_max_len)?;
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            (
+                Value::Reference(entity_id),
+                VecValue::Reference(vec),
+                Type::Reference(class_id, same_controller_status),
+            ) => {
+                Self::ensure_referancable(
+                    *class_id,
+                    *entity_id,
+                    *same_controller_status,
+                    current_entity_controller,
+                )?;
+                validate_prop_vec_len_after_value_insert(vec, max_vec_len)
+            }
+            _ => Err(ERROR_PROP_VALUE_TYPE_DOESNT_MATCH_INTERNAL_ENTITY_VECTOR_TYPE),
+        }
+    }
+
+    pub fn validate_max_len_if_text_prop(&self, value: &PropertyValue<T>) -> dispatch::Result {
+        let single_value = value
+            .as_single_property_value()
+            .map(|single_prop_value| single_prop_value.get_value_ref());
+        match (single_value, &self.property_type.as_single_value_type()) {
+            (Some(Value::Text(text)), Some(Type::Text(max_len))) => {
+                Self::validate_max_len_of_text(text, *max_len)
+            }
+            _ => Ok(()),
+        }
+    }
+
+    pub fn validate_max_len_of_text(text: &[u8], max_len: TextMaxLength) -> dispatch::Result {
+        ensure!(text.len() <= max_len as usize, ERROR_TEXT_PROP_IS_TOO_LONG);
+        Ok(())
+    }
+
+    fn validate_vec_len<V>(vec: &[V], max_len: VecMaxLength) -> dispatch::Result {
+        ensure!(vec.len() <= max_len as usize, ERROR_VEC_PROP_IS_TOO_LONG);
+        Ok(())
+    }
+
+    pub fn validate_max_len_if_vec_prop(&self, value: &PropertyValue<T>) -> dispatch::Result {
+        let (vec_value, vec_property_type) = if let (Some(vec_value), Some(vec_property_type)) = (
+            value
+                .as_vec_property_value()
+                .map(|vec_property_value| vec_property_value.get_vec_value()),
+            self.property_type.as_vec_type(),
+        ) {
+            (vec_value, vec_property_type)
+        } else {
+            return Ok(());
+        };
+        let max_len = vec_property_type.get_max_len();
+        match vec_value {
+            VecValue::Bool(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Uint16(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Uint32(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Uint64(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Int16(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Int32(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Int64(vec) => Self::validate_vec_len(vec, max_len),
+            VecValue::Text(vec) => {
+                Self::validate_vec_len(vec, max_len)?;
+                if let Type::Text(text_max_len) = vec_property_type.get_vec_type() {
+                    for text_item in vec.iter() {
+                        Self::validate_max_len_of_text(text_item, *text_max_len)?;
+                    }
+                }
+                Ok(())
+            }
+            VecValue::Reference(vec) => Self::validate_vec_len(vec, max_len),
+        }
+    }
+
+    pub fn ensure_prop_value_matches_its_type(&self, value: &PropertyValue<T>) -> dispatch::Result {
+        ensure!(
+            self.does_prop_value_match_type(value),
+            ERROR_PROP_VALUE_DONT_MATCH_TYPE
+        );
+        Ok(())
+    }
+
+    pub fn does_prop_value_match_type(&self, value: &PropertyValue<T>) -> bool {
+        // A non required property can be updated to Bool(false):
+        if !self.required && *value == PropertyValue::default() {
+            return true;
+        }
+        match (value, &self.property_type) {
+            (
+                PropertyValue::Single(single_property_value),
+                PropertyType::Single(ref single_property_type),
+            ) => {
+                match (
+                    single_property_value.get_value_ref(),
+                    single_property_type.deref(),
+                ) {
+                    (Value::Bool(_), Type::Bool)
+                    | (Value::Uint16(_), Type::Uint16)
+                    | (Value::Uint32(_), Type::Uint32)
+                    | (Value::Uint64(_), Type::Uint64)
+                    | (Value::Int16(_), Type::Int16)
+                    | (Value::Int32(_), Type::Int32)
+                    | (Value::Int64(_), Type::Int64)
+                    | (Value::Text(_), Type::Text(_))
+                    | (Value::Reference(_), Type::Reference(_, _)) => true,
+                    _ => false,
+                }
+            }
+            (
+                PropertyValue::Vector(vec_property_value),
+                PropertyType::Vector(ref vec_property_type),
+            ) => {
+                match (
+                    vec_property_value.get_vec_value(),
+                    vec_property_type.get_vec_type(),
+                ) {
+                    (VecValue::Bool(_), Type::Bool)
+                    | (VecValue::Uint16(_), Type::Uint16)
+                    | (VecValue::Uint32(_), Type::Uint32)
+                    | (VecValue::Uint64(_), Type::Uint64)
+                    | (VecValue::Int16(_), Type::Int16)
+                    | (VecValue::Int32(_), Type::Int32)
+                    | (VecValue::Int64(_), Type::Int64)
+                    | (VecValue::Text(_), Type::Text(_))
+                    | (VecValue::Reference(_), Type::Reference(_, _)) => true,
+                    _ => false,
+                }
+            }
+            _ => false,
+        }
+    }
+
+    pub fn ensure_valid_reference_prop(
+        &self,
+        value: &PropertyValue<T>,
+        current_entity_controller: &EntityController<T>,
+    ) -> dispatch::Result {
+        match (value, &self.property_type) {
+            (
+                PropertyValue::Single(single_property_value),
+                PropertyType::Single(single_property_type),
+            ) => {
+                if let (
+                    Value::Reference(entity_id),
+                    Type::Reference(class_id, same_controller_status),
+                ) = (
+                    single_property_value.get_value_ref(),
+                    single_property_type.deref(),
+                ) {
+                    Self::ensure_referancable(
+                        *class_id,
+                        *entity_id,
+                        *same_controller_status,
+                        current_entity_controller,
+                    )?;
+                }
+            }
+            (
+                PropertyValue::Vector(vec_property_value),
+                PropertyType::Vector(vec_property_type),
+            ) => {
+                if let (
+                    VecValue::Reference(entities_vec),
+                    Type::Reference(class_id, same_controller_status),
+                ) = (
+                    vec_property_value.get_vec_value(),
+                    vec_property_type.get_vec_type(),
+                ) {
+                    for entity_id in entities_vec.iter() {
+                        Self::ensure_referancable(
+                            *class_id,
+                            *entity_id,
+                            *same_controller_status,
+                            current_entity_controller,
+                        )?;
+                    }
+                }
+            }
+            _ => (),
+        }
+        Ok(())
+    }
+
+    pub fn ensure_referancable(
+        class_id: T::ClassId,
+        entity_id: T::EntityId,
+        same_controller_status: bool,
+        current_entity_controller: &EntityController<T>,
+    ) -> dispatch::Result {
+        Module::<T>::ensure_known_class_id(class_id)?;
+        Module::<T>::ensure_known_entity_id(entity_id)?;
+        let entity = Module::<T>::entity_by_id(entity_id);
+
+        let entity_permissions = entity.get_permissions_ref();
+
+        ensure!(
+            entity_permissions.is_referancable(),
+            ERROR_ENTITY_CAN_NOT_BE_REFRENCED
+        );
+
+        ensure!(
+            entity.class_id == class_id,
+            ERROR_PROP_DOES_NOT_MATCH_ITS_CLASS
+        );
+        if same_controller_status {
+            ensure!(
+                entity_permissions.controller_is_equal_to(current_entity_controller),
+                ERROR_SAME_CONTROLLER_CONSTRAINT_VIOLATION
+            );
+        }
+        Ok(())
+    }
+
+    /// Ensure `PropertyNameLengthConstraint` satisfied
+    pub fn ensure_name_is_valid(&self) -> dispatch::Result {
+        T::PropertyNameLengthConstraint::get().ensure_valid(
+            self.name.len(),
+            ERROR_PROPERTY_NAME_TOO_SHORT,
+            ERROR_PROPERTY_NAME_TOO_LONG,
+        )
+    }
+
+    /// Ensure `PropertyDescriptionLengthConstraint` satisfied
+    pub fn ensure_description_is_valid(&self) -> dispatch::Result {
+        T::PropertyDescriptionLengthConstraint::get().ensure_valid(
+            self.description.len(),
+            ERROR_PROPERTY_DESCRIPTION_TOO_SHORT,
+            ERROR_PROPERTY_DESCRIPTION_TOO_LONG,
+        )
+    }
+
+    /// Ensure `Type` specific constraints satisfied
+    pub fn ensure_property_type_size_is_valid(&self) -> dispatch::Result {
+        match &self.property_type {
+            PropertyType::Single(single_property_type) => {
+                single_property_type.ensure_property_type_size_is_valid()
+            }
+            PropertyType::Vector(vec_property_type) => {
+                vec_property_type.ensure_property_type_size_is_valid()
+            }
+        }
+    }
+
+    /// If `Type::Reference`, ensure refers to existing `class_id`
+    pub fn ensure_property_type_reference_is_valid(&self) -> dispatch::Result {
+        let has_unknown_reference =
+            if let Type::Reference(other_class_id, _) = self.property_type.get_inner_type() {
+                !<ClassById<T>>::exists(other_class_id)
+            } else {
+                false
+            };
+
+        ensure!(
+            !has_unknown_reference,
+            ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_CLASS
+        );
+
+        Ok(())
+    }
+}

+ 1692 - 0
runtime-modules/content-directory/src/tests.rs

@@ -0,0 +1,1692 @@
+// #![cfg(test)]
+
+// use super::*;
+// use crate::mock::*;
+// use core::iter::FromIterator;
+// use rstd::collections::btree_set::BTreeSet;
+// use srml_support::{assert_err, assert_ok};
+
+// #[test]
+// fn create_class_then_entity_with_default_class() {
+//     with_test_externalities(|| {
+//         // Only authorized accounts can create classes
+//         assert_err!(
+//             TestModule::create_class_with_default_permissions(
+//                 Origin::signed(UNAUTHORIZED_CLASS_PERMISSIONS_CREATOR),
+//                 b"class_name".to_vec(),
+//                 b"class_description".to_vec(),
+//             ),
+//             "NotPermittedToCreateClass"
+//         );
+
+//         let class_id = create_simple_class_with_default_permissions();
+
+//         assert!(<ClassById<Runtime>>::exists(class_id));
+
+//         assert_eq!(TestModule::next_class_id(), class_id + 1);
+
+//         // default class permissions have empty add_schema acl
+//         assert_err!(
+//             TestModule::add_class_schema(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//                 Some(0),
+//                 class_id,
+//                 vec![],
+//                 simple_test_schema()
+//             ),
+//             "NotInAddSchemasSet"
+//         );
+
+//         // attemt to add class schema to nonexistent class
+//         assert_err!(
+//             TestModule::add_class_schema(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//                 Some(0),
+//                 class_id + 1,
+//                 vec![],
+//                 simple_test_schema()
+//             ),
+//             ERROR_CLASS_NOT_FOUND
+//         );
+
+//         // give members of GROUP_ZERO permission to add schemas
+//         let add_schema_set = CredentialSet::from(vec![0]);
+//         assert_ok!(TestModule::set_class_add_schemas_set(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             add_schema_set
+//         ));
+
+//         // successfully add a new schema
+//         assert_ok!(TestModule::add_class_schema(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//             Some(0),
+//             class_id,
+//             vec![],
+//             simple_test_schema()
+//         ));
+
+//         // System can always create entities (provided class exists) bypassing any permissions
+//         let entity_id_1 = next_entity_id();
+//         assert_ok!(TestModule::create_entity(Origin::ROOT, None, class_id,));
+//         // entities created by system are "un-owned"
+//         assert!(!<EntityMaintainerByEntityId<Runtime>>::exists(entity_id_1));
+//         assert_eq!(
+//             TestModule::entity_maintainer_by_entity_id(entity_id_1),
+//             None
+//         );
+
+//         assert_eq!(TestModule::next_entity_id(), entity_id_1 + 1);
+
+//         // default permissions have empty create_entities set and by default no entities can be created
+//         assert_err!(
+//             TestModule::create_entity(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//             ),
+//             "EntitiesCannotBeCreated"
+//         );
+
+//         assert_ok!(TestModule::set_class_entities_can_be_created(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             true
+//         ));
+
+//         assert_err!(
+//             TestModule::create_entity(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//             ),
+//             "NotInCreateEntitiesSet"
+//         );
+
+//         // give members of GROUP_ONE permission to create entities
+//         let create_entities_set = CredentialSet::from(vec![1]);
+//         assert_ok!(TestModule::set_class_create_entities_set(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             create_entities_set
+//         ));
+
+//         let entity_id_2 = next_entity_id();
+//         assert_ok!(TestModule::create_entity(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//             Some(1),
+//             class_id,
+//         ));
+
+//         assert!(<EntityMaintainerByEntityId<Runtime>>::exists(entity_id_2));
+//         assert_eq!(
+//             TestModule::entity_maintainer_by_entity_id(entity_id_2),
+//             Some(1)
+//         );
+
+//         assert_eq!(TestModule::next_entity_id(), entity_id_2 + 1);
+
+//         // Updating entity must be authorized
+//         assert_err!(
+//             TestModule::add_schema_support_to_entity(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//                 Some(0),
+//                 false, // not claiming to be entity maintainer
+//                 entity_id_2,
+//                 0, // first schema created
+//                 simple_test_entity_property_values()
+//             ),
+//             "CredentialNotInEntityPermissionssUpdateSet"
+//         );
+
+//         // default permissions give entity maintainer permission to update and delete
+//         assert_ok!(TestModule::add_schema_support_to_entity(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//             Some(1),
+//             true, // we are claiming to be the entity maintainer
+//             entity_id_2,
+//             0,
+//             simple_test_entity_property_values()
+//         ));
+//         assert_ok!(TestModule::update_entity_property_values(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//             Some(1),
+//             true, // we are claiming to be the entity maintainer
+//             entity_id_2,
+//             simple_test_entity_property_values()
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_create_class_with_empty_name() {
+//     with_test_externalities(|| {
+//         let empty_name = vec![];
+//         assert_err!(
+//             TestModule::create_class_with_default_permissions(
+//                 Origin::signed(CLASS_PERMISSIONS_CREATOR1),
+//                 empty_name,
+//                 good_class_description(),
+//             ),
+//             ERROR_CLASS_NAME_TOO_SHORT
+//         );
+//     })
+// }
+
+// #[test]
+// fn create_class_with_empty_description() {
+//     with_test_externalities(|| {
+//         let empty_description = vec![];
+//         assert_ok!(TestModule::create_class_with_default_permissions(
+//             Origin::signed(CLASS_PERMISSIONS_CREATOR1),
+//             good_class_name(),
+//             empty_description
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_create_entity_with_unknown_class_id() {
+//     with_test_externalities(|| {
+//         assert_err!(
+//             TestModule::create_entity(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 UNKNOWN_CLASS_ID,
+//             ),
+//             ERROR_CLASS_NOT_FOUND
+//         );
+//     })
+// }
+
+// #[test]
+// fn class_set_admins() {
+//     with_test_externalities(|| {
+//         // create a class where all permission sets are empty
+//         let class_id = create_simple_class(class_minimal());
+//         let class = TestModule::class_by_id(class_id);
+
+//         assert!(class.get_permissions_ref().admins.is_empty());
+
+//         let credential_set = CredentialSet::from(vec![1]);
+
+//         // only root should be able to set admins
+//         assert_err!(
+//             TestModule::set_class_admins(Origin::signed(1), class_id, credential_set.clone()),
+//             "NotRootOrigin"
+//         );
+//         assert_err!(
+//             TestModule::set_class_admins(
+//                 Origin::NONE, //unsigned inherent?
+//                 class_id,
+//                 credential_set.clone()
+//             ),
+//             "BadOrigin:ExpectedRootOrSigned"
+//         );
+
+//         // root origin can set admins
+//         assert_ok!(TestModule::set_class_admins(
+//             Origin::ROOT,
+//             class_id,
+//             credential_set.clone()
+//         ));
+
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().admins, credential_set);
+//     })
+// }
+
+// #[test]
+// fn class_set_add_schemas_set() {
+//     with_test_externalities(|| {
+//         const ADMIN_ACCOUNT: u64 = MEMBER_ONE_WITH_CREDENTIAL_ZERO;
+//         // create a class where all permission sets are empty
+//         let class_id = create_simple_class(class_minimal_with_admins(vec![0]));
+//         let class = TestModule::class_by_id(class_id);
+
+//         assert!(class.get_permissions_ref().add_schemas.is_empty());
+
+//         let credential_set1 = CredentialSet::from(vec![1, 2]);
+//         let credential_set2 = CredentialSet::from(vec![3, 4]);
+
+//         // root
+//         assert_ok!(TestModule::set_class_add_schemas_set(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             credential_set1.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().add_schemas, credential_set1);
+
+//         // admins
+//         assert_ok!(TestModule::set_class_add_schemas_set(
+//             Origin::signed(ADMIN_ACCOUNT),
+//             Some(0),
+//             class_id,
+//             credential_set2.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().add_schemas, credential_set2);
+
+//         // non-admins
+//         assert_err!(
+//             TestModule::set_class_add_schemas_set(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//                 credential_set2.clone()
+//             ),
+//             "NotInAdminsSet"
+//         );
+//     })
+// }
+
+// #[test]
+// fn class_set_class_create_entities_set() {
+//     with_test_externalities(|| {
+//         const ADMIN_ACCOUNT: u64 = MEMBER_ONE_WITH_CREDENTIAL_ZERO;
+//         // create a class where all permission sets are empty
+//         let class_id = create_simple_class(class_minimal_with_admins(vec![0]));
+//         let class = TestModule::class_by_id(class_id);
+
+//         assert!(class.get_permissions_ref().create_entities.is_empty());
+
+//         let credential_set1 = CredentialSet::from(vec![1, 2]);
+//         let credential_set2 = CredentialSet::from(vec![3, 4]);
+
+//         // root
+//         assert_ok!(TestModule::set_class_create_entities_set(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             credential_set1.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().create_entities, credential_set1);
+
+//         // admins
+//         assert_ok!(TestModule::set_class_create_entities_set(
+//             Origin::signed(ADMIN_ACCOUNT),
+//             Some(0),
+//             class_id,
+//             credential_set2.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().create_entities, credential_set2);
+
+//         // non-admins
+//         assert_err!(
+//             TestModule::set_class_create_entities_set(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//                 credential_set2.clone()
+//             ),
+//             "NotInAdminsSet"
+//         );
+//     })
+// }
+
+// #[test]
+// fn class_set_class_entities_can_be_created() {
+//     with_test_externalities(|| {
+//         const ADMIN_ACCOUNT: u64 = MEMBER_ONE_WITH_CREDENTIAL_ZERO;
+//         // create a class where all permission sets are empty
+//         let class_id = create_simple_class(class_minimal_with_admins(vec![0]));
+//         let class = TestModule::class_by_id(class_id);
+
+//         assert_eq!(class.get_permissions_ref().entity_creation_blocked, false);
+
+//         // root
+//         assert_ok!(TestModule::set_class_entities_can_be_created(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             true
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().entity_creation_blocked, true);
+
+//         // admins
+//         assert_ok!(TestModule::set_class_entities_can_be_created(
+//             Origin::signed(ADMIN_ACCOUNT),
+//             Some(0),
+//             class_id,
+//             false
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(class.get_permissions_ref().entity_creation_blocked, false);
+
+//         // non-admins
+//         assert_err!(
+//             TestModule::set_class_entities_can_be_created(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//                 true
+//             ),
+//             "NotInAdminsSet"
+//         );
+//     })
+// }
+
+// #[test]
+// fn class_set_class_entity_permissions() {
+//     with_test_externalities(|| {
+//         const ADMIN_ACCOUNT: u64 = MEMBER_ONE_WITH_CREDENTIAL_ZERO;
+//         // create a class where all permission sets are empty
+//         let class_id = create_simple_class(class_minimal_with_admins(vec![0]));
+//         let class = TestModule::class_by_id(class_id);
+
+//         assert!(class.get_permissions_ref().entity_permissions.update.is_empty());
+
+//         let entity_permissions1 = EntityPermissionss {
+//             update: CredentialSet::from(vec![1]),
+//             maintainer_has_all_permissions: true,
+//         };
+
+//         //root
+//         assert_ok!(TestModule::set_class_entity_permissions(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             entity_permissions1.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(
+//             class.get_permissions_ref().entity_permissions,
+//             entity_permissions1
+//         );
+
+//         let entity_permissions2 = EntityPermissionss {
+//             update: CredentialSet::from(vec![4]),
+//             maintainer_has_all_permissions: true,
+//         };
+//         //admins
+//         assert_ok!(TestModule::set_class_entity_permissions(
+//             Origin::signed(ADMIN_ACCOUNT),
+//             Some(0),
+//             class_id,
+//             entity_permissions2.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(
+//             class.get_permissions_ref().entity_permissions,
+//             entity_permissions2
+//         );
+
+//         // non admins
+//         assert_err!(
+//             TestModule::set_class_entity_permissions(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//                 entity_permissions2.clone()
+//             ),
+//             "NotInAdminsSet"
+//         );
+//     })
+// }
+
+// #[test]
+// fn class_set_class_reference_constraint() {
+//     with_test_externalities(|| {
+//         const ADMIN_ACCOUNT: u64 = MEMBER_ONE_WITH_CREDENTIAL_ZERO;
+//         // create a class where all permission sets are empty
+//         let class_id = create_simple_class(class_minimal_with_admins(vec![0]));
+//         let class = TestModule::class_by_id(class_id);
+
+//         assert_eq!(
+//             class.get_permissions_ref().reference_constraint,
+//             Default::default()
+//         );
+
+//         let mut constraints_set = BTreeSet::new();
+//         constraints_set.insert(PropertyOfClass {
+//             class_id: 1,
+//             property_index: 0,
+//         });
+//         let reference_constraint1 = ReferenceConstraint::Restricted(constraints_set);
+
+//         //root
+//         assert_ok!(TestModule::set_class_reference_constraint(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             reference_constraint1.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(
+//             class.get_permissions_ref().reference_constraint,
+//             reference_constraint1
+//         );
+
+//         let mut constraints_set = BTreeSet::new();
+//         constraints_set.insert(PropertyOfClass {
+//             class_id: 2,
+//             property_index: 2,
+//         });
+//         let reference_constraint2 = ReferenceConstraint::Restricted(constraints_set);
+
+//         //admins
+//         assert_ok!(TestModule::set_class_reference_constraint(
+//             Origin::signed(ADMIN_ACCOUNT),
+//             Some(0),
+//             class_id,
+//             reference_constraint2.clone()
+//         ));
+//         let class = TestModule::class_by_id(class_id);
+//         assert_eq!(
+//             class.get_permissions_ref().reference_constraint,
+//             reference_constraint2
+//         );
+
+//         // non admins
+//         assert_err!(
+//             TestModule::set_class_reference_constraint(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//                 Some(1),
+//                 class_id,
+//                 reference_constraint2.clone()
+//             ),
+//             "NotInAdminsSet"
+//         );
+//     })
+// }
+
+// #[test]
+// fn batch_transaction_simple() {
+//     with_test_externalities(|| {
+//         const CREDENTIAL_ONE: u64 = 1;
+
+//         let new_class_id = create_simple_class(ClassPermissions {
+//             entity_creation_blocked: true,
+//             create_entities: vec![CREDENTIAL_ONE].into(),
+//             reference_constraint: ReferenceConstraint::NoConstraint,
+//             ..Default::default()
+//         });
+
+//         let new_properties = vec![Property {
+//             property_type: PropertyType::Reference(new_class_id),
+//             required: true,
+//             name: b"entity".to_vec(),
+//             description: b"another entity of same class".to_vec(),
+//         }];
+
+//         assert_ok!(TestModule::add_class_schema(
+//             Origin::ROOT,
+//             None,
+//             new_class_id,
+//             vec![],
+//             new_properties
+//         ));
+
+//         let operations = vec![
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: false,
+//                 operation_type: OperationType::CreateEntity(CreateEntityOperation {
+//                     class_id: new_class_id,
+//                 }),
+//             },
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: true, // in prior operation CREDENTIAL_ONE became the maintainer
+//                 operation_type: OperationType::AddSchemaSupportToEntity(
+//                     AddSchemaSupportToEntityOperation {
+//                         entity_id: ParameterizedEntity::InternalEntityJustAdded(0), // index 0 (prior operation)
+//                         schema_id: 0,
+//                         parametrized_property_values: vec![ParametrizedClassPropertyValue {
+//                             in_class_index: 0,
+//                             value: ParametrizedPropertyValue::InternalEntityJustAdded(0),
+//                         }],
+//                     },
+//                 ),
+//             },
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: false,
+//                 operation_type: OperationType::CreateEntity(CreateEntityOperation {
+//                     class_id: new_class_id,
+//                 }),
+//             },
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: true, // in prior operation CREDENTIAL_ONE became the maintainer
+//                 operation_type: OperationType::UpdatePropertyValues(
+//                     UpdatePropertyValuesOperation {
+//                         entity_id: ParameterizedEntity::InternalEntityJustAdded(0), // index 0 (prior operation)
+//                         new_parametrized_property_values: vec![ParametrizedClassPropertyValue {
+//                             in_class_index: 0,
+//                             value: ParametrizedPropertyValue::InternalEntityJustAdded(2),
+//                         }],
+//                     },
+//                 ),
+//             },
+//         ];
+
+//         let entity_id = next_entity_id();
+
+//         assert_ok!(TestModule::transaction(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//             operations
+//         ));
+
+//         // two entities created
+//         assert!(<EntityById<Runtime>>::exists(entity_id));
+//         assert!(<EntityById<Runtime>>::exists(entity_id + 1));
+//     })
+// }
+
+// #[test]
+// fn batch_transaction_vector_of_entities() {
+//     with_test_externalities(|| {
+//         const CREDENTIAL_ONE: u64 = 1;
+
+//         let new_class_id = create_simple_class(ClassPermissions {
+//             entity_creation_blocked: true,
+//             create_entities: vec![CREDENTIAL_ONE].into(),
+//             reference_constraint: ReferenceConstraint::NoConstraint,
+//             ..Default::default()
+//         });
+
+//         let new_properties = vec![Property {
+//             property_type: PropertyType::ReferenceVec(10, new_class_id),
+//             required: true,
+//             name: b"entities".to_vec(),
+//             description: b"vector of entities of same class".to_vec(),
+//         }];
+
+//         assert_ok!(TestModule::add_class_schema(
+//             Origin::ROOT,
+//             None,
+//             new_class_id,
+//             vec![],
+//             new_properties
+//         ));
+
+//         let operations = vec![
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: false,
+//                 operation_type: OperationType::CreateEntity(CreateEntityOperation {
+//                     class_id: new_class_id,
+//                 }),
+//             },
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: false,
+//                 operation_type: OperationType::CreateEntity(CreateEntityOperation {
+//                     class_id: new_class_id,
+//                 }),
+//             },
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: false,
+//                 operation_type: OperationType::CreateEntity(CreateEntityOperation {
+//                     class_id: new_class_id,
+//                 }),
+//             },
+//             Operation {
+//                 with_credential: Some(CREDENTIAL_ONE),
+//                 as_entity_maintainer: true, // in prior operation CREDENTIAL_ONE became the maintainer
+//                 operation_type: OperationType::AddSchemaSupportToEntity(
+//                     AddSchemaSupportToEntityOperation {
+//                         entity_id: ParameterizedEntity::InternalEntityJustAdded(0),
+//                         schema_id: 0,
+//                         parametrized_property_values: vec![ParametrizedClassPropertyValue {
+//                             in_class_index: 0,
+//                             value: ParametrizedPropertyValue::InternalEntityVec(vec![
+//                                 ParameterizedEntity::InternalEntityJustAdded(1),
+//                                 ParameterizedEntity::InternalEntityJustAdded(2),
+//                             ]),
+//                         }],
+//                     },
+//                 ),
+//             },
+//         ];
+
+//         let entity_id = next_entity_id();
+
+//         assert_ok!(TestModule::transaction(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
+//             operations
+//         ));
+
+//         // three entities created
+//         assert!(<EntityById<Runtime>>::exists(entity_id));
+//         assert!(<EntityById<Runtime>>::exists(entity_id + 1));
+//         assert!(<EntityById<Runtime>>::exists(entity_id + 2));
+
+//         assert_eq!(
+//             TestModule::entity_by_id(entity_id),
+//             Entity::new(
+//                 new_class_id,
+//                 BTreeSet::from_iter(vec![SCHEMA_ID_0].into_iter()),
+//                 prop_value(
+//                     0,
+//                     PropertyValue::ReferenceVec(
+//                         vec![entity_id + 1, entity_id + 2,],
+//                         <Runtime as Trait>::Nonce::default()
+//                     )
+//                 )
+//             )
+//         );
+//     })
+// }
+
+// Add class schema
+// --------------------------------------
+
+// #[test]
+// fn cannot_add_schema_to_unknown_class() {
+//     with_test_externalities(|| {
+//         assert_err!(
+//             TestModule::append_class_schema(UNKNOWN_CLASS_ID, good_prop_ids(), good_props()),
+//             ERROR_CLASS_NOT_FOUND
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_class_schema_when_no_props_passed() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+//         assert_err!(
+//             TestModule::append_class_schema(class_id, vec![], vec![]),
+//             ERROR_NO_PROPS_IN_CLASS_SCHEMA
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_class_schema_when_it_refers_unknown_prop_index_and_class_has_no_props() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+//         assert_err!(
+//             TestModule::append_class_schema(class_id, vec![UNKNOWN_PROP_ID], vec![]),
+//             ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_PROP_INDEX
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_class_schema_when_it_refers_unknown_prop_index() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, vec![], good_props()),
+//             Ok(SCHEMA_ID_0)
+//         );
+
+//         // Try to add a new schema that is based on one valid prop ids
+//         // plus another prop id is unknown on this class.
+//         assert_err!(
+//             TestModule::append_class_schema(class_id, vec![0, UNKNOWN_PROP_ID], vec![]),
+//             ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_PROP_INDEX
+//         );
+
+//         // Verify that class props and schemas remain unchanged:
+//         assert_class_props(class_id, good_props());
+//         assert_class_schemas(class_id, vec![good_prop_ids()]);
+//     })
+// }
+
+// #[test]
+// fn cannot_add_class_schema_when_it_refers_unknown_internal_id() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+//         let bad_internal_prop = new_reference_class_prop(UNKNOWN_CLASS_ID);
+
+//         assert_err!(
+//             TestModule::append_class_schema(
+//                 class_id,
+//                 vec![],
+//                 vec![good_prop_bool(), bad_internal_prop]
+//             ),
+//             ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_CLASS
+//         );
+//     })
+// }
+
+// #[test]
+// fn should_add_class_schema_with_internal_class_prop() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+//         let internal_class_prop = new_reference_class_prop(class_id);
+
+//         // Add first schema with new props.
+//         // No other props on the class at this time.
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, vec![], vec![internal_class_prop.clone()]),
+//             Ok(SCHEMA_ID_0)
+//         );
+
+//         assert_class_props(class_id, vec![internal_class_prop]);
+//         assert_class_schemas(class_id, vec![vec![SCHEMA_ID_0]]);
+//     })
+// }
+
+// #[test]
+// fn should_add_class_schema_when_only_new_props_passed() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+
+//         // Add first schema with new props.
+//         // No other props on the class at this time.
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, vec![], good_props()),
+//             Ok(SCHEMA_ID_0)
+//         );
+
+//         assert_class_props(class_id, good_props());
+//         assert_class_schemas(class_id, vec![good_prop_ids()]);
+//     })
+// }
+
+// #[test]
+// fn should_add_class_schema_when_only_prop_ids_passed() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+
+//         // Add first schema with new props.
+//         // No other props on the class at this time.
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, vec![], good_props()),
+//             Ok(SCHEMA_ID_0)
+//         );
+
+//         // Add a new schema that is based solely on the props ids
+//         // of the previously added schema.
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, good_prop_ids(), vec![]),
+//             Ok(SCHEMA_ID_1)
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_class_schema_when_new_props_have_duplicate_names() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+
+//         // Add first schema with new props.
+//         // No other props on the class at this time.
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, vec![], good_props()),
+//             Ok(SCHEMA_ID_0)
+//         );
+
+//         // Add a new schema with not unique property names:
+//         assert_err!(
+//             TestModule::append_class_schema(class_id, vec![], good_props()),
+//             ERROR_PROP_NAME_NOT_UNIQUE_IN_A_CLASS
+//         );
+//     })
+// }
+
+// #[test]
+// fn should_add_class_schema_when_both_prop_ids_and_new_props_passed() {
+//     with_test_externalities(|| {
+//         let class_id = create_simple_class_with_default_permissions();
+
+//         // Add first schema with new props.
+//         // No other props on the class at this time.
+//         assert_eq!(
+//             TestModule::append_class_schema(
+//                 class_id,
+//                 vec![],
+//                 vec![good_prop_bool(), good_prop_u32()]
+//             ),
+//             Ok(SCHEMA_ID_0)
+//         );
+
+//         // Add a new schema that is based on some prop ids
+//         // added with previous schema plus some new props,
+//         // introduced by this new schema.
+//         assert_eq!(
+//             TestModule::append_class_schema(class_id, vec![1], vec![good_prop_text()]),
+//             Ok(SCHEMA_ID_1)
+//         );
+
+//         assert_class_props(
+//             class_id,
+//             vec![good_prop_bool(), good_prop_u32(), good_prop_text()],
+//         );
+
+//         assert_class_schemas(class_id, vec![vec![0, 1], vec![1, 2]]);
+//     })
+// }
+
+// Update class schema status
+// --------------------------------------
+
+// #[test]
+// fn update_class_schema_status_success() {
+//     with_test_externalities(|| {
+//         let (class_id, schema_id) = create_class_with_schema();
+
+//         // Check given class schema status before update performed
+//         assert_eq!(
+//             TestModule::class_by_id(class_id).is_active_schema(schema_id),
+//             true
+//         );
+
+//         // Give members of GROUP_ZERO permission to add schemas
+//         let update_schema_set = CredentialSet::from(vec![0]);
+//         assert_ok!(TestModule::set_class_update_schemas_status_set(
+//             Origin::ROOT,
+//             None,
+//             class_id,
+//             update_schema_set
+//         ));
+
+//         // Make class schema under given index inactive.
+//         assert_ok!(TestModule::update_class_schema_status(
+//             Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//             Some(0),
+//             class_id,
+//             schema_id,
+//             false
+//         ));
+
+//         // Check given class schema status after update performed
+//         assert_eq!(
+//             TestModule::class_by_id(class_id).is_active_schema(schema_id),
+//             false
+//         );
+//     })
+// }
+
+// #[test]
+// fn update_class_schema_status_class_not_found() {
+//     with_test_externalities(|| {
+//         // attemt to update class schema of nonexistent class
+//         assert_err!(
+//             TestModule::update_class_schema_status(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//                 Some(0),
+//                 UNKNOWN_CLASS_ID,
+//                 UNKNOWN_SCHEMA_ID,
+//                 false
+//             ),
+//             ERROR_CLASS_NOT_FOUND
+//         );
+//     })
+// }
+
+// #[test]
+// fn update_class_schema_status_not_in_update_class_schema_status_set() {
+//     with_test_externalities(|| {
+//         let (class_id, schema_id) = create_class_with_schema();
+
+//         // Check given class schema status before update performed
+//         assert_eq!(
+//             TestModule::class_by_id(class_id).is_active_schema(schema_id),
+//             true
+//         );
+
+//         // attemt to update class schema of nonexistent schema
+//         assert_err!(
+//             TestModule::update_class_schema_status(
+//                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+//                 Some(0),
+//                 class_id,
+//                 schema_id,
+//                 false
+//             ),
+//             "NotInUpdateSchemasStatusSet"
+//         );
+
+//         // Check given class schema status after update performed
+//         assert_eq!(
+//             TestModule::class_by_id(class_id).is_active_schema(schema_id),
+//             true
+//         );
+//     })
+// }
+
+// // #[test]
+// // fn update_class_schema_status_schema_not_found() {
+// //     with_test_externalities(|| {
+// //         let class_id = create_simple_class_with_default_permissions();
+
+// //         // give members of GROUP_ZERO permission to update schemas
+// //         let update_schema_set = CredentialSet::from(vec![0]);
+// //         assert_ok!(TestModule::set_class_update_schemas_status_set(
+// //             Origin::ROOT,
+// //             None,
+// //             class_id,
+// //             update_schema_set
+// //         ));
+
+// //         // attemt to update class schema of nonexistent class
+// //         assert_err!(
+// //             TestModule::update_class_schema_status(
+// //                 Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
+// //                 Some(0),
+// //                 class_id,
+// //                 UNKNOWN_SCHEMA_ID,
+// //                 false
+// //             ),
+// //             ERROR_UNKNOWN_CLASS_SCHEMA_ID
+// //         );
+// //     })
+// // }
+
+// // Add schema support to entity
+// // --------------------------------------
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_entity_not_found(TestModule::add_entity_schema_support(
+//             UNKNOWN_ENTITY_ID,
+//             1,
+//             BTreeMap::new(),
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_schema_is_not_active() {
+//     with_test_externalities(|| {
+//         let (class_id, schema_id, entity_id) = create_class_with_schema_and_entity();
+
+//         // Firstly we make class schema under given index inactive.
+//         assert_ok!(TestModule::complete_class_schema_status_update(
+//             class_id, schema_id, false
+//         ));
+
+//         // Secondly we try to add support for the same schema.
+//         assert_err!(
+//             TestModule::add_entity_schema_support(entity_id, schema_id, bool_prop_value()),
+//             ERROR_CLASS_SCHEMA_NOT_ACTIVE
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_schema_already_added_to_entity() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+
+//         // Firstly we just add support for a valid class schema.
+//         assert_ok!(TestModule::add_entity_schema_support(
+//             entity_id,
+//             schema_id,
+//             bool_prop_value()
+//         ));
+
+//         // Secondly we try to add support for the same schema.
+//         assert_err!(
+//             TestModule::add_entity_schema_support(entity_id, schema_id, BTreeMap::new()),
+//             ERROR_SCHEMA_ALREADY_ADDED_TO_ENTITY
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_schema_id_is_unknown() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+//         let unknown_schema_id = schema_id + 1;
+//         assert_err!(
+//             TestModule::add_entity_schema_support(
+//                 entity_id,
+//                 unknown_schema_id,
+//                 prop_value(0, PropertyValue::Bool(false))
+//             ),
+//             ERROR_UNKNOWN_CLASS_SCHEMA_ID
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_prop_value_dont_match_type() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+//         let mut prop_values = bool_prop_value();
+//         prop_values.insert(PROP_ID_U32, PropertyValue::Bool(true));
+//         assert_err!(
+//             TestModule::add_entity_schema_support(entity_id, schema_id, prop_values),
+//             ERROR_PROP_VALUE_DONT_MATCH_TYPE
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_unknown_internal_entity_id() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+//         let mut prop_values = bool_prop_value();
+//         prop_values.insert(
+//             PROP_ID_REFERENCE,
+//             PropertyValue::Reference(UNKNOWN_ENTITY_ID),
+//         );
+//         assert_err!(
+//             TestModule::add_entity_schema_support(entity_id, schema_id, prop_values),
+//             ERROR_ENTITY_NOT_FOUND
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_add_schema_to_entity_when_missing_required_prop() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+//         assert_err!(
+//             TestModule::add_entity_schema_support(
+//                 entity_id,
+//                 schema_id,
+//                 prop_value(PROP_ID_U32, PropertyValue::Uint32(456))
+//             ),
+//             ERROR_MISSING_REQUIRED_PROP
+//         );
+//     })
+// }
+
+// #[test]
+// fn should_add_schema_to_entity_when_some_optional_props_provided() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+//         let mut prop_values = bool_prop_value();
+//         prop_values.insert(PROP_ID_U32, PropertyValue::Uint32(123));
+//         assert_ok!(TestModule::add_entity_schema_support(
+//             entity_id,
+//             schema_id,
+//             // Note that an optional internal prop is not provided here.
+//             prop_values.clone()
+//         ));
+
+//         let entity = TestModule::entity_by_id(entity_id);
+//         assert_eq!(
+//             entity.supported_schemas,
+//             BTreeSet::from_iter(vec![SCHEMA_ID_0].into_iter())
+//         );
+//         prop_values.insert(PROP_ID_REFERENCE, PropertyValue::Bool(false));
+//         prop_values.insert(PROP_ID_U32_VEC, PropertyValue::Bool(false));
+//         assert_eq!(entity.values, prop_values);
+//     })
+// }
+
+// // Update entity properties
+// // --------------------------------------
+
+// #[test]
+// fn update_entity_props_successfully() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         let mut prop_values = prop_value(PROP_ID_BOOL, PropertyValue::Bool(true));
+//         prop_values.insert(PROP_ID_U32, PropertyValue::Bool(false));
+//         prop_values.insert(PROP_ID_REFERENCE, PropertyValue::Bool(false));
+//         prop_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(vec![123, 234, 44], <Runtime as Trait>::Nonce::default()),
+//         );
+//         assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+//         prop_values = prop_value(PROP_ID_BOOL, PropertyValue::Bool(false));
+//         prop_values.insert(PROP_ID_U32, PropertyValue::Uint32(123));
+//         prop_values.insert(PROP_ID_REFERENCE, PropertyValue::Reference(entity_id));
+//         prop_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(vec![123, 234, 44, 88, 43], <Runtime as Trait>::Nonce::one()),
+//         );
+//         assert_ok!(TestModule::complete_entity_property_values_update(
+//             entity_id,
+//             prop_values.clone()
+//         ));
+//         assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+//     })
+// }
+
+// #[test]
+// fn cannot_update_entity_props_when_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_entity_not_found(TestModule::complete_entity_property_values_update(
+//             UNKNOWN_ENTITY_ID,
+//             BTreeMap::new(),
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_update_entity_props_when_prop_value_dont_match_type() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_entity_property_values_update(
+//                 entity_id,
+//                 prop_value(PROP_ID_BOOL, PropertyValue::Uint32(1))
+//             ),
+//             ERROR_PROP_VALUE_DONT_MATCH_TYPE
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_update_entity_props_when_unknown_internal_entity_id() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_entity_property_values_update(
+//                 entity_id,
+//                 prop_value(
+//                     PROP_ID_REFERENCE,
+//                     PropertyValue::Reference(UNKNOWN_ENTITY_ID)
+//                 )
+//             ),
+//             ERROR_ENTITY_NOT_FOUND
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_update_entity_props_when_unknown_entity_prop_id() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_entity_property_values_update(
+//                 entity_id,
+//                 prop_value(UNKNOWN_PROP_ID, PropertyValue::Bool(true))
+//             ),
+//             ERROR_UNKNOWN_ENTITY_PROP_ID
+//         );
+//     })
+// }
+
+// // Entity property vector cleaning
+// // --------------------------------------
+
+// #[test]
+// fn complete_entity_property_vector_cleaning_successfully() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         let mut prop_values = prop_value(PROP_ID_BOOL, PropertyValue::Bool(true));
+//         prop_values.insert(PROP_ID_U32, PropertyValue::Bool(false));
+//         prop_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(vec![123, 234, 44], <Runtime as Trait>::Nonce::default()),
+//         );
+//         prop_values.insert(PROP_ID_REFERENCE, PropertyValue::Bool(false));
+
+//         // Check property values runtime storage related to an entity before cleaning of entity property vector value under given schema id
+//         assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+
+//         // Perform cleaning of entity property vector value under given schema id
+//         assert_ok!(TestModule::complete_entity_property_vector_cleaning(
+//             entity_id,
+//             PROP_ID_U32_VEC
+//         ));
+
+//         // Update entity property values to compare with runtime storage entity value under given schema id
+//         prop_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(vec![], <Runtime as Trait>::Nonce::one()),
+//         );
+
+//         // Check property values runtime storage related to a entity right after
+//         // cleaning entity property vector under given schema id
+//         assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_entity_property_vector_cleaning_when_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_entity_not_found(TestModule::complete_entity_property_vector_cleaning(
+//             UNKNOWN_ENTITY_ID,
+//             PROP_ID_U32_VEC,
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_entity_property_vector_cleaning_when_unknown_entity_prop_id() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_entity_property_vector_cleaning(entity_id, UNKNOWN_PROP_ID),
+//             ERROR_UNKNOWN_ENTITY_PROP_ID
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_entity_property_vector_cleaning_when_entity_prop_id_is_not_a_vector() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_entity_property_vector_cleaning(entity_id, PROP_ID_U32),
+//             ERROR_PROP_VALUE_UNDER_GIVEN_INDEX_IS_NOT_A_VECTOR
+//         );
+//     })
+// }
+
+// // Remove at entity property vector
+// // --------------------------------------
+
+// fn complete_remove_at_entity_property_vector() -> <Runtime as Trait>::EntityId {
+//     let entity_id = create_entity_with_schema_support();
+//     let mut prop_values = prop_value(PROP_ID_BOOL, PropertyValue::Bool(true));
+//     prop_values.insert(PROP_ID_U32, PropertyValue::Bool(false));
+//     prop_values.insert(
+//         PROP_ID_U32_VEC,
+//         PropertyValue::Uint32Vec(vec![123, 234, 44], <Runtime as Trait>::Nonce::default()),
+//     );
+//     prop_values.insert(PROP_ID_REFERENCE, PropertyValue::Bool(false));
+
+//     // Check property values runtime storage related to an entity before removing at given index of entity property vector value
+//     assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+
+//     // Perform removing at given index of entity property vector value
+//     assert_ok!(TestModule::complete_remove_at_entity_property_vector(
+//         entity_id,
+//         PROP_ID_U32_VEC,
+//         VALID_PROPERTY_VEC_INDEX,
+//         ZERO_NONCE
+//     ));
+
+//     // Update entity property values to compare with runtime storage entity value under given schema id
+//     prop_values.insert(
+//         PROP_ID_U32_VEC,
+//         PropertyValue::Uint32Vec(vec![234, 44], <Runtime as Trait>::Nonce::one()),
+//     );
+
+//     // Check property values runtime storage related to a entity right after
+//     // removing at given index of entity property vector value
+//     assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+//     entity_id
+// }
+
+// #[test]
+// fn complete_remove_at_entity_property_vector_successfully() {
+//     with_test_externalities(|| {
+//         let entity_id = complete_remove_at_entity_property_vector();
+//         // Perform second removal at given index of entity property vector value with new nonce
+//         assert_ok!(TestModule::complete_remove_at_entity_property_vector(
+//             entity_id,
+//             PROP_ID_U32_VEC,
+//             VALID_PROPERTY_VEC_INDEX,
+//             FIRST_NONCE
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_remove_at_entity_property_vector_when_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_entity_not_found(TestModule::complete_remove_at_entity_property_vector(
+//             UNKNOWN_ENTITY_ID,
+//             PROP_ID_U32_VEC,
+//             VALID_PROPERTY_VEC_INDEX,
+//             ZERO_NONCE,
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_remove_at_entity_property_vector_when_unknown_entity_prop_id() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_remove_at_entity_property_vector(
+//                 entity_id,
+//                 UNKNOWN_PROP_ID,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 ZERO_NONCE
+//             ),
+//             ERROR_UNKNOWN_ENTITY_PROP_ID
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_remove_at_entity_property_vector_when_entity_prop_vector_index_out_of_range() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_remove_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32,
+//                 INVALID_PROPERTY_VEC_INDEX,
+//                 ZERO_NONCE
+//             ),
+//             ERROR_PROP_VALUE_UNDER_GIVEN_INDEX_IS_NOT_A_VECTOR
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_remove_at_entity_property_vector_when_entity_prop_id_is_not_a_vector() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_remove_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 ZERO_NONCE
+//             ),
+//             ERROR_PROP_VALUE_UNDER_GIVEN_INDEX_IS_NOT_A_VECTOR
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_remove_at_entity_property_vector_when_already_updated() {
+//     with_test_externalities(|| {
+//         let entity_id = complete_remove_at_entity_property_vector();
+//         assert_err!(
+//             TestModule::complete_remove_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32_VEC,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 SECOND_NONCE
+//             ),
+//             ERROR_PROP_VALUE_VEC_NONCES_DOES_NOT_MATCH
+//         );
+//     })
+// }
+
+// #[test]
+// fn complete_insert_at_entity_property_vector_successfully() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         let mut prop_values = prop_value(PROP_ID_BOOL, PropertyValue::Bool(true));
+//         prop_values.insert(PROP_ID_U32, PropertyValue::Bool(false));
+//         prop_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(vec![123, 234, 44], <Runtime as Trait>::Nonce::default()),
+//         );
+//         prop_values.insert(PROP_ID_REFERENCE, PropertyValue::Bool(false));
+
+//         // Check property values runtime storage related to an entity before inserting at given index of entity property vector value
+//         assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+
+//         // Perform inserting at given index of entity property vector value
+//         assert_ok!(TestModule::complete_insert_at_entity_property_vector(
+//             entity_id,
+//             PROP_ID_U32_VEC,
+//             VALID_PROPERTY_VEC_INDEX,
+//             PropertyValue::Uint32(33),
+//             ZERO_NONCE
+//         ));
+
+//         // Perform second inserting at given index of entity property vector value with new nonce
+//         assert_ok!(TestModule::complete_insert_at_entity_property_vector(
+//             entity_id,
+//             PROP_ID_U32_VEC,
+//             VALID_PROPERTY_VEC_INDEX,
+//             PropertyValue::Uint32(55),
+//             FIRST_NONCE
+//         ));
+
+//         // Update entity property values to compare with runtime storage entity value under given schema id
+//         prop_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(vec![55, 33, 123, 234, 44], 2_u32.into()),
+//         );
+
+//         // Check property values runtime storage related to a entity right after
+//         // inserting at given index of entity property vector value
+//         assert_eq!(TestModule::entity_by_id(entity_id).values, prop_values);
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_property_vector_when_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_entity_not_found(TestModule::complete_insert_at_entity_property_vector(
+//             UNKNOWN_ENTITY_ID,
+//             PROP_ID_U32_VEC,
+//             VALID_PROPERTY_VEC_INDEX,
+//             PropertyValue::Uint32(33),
+//             ZERO_NONCE,
+//         ));
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_property_vector_when_unknown_entity_prop_id() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 UNKNOWN_PROP_ID,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Uint32(33),
+//                 ZERO_NONCE
+//             ),
+//             ERROR_UNKNOWN_ENTITY_PROP_ID
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_property_vector_when_entity_prop_id_is_not_a_vector() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Uint32(17),
+//                 ZERO_NONCE
+//             ),
+//             ERROR_PROP_VALUE_UNDER_GIVEN_INDEX_IS_NOT_A_VECTOR
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_when_entity_prop_value_vector_index_out_of_range() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32_VEC,
+//                 INVALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Uint32(33),
+//                 ZERO_NONCE
+//             ),
+//             ERROR_ENTITY_PROP_VALUE_VECTOR_INDEX_IS_OUT_OF_RANGE
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_when_property_type_does_not_match_internal_entity_vector_type()
+// {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity_with_schema_support();
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32_VEC,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Uint16(33),
+//                 ZERO_NONCE
+//             ),
+//             ERROR_PROP_VALUE_TYPE_DOESNT_MATCH_INTERNAL_ENTITY_VECTOR_TYPE
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_property_vector_when_entity_prop_value_vector_is_too_long() {
+//     with_test_externalities(|| {
+//         let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
+//         let mut property_values = BTreeMap::new();
+//         property_values.insert(PROP_ID_BOOL, PropertyValue::Bool(true));
+//         property_values.insert(
+//             PROP_ID_U32_VEC,
+//             PropertyValue::Uint32Vec(
+//                 vec![5; PROP_ID_U32_VEC_MAX_LEN as usize],
+//                 <Runtime as Trait>::Nonce::default(),
+//             ),
+//         );
+//         assert_ok!(TestModule::add_entity_schema_support(
+//             entity_id,
+//             schema_id,
+//             property_values
+//         ));
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32_VEC,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Uint32(33),
+//                 ZERO_NONCE
+//             ),
+//             ERROR_ENTITY_PROP_VALUE_VECTOR_IS_TOO_LONG
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_property_vector_when_nonce_does_not_match() {
+//     with_test_externalities(|| {
+//         let entity_id = complete_remove_at_entity_property_vector();
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_U32_VEC,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Uint32(33),
+//                 SECOND_NONCE
+//             ),
+//             ERROR_PROP_VALUE_VEC_NONCES_DOES_NOT_MATCH
+//         );
+//     })
+// }
+
+// fn create_entity_with_prop_value_referencing_another_entity(
+// ) -> (<Runtime as Trait>::EntityId, <Runtime as Trait>::EntityId) {
+//     let class_id = create_simple_class_with_default_permissions();
+//     let schema_id = TestModule::append_class_schema(
+//         class_id,
+//         vec![],
+//         vec![
+//             good_prop_bool().required(),
+//             new_reference_class_prop_vec(class_id),
+//         ],
+//     )
+//     .expect("This should not happen");
+//     let entity_id = create_entity_of_class(class_id);
+//     let entity_id_2 = create_entity_of_class(class_id);
+//     let mut property_values = BTreeMap::new();
+//     property_values.insert(PROP_ID_BOOL, PropertyValue::Bool(true));
+//     property_values.insert(
+//         PROP_ID_REFERENCE_VEC,
+//         PropertyValue::ReferenceVec(vec![entity_id_2], <Runtime as Trait>::Nonce::default()),
+//     );
+//     assert_ok!(TestModule::add_entity_schema_support(
+//         entity_id,
+//         schema_id,
+//         property_values
+//     ));
+//     (entity_id, entity_id_2)
+// }
+
+// #[test]
+// fn cannot_complete_insert_at_entity_property_vector_when_unknown_internal_entity_id() {
+//     with_test_externalities(|| {
+//         let (entity_id, _) = create_entity_with_prop_value_referencing_another_entity();
+//         assert_err!(
+//             TestModule::complete_insert_at_entity_property_vector(
+//                 entity_id,
+//                 PROP_ID_REFERENCE_VEC,
+//                 VALID_PROPERTY_VEC_INDEX,
+//                 PropertyValue::Reference(UNKNOWN_ENTITY_ID),
+//                 ZERO_NONCE
+//             ),
+//             ERROR_ENTITY_NOT_FOUND
+//         );
+//     })
+// }
+
+// Remove entity
+// --------------------------------------
+
+// #[test]
+// fn remove_entity_successfully() {
+//     with_test_externalities(|| {
+//         let (_, _, entity_id) = create_class_with_schema_and_entity();
+//         assert_ok!(TestModule::remove_entity(Origin::ROOT, None, entity_id));
+//         // Ensure entity related storage was cleared successfully.
+//         assert_eq!(
+//             TestModule::entity_by_id(entity_id),
+//             Entity::<Runtime>::default()
+//         );
+//         assert_eq!(TestModule::entity_maintainer_by_entity_id(entity_id), None);
+//     })
+// }
+
+// #[test]
+// fn remove_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_err!(
+//             TestModule::remove_entity(Origin::ROOT, None, UNKNOWN_ENTITY_ID),
+//             ERROR_ENTITY_NOT_FOUND
+//         );
+//     })
+// }
+
+// #[test]
+// fn remove_entity_reference_counter_does_not_equal_zero() {
+//     with_test_externalities(|| {
+//         let (_, entity_by_id_2) = create_entity_with_prop_value_referencing_another_entity();
+//         assert_err!(
+//             TestModule::remove_entity(Origin::ROOT, None, entity_by_id_2),
+//             ERROR_ENTITY_RC_DOES_NOT_EQUAL_TO_ZERO
+//         );
+//     })
+// }
+
+// TODO test text max len
+
+// TODO test vec max len
+
+// Delete entity
+// --------------------------------------
+
+// #[test]
+// fn delete_entity_successfully() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity();
+//         assert_ok!(
+//             TestModule::delete_entity(entity_id),
+//             ()
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_delete_entity_when_entity_not_found() {
+//     with_test_externalities(|| {
+//         assert_entity_not_found(
+//             TestModule::delete_entity(UNKNOWN_ENTITY_ID)
+//         );
+//     })
+// }
+
+// #[test]
+// fn cannot_delete_already_deleted_entity() {
+//     with_test_externalities(|| {
+//         let entity_id = create_entity();
+//         let _ok = TestModule::delete_entity(entity_id);
+//         assert_err!(
+//             TestModule::delete_entity(entity_id),
+//             ERROR_ENTITY_ALREADY_DELETED
+//         );
+//     })
+// }