Ver Fonte

Merge branch 'olympia' into working_team_olympia_merge2

# Conflicts:
#	node/src/chain_spec/mod.rs
#	runtime-modules/proposals/engine/Cargo.toml
#	runtime/src/integration/mod.rs
#	runtime/src/lib.rs
#	runtime/src/tests/proposals_integration/working_group_proposals.rs
Shamil Gadelshin há 4 anos atrás
pai
commit
3030a47218
53 ficheiros alterados com 4545 adições e 12035 exclusões
  1. 7 65
      Cargo.lock
  2. 0 3
      Cargo.toml
  3. 0 391
      node/src/chain_spec/content_config.rs
  4. 47 50
      node/src/chain_spec/forum_config.rs
  5. 4 30
      node/src/chain_spec/mod.rs
  6. 2 4
      runtime-modules/common/src/working_group.rs
  7. 0 48
      runtime-modules/content-working-group/Cargo.toml
  8. 0 76
      runtime-modules/content-working-group/src/genesis.rs
  9. 0 2677
      runtime-modules/content-working-group/src/lib.rs
  10. 0 252
      runtime-modules/content-working-group/src/mock.rs
  11. 0 2285
      runtime-modules/content-working-group/src/tests.rs
  12. 3 2
      runtime-modules/forum/Cargo.toml
  13. 955 619
      runtime-modules/forum/src/lib.rs
  14. 579 355
      runtime-modules/forum/src/mock.rs
  15. 1653 747
      runtime-modules/forum/src/tests.rs
  16. 0 1
      runtime-modules/proposals/codex/src/lib.rs
  17. 55 1
      runtime-modules/proposals/codex/src/tests/mock.rs
  18. 2 2
      runtime-modules/proposals/discussion/Cargo.toml
  19. 4 7
      runtime-modules/proposals/discussion/src/benchmarking.rs
  20. 54 4
      runtime-modules/proposals/discussion/src/lib.rs
  21. 17 1
      runtime-modules/proposals/discussion/src/tests/mock.rs
  22. 16 1
      runtime-modules/proposals/engine/Cargo.toml
  23. 631 0
      runtime-modules/proposals/engine/src/benchmarking.rs
  24. 151 16
      runtime-modules/proposals/engine/src/lib.rs
  25. 57 1
      runtime-modules/proposals/engine/src/tests/mock/mod.rs
  26. 29 31
      runtime-modules/proposals/engine/src/tests/mod.rs
  27. 0 34
      runtime-modules/versioned-store-permissions/Cargo.toml
  28. 0 32
      runtime-modules/versioned-store-permissions/src/constraint.rs
  29. 0 61
      runtime-modules/versioned-store-permissions/src/credentials.rs
  30. 0 664
      runtime-modules/versioned-store-permissions/src/lib.rs
  31. 0 173
      runtime-modules/versioned-store-permissions/src/mock.rs
  32. 0 136
      runtime-modules/versioned-store-permissions/src/operations.rs
  33. 0 159
      runtime-modules/versioned-store-permissions/src/permissions.rs
  34. 0 673
      runtime-modules/versioned-store-permissions/src/tests.rs
  35. 0 30
      runtime-modules/versioned-store/Cargo.toml
  36. 0 528
      runtime-modules/versioned-store/src/example.rs
  37. 0 775
      runtime-modules/versioned-store/src/lib.rs
  38. 0 262
      runtime-modules/versioned-store/src/mock.rs
  39. 0 503
      runtime-modules/versioned-store/src/tests.rs
  40. 1 6
      runtime/Cargo.toml
  41. 0 142
      runtime/src/integration/content_working_group.rs
  42. 0 31
      runtime/src/integration/forum.rs
  43. 0 3
      runtime/src/integration/mod.rs
  44. 1 0
      runtime/src/integration/proposals/proposal_encoder.rs
  45. 0 50
      runtime/src/integration/versioned_store_permissions.rs
  46. 96 27
      runtime/src/lib.rs
  47. 16 22
      runtime/src/runtime_api.rs
  48. 54 1
      runtime/src/tests/proposals_integration/working_group_proposals.rs
  49. 3 0
      runtime/src/weights/mod.rs
  50. 27 0
      runtime/src/weights/proposals_discussion.rs
  51. 66 0
      runtime/src/weights/proposals_engine.rs
  52. 14 0
      scripts/generate-weights.sh
  53. 1 54
      utils/chain-spec-builder/src/main.rs

+ 7 - 65
Cargo.lock

@@ -2374,7 +2374,6 @@ dependencies = [
  "pallet-common",
  "pallet-constitution",
  "pallet-content-directory",
- "pallet-content-working-group",
  "pallet-finality-tracker",
  "pallet-forum",
  "pallet-governance",
@@ -2403,8 +2402,6 @@ dependencies = [
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-utility",
- "pallet-versioned-store",
- "pallet-versioned-store-permissions",
  "pallet-working-group",
  "parity-scale-codec",
  "serde",
@@ -3826,31 +3823,6 @@ dependencies = [
  "sp-std",
 ]
 
-[[package]]
-name = "pallet-content-working-group"
-version = "3.1.0"
-dependencies = [
- "frame-support",
- "frame-system",
- "pallet-balances",
- "pallet-common",
- "pallet-hiring",
- "pallet-membership",
- "pallet-recurring-reward",
- "pallet-stake",
- "pallet-timestamp",
- "pallet-token-mint",
- "pallet-versioned-store",
- "pallet-versioned-store-permissions",
- "parity-scale-codec",
- "serde",
- "sp-arithmetic",
- "sp-core",
- "sp-io",
- "sp-runtime",
- "sp-std",
-]
-
 [[package]]
 name = "pallet-finality-tracker"
 version = "2.0.0"
@@ -3869,7 +3841,7 @@ dependencies = [
 
 [[package]]
 name = "pallet-forum"
-version = "3.1.0"
+version = "4.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
@@ -4066,7 +4038,7 @@ dependencies = [
 
 [[package]]
 name = "pallet-proposals-discussion"
-version = "4.0.0"
+version = "4.0.1"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -4085,14 +4057,18 @@ dependencies = [
 
 [[package]]
 name = "pallet-proposals-engine"
-version = "4.0.0"
+version = "4.0.1"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-balances",
  "pallet-common",
+ "pallet-governance",
  "pallet-membership",
+ "pallet-recurring-reward",
  "pallet-timestamp",
+ "pallet-token-mint",
  "parity-scale-codec",
  "serde",
  "sp-arithmetic",
@@ -4376,40 +4352,6 @@ dependencies = [
  "sp-std",
 ]
 
-[[package]]
-name = "pallet-versioned-store"
-version = "3.1.0"
-dependencies = [
- "frame-support",
- "frame-system",
- "pallet-common",
- "pallet-timestamp",
- "parity-scale-codec",
- "serde",
- "sp-core",
- "sp-io",
- "sp-runtime",
- "sp-std",
-]
-
-[[package]]
-name = "pallet-versioned-store-permissions"
-version = "3.1.0"
-dependencies = [
- "frame-support",
- "frame-system",
- "pallet-common",
- "pallet-timestamp",
- "pallet-versioned-store",
- "parity-scale-codec",
- "serde",
- "sp-arithmetic",
- "sp-core",
- "sp-io",
- "sp-runtime",
- "sp-std",
-]
-
 [[package]]
 name = "pallet-working-group"
 version = "4.0.0"

+ 0 - 3
Cargo.toml

@@ -5,7 +5,6 @@ members = [
 	"runtime-modules/proposals/codex",
 	"runtime-modules/proposals/discussion",
 	"runtime-modules/common",
-	"runtime-modules/content-working-group",
 	"runtime-modules/forum",
 	"runtime-modules/governance",
 	"runtime-modules/hiring",
@@ -16,8 +15,6 @@ members = [
 	"runtime-modules/stake",
 	"runtime-modules/storage",
 	"runtime-modules/token-minting",
-	"runtime-modules/versioned-store",
-	"runtime-modules/versioned-store-permissions",
 	"runtime-modules/working-group",
 	"runtime-modules/content-directory",
 	"runtime-modules/constitution",

+ 0 - 391
node/src/chain_spec/content_config.rs

@@ -1,391 +0,0 @@
-use codec::Decode;
-use node_runtime::common::constraints::InputValidationLengthConstraint;
-use node_runtime::{
-    content_wg::{Channel, ChannelId, Principal, PrincipalId},
-    data_directory::DataObject,
-    primitives::{AccountId, BlockNumber, Credential},
-    versioned_store::{Class, ClassId, Entity, EntityId},
-    versioned_store_permissions::ClassPermissions,
-    ContentId, ContentWorkingGroupConfig, DataDirectoryConfig, Runtime, VersionedStoreConfig,
-    VersionedStorePermissionsConfig,
-};
-use serde::Deserialize;
-use std::{fs, path::Path};
-
-// Because of the way that the @joystream/types were implemented the getters for
-// the string types return a `string` not the `Text` type so when we are serializing
-// them to json we get a string rather than an array of bytes, so deserializing them
-// is failing. So we are relying on parity codec encoding instead..
-#[derive(Decode)]
-struct ClassAndPermissions {
-    class: Class,
-    permissions: ClassPermissions<ClassId, Credential, u16, BlockNumber>,
-}
-
-#[derive(Decode)]
-struct EntityAndMaintainer {
-    entity: Entity,
-    maintainer: Option<Credential>,
-}
-
-#[derive(Decode)]
-struct DataObjectAndContentId {
-    content_id: ContentId,
-    data_object: DataObject<Runtime>,
-}
-
-#[derive(Decode)]
-struct ContentData {
-    /// classes and their associted permissions
-    classes: Vec<ClassAndPermissions>,
-    /// entities and their associated maintainer
-    entities: Vec<EntityAndMaintainer>,
-    /// DataObject(s) and ContentId
-    data_objects: Vec<DataObjectAndContentId>,
-    /// Media Channels
-    channels: Vec<ChannelAndId>,
-}
-
-#[derive(Deserialize)]
-struct EncodedClassAndPermissions {
-    /// hex encoded Class
-    class: String,
-    /// hex encoded ClassPermissions<ClassId, Credential, u16, BlockNumber>,
-    permissions: String,
-}
-
-impl EncodedClassAndPermissions {
-    fn decode(&self) -> ClassAndPermissions {
-        // hex string must not include '0x' prefix!
-        let encoded_class =
-            hex::decode(&self.class[2..].as_bytes()).expect("failed to parse class hex string");
-        let encoded_permissions = hex::decode(&self.permissions[2..].as_bytes())
-            .expect("failed to parse class permissions hex string");
-        ClassAndPermissions {
-            class: Decode::decode(&mut encoded_class.as_slice()).unwrap(),
-            permissions: Decode::decode(&mut encoded_permissions.as_slice()).unwrap(),
-        }
-    }
-}
-
-#[derive(Deserialize)]
-struct EncodedEntityAndMaintainer {
-    /// hex encoded Entity
-    entity: String,
-    /// hex encoded Option<Credential>
-    maintainer: Option<String>,
-}
-
-impl EncodedEntityAndMaintainer {
-    fn decode(&self) -> EntityAndMaintainer {
-        // hex string must not include '0x' prefix!
-        let encoded_entity =
-            hex::decode(&self.entity[2..].as_bytes()).expect("failed to parse entity hex string");
-        let encoded_maintainer = self.maintainer.as_ref().map(|maintainer| {
-            hex::decode(&maintainer[2..].as_bytes()).expect("failed to parse maintainer hex string")
-        });
-        EntityAndMaintainer {
-            entity: Decode::decode(&mut encoded_entity.as_slice()).unwrap(),
-            maintainer: encoded_maintainer
-                .map(|maintainer| Decode::decode(&mut maintainer.as_slice()).unwrap()),
-        }
-    }
-}
-
-#[derive(Deserialize)]
-struct EncodedDataObjectAndContentId {
-    /// hex encoded ContentId
-    content_id: String,
-    /// hex encoded DataObject<Runtime>
-    data_object: String,
-}
-
-impl EncodedDataObjectAndContentId {
-    fn decode(&self) -> DataObjectAndContentId {
-        // hex string must not include '0x' prefix!
-        let encoded_content_id = hex::decode(&self.content_id[2..].as_bytes())
-            .expect("failed to parse content_id hex string");
-        let encoded_data_object = hex::decode(&self.data_object[2..].as_bytes())
-            .expect("failed to parse data_object hex string");
-        DataObjectAndContentId {
-            content_id: Decode::decode(&mut encoded_content_id.as_slice()).unwrap(),
-            data_object: Decode::decode(&mut encoded_data_object.as_slice()).unwrap(),
-        }
-    }
-}
-
-#[derive(Decode)]
-struct ChannelAndId {
-    id: ChannelId<Runtime>,
-    channel: Channel<u64, AccountId, BlockNumber, PrincipalId<Runtime>>,
-}
-
-#[derive(Deserialize)]
-struct EncodedChannelAndId {
-    /// ChannelId number
-    id: u64,
-    /// hex encoded Channel
-    channel: String,
-}
-
-impl EncodedChannelAndId {
-    fn decode(&self) -> ChannelAndId {
-        let id = self.id;
-        let encoded_channel =
-            hex::decode(&self.channel[2..].as_bytes()).expect("failed to parse channel hex string");
-        ChannelAndId {
-            id: id as ChannelId<Runtime>,
-            channel: Decode::decode(&mut encoded_channel.as_slice()).unwrap(),
-        }
-    }
-}
-
-#[derive(Deserialize)]
-struct EncodedContentData {
-    /// classes and their associted permissions
-    classes: Vec<EncodedClassAndPermissions>,
-    /// entities and their associated maintainer
-    entities: Vec<EncodedEntityAndMaintainer>,
-    /// DataObject(s) and ContentId
-    data_objects: Vec<EncodedDataObjectAndContentId>,
-    /// Media Channels
-    channels: Vec<EncodedChannelAndId>,
-}
-
-fn parse_content_data(data_file: &Path) -> EncodedContentData {
-    let data = fs::read_to_string(data_file).expect("Failed reading file");
-    serde_json::from_str(&data).expect("failed parsing content data")
-}
-
-impl EncodedContentData {
-    pub fn decode(&self) -> ContentData {
-        ContentData {
-            classes: self
-                .classes
-                .iter()
-                .map(|class_and_perm| class_and_perm.decode())
-                .collect(),
-            entities: self
-                .entities
-                .iter()
-                .map(|entities_and_maintainer| entities_and_maintainer.decode())
-                .collect(),
-            data_objects: self
-                .data_objects
-                .iter()
-                .map(|data_objects| data_objects.decode())
-                .collect(),
-            channels: self
-                .channels
-                .iter()
-                .map(|channel_and_id| channel_and_id.decode())
-                .collect(),
-        }
-    }
-}
-
-/// Generates a VersionedStoreConfig genesis config
-/// with pre-populated classes and entities parsed from a json file serialized
-/// as a ContentData struct.
-pub fn versioned_store_config_from_json(data_file: &Path) -> VersionedStoreConfig {
-    let content = parse_content_data(data_file).decode();
-    let base_config = empty_versioned_store_config();
-    let first_id = 1;
-
-    let next_class_id: ClassId = content
-        .classes
-        .last()
-        .map_or(first_id, |class_and_perm| class_and_perm.class.id + 1);
-    assert_eq!(next_class_id, (content.classes.len() + 1) as ClassId);
-
-    let next_entity_id: EntityId = content
-        .entities
-        .last()
-        .map_or(first_id, |entity_and_maintainer| {
-            entity_and_maintainer.entity.id + 1
-        });
-
-    VersionedStoreConfig {
-        class_by_id: content
-            .classes
-            .into_iter()
-            .map(|class_and_permissions| {
-                (class_and_permissions.class.id, class_and_permissions.class)
-            })
-            .collect(),
-        entity_by_id: content
-            .entities
-            .into_iter()
-            .map(|entity_and_maintainer| {
-                (
-                    entity_and_maintainer.entity.id,
-                    entity_and_maintainer.entity,
-                )
-            })
-            .collect(),
-        next_class_id,
-        next_entity_id,
-        ..base_config
-    }
-}
-
-/// Generates basic empty VersionedStoreConfig genesis config
-pub fn empty_versioned_store_config() -> VersionedStoreConfig {
-    VersionedStoreConfig {
-        class_by_id: vec![],
-        entity_by_id: vec![],
-        next_class_id: 1,
-        next_entity_id: 1,
-        property_name_constraint: InputValidationLengthConstraint::new(1, 99),
-        property_description_constraint: InputValidationLengthConstraint::new(1, 999),
-        class_name_constraint: InputValidationLengthConstraint::new(1, 99),
-        class_description_constraint: InputValidationLengthConstraint::new(1, 999),
-    }
-}
-
-/// Generates a basic empty VersionedStorePermissionsConfig genesis config
-pub fn empty_versioned_store_permissions_config() -> VersionedStorePermissionsConfig {
-    VersionedStorePermissionsConfig {
-        class_permissions_by_class_id: vec![],
-        entity_maintainer_by_entity_id: vec![],
-    }
-}
-
-/// Generates a `VersionedStorePermissionsConfig` genesis config
-/// pre-populated with permissions and entity maintainers parsed from
-/// a json file serialized as a `ContentData` struct.
-pub fn versioned_store_permissions_config_from_json(
-    data_file: &Path,
-) -> VersionedStorePermissionsConfig {
-    let content = parse_content_data(data_file).decode();
-
-    VersionedStorePermissionsConfig {
-        class_permissions_by_class_id: content
-            .classes
-            .into_iter()
-            .map(|class_and_perm| (class_and_perm.class.id, class_and_perm.permissions))
-            .collect(),
-        entity_maintainer_by_entity_id: content
-            .entities
-            .into_iter()
-            .filter_map(|entity_and_maintainer| {
-                entity_and_maintainer
-                    .maintainer
-                    .map(|maintainer| (entity_and_maintainer.entity.id, maintainer))
-            })
-            .collect(),
-    }
-}
-
-/// Generates a basic empty `DataDirectoryConfig` genesis config
-pub fn empty_data_directory_config() -> DataDirectoryConfig {
-    DataDirectoryConfig {
-        data_object_by_content_id: vec![],
-        known_content_ids: vec![],
-    }
-}
-
-/// Generates a `DataDirectoryConfig` genesis config
-/// pre-populated with data objects and known content ids parsed from
-/// a json file serialized as a `ContentData` struct
-pub fn data_directory_config_from_json(data_file: &Path) -> DataDirectoryConfig {
-    let content = parse_content_data(data_file).decode();
-
-    DataDirectoryConfig {
-        data_object_by_content_id: content
-            .data_objects
-            .iter()
-            .map(|object| (object.content_id, object.data_object.clone()))
-            .collect(),
-        known_content_ids: content
-            .data_objects
-            .into_iter()
-            .map(|object| object.content_id)
-            .collect(),
-    }
-}
-
-/// Generates a basic `ContentWorkingGroupConfig` genesis config without any active curators
-/// curator lead or channels.
-pub fn empty_content_working_group_config() -> ContentWorkingGroupConfig {
-    ContentWorkingGroupConfig {
-        mint_capacity: 0,
-        curator_opening_by_id: vec![],
-        next_curator_opening_id: 0,
-        curator_application_by_id: vec![],
-        next_curator_application_id: 0,
-        channel_by_id: vec![],
-        next_channel_id: 1,
-        channel_id_by_handle: vec![],
-        curator_by_id: vec![],
-        next_curator_id: 0,
-        principal_by_id: vec![],
-        next_principal_id: 0,
-        channel_creation_enabled: true, // there is no extrinsic to change it so enabling at genesis
-        unstaker_by_stake_id: vec![],
-        channel_handle_constraint: InputValidationLengthConstraint::new(5, 20),
-        channel_description_constraint: InputValidationLengthConstraint::new(1, 1024),
-        opening_human_readable_text: InputValidationLengthConstraint::new(1, 2048),
-        curator_application_human_readable_text: InputValidationLengthConstraint::new(1, 2048),
-        curator_exit_rationale_text: InputValidationLengthConstraint::new(1, 2048),
-        channel_avatar_constraint: InputValidationLengthConstraint::new(5, 1024),
-        channel_banner_constraint: InputValidationLengthConstraint::new(5, 1024),
-        channel_title_constraint: InputValidationLengthConstraint::new(5, 1024),
-    }
-}
-
-/// Generates a `ContentWorkingGroupConfig` genesis config
-/// pre-populated with channels and corresponding princial channel owners
-/// parsed from a json file serialized as a `ContentData` struct
-pub fn content_working_group_config_from_json(data_file: &Path) -> ContentWorkingGroupConfig {
-    let content = parse_content_data(data_file).decode();
-    let first_channel_id = 1;
-    let first_principal_id = 0;
-
-    let next_channel_id: ChannelId<Runtime> = content
-        .channels
-        .last()
-        .map_or(first_channel_id, |channel_and_id| channel_and_id.id + 1);
-    assert_eq!(
-        next_channel_id,
-        (content.channels.len() + 1) as ChannelId<Runtime>
-    );
-
-    let base_config = empty_content_working_group_config();
-
-    ContentWorkingGroupConfig {
-        channel_by_id: content
-            .channels
-            .iter()
-            .enumerate()
-            .map(|(ix, channel_and_id)| {
-                (
-                    channel_and_id.id,
-                    Channel {
-                        principal_id: first_principal_id + ix as PrincipalId<Runtime>,
-                        ..channel_and_id.channel.clone()
-                    },
-                )
-            })
-            .collect(),
-        next_channel_id,
-        channel_id_by_handle: content
-            .channels
-            .iter()
-            .map(|channel_and_id| (channel_and_id.channel.handle.clone(), channel_and_id.id))
-            .collect(),
-        principal_by_id: content
-            .channels
-            .iter()
-            .enumerate()
-            .map(|(ix, channel_and_id)| {
-                (
-                    first_principal_id + ix as PrincipalId<Runtime>,
-                    Principal::ChannelOwner(channel_and_id.id),
-                )
-            })
-            .collect(),
-        next_principal_id: first_principal_id + content.channels.len() as PrincipalId<Runtime>,
-        ..base_config
-    }
-}

+ 47 - 50
node/src/chain_spec/forum_config.rs

@@ -1,21 +1,29 @@
 use codec::Decode;
 use node_runtime::{
-    common::constraints::InputValidationLengthConstraint,
-    forum::{Category, CategoryId, Post, Thread},
-    AccountId, BlockNumber, ForumConfig, Moment, PostId, ThreadId,
+    forum,
+    forum::{Category, Post, Thread},
+    AccountId, ForumConfig, Moment, PostId, Runtime, ThreadId,
 };
 use serde::Deserialize;
+use sp_core::H256;
 use std::{fs, path::Path};
 
-fn new_validation(min: u16, max_min_diff: u16) -> InputValidationLengthConstraint {
-    InputValidationLengthConstraint { min, max_min_diff }
-}
+type CategoryId = <Runtime as forum::Trait>::CategoryId;
+type ForumUserId = <Runtime as forum::Trait>::ForumUserId;
+type ModeratorId = <Runtime as forum::Trait>::ModeratorId;
+type ThreadOf = (
+    CategoryId,
+    ThreadId,
+    Thread<ForumUserId, CategoryId, Moment, H256>,
+);
 
 #[derive(Decode)]
 struct ForumData {
-    categories: Vec<Category<BlockNumber, Moment, AccountId>>,
-    posts: Vec<Post<BlockNumber, Moment, AccountId, ThreadId, PostId>>,
-    threads: Vec<Thread<BlockNumber, Moment, AccountId, ThreadId>>,
+    categories: Vec<(CategoryId, Category<CategoryId, ThreadId, H256>)>,
+    posts: Vec<(ThreadId, PostId, Post<ForumUserId, ThreadId, H256>)>,
+    threads: Vec<ThreadOf>,
+    category_by_moderator: Vec<(CategoryId, ModeratorId, ())>,
+    data_migration_done: bool,
 }
 
 #[derive(Deserialize)]
@@ -26,6 +34,10 @@ struct EncodedForumData {
     posts: Vec<String>,
     /// hex encoded threads
     threads: Vec<String>,
+    /// hex encoded categories by moderator set
+    category_by_moderator: Vec<String>,
+    /// hex encoded data migration done bool flag
+    data_migration_done: String,
 }
 
 impl EncodedForumData {
@@ -58,6 +70,20 @@ impl EncodedForumData {
                     Decode::decode(&mut encoded_thread.as_slice()).unwrap()
                 })
                 .collect(),
+            category_by_moderator: self
+                .category_by_moderator
+                .iter()
+                .map(|category_by_moderator| {
+                    let category_by_moderator = hex::decode(&category_by_moderator[2..].as_bytes())
+                        .expect("failed to parse thread hex string");
+                    Decode::decode(&mut category_by_moderator.as_slice()).unwrap()
+                })
+                .collect(),
+            data_migration_done: {
+                let data_migration_done = hex::decode(&self.data_migration_done[2..].as_bytes())
+                    .expect("failed to parse thread hex string");
+                Decode::decode(&mut data_migration_done.as_slice()).unwrap()
+            },
         }
     }
 }
@@ -81,69 +107,40 @@ pub fn empty(forum_sudo: AccountId) -> ForumConfig {
         categories: vec![],
         threads: vec![],
         posts: vec![],
+        category_by_moderator: vec![],
+        data_migration_done: String::new(),
     };
     create(forum_sudo, forum_data)
 }
 
-fn create(forum_sudo: AccountId, forum_data: EncodedForumData) -> ForumConfig {
+fn create(_forum_sudo: AccountId, forum_data: EncodedForumData) -> ForumConfig {
     let first_id = 1;
     let forum_data = forum_data.decode();
 
-    let next_category_id: CategoryId = forum_data
-        .categories
-        .last()
-        .map_or(first_id, |category| category.id + 1);
+    let next_category_id = first_id + forum_data.categories.len() as CategoryId;
 
     assert_eq!(
         next_category_id,
         (forum_data.categories.len() + 1) as CategoryId
     );
 
-    let next_thread_id: ThreadId = forum_data
-        .threads
-        .last()
-        .map_or(first_id, |thread| thread.id + 1);
+    let next_thread_id = first_id + forum_data.threads.len() as ThreadId;
 
     assert_eq!(next_thread_id, (forum_data.threads.len() + 1) as ThreadId);
 
-    let next_post_id: PostId = forum_data.posts.last().map_or(first_id, |post| post.id + 1);
+    let next_post_id = first_id + forum_data.posts.len() as PostId;
 
     assert_eq!(next_post_id, (forum_data.posts.len() + 1) as PostId);
 
     ForumConfig {
-        category_by_id: forum_data
-            .categories
-            .into_iter()
-            .map(|encoded_category| {
-                let category = encoded_category;
-                (category.id, category)
-            })
-            .collect(),
-        thread_by_id: forum_data
-            .threads
-            .into_iter()
-            .map(|encoded_thread| {
-                let thread = encoded_thread;
-                (thread.id, thread)
-            })
-            .collect(),
-        post_by_id: forum_data
-            .posts
-            .into_iter()
-            .map(|encoded_post| {
-                let post = encoded_post;
-                (post.id, post)
-            })
-            .collect(),
+        category_by_id: forum_data.categories,
+        thread_by_id: forum_data.threads,
+        post_by_id: forum_data.posts,
+        category_by_moderator: forum_data.category_by_moderator,
         next_category_id,
         next_thread_id,
         next_post_id,
-        forum_sudo,
-        category_title_constraint: new_validation(10, 90),
-        category_description_constraint: new_validation(10, 490),
-        thread_title_constraint: new_validation(10, 90),
-        post_text_constraint: new_validation(10, 2990),
-        thread_moderation_rationale_constraint: new_validation(10, 290),
-        post_moderation_rationale_constraint: new_validation(10, 290),
+        category_counter: next_category_id - 1,
+        data_migration_done: forum_data.data_migration_done,
     }
 }

+ 4 - 30
node/src/chain_spec/mod.rs

@@ -30,17 +30,15 @@ use sp_runtime::Perbill;
 
 use node_runtime::{
     membership, wasm_binary_unwrap, AuthorityDiscoveryConfig, BabeConfig, Balance, BalancesConfig,
-    ContentDirectoryConfig, ContentWorkingGroupConfig, CouncilConfig, CouncilElectionConfig,
-    DataDirectoryConfig, DataObjectStorageRegistryConfig, DataObjectTypeRegistryConfig,
-    ElectionParameters, ForumConfig, GrandpaConfig, ImOnlineConfig, MembersConfig, Moment,
-    SessionConfig, SessionKeys, Signature, StakerStatus, StakingConfig, SudoConfig, SystemConfig,
-    VersionedStoreConfig, VersionedStorePermissionsConfig, DAYS,
+    ContentDirectoryConfig, CouncilConfig, CouncilElectionConfig, DataObjectStorageRegistryConfig,
+    DataObjectTypeRegistryConfig, ElectionParameters, ForumConfig, GrandpaConfig, ImOnlineConfig,
+    MembersConfig, Moment, SessionConfig, SessionKeys, Signature, StakerStatus, StakingConfig,
+    SudoConfig, SystemConfig, DAYS,
 };
 
 // Exported to be used by chain-spec-builder
 pub use node_runtime::{AccountId, GenesisConfig};
 
-pub mod content_config;
 pub mod forum_config;
 pub mod initial_balances;
 pub mod initial_members;
@@ -133,10 +131,6 @@ impl Alternative {
                         ],
                         initial_members::none(),
                         forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
-                        content_config::empty_versioned_store_config(),
-                        content_config::empty_versioned_store_permissions_config(),
-                        content_config::empty_data_directory_config(),
-                        content_config::empty_content_working_group_config(),
                         vec![],
                     )
                 },
@@ -173,10 +167,6 @@ impl Alternative {
                         ],
                         initial_members::none(),
                         forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
-                        content_config::empty_versioned_store_config(),
-                        content_config::empty_versioned_store_permissions_config(),
-                        content_config::empty_data_directory_config(),
-                        content_config::empty_content_working_group_config(),
                         vec![],
                     )
                 },
@@ -218,10 +208,6 @@ pub fn testnet_genesis(
     endowed_accounts: Vec<AccountId>,
     members: Vec<membership::genesis::Member<u64, AccountId, Moment>>,
     forum_config: ForumConfig,
-    versioned_store_config: VersionedStoreConfig,
-    versioned_store_permissions_config: VersionedStorePermissionsConfig,
-    data_directory_config: DataDirectoryConfig,
-    content_working_group_config: ContentWorkingGroupConfig,
     initial_balances: Vec<(AccountId, Balance)>,
 ) -> GenesisConfig {
     const STASH: Balance = 5_000;
@@ -300,7 +286,6 @@ pub fn testnet_genesis(
             members,
         }),
         forum: Some(forum_config),
-        data_directory: Some(data_directory_config),
         data_object_type_registry: Some(DataObjectTypeRegistryConfig {
             first_data_object_type_id: 1,
         }),
@@ -317,9 +302,6 @@ pub fn testnet_genesis(
                 next_curator_group_id: 1,
             }
         }),
-        versioned_store: Some(versioned_store_config),
-        versioned_store_permissions: Some(versioned_store_permissions_config),
-        content_wg: Some(content_working_group_config),
     }
 }
 
@@ -336,10 +318,6 @@ pub(crate) mod tests {
             vec![get_authority_keys_from_seed("Alice").0],
             initial_members::none(),
             forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
-            content_config::empty_versioned_store_config(),
-            content_config::empty_versioned_store_permissions_config(),
-            content_config::empty_data_directory_config(),
-            content_config::empty_content_working_group_config(),
             vec![],
         )
     }
@@ -372,10 +350,6 @@ pub(crate) mod tests {
             ],
             initial_members::none(),
             forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
-            content_config::empty_versioned_store_config(),
-            content_config::empty_versioned_store_permissions_config(),
-            content_config::empty_data_directory_config(),
-            content_config::empty_content_working_group_config(),
             vec![],
         )
     }

+ 2 - 4
runtime-modules/common/src/working_group.rs

@@ -8,10 +8,8 @@ use strum_macros::EnumIter;
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize, EnumIter))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Copy, Debug)]
 pub enum WorkingGroup {
-    /* Reserved
-        /// Forum working group: working_group::Instance1.
-        Forum,
-    */
+    /// Forum working group: working_group::Instance1.
+    Forum,
     /// Storage working group: working_group::Instance2.
     Storage,
     /// Storage working group: working_group::Instance3.

+ 0 - 48
runtime-modules/content-working-group/Cargo.toml

@@ -1,48 +0,0 @@
-[package]
-name = 'pallet-content-working-group'
-version = '3.1.0'
-authors = ['Joystream contributors']
-edition = '2018'
-
-[dependencies]
-serde = { version = "1.0.101", optional = true, features = ["derive"] }
-codec = { package = 'parity-scale-codec', version = '1.3.4', default-features = false, features = ['derive'] }
-sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
-stake = { package = 'pallet-stake', default-features = false, path = '../stake'}
-hiring = { package = 'pallet-hiring', default-features = false, path = '../hiring'}
-minting = { package = 'pallet-token-mint', default-features = false, path = '../token-minting'}
-recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../recurring-reward'}
-versioned_store = { package = 'pallet-versioned-store', default-features = false, path = '../versioned-store'}
-versioned_store_permissions = { package = 'pallet-versioned-store-permissions', default-features = false, path = '../versioned-store-permissions'}
-common = { package = 'pallet-common', default-features = false, path = '../common'}
-
-[dev-dependencies]
-sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-
-[features]
-default = ['std']
-std = [
-	'serde',
-	'codec/std',
-	'sp-std/std',
-	'frame-support/std',
-	'frame-system/std',
-	'sp-arithmetic/std',
-	'sp-runtime/std',
-	'membership/std',
-	'stake/std',
-	'hiring/std',
-	'minting/std',
-	'recurringrewards/std',
-    'versioned_store/std',
-    'versioned_store_permissions/std',
-	'common/std',
-]

+ 0 - 76
runtime-modules/content-working-group/src/genesis.rs

@@ -1,76 +0,0 @@
-#![cfg(test)]
-
-use crate::{Trait, *};
-use sp_std::map;
-
-use common::constraints::InputValidationLengthConstraint;
-
-/// Builder of genesis configuration of content working group.
-pub struct GenesisConfigBuilder<T: Trait> {
-    mint_capacity: minting::BalanceOf<T>,
-    channel_creation_enabled: bool,
-    channel_handle_constraint: InputValidationLengthConstraint,
-    channel_description_constraint: InputValidationLengthConstraint,
-    curator_application_human_readable_text: InputValidationLengthConstraint,
-    curator_exit_rationale_text: InputValidationLengthConstraint,
-    channel_title_constraint: InputValidationLengthConstraint,
-    channel_avatar_constraint: InputValidationLengthConstraint,
-    channel_banner_constraint: InputValidationLengthConstraint,
-    opening_human_readable_text: InputValidationLengthConstraint,
-}
-
-impl<T: Trait> GenesisConfigBuilder<T> {
-    pub fn with_mint_capacity(mut self, capacity: minting::BalanceOf<T>) -> Self {
-        self.mint_capacity = capacity;
-        self
-    }
-
-    pub fn build(self) -> GenesisConfig<T> {
-        GenesisConfig {
-            mint_capacity: self.mint_capacity,
-            curator_opening_by_id: map![], //GenesisConfigMap<CuratorOpeningId, Opening>,
-            next_curator_opening_id: CuratorOpeningId::<T>::default(),
-            curator_application_by_id: map![], //GenesisConfigMap<CuratorApplicationId,CuratorApplication>,
-            next_curator_application_id: CuratorApplicationId::<T>::default(),
-            channel_by_id: map![], //GenesisConfigMap<ChannelId, Channel>,
-            next_channel_id: ChannelId::<T>::default(),
-            channel_id_by_handle: map![], //GenesisConfigMap<Vec<u8>, ChannelId>,
-            curator_by_id: map![],        //GenesisConfigMap<CuratorId, Curator>,
-            next_curator_id: CuratorId::<T>::default(),
-            principal_by_id: map![], //GenesisConfigMap<PrinicipalId, Prinicipal>,
-            next_principal_id: PrincipalId::<T>::default(),
-            channel_creation_enabled: self.channel_creation_enabled,
-            unstaker_by_stake_id: map![], //GenesisConfigMap<LeadId, CuratorId>,
-            channel_handle_constraint: self.channel_handle_constraint,
-            channel_description_constraint: self.channel_description_constraint,
-            curator_application_human_readable_text: self.curator_application_human_readable_text,
-            curator_exit_rationale_text: self.curator_exit_rationale_text,
-            channel_title_constraint: self.channel_title_constraint,
-            channel_avatar_constraint: self.channel_avatar_constraint,
-            channel_banner_constraint: self.channel_banner_constraint,
-            opening_human_readable_text: self.opening_human_readable_text,
-        }
-    }
-}
-
-impl<T: Trait> Default for GenesisConfigBuilder<T> {
-    fn default() -> Self {
-        let default_constraint = InputValidationLengthConstraint {
-            min: 8,
-            max_min_diff: 44,
-        };
-
-        Self {
-            mint_capacity: minting::BalanceOf::<T>::from(10000),
-            channel_creation_enabled: true,
-            channel_handle_constraint: default_constraint.clone(),
-            channel_description_constraint: default_constraint.clone(),
-            curator_application_human_readable_text: default_constraint.clone(),
-            curator_exit_rationale_text: default_constraint.clone(),
-            channel_title_constraint: default_constraint.clone(),
-            channel_avatar_constraint: default_constraint.clone(),
-            channel_banner_constraint: default_constraint.clone(),
-            opening_human_readable_text: default_constraint.clone(),
-        }
-    }
-}

+ 0 - 2677
runtime-modules/content-working-group/src/lib.rs

@@ -1,2677 +0,0 @@
-// Clippy linter warning. TODO: remove after the Constaninople release
-#![allow(clippy::type_complexity)]
-// disable it because of possible frontend API break
-
-// Clippy linter warning. TODO: refactor "this function has too many argument"
-#![allow(clippy::too_many_arguments)] // disable it because of possible API break
-
-// Ensure we're `no_std` when compiling for Wasm.
-#![cfg_attr(not(feature = "std"), no_std)]
-
-#[cfg(test)]
-mod tests;
-
-#[cfg(test)]
-mod mock;
-
-pub mod genesis;
-
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-
-use codec::{Decode, Encode};
-use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons};
-use frame_support::{decl_event, decl_module, decl_storage, ensure};
-use frame_system::{ensure_root, ensure_signed};
-use sp_arithmetic::traits::{One, Zero};
-use sp_std::borrow::ToOwned;
-use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
-use sp_std::vec;
-use sp_std::vec::Vec;
-
-use common::constraints::InputValidationLengthConstraint;
-
-/// Module configuration trait for this Substrate module.
-pub trait Trait:
-    frame_system::Trait
-    + minting::Trait
-    + recurringrewards::Trait
-    + stake::Trait
-    + hiring::Trait
-    + versioned_store_permissions::Trait
-    + membership::Trait
-{
-    /// The event type.
-    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
-}
-
-/// Type constraint for identifer used for actors.
-pub type ActorId<T> = <T as membership::Trait>::ActorId;
-
-/// Type for identifier for channels.
-/// The ChannelId must be capable of behaving like an actor id for membership module,
-/// since publishers are identified by their channel id.
-pub type ChannelId<T> = ActorId<T>;
-
-/// Type identifier for lead role, which must be same as membership actor identifeir
-pub type LeadId<T> = ActorId<T>;
-
-/// Type identifier for curator role, which must be same as membership actor identifeir
-pub type CuratorId<T> = ActorId<T>;
-
-/// Type for the identifer for an opening for a curator.
-pub type CuratorOpeningId<T> = <T as hiring::Trait>::OpeningId;
-
-/// Tyoe for the indentifier for an application as a curator.
-pub type CuratorApplicationId<T> = <T as hiring::Trait>::ApplicationId;
-
-/// Balance type of runtime
-pub type BalanceOf<T> =
-    <<T as stake::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
-
-/// Balance type of runtime
-pub type CurrencyOf<T> = <T as stake::Trait>::Currency;
-
-/// Negative imbalance of runtime.
-pub type NegativeImbalance<T> = <<T as stake::Trait>::Currency as Currency<
-    <T as frame_system::Trait>::AccountId,
->>::NegativeImbalance;
-
-/// Type of mintin reward relationship identifiers
-pub type RewardRelationshipId<T> = <T as recurringrewards::Trait>::RewardRelationshipId;
-
-/// Stake identifier in staking module
-pub type StakeId<T> = <T as stake::Trait>::StakeId;
-
-/// Type of permissions module prinicipal identifiers
-pub type PrincipalId<T> = <T as versioned_store_permissions::Trait>::Credential;
-
-pub type CuratorApplicationIdToCuratorIdMap<T> = BTreeMap<CuratorApplicationId<T>, CuratorId<T>>;
-
-// Workaround for BTreeSet type
-pub type CuratorApplicationIdSet<T> = BTreeSet<CuratorApplicationId<T>>;
-
-//TODO: Convert errors to the Substrate decl_error! macro.
-/// Result with string error message. This exists for backward compatibility purpose.
-pub type DispatchResult = Result<(), &'static str>;
-
-/*
- * MOVE ALL OF THESE OUT TO COMMON LATER
- */
-
-pub static MSG_CHANNEL_HANDLE_TOO_SHORT: &str = "Channel handle too short.";
-pub static MSG_CHANNEL_HANDLE_TOO_LONG: &str = "Channel handle too long.";
-pub static MSG_CHANNEL_DESCRIPTION_TOO_SHORT: &str = "Channel description too short";
-pub static MSG_CHANNEL_DESCRIPTION_TOO_LONG: &str = "Channel description too long";
-pub static MSG_CHANNEL_ID_INVALID: &str = "Channel id invalid";
-pub static MSG_CHANNEL_CREATION_DISABLED: &str = "Channel creation currently disabled";
-static MSG_CHANNEL_HANDLE_ALREADY_TAKEN: &str = "Channel handle is already taken";
-static MSG_CHANNEL_TITLE_TOO_SHORT: &str = "Channel title too short";
-static MSG_CHANNEL_TITLE_TOO_LONG: &str = "Channel title too long";
-static MSG_CHANNEL_AVATAR_TOO_SHORT: &str = "Channel avatar URL too short";
-static MSG_CHANNEL_AVATAR_TOO_LONG: &str = "Channel avatar URL too long";
-static MSG_CHANNEL_BANNER_TOO_SHORT: &str = "Channel banner URL too short";
-static MSG_CHANNEL_BANNER_TOO_LONG: &str = "Channel banner URL too long";
-
-//static MSG_MEMBER_CANNOT_BECOME_PUBLISHER: &str =
-//    "Member cannot become a publisher";
-static MSG_ORIGIN_DOES_NOT_MATCH_CHANNEL_ROLE_ACCOUNT: &str =
-    "Origin does not match channel role account";
-pub static MSG_CURRENT_LEAD_ALREADY_SET: &str = "Current lead is already set";
-pub static MSG_CURRENT_LEAD_NOT_SET: &str = "Current lead is not set";
-pub static MSG_ORIGIN_IS_NOT_LEAD: &str = "Origin is not lead";
-pub static MSG_ORIGIN_IS_NOT_APPLICANT: &str = "Origin is not applicant";
-pub static MSG_CURATOR_OPENING_DOES_NOT_EXIST: &str = "Curator opening does not exist";
-pub static MSG_CURATOR_APPLICATION_DOES_NOT_EXIST: &str = "Curator application does not exist";
-pub static MSG_INSUFFICIENT_BALANCE_TO_APPLY: &str = "Insufficient balance to apply";
-pub static MSG_SUCCESSFUL_CURATOR_APPLICATION_DOES_NOT_EXIST: &str =
-    "Successful curatora pplication does not exist";
-pub static MSG_MEMBER_NO_LONGER_REGISTRABLE_AS_CURATOR: &str =
-    "Member no longer registrable as curator";
-pub static MSG_CURATOR_DOES_NOT_EXIST: &str = "Curator does not exist";
-pub static MSG_CURATOR_IS_NOT_ACTIVE: &str = "Curator is not active";
-pub static MSG_CURATOR_EXIT_RATIONALE_TEXT_TOO_LONG: &str =
-    "Curator exit rationale text is too long";
-pub static MSG_CURATOR_EXIT_RATIONALE_TEXT_TOO_SHORT: &str =
-    "Curator exit rationale text is too short";
-pub static MSG_CURATOR_APPLICATION_TEXT_TOO_LONG: &str = "Curator application text too long";
-pub static MSG_CURATOR_APPLICATION_TEXT_TOO_SHORT: &str = "Curator application text too short";
-pub static MSG_SIGNER_IS_NOT_CURATOR_ROLE_ACCOUNT: &str = "Signer is not curator role account";
-pub static MSG_UNSTAKER_DOES_NOT_EXIST: &str = "Unstaker does not exist";
-pub static MSG_CURATOR_HAS_NO_REWARD: &str = "Curator has no recurring reward";
-pub static MSG_CURATOR_NOT_CONTROLLED_BY_MEMBER: &str = "Curator not controlled by member";
-pub static MSG_INSUFFICIENT_BALANCE_TO_COVER_STAKE: &str = "Insuffieicnt balance to cover stake";
-
-/*
- * The errors below, while in many cases encoding similar outcomes,
- * are scoped to the specific extrinsic for which they are used.
- * The reason for this is that it will later to easier to convert this
- * representation into into the type safe error encoding coming in
- * later versions of Substrate.
- */
-
-// Errors for `accept_curator_applications`
-pub static MSG_ACCEPT_CURATOR_APPLICATIONS_OPENING_DOES_NOT_EXIST: &str = "Opening does not exist";
-pub static MSG_ACCEPT_CURATOR_APPLICATIONS_OPENING_IS_NOT_WAITING_TO_BEGIN: &str =
-    "Opening Is Not in Waiting to begin";
-
-// Errors for `begin_curator_applicant_review`
-pub static MSG_BEGIN_CURATOR_APPLICANT_REVIEW_OPENING_DOES_NOT_EXIST: &str =
-    "Opening does not exist";
-pub static MSG_BEGIN_CURATOR_APPLICANT_REVIEW_OPENING_OPENING_IS_NOT_WAITING_TO_BEGIN: &str =
-    "Opening Is Not in Waiting";
-
-// Errors for `fill_curator_opening`
-pub static MSG_FULL_CURATOR_OPENING_OPENING_DOES_NOT_EXIST: &str = "OpeningDoesNotExist";
-pub static MSG_FULL_CURATOR_OPENING_OPENING_NOT_IN_REVIEW_PERIOD_STAGE: &str =
-    "OpeningNotInReviewPeriodStage";
-pub static MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_TOO_SHORT:
-    &str = "Application stake unstaking period for successful applicants too short";
-pub static MSG_FULL_CURATOR_OPENING_SUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_TOO_SHORT: &str =
-    "Application stake unstaking period for failed applicants too short";
-pub static MSG_FULL_CURATOR_OPENING_SUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_TOO_SHORT: &str =
-    "Role stake unstaking period for successful applicants too short";
-pub static MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_TOO_SHORT: &str =
-    "Role stake unstaking period for failed applicants too short";
-pub static MSG_FULL_CURATOR_OPENING_SUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_REDUNDANT: &str =
-    "Application stake unstaking period for successful applicants redundant";
-pub static MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_REDUNDANT:
-    &str = "Application stake unstaking period for failed applicants redundant";
-pub static MSG_FULL_CURATOR_OPENING_SUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_REDUNDANT: &str =
-    "Role stake unstaking period for successful applicants redundant";
-pub static MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_REDUNDANT: &str =
-    "Role stake unstaking period for failed applicants redundant";
-pub static MSG_FULL_CURATOR_OPENING_APPLICATION_DOES_NOT_EXIST: &str = "ApplicationDoesNotExist";
-pub static MSG_FULL_CURATOR_OPENING_APPLICATION_NOT_ACTIVE: &str = "ApplicationNotInActiveStage";
-pub static MSG_FILL_CURATOR_OPENING_INVALID_NEXT_PAYMENT_BLOCK: &str =
-    "Reward policy has invalid next payment block number";
-pub static MSG_FILL_CURATOR_OPENING_MINT_DOES_NOT_EXIST: &str = "Working group mint does not exist";
-pub static MSG_FILL_CURATOR_OPENING_APPLICATION_FOR_WRONG_OPENING: &str =
-    "Applications not for opening";
-// Errors for `withdraw_curator_application`
-pub static MSG_WITHDRAW_CURATOR_APPLICATION_APPLICATION_DOES_NOT_EXIST: &str =
-    "ApplicationDoesNotExist";
-pub static MSG_WITHDRAW_CURATOR_APPLICATION_APPLICATION_NOT_ACTIVE: &str = "ApplicationNotActive";
-pub static MSG_WITHDRAW_CURATOR_APPLICATION_OPENING_NOT_ACCEPTING_APPLICATIONS: &str =
-    "OpeningNotAcceptingApplications";
-pub static MSG_WITHDRAW_CURATOR_APPLICATION_UNSTAKING_PERIOD_TOO_SHORT: &str =
-    "UnstakingPeriodTooShort ..."; // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-pub static MSG_WITHDRAW_CURATOR_APPLICATION_REDUNDANT_UNSTAKING_PERIOD: &str =
-    "RedundantUnstakingPeriodProvided ...";
-
-// Errors for `create_channel`
-pub static MSG_CREATE_CHANNEL_IS_NOT_MEMBER: &str = "Is not a member";
-pub static MSG_CREATE_CHANNEL_NOT_CONTROLLER_ACCOUNT: &str =
-    "Account is not controller account of member";
-
-// Errors for `add_curator_opening`
-pub static MSG_ADD_CURATOR_OPENING_ACTIVATES_IN_THE_PAST: &str =
-    "Opening does not activate in the future";
-pub static MSG_ADD_CURATOR_OPENING_ROLE_STAKE_LESS_THAN_MINIMUM: &str =
-    "Role stake amount less than minimum currency balance";
-pub static MSG_ADD_CURATOR_OPENING_APPLIICATION_STAKE_LESS_THAN_MINIMUM: &str =
-    "Application stake amount less than minimum currency balance";
-pub static MSG_ADD_CURATOR_OPENING_OPENING_DOES_NOT_EXIST: &str = "OpeningDoesNotExist";
-pub static MSG_ADD_CURATOR_OPENING_STAKE_PROVIDED_WHEN_REDUNDANT: &str =
-    "StakeProvidedWhenRedundant ..."; // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-pub static MSG_ADD_CURATOR_OPENING_STAKE_MISSING_WHEN_REQUIRED: &str =
-    "StakeMissingWhenRequired ..."; // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-pub static MSG_ADD_CURATOR_OPENING_STAKE_AMOUNT_TOO_LOW: &str = "StakeAmountTooLow ..."; // <== SHOULD REALLY BE TWO SEPARATE, ONE FOR EACH STAKING PURPOSE
-pub static MSG_ADD_CURATOR_OPENING_OPENING_NOT_IN_ACCEPTING_APPLICATION_STAGE: &str =
-    "OpeningNotInAcceptingApplicationsStage";
-pub static MSG_ADD_CURATOR_OPENING_NEW_APPLICATION_WAS_CROWDED_OUT: &str =
-    "NewApplicationWasCrowdedOut";
-pub static MSG_ADD_CURATOR_OPENING_ZERO_MAX_APPLICANT_COUNT: &str =
-    "Application rationing has zero max active applicants";
-
-// Errors for `apply_on_curator_opening`
-pub static MSG_APPLY_ON_CURATOR_OPENING_UNSIGNED_ORIGIN: &str = "Unsigned origin";
-pub static MSG_MEMBER_ID_INVALID: &str = "Member id is invalid";
-pub static MSG_SIGNER_NOT_CONTROLLER_ACCOUNT: &str = "Signer does not match controller account";
-pub static MSG_ORIGIN_IS_NIETHER_MEMBER_CONTROLLER_OR_ROOT: &str =
-    "Origin must be controller or root account of member";
-pub static MSG_MEMBER_HAS_ACTIVE_APPLICATION_ON_OPENING: &str =
-    "Member already has an active application on the opening";
-pub static MSG_ADD_CURATOR_OPENING_ROLE_STAKE_CANNOT_BE_ZERO: &str =
-    "Add curator opening role stake cannot be zero";
-pub static MSG_ADD_CURATOR_OPENING_APPLICATION_STAKE_CANNOT_BE_ZERO: &str =
-    "Add curator opening application stake cannot be zero";
-
-/// The exit stage of a lead involvement in the working group.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub struct ExitedLeadRole<BlockNumber> {
-    /// When exit was initiated.
-    pub initiated_at_block_number: BlockNumber,
-}
-
-/// The stage of the involvement of a lead in the working group.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum LeadRoleState<BlockNumber> {
-    /// Currently active.
-    Active,
-
-    /// No longer active, for some reason
-    Exited(ExitedLeadRole<BlockNumber>),
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl<BlockNumber> Default for LeadRoleState<BlockNumber> {
-    fn default() -> Self {
-        LeadRoleState::Active
-    }
-}
-
-/// Working group lead: curator lead
-/// For now this role is not staked or inducted through an structured process, like the hiring module,
-/// hence information about this is missing. Recurring rewards is included, somewhat arbitrarily!
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Lead<AccountId, RewardRelationshipId, BlockNumber, MemberId> {
-    /// Leader member id,
-    pub member_id: MemberId,
-
-    /// Account used to authenticate in this role,
-    pub role_account: AccountId,
-
-    /// Whether the role has recurring reward, and if so an identifier for this.
-    pub reward_relationship: Option<RewardRelationshipId>,
-
-    /// When was inducted
-    /// TODO: Add richer information about circumstances of induction, like referencing a council proposal?
-    pub inducted: BlockNumber,
-
-    /// The stage of the involvement of this lead in the working group.
-    pub stage: LeadRoleState<BlockNumber>,
-}
-
-/// Origin of exit initiation on behalf of a curator.'
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum CuratorExitInitiationOrigin {
-    /// Lead is origin.
-    Lead,
-
-    /// The curator exiting is the origin.
-    Curator,
-}
-
-/// The exit stage of a curators involvement in the working group.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub struct CuratorExitSummary<BlockNumber> {
-    /// Origin for exit.
-    pub origin: CuratorExitInitiationOrigin,
-
-    /// When exit was initiated.
-    pub initiated_at_block_number: BlockNumber,
-
-    /// Explainer for why exit was initited.
-    pub rationale_text: Vec<u8>,
-}
-
-impl<BlockNumber: Clone> CuratorExitSummary<BlockNumber> {
-    pub fn new(
-        origin: &CuratorExitInitiationOrigin,
-        initiated_at_block_number: &BlockNumber,
-        rationale_text: &[u8],
-    ) -> Self {
-        CuratorExitSummary {
-            origin: (*origin).clone(),
-            initiated_at_block_number: (*initiated_at_block_number).clone(),
-            rationale_text: rationale_text.to_owned(),
-        }
-    }
-}
-
-/// The stage of the involvement of a curator in the working group.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum CuratorRoleStage<BlockNumber> {
-    /// Currently active.
-    Active,
-
-    /// Currently unstaking
-    Unstaking(CuratorExitSummary<BlockNumber>),
-
-    /// No longer active and unstaked
-    Exited(CuratorExitSummary<BlockNumber>),
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl<BlockNumber> Default for CuratorRoleStage<BlockNumber> {
-    fn default() -> Self {
-        CuratorRoleStage::Active
-    }
-}
-
-/// The induction of a curator in the working group.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct CuratorInduction<LeadId, CuratorApplicationId, BlockNumber> {
-    /// Lead responsible for inducting curator
-    pub lead: LeadId,
-
-    /// Application through which curator was inducted
-    pub curator_application_id: CuratorApplicationId,
-
-    /// When induction occurred
-    pub at_block: BlockNumber,
-}
-
-impl<LeadId: Clone, CuratorApplicationId: Clone, BlockNumber: Clone>
-    CuratorInduction<LeadId, CuratorApplicationId, BlockNumber>
-{
-    pub fn new(
-        lead: &LeadId,
-        curator_application_id: &CuratorApplicationId,
-        at_block: &BlockNumber,
-    ) -> Self {
-        CuratorInduction {
-            lead: (*lead).clone(),
-            curator_application_id: (*curator_application_id).clone(),
-            at_block: (*at_block).clone(),
-        }
-    }
-}
-
-/// Role stake information for a curator.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct CuratorRoleStakeProfile<StakeId, BlockNumber> {
-    /// Whether participant is staked, and if so, the identifier for this staking in the staking module.
-    pub stake_id: StakeId,
-
-    /// Unstaking period when terminated.
-    pub termination_unstaking_period: Option<BlockNumber>,
-
-    /// Unstaking period when exiting.
-    pub exit_unstaking_period: Option<BlockNumber>,
-}
-
-impl<StakeId: Clone, BlockNumber: Clone> CuratorRoleStakeProfile<StakeId, BlockNumber> {
-    pub fn new(
-        stake_id: &StakeId,
-        termination_unstaking_period: &Option<BlockNumber>,
-        exit_unstaking_period: &Option<BlockNumber>,
-    ) -> Self {
-        Self {
-            stake_id: (*stake_id).clone(),
-            termination_unstaking_period: (*termination_unstaking_period).clone(),
-            exit_unstaking_period: (*exit_unstaking_period).clone(),
-        }
-    }
-}
-
-/// Working group participant: curator
-/// This role can be staked, have reward and be inducted through the hiring module.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Curator<
-    AccountId,
-    RewardRelationshipId,
-    StakeId,
-    BlockNumber,
-    LeadId,
-    CuratorApplicationId,
-    PrincipalId,
-> {
-    /// Account used to authenticate in this role,
-    pub role_account: AccountId,
-
-    /// Whether the role has recurring reward, and if so an identifier for this.
-    pub reward_relationship: Option<RewardRelationshipId>,
-
-    /// When set, describes role stake of curator.
-    pub role_stake_profile: Option<CuratorRoleStakeProfile<StakeId, BlockNumber>>,
-
-    /// The stage of this curator in the working group.
-    pub stage: CuratorRoleStage<BlockNumber>,
-
-    /// How the curator was inducted into the working group.
-    pub induction: CuratorInduction<LeadId, CuratorApplicationId, BlockNumber>,
-
-    /// Permissions module principal id
-    pub principal_id: PrincipalId,
-}
-
-impl<
-        AccountId: Clone,
-        RewardRelationshipId: Clone,
-        StakeId: Clone,
-        BlockNumber: Clone,
-        LeadId: Clone,
-        ApplicationId: Clone,
-        PrincipalId: Clone,
-    >
-    Curator<
-        AccountId,
-        RewardRelationshipId,
-        StakeId,
-        BlockNumber,
-        LeadId,
-        ApplicationId,
-        PrincipalId,
-    >
-{
-    pub fn new(
-        role_account: &AccountId,
-        reward_relationship: &Option<RewardRelationshipId>,
-        role_stake_profile: &Option<CuratorRoleStakeProfile<StakeId, BlockNumber>>,
-        stage: &CuratorRoleStage<BlockNumber>,
-        induction: &CuratorInduction<LeadId, ApplicationId, BlockNumber>,
-        //can_update_channel_curation_status: bool,
-        principal_id: &PrincipalId,
-    ) -> Self {
-        Curator {
-            role_account: (*role_account).clone(),
-            reward_relationship: (*reward_relationship).clone(),
-            role_stake_profile: (*role_stake_profile).clone(),
-            stage: (*stage).clone(),
-            induction: (*induction).clone(),
-            //can_update_channel_curation_status: can_update_channel_curation_status,
-            principal_id: (*principal_id).clone(),
-        }
-    }
-}
-
-/// An opening for a curator role.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct CuratorOpening<OpeningId, BlockNumber, Balance, CuratorApplicationId: core::cmp::Ord> {
-    /// Identifer for underlying opening in the hiring module.
-    pub opening_id: OpeningId,
-
-    /// Set of identifiers for all curator applications ever added
-    pub curator_applications: BTreeSet<CuratorApplicationId>,
-
-    /// Commitment to policies in opening.
-    pub policy_commitment: OpeningPolicyCommitment<BlockNumber, Balance>, /*
-                                                                           * Add other stuff here in the future?
-                                                                           * Like default payment terms, privilidges etc.?
-                                                                           * Not obvious that it serves much of a purpose, they are mutable
-                                                                           * after all, they need to be.
-                                                                           * Revisit.
-                                                                           */
-}
-
-/// An application for the curator role.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct CuratorApplication<AccountId, CuratorOpeningId, MemberId, ApplicationId> {
-    /// Account used to authenticate in this role,
-    pub role_account: AccountId,
-
-    /// Opening on which this application applies
-    pub curator_opening_id: CuratorOpeningId,
-
-    /// Member applying
-    pub member_id: MemberId,
-
-    /// Underlying application in hiring module
-    pub application_id: ApplicationId,
-}
-
-impl<AccountId: Clone, CuratorOpeningId: Clone, MemberId: Clone, ApplicationId: Clone>
-    CuratorApplication<AccountId, CuratorOpeningId, MemberId, ApplicationId>
-{
-    pub fn new(
-        role_account: &AccountId,
-        curator_opening_id: &CuratorOpeningId,
-        member_id: &MemberId,
-        application_id: &ApplicationId,
-    ) -> Self {
-        CuratorApplication {
-            role_account: (*role_account).clone(),
-            curator_opening_id: (*curator_opening_id).clone(),
-            member_id: (*member_id).clone(),
-            application_id: (*application_id).clone(),
-        }
-    }
-}
-
-/// Type of .... .
-#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
-pub enum CurationActor<CuratorId> {
-    Lead,
-    Curator(CuratorId),
-}
-
-/*
- * BEGIN: =========================================================
- * Channel stuff
- */
-
-/// Type of channel content.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum ChannelContentType {
-    Video,
-    Music,
-    Ebook,
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl Default for ChannelContentType {
-    fn default() -> Self {
-        ChannelContentType::Video
-    }
-}
-
-/// Status of channel, as set by the owner.
-/// Is only meant to affect visibility, mutation of channel and child content
-/// is unaffected on runtime.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum ChannelPublicationStatus {
-    /// Compliant UIs should render.
-    Public,
-
-    /// Compliant UIs should not render it or any child content.
-    Unlisted,
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl Default for ChannelPublicationStatus {
-    fn default() -> Self {
-        ChannelPublicationStatus::Public
-    }
-}
-
-/// Status of channel, as set by curators.
-/// Is only meant to affect visibility currently, but in the future
-/// it will also gate publication of new child content,
-/// editing properties, revenue flows, etc.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ChannelCurationStatus {
-    Normal,
-    Censored,
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl Default for ChannelCurationStatus {
-    fn default() -> Self {
-        ChannelCurationStatus::Normal
-    }
-}
-
-pub type OptionalText = Option<Vec<u8>>;
-
-/// A channel for publishing content.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Channel<MemberId, AccountId, BlockNumber, PrincipalId> {
-    /// Whether channel has been verified, in the normal Web2.0 platform sense of being authenticated.
-    pub verified: bool,
-
-    /// Unique channel handle that could be used in channel URL.
-    pub handle: Vec<u8>,
-
-    /// Human readable title of channel. Not required to be unique.
-    pub title: OptionalText,
-
-    /// Human readable description of channel purpose and scope.
-    pub description: OptionalText,
-
-    /// URL of a small avatar (logo) image of this channel.
-    pub avatar: OptionalText,
-
-    /// URL of a big background image of this channel.
-    pub banner: OptionalText,
-
-    /// The type of channel.
-    pub content: ChannelContentType,
-
-    /// Member who owns channel.
-    pub owner: MemberId,
-
-    /// Account used to authenticate as owner.
-    /// Can be updated through membership role key.
-    pub role_account: AccountId,
-
-    /// Publication status of channel.
-    pub publication_status: ChannelPublicationStatus,
-
-    /// Curation status of channel.
-    pub curation_status: ChannelCurationStatus,
-
-    /// When channel was established.
-    pub created: BlockNumber,
-
-    /// Permissions module principal id
-    pub principal_id: PrincipalId,
-}
-
-/*
- * END: =========================================================
- * Channel stuff
- */
-
-/// Permissions module principal
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-pub enum Principal<CuratorId, ChannelId> {
-    /// Its sloppy to have this here, less safe,
-    /// but its not worth the ffort to solve.
-    Lead,
-
-    Curator(CuratorId),
-
-    ChannelOwner(ChannelId),
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl<CuratorId, ChannelId> Default for Principal<CuratorId, ChannelId> {
-    fn default() -> Self {
-        Principal::Lead
-    }
-}
-
-/// Terms for slashings applied to a given role
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
-pub struct SlashableTerms {
-    /// Maximum number of slashes.
-    pub max_count: u16,
-
-    /// Maximum percentage points of remaining stake which may be slashed in a single slash.
-    pub max_percent_pts_per_time: u16,
-}
-
-/// Terms for what slashing can be applied in some context
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
-pub enum SlashingTerms {
-    Unslashable,
-    Slashable(SlashableTerms),
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl Default for SlashingTerms {
-    fn default() -> Self {
-        Self::Unslashable
-    }
-}
-
-/// A commitment to the set of policy variables relevant to an opening.
-/// An applicant can observe this commitment and be secure that the terms
-/// of the application process cannot be changed ex-post.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
-pub struct OpeningPolicyCommitment<BlockNumber, Balance> {
-    /// Rationing to be used
-    pub application_rationing_policy: Option<hiring::ApplicationRationingPolicy>,
-
-    /// Maximum length of review period of applications
-    pub max_review_period_length: BlockNumber,
-
-    /// Staking policy for application
-    pub application_staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
-
-    /// Staking policy for role itself
-    pub role_staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
-
-    /// Slashing terms during role, NOT application itself!
-    pub role_slashing_terms: SlashingTerms,
-
-    /// When filling an opening: Unstaking period for application stake of successful applicants
-    pub fill_opening_successful_applicant_application_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When filling an opening:
-    pub fill_opening_failed_applicant_application_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When filling an opening:
-    pub fill_opening_failed_applicant_role_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When terminating a curator:
-    pub terminate_curator_application_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When terminating a curator:
-    pub terminate_curator_role_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When a curator exists: ..
-    pub exit_curator_role_application_stake_unstaking_period: Option<BlockNumber>,
-
-    /// When a curator exists: ..
-    pub exit_curator_role_stake_unstaking_period: Option<BlockNumber>,
-}
-
-/// Represents a possible unstaker in working group.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Debug, Eq, PartialEq, Clone, PartialOrd)]
-pub enum WorkingGroupUnstaker<LeadId, CuratorId> {
-    ///
-    Lead(LeadId),
-
-    ///
-    Curator(CuratorId),
-}
-
-/// Must be default constructible because it indirectly is a value in a storage map.
-/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
-impl<LeadId: Default, CuratorId> Default for WorkingGroupUnstaker<LeadId, CuratorId> {
-    fn default() -> Self {
-        Self::Lead(LeadId::default())
-    }
-}
-
-// ======================================================================== //
-// Move section below, this out in its own file later                       //
-// ======================================================================== //
-
-pub struct WrappedError<E> {
-    // can this be made generic, or does that undermine the whole orhpan rule spirit?
-    pub error: E,
-}
-
-/// ....
-macro_rules! ensure_on_wrapped_error {
-    ($call:expr) => {{
-        { $call }
-            .map_err(|err| WrappedError { error: err })
-            .map_err(<&str>::from)
-    }};
-}
-
-// Add macro here to make this
-//derive_from_impl(hiring::BeginAcceptingApplicationsError)
-//derive_from_impl(hiring::BeginAcceptingApplicationsError)
-
-impl sp_std::convert::From<WrappedError<hiring::BeginAcceptingApplicationsError>> for &str {
-    fn from(wrapper: WrappedError<hiring::BeginAcceptingApplicationsError>) -> Self {
-        match wrapper.error {
-            hiring::BeginAcceptingApplicationsError::OpeningDoesNotExist => {
-                MSG_ACCEPT_CURATOR_APPLICATIONS_OPENING_DOES_NOT_EXIST
-            }
-            hiring::BeginAcceptingApplicationsError::OpeningIsNotInWaitingToBeginStage => {
-                MSG_ACCEPT_CURATOR_APPLICATIONS_OPENING_IS_NOT_WAITING_TO_BEGIN
-            }
-        }
-    }
-}
-
-impl sp_std::convert::From<WrappedError<hiring::AddOpeningError>> for &str {
-    fn from(wrapper: WrappedError<hiring::AddOpeningError>) -> Self {
-        match wrapper.error {
-            hiring::AddOpeningError::OpeningMustActivateInTheFuture => {
-                MSG_ADD_CURATOR_OPENING_ACTIVATES_IN_THE_PAST
-            }
-            hiring::AddOpeningError::StakeAmountLessThanMinimumStakeBalance(purpose) => {
-                match purpose {
-                    hiring::StakePurpose::Role => {
-                        MSG_ADD_CURATOR_OPENING_ROLE_STAKE_LESS_THAN_MINIMUM
-                    }
-                    hiring::StakePurpose::Application => {
-                        MSG_ADD_CURATOR_OPENING_APPLIICATION_STAKE_LESS_THAN_MINIMUM
-                    }
-                }
-            }
-            hiring::AddOpeningError::ApplicationRationingZeroMaxApplicants => {
-                MSG_ADD_CURATOR_OPENING_ZERO_MAX_APPLICANT_COUNT
-            }
-            hiring::AddOpeningError::StakeAmountCannotBeZero(purpose) => match purpose {
-                hiring::StakePurpose::Role => MSG_ADD_CURATOR_OPENING_ROLE_STAKE_CANNOT_BE_ZERO,
-                hiring::StakePurpose::Application => {
-                    MSG_ADD_CURATOR_OPENING_APPLICATION_STAKE_CANNOT_BE_ZERO
-                }
-            },
-        }
-    }
-}
-
-impl sp_std::convert::From<WrappedError<hiring::BeginReviewError>> for &str {
-    fn from(wrapper: WrappedError<hiring::BeginReviewError>) -> Self {
-        match wrapper.error {
-            hiring::BeginReviewError::OpeningDoesNotExist => {
-                MSG_BEGIN_CURATOR_APPLICANT_REVIEW_OPENING_DOES_NOT_EXIST
-            }
-            hiring::BeginReviewError::OpeningNotInAcceptingApplicationsStage => {
-                MSG_BEGIN_CURATOR_APPLICANT_REVIEW_OPENING_OPENING_IS_NOT_WAITING_TO_BEGIN
-            }
-        }
-    }
-}
-
-impl<T: hiring::Trait> sp_std::convert::From<WrappedError<hiring::FillOpeningError<T>>> for &str {
-    fn from(wrapper: WrappedError<hiring::FillOpeningError<T>>) -> Self {
-        match wrapper.error {
-            hiring::FillOpeningError::<T>::OpeningDoesNotExist => MSG_FULL_CURATOR_OPENING_OPENING_DOES_NOT_EXIST,
-            hiring::FillOpeningError::<T>::OpeningNotInReviewPeriodStage => MSG_FULL_CURATOR_OPENING_OPENING_NOT_IN_REVIEW_PERIOD_STAGE,
-            hiring::FillOpeningError::<T>::UnstakingPeriodTooShort(
-                stake_purpose,
-                outcome_in_filled_opening,
-            ) => match stake_purpose {
-                hiring::StakePurpose::Application => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_TOO_SHORT,
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => MSG_FULL_CURATOR_OPENING_SUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_TOO_SHORT
-                },
-                hiring::StakePurpose::Role => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => MSG_FULL_CURATOR_OPENING_SUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_TOO_SHORT,
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_TOO_SHORT
-                },
-            },
-            hiring::FillOpeningError::<T>::RedundantUnstakingPeriodProvided(
-                stake_purpose,
-                outcome_in_filled_opening,
-            ) => match stake_purpose {
-                hiring::StakePurpose::Application => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => MSG_FULL_CURATOR_OPENING_SUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_REDUNDANT,
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_APPLICATION_STAKE_UNSTAKING_PERIOD_REDUNDANT
-                },
-                hiring::StakePurpose::Role => match outcome_in_filled_opening {
-                    hiring::ApplicationOutcomeInFilledOpening::Success => MSG_FULL_CURATOR_OPENING_SUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_REDUNDANT,
-                    hiring::ApplicationOutcomeInFilledOpening::Failure => MSG_FULL_CURATOR_OPENING_UNSUCCESSFUL_ROLE_STAKE_UNSTAKING_PERIOD_REDUNDANT
-                },
-            },
-            hiring::FillOpeningError::<T>::ApplicationDoesNotExist(_application_id) => MSG_FULL_CURATOR_OPENING_APPLICATION_DOES_NOT_EXIST,
-            hiring::FillOpeningError::<T>::ApplicationNotInActiveStage(_application_id) => MSG_FULL_CURATOR_OPENING_APPLICATION_NOT_ACTIVE,
-            hiring::FillOpeningError::<T>::ApplicationForWrongOpening(_application_id) => MSG_FILL_CURATOR_OPENING_APPLICATION_FOR_WRONG_OPENING,
-        }
-    }
-}
-
-impl sp_std::convert::From<WrappedError<hiring::DeactivateApplicationError>> for &str {
-    fn from(wrapper: WrappedError<hiring::DeactivateApplicationError>) -> Self {
-        match wrapper.error {
-            hiring::DeactivateApplicationError::ApplicationDoesNotExist => {
-                MSG_WITHDRAW_CURATOR_APPLICATION_APPLICATION_DOES_NOT_EXIST
-            }
-            hiring::DeactivateApplicationError::ApplicationNotActive => {
-                MSG_WITHDRAW_CURATOR_APPLICATION_APPLICATION_NOT_ACTIVE
-            }
-            hiring::DeactivateApplicationError::OpeningNotAcceptingApplications => {
-                MSG_WITHDRAW_CURATOR_APPLICATION_OPENING_NOT_ACCEPTING_APPLICATIONS
-            }
-            hiring::DeactivateApplicationError::UnstakingPeriodTooShort(_stake_purpose) => {
-                MSG_WITHDRAW_CURATOR_APPLICATION_UNSTAKING_PERIOD_TOO_SHORT
-            }
-            hiring::DeactivateApplicationError::RedundantUnstakingPeriodProvided(
-                _stake_purpose,
-            ) => MSG_WITHDRAW_CURATOR_APPLICATION_REDUNDANT_UNSTAKING_PERIOD,
-        }
-    }
-}
-
-impl sp_std::convert::From<WrappedError<membership::ControllerAccountForMemberCheckFailed>>
-    for &str
-{
-    fn from(wrapper: WrappedError<membership::ControllerAccountForMemberCheckFailed>) -> Self {
-        match wrapper.error {
-            membership::ControllerAccountForMemberCheckFailed::NotMember => {
-                MSG_CREATE_CHANNEL_IS_NOT_MEMBER
-            }
-            membership::ControllerAccountForMemberCheckFailed::NotControllerAccount => {
-                MSG_CREATE_CHANNEL_NOT_CONTROLLER_ACCOUNT
-            }
-        }
-    }
-}
-
-impl sp_std::convert::From<WrappedError<hiring::AddApplicationError>> for &str {
-    fn from(wrapper: WrappedError<hiring::AddApplicationError>) -> Self {
-        match wrapper.error {
-            hiring::AddApplicationError::OpeningDoesNotExist => {
-                MSG_ADD_CURATOR_OPENING_OPENING_DOES_NOT_EXIST
-            }
-            hiring::AddApplicationError::StakeProvidedWhenRedundant(_stake_purpose) => {
-                MSG_ADD_CURATOR_OPENING_STAKE_PROVIDED_WHEN_REDUNDANT
-            }
-            hiring::AddApplicationError::StakeMissingWhenRequired(_stake_purpose) => {
-                MSG_ADD_CURATOR_OPENING_STAKE_MISSING_WHEN_REQUIRED
-            }
-            hiring::AddApplicationError::StakeAmountTooLow(_stake_purpose) => {
-                MSG_ADD_CURATOR_OPENING_STAKE_AMOUNT_TOO_LOW
-            }
-            hiring::AddApplicationError::OpeningNotInAcceptingApplicationsStage => {
-                MSG_ADD_CURATOR_OPENING_OPENING_NOT_IN_ACCEPTING_APPLICATION_STAGE
-            }
-            hiring::AddApplicationError::NewApplicationWasCrowdedOut => {
-                MSG_ADD_CURATOR_OPENING_NEW_APPLICATION_WAS_CROWDED_OUT
-            }
-        }
-    }
-}
-
-impl sp_std::convert::From<WrappedError<membership::MemberControllerAccountDidNotSign>> for &str {
-    fn from(wrapper: WrappedError<membership::MemberControllerAccountDidNotSign>) -> Self {
-        match wrapper.error {
-            membership::MemberControllerAccountDidNotSign::UnsignedOrigin => {
-                MSG_APPLY_ON_CURATOR_OPENING_UNSIGNED_ORIGIN
-            }
-            membership::MemberControllerAccountDidNotSign::MemberIdInvalid => MSG_MEMBER_ID_INVALID,
-            membership::MemberControllerAccountDidNotSign::SignerControllerAccountMismatch => {
-                MSG_SIGNER_NOT_CONTROLLER_ACCOUNT
-            }
-        }
-    }
-}
-
-/// The recurring reward if any to be assigned to an actor when filling in the position.
-#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)]
-pub struct RewardPolicy<Balance, BlockNumber> {
-    amount_per_payout: Balance,
-    next_payment_at_block: BlockNumber,
-    payout_interval: Option<BlockNumber>,
-}
-
-// ======================================================================== //
-// Move section above, this out in its own file later                       //
-// ======================================================================== //
-
-decl_storage! {
-    trait Store for Module<T: Trait> as ContentWorkingGroup {
-
-        /// The mint currently funding the rewards for this module.
-        pub Mint get(fn mint) : <T as minting::Trait>::MintId;
-
-        /// The current lead.
-        pub CurrentLeadId get(fn current_lead_id) : Option<LeadId<T>>;
-
-        /// Maps identifier to corresponding lead.
-        pub LeadById get(fn lead_by_id): map hasher(blake2_128_concat)
-            LeadId<T> => Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber, T::MemberId>;
-
-        /// Next identifier for new current lead.
-        pub NextLeadId get(fn next_lead_id): LeadId<T>;
-
-        /// Maps identifeir to curator opening.
-        pub CuratorOpeningById get(fn curator_opening_by_id) config(): map hasher(blake2_128_concat)
-            CuratorOpeningId<T> => CuratorOpening<T::OpeningId, T::BlockNumber, BalanceOf<T>, CuratorApplicationId<T>>;
-
-        /// Next identifier valuefor new curator opening.
-        pub NextCuratorOpeningId get(fn next_curator_opening_id) config(): CuratorOpeningId<T>;
-
-        /// Maps identifier to curator application on opening.
-        pub CuratorApplicationById get(fn curator_application_by_id) config(): map hasher(blake2_128_concat)
-            CuratorApplicationId<T> => CuratorApplication<T::AccountId, CuratorOpeningId<T>, T::MemberId, T::ApplicationId>;
-
-        /// Next identifier value for new curator application.
-        pub NextCuratorApplicationId get(fn next_curator_application_id) config(): CuratorApplicationId<T>;
-
-        /// Maps identifier to corresponding channel.
-        pub ChannelById get(fn channel_by_id) config(): map hasher(blake2_128_concat)
-            ChannelId<T> => Channel<T::MemberId, T::AccountId, T::BlockNumber, PrincipalId<T>>;
-
-        /// Identifier to be used by the next channel introduced.
-        pub NextChannelId get(fn next_channel_id) config(): ChannelId<T>;
-
-        /// Maps (unique) channel handle to the corresponding identifier for the channel.
-        /// Mapping is required to allow efficient (O(log N)) on-chain verification that a proposed handle is indeed unique
-        /// at the time it is being proposed.
-        pub ChannelIdByHandle get(fn channel_id_by_handle) config(): map hasher(blake2_128_concat)
-            Vec<u8> => ChannelId<T>;
-
-        /// Maps identifier to corresponding curator.
-        pub CuratorById get(fn curator_by_id) config(): map hasher(blake2_128_concat)
-            CuratorId<T> => Curator<T::AccountId, T::RewardRelationshipId, T::StakeId, T::BlockNumber, LeadId<T>, CuratorApplicationId<T>, PrincipalId<T>>;
-
-        /// Next identifier for new curator.
-        pub NextCuratorId get(fn next_curator_id) config(): CuratorId<T>;
-
-        /// Maps identifier to principal.
-        pub PrincipalById get(fn principal_by_id) config(): map hasher(blake2_128_concat)
-            PrincipalId<T> => Principal<CuratorId<T>, ChannelId<T>>;
-
-        /// Next identifier for
-        pub NextPrincipalId get(fn next_principal_id) config(): PrincipalId<T>;
-
-        /// Whether it is currently possible to create a channel via `create_channel` extrinsic.
-        pub ChannelCreationEnabled get(fn channel_creation_enabled) config(): bool;
-
-        /// Recover curator by the role stake which is currently unstaking.
-        pub UnstakerByStakeId get(fn unstaker_by_stake_id) config(): map hasher(blake2_128_concat)
-            StakeId<T> => WorkingGroupUnstaker<LeadId<T>, CuratorId<T>>;
-
-
-        // Vector length input guards
-        pub ChannelHandleConstraint get(fn channel_handle_constraint) config(): InputValidationLengthConstraint;
-        pub ChannelTitleConstraint get(fn channel_title_constraint) config(): InputValidationLengthConstraint;
-        pub ChannelDescriptionConstraint get(fn channel_description_constraint) config(): InputValidationLengthConstraint;
-        pub ChannelAvatarConstraint get(fn channel_avatar_constraint) config(): InputValidationLengthConstraint;
-        pub ChannelBannerConstraint get(fn channel_banner_constraint) config(): InputValidationLengthConstraint;
-        pub OpeningHumanReadableText get(fn opening_human_readable_text) config(): InputValidationLengthConstraint;
-        pub CuratorApplicationHumanReadableText get(fn curator_application_human_readable_text) config(): InputValidationLengthConstraint;
-        pub CuratorExitRationaleText get(fn curator_exit_rationale_text) config(): InputValidationLengthConstraint;
-    }
-    add_extra_genesis {
-        config(mint_capacity): minting::BalanceOf<T>;
-        build(|config: &GenesisConfig<T>| {
-            // create mint
-            let mint_id = <minting::Module<T>>::add_mint(config.mint_capacity, None)
-                .expect("Failed to create a mint for the content working group");
-            Mint::<T>::put(mint_id);
-        });
-    }
-}
-
-decl_event! {
-    pub enum Event<T> where
-        ChannelId = ChannelId<T>,
-        LeadId = LeadId<T>,
-        CuratorOpeningId = CuratorOpeningId<T>,
-        CuratorApplicationId = CuratorApplicationId<T>,
-        CuratorId = CuratorId<T>,
-        CuratorApplicationIdToCuratorIdMap = CuratorApplicationIdToCuratorIdMap<T>,
-        MintBalanceOf = minting::BalanceOf<T>,
-        <T as frame_system::Trait>::AccountId,
-        <T as minting::Trait>::MintId,
-    {
-        ChannelCreated(ChannelId),
-        ChannelOwnershipTransferred(ChannelId),
-        LeadSet(LeadId),
-        LeadUnset(LeadId),
-        CuratorOpeningAdded(CuratorOpeningId),
-        AcceptedCuratorApplications(CuratorOpeningId),
-        BeganCuratorApplicationReview(CuratorOpeningId),
-        CuratorOpeningFilled(CuratorOpeningId, CuratorApplicationIdToCuratorIdMap),
-        TerminatedCurator(CuratorId),
-        AppliedOnCuratorOpening(CuratorOpeningId, CuratorApplicationId),
-        CuratorExited(CuratorId),
-        CuratorUnstaking(CuratorId),
-        CuratorApplicationTerminated(CuratorApplicationId),
-        CuratorApplicationWithdrawn(CuratorApplicationId),
-        CuratorRoleAccountUpdated(CuratorId, AccountId),
-        CuratorRewardAccountUpdated(CuratorId, AccountId),
-        ChannelUpdatedByCurationActor(ChannelId),
-        ChannelCreationEnabledUpdated(bool),
-        MintCapacityIncreased(MintId, MintBalanceOf, MintBalanceOf),
-        MintCapacityDecreased(MintId, MintBalanceOf, MintBalanceOf),
-    }
-}
-
-decl_module! {
-    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
-
-        fn deposit_event() = default;
-
-        /*
-         * Channel management
-         */
-
-        /// Create a new channel.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_channel(
-            origin,
-            owner: T::MemberId,
-            role_account: T::AccountId,
-            content: ChannelContentType,
-            handle: Vec<u8>,
-            title: OptionalText,
-            description: OptionalText,
-            avatar: OptionalText,
-            banner: OptionalText,
-            publication_status: ChannelPublicationStatus
-        ) {
-            // Ensure that owner member is signed and can authenticate with signer account
-            ensure_on_wrapped_error!(
-                membership::Module::<T>::ensure_member_controller_account_signed(
-                    origin,
-                    &owner
-                )
-            )?;
-
-            // Ensure it is currently possible to create channels (ChannelCreationEnabled).
-            ensure!(
-                ChannelCreationEnabled::get(),
-                MSG_CHANNEL_CREATION_DISABLED
-            );
-
-            // Ensure channel handle is acceptable length
-            Self::ensure_channel_handle_is_valid(&handle)?;
-
-            // Ensure title is acceptable length
-            Self::ensure_channel_title_is_valid(&title)?;
-
-            // Ensure description is acceptable length
-            Self::ensure_channel_description_is_valid(&description)?;
-
-            // Ensure avatar URL is acceptable length
-            Self::ensure_channel_avatar_is_valid(&avatar)?;
-
-            // Ensure banner URL is acceptable length
-            Self::ensure_channel_banner_is_valid(&banner)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Make and add new principal
-            let next_channel_id = NextChannelId::<T>::get();
-            let principal_id = Self::add_new_principal(&Principal::ChannelOwner(next_channel_id));
-
-            // Construct channel
-            let new_channel = Channel {
-                verified: false,
-                handle: handle.clone(),
-                title,
-                description,
-                avatar,
-                banner,
-                content,
-                owner,
-                role_account,
-                publication_status,
-                curation_status: ChannelCurationStatus::Normal,
-                created: <frame_system::Module<T>>::block_number(),
-                principal_id,
-            };
-
-            // Add channel to ChannelById under id
-            ChannelById::<T>::insert(next_channel_id, new_channel);
-
-            // Add id to ChannelIdByHandle under handle
-            ChannelIdByHandle::<T>::insert(handle, next_channel_id);
-
-            // Increment NextChannelId
-            NextChannelId::<T>::mutate(|id| *id += <ChannelId<T> as One>::one());
-
-            // CREDENTIAL STUFF //
-
-            // Trigger event
-            Self::deposit_event(RawEvent::ChannelCreated(next_channel_id));
-        }
-
-        /// An owner transfers channel ownership to a new owner.
-        ///
-        /// Notice that working group participants cannot do this.
-        /// Notice that censored or unlisted channel may still be transferred.
-        /// Notice that transfers are unilateral, so new owner cannot block. This may be problematic: https://github.com/Joystream/substrate-runtime-joystream/issues/95
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn transfer_channel_ownership(origin, channel_id: ChannelId<T>, new_owner: T::MemberId, new_role_account: T::AccountId) {
-
-            // Ensure channel owner has signed
-            let channel = Self::ensure_channel_owner_signed(origin, &channel_id)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Construct new channel with altered properties
-            let new_channel = Channel {
-                owner: new_owner,
-                role_account: new_role_account,
-                ..channel
-            };
-
-            // Overwrite entry in ChannelById
-            ChannelById::<T>::insert(channel_id, new_channel);
-
-            // Trigger event
-            Self::deposit_event(RawEvent::ChannelOwnershipTransferred(channel_id));
-        }
-
-        /// Channel owner updates some channel properties
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn update_channel_as_owner(
-            origin,
-            channel_id: ChannelId<T>,
-            new_handle: Option<Vec<u8>>,
-            new_title: Option<OptionalText>,
-            new_description: Option<OptionalText>,
-            new_avatar: Option<OptionalText>,
-            new_banner: Option<OptionalText>,
-            new_publication_status: Option<ChannelPublicationStatus>
-        ) {
-
-            // Ensure channel owner has signed
-            Self::ensure_channel_owner_signed(origin, &channel_id)?;
-
-            // If set, ensure handle is acceptable length
-            if let Some(ref handle) = new_handle {
-                Self::ensure_channel_handle_is_valid(handle)?;
-            }
-
-            // If set, ensure title is acceptable length
-            if let Some(ref title) = new_title {
-                Self::ensure_channel_title_is_valid(title)?;
-            }
-
-            // If set, ensure description is acceptable length
-            if let Some(ref description) = new_description {
-                Self::ensure_channel_description_is_valid(description)?;
-            }
-
-            // If set, ensure avatar image URL is acceptable length
-            if let Some(ref avatar) = new_avatar {
-                Self::ensure_channel_avatar_is_valid(avatar)?;
-            }
-
-            // If set, ensure banner image URL is acceptable length
-            if let Some(ref banner) = new_banner {
-                Self::ensure_channel_banner_is_valid(banner)?;
-            }
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            Self::update_channel(
-                &channel_id,
-                None, // verified
-                &new_handle,
-                &new_title,
-                &new_description,
-                &new_avatar,
-                &new_banner,
-                new_publication_status,
-                None // curation_status
-            );
-        }
-
-        /// Update channel as a curation actor
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn update_channel_as_curation_actor(
-            origin,
-            curation_actor: CurationActor<CuratorId<T>>,
-            channel_id: ChannelId<T>,
-            new_verified: Option<bool>,
-            new_curation_status: Option<ChannelCurationStatus>
-        ) {
-
-            // Ensure curation actor signed
-            Self::ensure_curation_actor_signed(origin, &curation_actor)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            Self::update_channel(
-                &channel_id,
-                new_verified,
-                &None, // handle
-                &None, // title
-                &None, // description,
-                &None, // avatar
-                &None, // banner
-                None, // publication_status
-                new_curation_status
-            );
-        }
-
-        /// Add an opening for a curator role.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn add_curator_opening(origin, activate_at: hiring::ActivateOpeningAt<T::BlockNumber>, commitment: OpeningPolicyCommitment<T::BlockNumber, BalanceOf<T>>, human_readable_text: Vec<u8>)  {
-
-            // Ensure lead is set and is origin signer
-            Self::ensure_origin_is_set_lead(origin)?;
-
-            // Ensure human radable text is valid
-            Self::ensure_opening_human_readable_text_is_valid(&human_readable_text)?;
-
-            // Add opening
-            // NB: This call can in principle fail, because the staking policies
-            // may not respect the minimum currency requirement.
-
-            let policy_commitment = commitment.clone();
-
-            let opening_id = ensure_on_wrapped_error!(
-                hiring::Module::<T>::add_opening(
-                    activate_at,
-                    commitment.max_review_period_length,
-                    commitment.application_rationing_policy,
-                    commitment.application_staking_policy,
-                    commitment.role_staking_policy,
-                    human_readable_text,
-                ))?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            let new_curator_opening_id = NextCuratorOpeningId::<T>::get();
-
-            // Create and add curator opening.
-            let new_opening_by_id = CuratorOpening {
-                opening_id,
-                curator_applications: BTreeSet::new(),
-                policy_commitment,
-            };
-
-            CuratorOpeningById::<T>::insert(new_curator_opening_id, new_opening_by_id);
-
-            // Update NextCuratorOpeningId
-            NextCuratorOpeningId::<T>::mutate(|id| *id += <CuratorOpeningId<T> as One>::one());
-
-            // Trigger event
-            Self::deposit_event(RawEvent::CuratorOpeningAdded(new_curator_opening_id));
-        }
-
-        /// Begin accepting curator applications to an opening that is active.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn accept_curator_applications(origin, curator_opening_id: CuratorOpeningId<T>)  {
-
-            // Ensure lead is set and is origin signer
-            Self::ensure_origin_is_set_lead(origin)?;
-
-            // Ensure opening exists in this working group
-            // NB: Even though call to hiring modul will have implicit check for
-            // existence of opening as well, this check is to make sure that the opening is for
-            // this working group, not something else.
-            let (curator_opening, _opening) = Self::ensure_curator_opening_exists(&curator_opening_id)?;
-
-            // Attempt to begin accepting applicationsa
-            // NB: Combined ensure check and mutation in hiring module
-            ensure_on_wrapped_error!(
-                hiring::Module::<T>::begin_accepting_applications(curator_opening.opening_id)
-                )?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Trigger event
-            Self::deposit_event(RawEvent::AcceptedCuratorApplications(curator_opening_id));
-        }
-
-        /// Begin reviewing, and therefore not accepting new applications.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn begin_curator_applicant_review(origin, curator_opening_id: CuratorOpeningId<T>) {
-
-            // Ensure lead is set and is origin signer
-            let (_lead_id, _lead) = Self::ensure_origin_is_set_lead(origin)?;
-
-            // Ensure opening exists
-            // NB: Even though call to hiring modul will have implicit check for
-            // existence of opening as well, this check is to make sure that the opening is for
-            // this working group, not something else.
-            let (curator_opening, _opening) = Self::ensure_curator_opening_exists(&curator_opening_id)?;
-
-            // Attempt to begin review of applications
-            // NB: Combined ensure check and mutation in hiring module
-            ensure_on_wrapped_error!(
-                hiring::Module::<T>::begin_review(curator_opening.opening_id)
-                )?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Trigger event
-            Self::deposit_event(RawEvent::BeganCuratorApplicationReview(curator_opening_id));
-        }
-
-        /// Fill opening for curator
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn fill_curator_opening(
-            origin,
-            curator_opening_id: CuratorOpeningId<T>,
-            successful_curator_application_ids: CuratorApplicationIdSet<T>,
-            reward_policy: Option<RewardPolicy<minting::BalanceOf<T>, T::BlockNumber>>
-        ) {
-            // Ensure lead is set and is origin signer
-            let (lead_id, _lead) = Self::ensure_origin_is_set_lead(origin)?;
-
-            // Ensure curator opening exists
-            let (curator_opening, _) = Self::ensure_curator_opening_exists(&curator_opening_id)?;
-
-            // Ensure a mint exists if lead is providing a reward for positions being filled
-            let create_reward_settings = if let Some(policy) = reward_policy {
-                // A reward will need to be created so ensure our configured mint exists
-                let mint_id = Self::mint();
-
-                // Technically this is a bug-check and should not be here.
-                ensure!(<minting::Mints<T>>::contains_key(mint_id), MSG_FILL_CURATOR_OPENING_MINT_DOES_NOT_EXIST);
-
-                // Make sure valid parameters are selected for next payment at block number
-                ensure!(policy.next_payment_at_block > <frame_system::Module<T>>::block_number(), MSG_FILL_CURATOR_OPENING_INVALID_NEXT_PAYMENT_BLOCK);
-
-                // The verified reward settings to use
-                Some((mint_id, policy))
-            } else {
-                None
-            };
-
-            // Make iterator over successful curator application
-            let successful_iter = successful_curator_application_ids
-                                    .iter()
-                                    // recover curator application from id
-                                    .map(|curator_application_id| { Self::ensure_curator_application_exists(curator_application_id)})
-                                    // remove Err cases, i.e. non-existing applications
-                                    .filter_map(|result| result.ok());
-
-            // Count number of successful curators provided
-            let num_provided_successful_curator_application_ids = successful_curator_application_ids.len();
-
-            // Ensure all curator applications exist
-            let number_of_successful_applications = successful_iter
-                                                    .clone()
-                                                    .count();
-
-            ensure!(
-                number_of_successful_applications == num_provided_successful_curator_application_ids,
-                MSG_SUCCESSFUL_CURATOR_APPLICATION_DOES_NOT_EXIST
-            );
-
-            // Attempt to fill opening
-            let successful_application_ids = successful_iter
-                                            .clone()
-                                            .map(|(successful_curator_application, _, _)| successful_curator_application.application_id)
-                                            .collect::<BTreeSet<_>>();
-
-            // NB: Combined ensure check and mutation in hiring module
-            ensure_on_wrapped_error!(
-                hiring::Module::<T>::fill_opening(
-                    curator_opening.opening_id,
-                    successful_application_ids,
-                    curator_opening.policy_commitment.fill_opening_successful_applicant_application_stake_unstaking_period,
-                    curator_opening.policy_commitment.fill_opening_failed_applicant_application_stake_unstaking_period,
-                    curator_opening.policy_commitment.fill_opening_failed_applicant_role_stake_unstaking_period
-                )
-            )?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            let current_block = <frame_system::Module<T>>::block_number();
-
-            // For each successful application
-            // - create and hold on to curator
-            // - register role with membership module
-
-            let mut curator_application_id_to_curator_id = BTreeMap::new();
-
-            successful_iter
-            .clone()
-            .for_each(|(successful_curator_application, id, _)| {
-
-                // Create a reward relationship
-                let reward_relationship = if let Some((mint_id, checked_policy)) = create_reward_settings.clone() {
-
-                    // Create a new recipient for the new relationship
-                    let recipient = <recurringrewards::Module<T>>::add_recipient();
-
-                    // member must exist, since it was checked that it can enter the role
-                    let membership = <membership::Module<T>>::membership(successful_curator_application.member_id);
-
-                    // rewards are deposited in the member's root account
-                    let reward_destination_account = membership.root_account;
-
-                    // values have been checked so this should not fail!
-                    let relationship_id = <recurringrewards::Module<T>>::add_reward_relationship(
-                        mint_id,
-                        recipient,
-                        reward_destination_account,
-                        checked_policy.amount_per_payout,
-                        checked_policy.next_payment_at_block,
-                        checked_policy.payout_interval,
-                    ).expect("Failed to create reward relationship!");
-
-                    Some(relationship_id)
-                } else {
-                    None
-                };
-
-                // Get possible stake for role
-                let application = hiring::ApplicationById::<T>::get(successful_curator_application.application_id);
-
-                // Staking profile for curator
-                let stake_profile =
-                    if let Some(ref stake_id) = application.active_role_staking_id {
-
-                        Some(
-                            CuratorRoleStakeProfile::new(
-                                stake_id,
-                                &curator_opening.policy_commitment.terminate_curator_role_stake_unstaking_period,
-                                &curator_opening.policy_commitment.exit_curator_role_stake_unstaking_period
-                            )
-                        )
-                    } else {
-                        None
-                    };
-
-                // Get curator id
-                let new_curator_id = NextCuratorId::<T>::get();
-
-                // Make and add new principal
-                let principal_id = Self::add_new_principal(&Principal::Curator(new_curator_id));
-
-                // Construct curator
-                let curator = Curator::new(
-                    &(successful_curator_application.role_account),
-                    &reward_relationship,
-                    &stake_profile,
-                    &CuratorRoleStage::Active,
-                    &CuratorInduction::new(&lead_id, &id, &current_block),
-                    //false,
-                    &principal_id
-                );
-
-                // Store curator
-                CuratorById::<T>::insert(new_curator_id, curator);
-
-                // Update next curator id
-                NextCuratorId::<T>::mutate(|id| *id += <CuratorId<T> as One>::one());
-
-                curator_application_id_to_curator_id.insert(id, new_curator_id);
-            });
-
-            // Trigger event
-            Self::deposit_event(RawEvent::CuratorOpeningFilled(curator_opening_id, curator_application_id_to_curator_id));
-
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn withdraw_curator_application(
-            origin,
-            curator_application_id: CuratorApplicationId<T>
-        ) {
-            // Ensuring curator application actually exists
-            let (curator_application, _, curator_opening) = Self::ensure_curator_application_exists(&curator_application_id)?;
-
-            // Ensure that it is signed
-            let signer_account = ensure_signed(origin)?;
-
-            // Ensure that signer is applicant role account
-            ensure!(
-                signer_account == curator_application.role_account,
-                MSG_ORIGIN_IS_NOT_APPLICANT
-            );
-
-            // Attempt to deactivate application
-            // NB: Combined ensure check and mutation in hiring module
-            ensure_on_wrapped_error!(
-                hiring::Module::<T>::deactive_application(
-                    curator_application.application_id,
-                    curator_opening.policy_commitment.exit_curator_role_application_stake_unstaking_period,
-                    curator_opening.policy_commitment.exit_curator_role_stake_unstaking_period
-                )
-            )?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Trigger event
-            Self::deposit_event(RawEvent::CuratorApplicationWithdrawn(curator_application_id));
-
-        }
-
-        /// Lead terminate curator application
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn terminate_curator_application(
-            origin,
-            curator_application_id: CuratorApplicationId<T>
-            ) {
-
-            // Ensure lead is set and is origin signer
-            Self::ensure_origin_is_set_lead(origin)?;
-
-            // Ensuring curator application actually exists
-            let (curator_application, _, curator_opening) = Self::ensure_curator_application_exists(&curator_application_id)?;
-
-            // Attempt to deactivate application
-            // NB: Combined ensure check and mutation in hiring module
-            ensure_on_wrapped_error!(
-                hiring::Module::<T>::deactive_application(
-                    curator_application.application_id,
-                    curator_opening.policy_commitment.terminate_curator_application_stake_unstaking_period,
-                    curator_opening.policy_commitment.terminate_curator_role_stake_unstaking_period
-                )
-            )?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Trigger event
-            Self::deposit_event(RawEvent::CuratorApplicationTerminated(curator_application_id));
-        }
-
-        /// Apply on a curator opening.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn apply_on_curator_opening(
-            origin,
-            member_id: T::MemberId,
-            curator_opening_id: CuratorOpeningId<T>,
-            role_account: T::AccountId,
-            opt_role_stake_balance: Option<BalanceOf<T>>,
-            opt_application_stake_balance: Option<BalanceOf<T>>,
-            human_readable_text: Vec<u8>
-        ) {
-            // Ensure origin which will server as the source account for staked funds is signed
-            let source_account = ensure_signed(origin)?;
-
-            // In absense of a more general key delegation system which allows an account with some funds to
-            // grant another account permission to stake from its funds, the origin of this call must have the funds
-            // and cannot specify another arbitrary account as the source account.
-            // Ensure the source_account is either the controller or root account of member with given id
-            ensure!(
-                membership::Module::<T>::ensure_member_controller_account(&source_account, &member_id).is_ok() ||
-                membership::Module::<T>::ensure_member_root_account(&source_account, &member_id).is_ok(),
-                MSG_ORIGIN_IS_NIETHER_MEMBER_CONTROLLER_OR_ROOT
-            );
-
-            // Ensure curator opening exists
-            let (curator_opening, _opening) = Self::ensure_curator_opening_exists(&curator_opening_id)?;
-
-            // Ensure that there is sufficient balance to cover stake proposed
-            Self::ensure_can_make_stake_imbalance(
-                vec![&opt_role_stake_balance, &opt_application_stake_balance],
-                &source_account)
-                .map_err(|_err| MSG_INSUFFICIENT_BALANCE_TO_APPLY)?;
-
-            // Ensure application text is valid
-            Self::ensure_curator_application_text_is_valid(&human_readable_text)?;
-
-            // Ensure application can actually be added
-            ensure_on_wrapped_error!(
-                hiring::Module::<T>::ensure_can_add_application(curator_opening.opening_id, opt_role_stake_balance, opt_application_stake_balance)
-            )?;
-
-            // Ensure member does not have an active application to this opening
-            Self::ensure_member_has_no_active_application_on_opening(
-                curator_opening.curator_applications,
-                member_id
-            )?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Make imbalances for staking
-            let opt_role_stake_imbalance = Self::make_stake_opt_imbalance(&opt_role_stake_balance, &source_account);
-            let opt_application_stake_imbalance = Self::make_stake_opt_imbalance(&opt_application_stake_balance, &source_account);
-
-            // Call hiring module to add application
-            let add_application_result = hiring::Module::<T>::add_application(
-                curator_opening.opening_id,
-                opt_role_stake_imbalance,
-                opt_application_stake_imbalance,
-                human_readable_text
-            );
-
-            // Has to hold
-            assert!(add_application_result.is_ok());
-
-            let application_id = add_application_result.unwrap().application_id_added;
-
-            // Get id of new curator application
-            let new_curator_application_id = NextCuratorApplicationId::<T>::get();
-
-            // Make curator application
-            let curator_application = CuratorApplication::new(&role_account, &curator_opening_id, &member_id, &application_id);
-
-            // Store application
-            CuratorApplicationById::<T>::insert(new_curator_application_id, curator_application);
-
-            // Update next curator application identifier value
-            NextCuratorApplicationId::<T>::mutate(|id| *id += <CuratorApplicationId<T> as One>::one());
-
-            // Add application to set of application in curator opening
-            CuratorOpeningById::<T>::mutate(curator_opening_id, |curator_opening| {
-                curator_opening.curator_applications.insert(new_curator_application_id);
-            });
-
-            // Trigger event
-            Self::deposit_event(RawEvent::AppliedOnCuratorOpening(curator_opening_id, new_curator_application_id));
-        }
-
-        /// An active curator can update the associated role account.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn update_curator_role_account(
-            origin,
-            member_id: T::MemberId,
-            curator_id: CuratorId<T>,
-            new_role_account: T::AccountId
-        ) {
-            // Ensure that origin is signed by member with given id.
-            ensure_on_wrapped_error!(
-                membership::Module::<T>::ensure_member_controller_account_signed(origin, &member_id)
-            )?;
-
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Update role account
-            CuratorById::<T>::mutate(curator_id, |curator| {
-                curator.role_account = new_role_account.clone()
-            });
-
-            // Trigger event
-            Self::deposit_event(RawEvent::CuratorRoleAccountUpdated(curator_id, new_role_account));
-        }
-
-        /// An active curator can update the reward account associated
-        /// with a set reward relationship.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn update_curator_reward_account(
-            origin,
-            curator_id: CuratorId<T>,
-            new_reward_account: T::AccountId
-        ) {
-
-            // Ensure there is a signer which matches role account of curator corresponding to provided id.
-            let curator = Self::ensure_active_curator_signed(origin, &curator_id)?;
-
-            // Ensure the curator actually has a recurring reward
-            let relationship_id = Self::ensure_curator_has_recurring_reward(&curator)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Update, only, reward account.
-            recurringrewards::Module::<T>::set_reward_relationship(
-                relationship_id,
-                Some(new_reward_account.clone()), // new_account
-                None, // new_payout
-                None, //new_next_payment_at
-                None //new_payout_interval
-            )
-            .expect("Must be set, since curator has recurring reward");
-
-            // Trigger event
-            Self::deposit_event(RawEvent::CuratorRewardAccountUpdated(curator_id, new_reward_account));
-        }
-
-        /// An active curator leaves role
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn leave_curator_role(
-            origin,
-            curator_id: CuratorId<T>,
-            rationale_text: Vec<u8>
-        ) {
-            // Ensure there is a signer which matches role account of curator corresponding to provided id.
-            let active_curator = Self::ensure_active_curator_signed(origin, &curator_id)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            Self::deactivate_curator(
-                &curator_id,
-                &active_curator,
-                &CuratorExitInitiationOrigin::Curator,
-                &rationale_text
-            );
-        }
-
-        /// Lead can terminate and active curator
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn terminate_curator_role(
-            origin,
-            curator_id: CuratorId<T>,
-            rationale_text: Vec<u8>
-        ) {
-
-            // Ensure lead is set and is origin signer
-            Self::ensure_origin_is_set_lead(origin)?;
-
-            // Ensuring curator actually exists and is active
-            let curator = Self::ensure_active_curator_exists(&curator_id)?;
-
-            // Ensure rationale text is valid
-            Self::ensure_curator_exit_rationale_text_is_valid(&rationale_text)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            Self::deactivate_curator(
-                &curator_id,
-                &curator,
-                &CuratorExitInitiationOrigin::Lead,
-                &rationale_text
-            );
-        }
-
-        /// Replace the current lead. First unsets the active lead if there is one.
-        /// If a value is provided for new_lead it will then set that new lead.
-        /// It is responsibility of the caller to ensure the new lead can be set
-        /// to avoid the lead role being vacant at the end of the call.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn replace_lead(origin, new_lead: Option<(T::MemberId, T::AccountId)>) {
-            // Ensure root is origin
-            ensure_root(origin)?;
-
-            // Unset current lead first
-            if Self::ensure_lead_is_set().is_ok() {
-                Self::unset_lead()?;
-            }
-
-            // Try to set new lead
-            if let Some((member_id, role_account)) = new_lead {
-                Self::set_lead(member_id, role_account)?;
-            }
-        }
-
-        /// Add an opening for a curator role.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn set_channel_creation_enabled(origin, enabled: bool)  {
-
-            // Ensure lead is set and is origin signer
-            Self::ensure_origin_is_set_lead(origin)?;
-
-            //
-            // == MUTATION SAFE ==
-            //
-
-            // Update storage value
-            ChannelCreationEnabled::put(enabled);
-
-            // Trigger event
-            Self::deposit_event(RawEvent::ChannelCreationEnabledUpdated(enabled));
-        }
-
-        /// Add to capacity of current acive mint.
-        /// This may be deprecated in the future, since set_mint_capacity is sufficient to
-        /// both increase and decrease capacity. Although when considering that it may be executed
-        /// by a proposal, given the temporal delay in approving a proposal, it might be more suitable
-        /// than set_mint_capacity?
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn increase_mint_capacity(
-            origin,
-            additional_capacity: minting::BalanceOf<T>
-        ) {
-            ensure_root(origin)?;
-
-            let mint_id = Self::mint();
-            let mint = <minting::Module<T>>::mints(mint_id); // must exist
-            let new_capacity = mint.capacity() + additional_capacity;
-            <minting::Module<T>>::set_mint_capacity(mint_id, new_capacity).map_err(<&str>::from)?;
-
-            Self::deposit_event(RawEvent::MintCapacityIncreased(
-                mint_id, additional_capacity, new_capacity
-            ));
-        }
-
-        /// Sets the capacity of the current active mint
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn set_mint_capacity(
-            origin,
-            new_capacity: minting::BalanceOf<T>
-        ) {
-            ensure_root(origin)?;
-
-            ensure!(<Mint<T>>::exists(), MSG_FILL_CURATOR_OPENING_MINT_DOES_NOT_EXIST);
-
-            let mint_id = Self::mint();
-
-            // Mint must exist - it is set at genesis
-            let mint = <minting::Module<T>>::mints(mint_id);
-
-            let current_capacity = mint.capacity();
-
-            if new_capacity != current_capacity {
-                // Cannot fail if mint exists
-                <minting::Module<T>>::set_mint_capacity(mint_id, new_capacity).map_err(<&str>::from)?;
-
-                if new_capacity > current_capacity {
-                    Self::deposit_event(RawEvent::MintCapacityIncreased(
-                        mint_id, new_capacity - current_capacity, new_capacity
-                    ));
-                } else {
-                    Self::deposit_event(RawEvent::MintCapacityDecreased(
-                        mint_id, current_capacity - new_capacity, new_capacity
-                    ));
-                }
-            }
-        }
-    }
-}
-
-impl<T: Trait> versioned_store_permissions::CredentialChecker<T> for Module<T> {
-    fn account_has_credential(account: &T::AccountId, id: PrincipalId<T>) -> bool {
-        // Check that principal exists
-        if !PrincipalById::<T>::contains_key(&id) {
-            return false;
-        }
-
-        // Get principal
-        let principal = PrincipalById::<T>::get(&id);
-
-        // Get possible
-        let opt_prinicipal_account = match principal {
-            Principal::Lead => {
-                // Try to get lead
-                match Self::ensure_lead_is_set() {
-                    Ok((_, lead)) => Some(lead.role_account),
-                    Err(_) => None,
-                }
-            }
-
-            Principal::Curator(curator_id) => Some(
-                Self::ensure_curator_exists(&curator_id)
-                    .expect("Curator must exist")
-                    .role_account,
-            ),
-
-            Principal::ChannelOwner(channel_id) => Some(
-                Self::ensure_channel_id_is_valid(&channel_id)
-                    .expect("Channel must exist")
-                    .role_account,
-            ),
-        };
-
-        // Compare, possibly set, principal account with the given account
-        match opt_prinicipal_account {
-            Some(principal_account) => *account == principal_account,
-            None => false,
-        }
-    }
-}
-
-impl<T: Trait> Module<T> {
-    /// Introduce a lead when one is not currently set.
-    fn set_lead(member: T::MemberId, role_account: T::AccountId) -> DispatchResult {
-        // Ensure there is no current lead
-        ensure!(
-            <CurrentLeadId<T>>::get().is_none(),
-            MSG_CURRENT_LEAD_ALREADY_SET
-        );
-
-        let new_lead_id = <NextLeadId<T>>::get();
-
-        //
-        // == MUTATION SAFE ==
-        //
-
-        // Construct lead
-        let new_lead = Lead {
-            member_id: member,
-            role_account,
-            reward_relationship: None,
-            inducted: <frame_system::Module<T>>::block_number(),
-            stage: LeadRoleState::Active,
-        };
-
-        // Store lead
-        <LeadById<T>>::insert(new_lead_id, new_lead);
-
-        // Update current lead
-        <CurrentLeadId<T>>::put(new_lead_id);
-
-        // Update next lead counter
-        <NextLeadId<T>>::mutate(|id| *id += <LeadId<T> as One>::one());
-
-        // Trigger event
-        Self::deposit_event(RawEvent::LeadSet(new_lead_id));
-
-        Ok(())
-    }
-
-    /// Evict the currently set lead
-    fn unset_lead() -> DispatchResult {
-        // Ensure there is a lead set
-        let (lead_id, lead) = Self::ensure_lead_is_set()?;
-
-        //
-        // == MUTATION SAFE ==
-        //
-
-        // Update lead stage as exited
-        let current_block = <frame_system::Module<T>>::block_number();
-
-        let new_lead = Lead {
-            stage: LeadRoleState::Exited(ExitedLeadRole {
-                initiated_at_block_number: current_block,
-            }),
-            ..lead
-        };
-
-        <LeadById<T>>::insert(lead_id, new_lead);
-
-        // Update current lead
-        <CurrentLeadId<T>>::take();
-
-        // Trigger event
-        Self::deposit_event(RawEvent::LeadUnset(lead_id));
-
-        Ok(())
-    }
-
-    fn ensure_member_has_no_active_application_on_opening(
-        curator_applications: CuratorApplicationIdSet<T>,
-        member_id: T::MemberId,
-    ) -> Result<(), &'static str> {
-        for curator_application_id in curator_applications {
-            let curator_application = CuratorApplicationById::<T>::get(curator_application_id);
-            // Look for application by the member for the opening
-            if curator_application.member_id != member_id {
-                continue;
-            }
-            // Get application details
-            let application = <hiring::ApplicationById<T>>::get(curator_application.application_id);
-            // Return error if application is in active stage
-            if application.stage == hiring::ApplicationStage::Active {
-                return Err(MSG_MEMBER_HAS_ACTIVE_APPLICATION_ON_OPENING);
-            }
-        }
-        // Member does not have any active applications to the opening
-        Ok(())
-    }
-
-    // TODO: convert InputConstraint ensurer routines into macroes
-    fn ensure_channel_handle_is_valid(handle: &[u8]) -> DispatchResult {
-        ChannelHandleConstraint::get().ensure_valid(
-            handle.len(),
-            MSG_CHANNEL_HANDLE_TOO_SHORT,
-            MSG_CHANNEL_HANDLE_TOO_LONG,
-        )?;
-
-        // Has to not already be occupied
-        ensure!(
-            !ChannelIdByHandle::<T>::contains_key(handle),
-            MSG_CHANNEL_HANDLE_ALREADY_TAKEN
-        );
-
-        Ok(())
-    }
-
-    fn ensure_channel_title_is_valid(text_opt: &OptionalText) -> DispatchResult {
-        if let Some(text) = text_opt {
-            ChannelTitleConstraint::get().ensure_valid(
-                text.len(),
-                MSG_CHANNEL_TITLE_TOO_SHORT,
-                MSG_CHANNEL_TITLE_TOO_LONG,
-            )
-        } else {
-            Ok(())
-        }
-    }
-
-    fn ensure_channel_description_is_valid(text_opt: &OptionalText) -> DispatchResult {
-        if let Some(text) = text_opt {
-            ChannelDescriptionConstraint::get().ensure_valid(
-                text.len(),
-                MSG_CHANNEL_DESCRIPTION_TOO_SHORT,
-                MSG_CHANNEL_DESCRIPTION_TOO_LONG,
-            )
-        } else {
-            Ok(())
-        }
-    }
-
-    fn ensure_channel_avatar_is_valid(text_opt: &OptionalText) -> DispatchResult {
-        if let Some(text) = text_opt {
-            ChannelAvatarConstraint::get().ensure_valid(
-                text.len(),
-                MSG_CHANNEL_AVATAR_TOO_SHORT,
-                MSG_CHANNEL_AVATAR_TOO_LONG,
-            )
-        } else {
-            Ok(())
-        }
-    }
-
-    fn ensure_channel_banner_is_valid(text_opt: &OptionalText) -> DispatchResult {
-        if let Some(text) = text_opt {
-            ChannelBannerConstraint::get().ensure_valid(
-                text.len(),
-                MSG_CHANNEL_BANNER_TOO_SHORT,
-                MSG_CHANNEL_BANNER_TOO_LONG,
-            )
-        } else {
-            Ok(())
-        }
-    }
-
-    fn ensure_curator_application_text_is_valid(text: &[u8]) -> DispatchResult {
-        CuratorApplicationHumanReadableText::get().ensure_valid(
-            text.len(),
-            MSG_CURATOR_APPLICATION_TEXT_TOO_SHORT,
-            MSG_CURATOR_APPLICATION_TEXT_TOO_LONG,
-        )
-    }
-
-    fn ensure_curator_exit_rationale_text_is_valid(text: &[u8]) -> DispatchResult {
-        CuratorExitRationaleText::get().ensure_valid(
-            text.len(),
-            MSG_CURATOR_EXIT_RATIONALE_TEXT_TOO_SHORT,
-            MSG_CURATOR_EXIT_RATIONALE_TEXT_TOO_LONG,
-        )
-    }
-
-    fn ensure_opening_human_readable_text_is_valid(text: &[u8]) -> DispatchResult {
-        OpeningHumanReadableText::get().ensure_valid(
-            text.len(),
-            MSG_CHANNEL_DESCRIPTION_TOO_SHORT,
-            MSG_CHANNEL_DESCRIPTION_TOO_LONG,
-        )
-    }
-
-    fn ensure_channel_id_is_valid(
-        channel_id: &ChannelId<T>,
-    ) -> Result<Channel<T::MemberId, T::AccountId, T::BlockNumber, PrincipalId<T>>, &'static str>
-    {
-        if ChannelById::<T>::contains_key(channel_id) {
-            let channel = ChannelById::<T>::get(channel_id);
-
-            Ok(channel)
-        } else {
-            Err(MSG_CHANNEL_ID_INVALID)
-        }
-    }
-
-    pub fn ensure_lead_is_set() -> Result<
-        (
-            LeadId<T>,
-            Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber, T::MemberId>,
-        ),
-        &'static str,
-    > {
-        // Ensure lead id is set
-        let lead_id = Self::ensure_lead_id_set()?;
-
-        // If so, grab actual lead
-        let lead = <LeadById<T>>::get(lead_id);
-
-        // and return both
-        Ok((lead_id, lead))
-    }
-
-    fn ensure_lead_id_set() -> Result<LeadId<T>, &'static str> {
-        let opt_current_lead_id = <CurrentLeadId<T>>::get();
-
-        if let Some(lead_id) = opt_current_lead_id {
-            Ok(lead_id)
-        } else {
-            Err(MSG_CURRENT_LEAD_NOT_SET)
-        }
-    }
-
-    fn ensure_origin_is_set_lead(
-        origin: T::Origin,
-    ) -> Result<
-        (
-            LeadId<T>,
-            Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber, T::MemberId>,
-        ),
-        &'static str,
-    > {
-        // Ensure lead is actually set
-        let (lead_id, lead) = Self::ensure_lead_is_set()?;
-
-        // Ensure is signed
-        let signer = ensure_signed(origin)?;
-
-        // Ensure signer is lead
-        ensure!(signer == lead.role_account, MSG_ORIGIN_IS_NOT_LEAD);
-
-        Ok((lead_id, lead))
-    }
-
-    fn ensure_curator_opening_exists(
-        curator_opening_id: &CuratorOpeningId<T>,
-    ) -> Result<
-        (
-            CuratorOpening<T::OpeningId, T::BlockNumber, BalanceOf<T>, CuratorApplicationId<T>>,
-            hiring::Opening<BalanceOf<T>, T::BlockNumber, <T as hiring::Trait>::ApplicationId>,
-        ),
-        &'static str,
-    > {
-        ensure!(
-            CuratorOpeningById::<T>::contains_key(curator_opening_id),
-            MSG_CURATOR_OPENING_DOES_NOT_EXIST
-        );
-
-        let curator_opening = CuratorOpeningById::<T>::get(curator_opening_id);
-
-        let opening = hiring::OpeningById::<T>::get(curator_opening.opening_id);
-
-        Ok((curator_opening, opening))
-    }
-
-    fn ensure_curator_exists(
-        curator_id: &CuratorId<T>,
-    ) -> Result<
-        Curator<
-            T::AccountId,
-            T::RewardRelationshipId,
-            T::StakeId,
-            T::BlockNumber,
-            LeadId<T>,
-            T::ApplicationId,
-            PrincipalId<T>,
-        >,
-        &'static str,
-    > {
-        ensure!(
-            CuratorById::<T>::contains_key(curator_id),
-            MSG_CURATOR_DOES_NOT_EXIST
-        );
-
-        let curator = CuratorById::<T>::get(curator_id);
-
-        Ok(curator)
-    }
-
-    fn ensure_unstaker_exists(
-        stake_id: &StakeId<T>,
-    ) -> Result<WorkingGroupUnstaker<LeadId<T>, CuratorId<T>>, &'static str> {
-        ensure!(
-            UnstakerByStakeId::<T>::contains_key(stake_id),
-            MSG_UNSTAKER_DOES_NOT_EXIST
-        );
-
-        let unstaker = UnstakerByStakeId::<T>::get(stake_id);
-
-        Ok(unstaker)
-    }
-
-    fn ensure_active_curator_exists(
-        curator_id: &CuratorId<T>,
-    ) -> Result<
-        Curator<
-            T::AccountId,
-            T::RewardRelationshipId,
-            T::StakeId,
-            T::BlockNumber,
-            LeadId<T>,
-            T::ApplicationId,
-            PrincipalId<T>,
-        >,
-        &'static str,
-    > {
-        // Ensuring curator actually exists
-        let curator = Self::ensure_curator_exists(curator_id)?;
-
-        // Ensure curator is still active
-        ensure!(
-            matches!(curator.stage, CuratorRoleStage::Active),
-            MSG_CURATOR_IS_NOT_ACTIVE
-        );
-
-        Ok(curator)
-    }
-
-    fn ensure_active_curator_signed(
-        origin: T::Origin,
-        curator_id: &CuratorId<T>,
-    ) -> Result<
-        Curator<
-            T::AccountId,
-            T::RewardRelationshipId,
-            T::StakeId,
-            T::BlockNumber,
-            LeadId<T>,
-            T::ApplicationId,
-            PrincipalId<T>,
-        >,
-        &'static str,
-    > {
-        // Ensure that it is signed
-        let signer_account = ensure_signed(origin)?;
-
-        // Ensure that id corresponds to active curator
-        let curator = Self::ensure_active_curator_exists(&curator_id)?;
-
-        // Ensure that signer is actually role account of curator
-        ensure!(
-            signer_account == curator.role_account,
-            MSG_SIGNER_IS_NOT_CURATOR_ROLE_ACCOUNT
-        );
-
-        Ok(curator)
-    }
-
-    fn ensure_curation_actor_signed(
-        origin: T::Origin,
-        curation_actor: &CurationActor<CuratorId<T>>,
-    ) -> Result<(), &'static str> {
-        match curation_actor {
-            CurationActor::Lead => {
-                // Ensure lead is set and is origin signer
-                Self::ensure_origin_is_set_lead(origin).map(|_| ())
-            }
-            CurationActor::Curator(curator_id) => {
-                // Ensure there is a signer which matches role account of curator corresponding to provided id.
-                Self::ensure_active_curator_signed(origin, &curator_id).map(|_| ())
-            }
-        }
-    }
-
-    /// Ensure origin is signed by account matching role account corresponding to the channel
-    fn ensure_channel_owner_signed(
-        origin: T::Origin,
-        channel_id: &ChannelId<T>,
-    ) -> Result<Channel<T::MemberId, T::AccountId, T::BlockNumber, PrincipalId<T>>, &'static str>
-    {
-        // Ensure that it is signed
-        let signer_account = ensure_signed(origin)?;
-
-        // Ensure channel id is valid
-        let channel = Self::ensure_channel_id_is_valid(&channel_id)?;
-
-        // Ensure origin matches channel role account
-        ensure!(
-            signer_account == channel.role_account,
-            MSG_ORIGIN_DOES_NOT_MATCH_CHANNEL_ROLE_ACCOUNT
-        );
-
-        Ok(channel)
-    }
-
-    fn ensure_curator_application_exists(
-        curator_application_id: &CuratorApplicationId<T>,
-    ) -> Result<
-        (
-            CuratorApplication<T::AccountId, CuratorOpeningId<T>, T::MemberId, T::ApplicationId>,
-            CuratorApplicationId<T>,
-            CuratorOpening<T::OpeningId, T::BlockNumber, BalanceOf<T>, CuratorApplicationId<T>>,
-        ),
-        &'static str,
-    > {
-        ensure!(
-            CuratorApplicationById::<T>::contains_key(curator_application_id),
-            MSG_CURATOR_APPLICATION_DOES_NOT_EXIST
-        );
-
-        let curator_application = CuratorApplicationById::<T>::get(curator_application_id);
-
-        let curator_opening = CuratorOpeningById::<T>::get(curator_application.curator_opening_id);
-
-        Ok((
-            curator_application,
-            *curator_application_id,
-            curator_opening,
-        ))
-    }
-
-    fn ensure_curator_has_recurring_reward(
-        curator: &Curator<
-            T::AccountId,
-            T::RewardRelationshipId,
-            T::StakeId,
-            T::BlockNumber,
-            LeadId<T>,
-            T::ApplicationId,
-            PrincipalId<T>,
-        >,
-    ) -> Result<T::RewardRelationshipId, &'static str> {
-        ensure!(
-            curator.reward_relationship.is_some(),
-            MSG_CURATOR_HAS_NO_REWARD
-        );
-
-        let relationship_id = curator.reward_relationship.unwrap();
-
-        Ok(relationship_id)
-    }
-
-    /// CRITICAL:
-    /// https://github.com/Joystream/substrate-runtime-joystream/issues/92
-    /// This assumes that ensure_can_withdraw can be don
-    /// for a sum of balance that later will be actually withdrawn
-    /// using individual terms in that sum.
-    /// This needs to be fully checked across all possibly scenarios
-    /// of actual balance, minimum balance limit, reservation, vesting and locking.
-    fn ensure_can_make_stake_imbalance(
-        opt_balances: Vec<&Option<BalanceOf<T>>>,
-        source_account: &T::AccountId,
-    ) -> Result<(), &'static str> {
-        let zero_balance = <BalanceOf<T> as Zero>::zero();
-
-        // Total amount to be staked
-        let total_amount = opt_balances.iter().fold(zero_balance, |sum, opt_balance| {
-            sum + if let Some(balance) = opt_balance {
-                *balance
-            } else {
-                zero_balance
-            }
-        });
-
-        if total_amount > zero_balance {
-            // Ensure that
-            if CurrencyOf::<T>::free_balance(source_account) < total_amount {
-                Err(MSG_INSUFFICIENT_BALANCE_TO_COVER_STAKE)
-            } else {
-                let new_balance = CurrencyOf::<T>::free_balance(source_account) - total_amount;
-
-                CurrencyOf::<T>::ensure_can_withdraw(
-                    source_account,
-                    total_amount,
-                    WithdrawReasons::all(),
-                    new_balance,
-                )
-                .map_err(<&str>::from)
-            }
-        } else {
-            Ok(())
-        }
-    }
-
-    fn make_stake_opt_imbalance(
-        opt_balance: &Option<BalanceOf<T>>,
-        source_account: &T::AccountId,
-    ) -> Option<NegativeImbalance<T>> {
-        if let Some(balance) = opt_balance {
-            let withdraw_result = CurrencyOf::<T>::withdraw(
-                source_account,
-                *balance,
-                WithdrawReasons::all(),
-                ExistenceRequirement::AllowDeath,
-            );
-
-            assert!(withdraw_result.is_ok());
-
-            withdraw_result.ok()
-        } else {
-            None
-        }
-    }
-
-    fn deactivate_curator(
-        curator_id: &CuratorId<T>,
-        curator: &Curator<
-            T::AccountId,
-            T::RewardRelationshipId,
-            T::StakeId,
-            T::BlockNumber,
-            LeadId<T>,
-            CuratorApplicationId<T>,
-            PrincipalId<T>,
-        >,
-        exit_initiation_origin: &CuratorExitInitiationOrigin,
-        rationale_text: &[u8],
-    ) {
-        // Stop any possible recurring rewards
-        let _did_deactivate_recurring_reward = if let Some(ref reward_relationship_id) =
-            curator.reward_relationship
-        {
-            // Attempt to deactivate
-            recurringrewards::Module::<T>::try_to_deactivate_relationship(*reward_relationship_id)
-                .expect("Relationship must exist")
-        } else {
-            // Did not deactivate, there was no reward relationship!
-            false
-        };
-
-        // When the curator is staked, unstaking must first be initiated,
-        // otherwise they can be terminated right away.
-
-        // Create exit summary for this termination
-        let current_block = <frame_system::Module<T>>::block_number();
-
-        let curator_exit_summary =
-            CuratorExitSummary::new(exit_initiation_origin, &current_block, rationale_text);
-
-        // Determine new curator stage and event to emit
-        let (new_curator_stage, unstake_directions, event) = if let Some(ref stake_profile) =
-            curator.role_stake_profile
-        {
-            // Determine unstaknig period based on who initiated deactivation
-            let unstaking_period = match curator_exit_summary.origin {
-                CuratorExitInitiationOrigin::Lead => stake_profile.termination_unstaking_period,
-                CuratorExitInitiationOrigin::Curator => stake_profile.exit_unstaking_period,
-            };
-
-            (
-                CuratorRoleStage::Unstaking(curator_exit_summary),
-                Some((stake_profile.stake_id, unstaking_period)),
-                RawEvent::CuratorUnstaking(*curator_id),
-            )
-        } else {
-            (
-                CuratorRoleStage::Exited(curator_exit_summary.clone()),
-                None,
-                match curator_exit_summary.origin {
-                    CuratorExitInitiationOrigin::Lead => RawEvent::TerminatedCurator(*curator_id),
-                    CuratorExitInitiationOrigin::Curator => RawEvent::CuratorExited(*curator_id),
-                },
-            )
-        };
-
-        // Update curator
-        let new_curator = Curator {
-            stage: new_curator_stage,
-            ..(curator.clone())
-        };
-
-        CuratorById::<T>::insert(curator_id, new_curator);
-
-        // Unstake if directions provided
-        if let Some(directions) = unstake_directions {
-            // Keep track of curator unstaking
-            let unstaker = WorkingGroupUnstaker::Curator(*curator_id);
-            UnstakerByStakeId::<T>::insert(directions.0, unstaker);
-
-            // Unstake
-            stake::Module::<T>::initiate_unstaking(&directions.0, directions.1)
-                .expect("Unstaking must be possible at this time");
-        }
-
-        // Trigger event
-        Self::deposit_event(event);
-    }
-
-    /// Adds the given principal to storage under the returned identifier.
-    fn add_new_principal(principal: &Principal<CuratorId<T>, ChannelId<T>>) -> PrincipalId<T> {
-        // Get principal id for curator
-        let principal_id = NextPrincipalId::<T>::get();
-
-        // Update next principal id value
-        NextPrincipalId::<T>::mutate(|id| *id += PrincipalId::<T>::one());
-
-        // Store principal
-        PrincipalById::<T>::insert(principal_id, principal);
-
-        // Return id
-        principal_id
-    }
-
-    fn update_channel(
-        channel_id: &ChannelId<T>,
-        new_verified: Option<bool>,
-        new_handle: &Option<Vec<u8>>,
-        new_title: &Option<OptionalText>,
-        new_description: &Option<OptionalText>,
-        new_avatar: &Option<OptionalText>,
-        new_banner: &Option<OptionalText>,
-        new_publication_status: Option<ChannelPublicationStatus>,
-        new_curation_status: Option<ChannelCurationStatus>,
-    ) {
-        // Update channel id to handle mapping, if there is a new handle.
-        if let Some(ref handle) = new_handle {
-            // Remove mapping under old handle
-            let current_handle = ChannelById::<T>::get(channel_id).handle;
-            ChannelIdByHandle::<T>::remove(current_handle);
-
-            // Establish mapping under new handle
-            ChannelIdByHandle::<T>::insert(handle.clone(), channel_id);
-        }
-
-        // Update channel
-        ChannelById::<T>::mutate(channel_id, |channel| {
-            if let Some(ref verified) = new_verified {
-                channel.verified = *verified;
-            }
-
-            if let Some(ref handle) = new_handle {
-                channel.handle = handle.clone();
-            }
-
-            if let Some(ref title) = new_title {
-                channel.title = title.clone();
-            }
-
-            if let Some(ref description) = new_description {
-                channel.description = description.clone();
-            }
-
-            if let Some(ref avatar) = new_avatar {
-                channel.avatar = avatar.clone();
-            }
-
-            if let Some(ref banner) = new_banner {
-                channel.banner = banner.clone();
-            }
-
-            if let Some(ref publication_status) = new_publication_status {
-                channel.publication_status = publication_status.clone();
-            }
-
-            if let Some(ref curation_status) = new_curation_status {
-                channel.curation_status = *curation_status;
-            }
-        });
-
-        // Trigger event
-        Self::deposit_event(RawEvent::ChannelUpdatedByCurationActor(*channel_id));
-    }
-
-    /// The stake, with the given id, was unstaked. Infalliable. Has no side effects if stake_id is not relevant
-    /// to this module.
-    pub fn unstaked(stake_id: StakeId<T>) {
-        // Ignore if unstaked doesn't exist
-        if !<UnstakerByStakeId<T>>::contains_key(stake_id) {
-            return;
-        }
-
-        // Unstaker must be in this group
-        let unstaker = Self::ensure_unstaker_exists(&stake_id).unwrap();
-
-        // Get curator doing the unstaking,
-        // urrently the only possible unstaker in this module.
-        let curator_id = if let WorkingGroupUnstaker::Curator(curator_id) = unstaker {
-            curator_id
-        } else {
-            panic!("Should not be possible, only curators unstake in this module currently.");
-        };
-
-        // Grab curator from id, unwrap, because this curator _must_ exist.
-        let unstaking_curator = Self::ensure_curator_exists(&curator_id).unwrap();
-
-        //
-        // == MUTATION SAFE ==
-        //
-
-        // Update stage of curator
-        let curator_exit_summary =
-            if let CuratorRoleStage::Unstaking(summary) = unstaking_curator.stage {
-                summary
-            } else {
-                panic!("Curator must be in unstaking stage.");
-            };
-
-        let new_curator = Curator {
-            stage: CuratorRoleStage::Exited(curator_exit_summary.clone()),
-            ..unstaking_curator
-        };
-
-        CuratorById::<T>::insert(curator_id, new_curator);
-
-        // Remove from unstaker
-        UnstakerByStakeId::<T>::remove(stake_id);
-
-        // Trigger event
-        let event = match curator_exit_summary.origin {
-            CuratorExitInitiationOrigin::Lead => RawEvent::TerminatedCurator(curator_id),
-            CuratorExitInitiationOrigin::Curator => RawEvent::CuratorExited(curator_id),
-        };
-
-        Self::deposit_event(event);
-    }
-}

+ 0 - 252
runtime-modules/content-working-group/src/mock.rs

@@ -1,252 +0,0 @@
-#![cfg(test)]
-
-pub use crate::*;
-
-use frame_support::traits::{OnFinalize, OnInitialize};
-use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
-pub use frame_system;
-use sp_core::H256;
-use sp_runtime::{
-    testing::Header,
-    traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
-};
-
-pub use common::currency::GovernanceCurrency;
-pub use hiring;
-pub use membership;
-pub use minting;
-pub use recurringrewards;
-pub use stake;
-pub use versioned_store;
-pub use versioned_store_permissions;
-
-use crate::genesis;
-
-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;
-    pub const ExistentialDeposit: u32 = 0;
-    pub const StakePoolId: [u8; 8] = *b"joystake";
-}
-
-// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct Test;
-
-impl_outer_origin! {
-    pub enum Origin for Test {}
-}
-
-mod lib {
-    pub use crate::Event;
-}
-
-impl_outer_event! {
-    pub enum TestEvent for Test {
-        versioned_store<T>,
-        membership<T>,
-        balances<T>,
-        frame_system<T>,
-        lib<T>,
-    }
-}
-
-pub type RawLibTestEvent = RawEvent<
-    ChannelId<Test>,
-    LeadId<Test>,
-    CuratorOpeningId<Test>,
-    CuratorApplicationId<Test>,
-    CuratorId<Test>,
-    CuratorApplicationIdToCuratorIdMap<Test>,
-    minting::BalanceOf<Test>,
-    <Test as frame_system::Trait>::AccountId,
-    <Test as minting::Trait>::MintId,
->;
-
-pub fn get_last_event_or_panic() -> RawLibTestEvent {
-    if let TestEvent::lib(ref x) = System::events().last().unwrap().event {
-        x.clone()
-    } else {
-        panic!("No event deposited.");
-    }
-}
-
-impl frame_system::Trait for Test {
-    type BaseCallFilter = ();
-    type Origin = Origin;
-    type Call = ();
-    type Index = u64;
-    type BlockNumber = u64;
-    type Hash = H256;
-    type Hashing = BlakeTwo256;
-    type AccountId = u64;
-    type Lookup = IdentityLookup<Self::AccountId>;
-    type Header = Header;
-    type Event = TestEvent;
-    type BlockHashCount = BlockHashCount;
-    type MaximumBlockWeight = MaximumBlockWeight;
-    type DbWeight = ();
-    type BlockExecutionWeight = ();
-    type ExtrinsicBaseWeight = ();
-    type MaximumExtrinsicWeight = ();
-    type MaximumBlockLength = MaximumBlockLength;
-    type AvailableBlockRatio = AvailableBlockRatio;
-    type Version = ();
-    type PalletInfo = ();
-    type AccountData = balances::AccountData<u64>;
-    type OnNewAccount = ();
-    type OnKilledAccount = ();
-    type SystemWeightInfo = ();
-}
-
-impl pallet_timestamp::Trait for Test {
-    type Moment = u64;
-    type OnTimestampSet = ();
-    type MinimumPeriod = MinimumPeriod;
-    type WeightInfo = ();
-}
-
-impl balances::Trait for Test {
-    type Balance = u64;
-    type DustRemoval = ();
-    type Event = TestEvent;
-    type ExistentialDeposit = ExistentialDeposit;
-    type AccountStore = System;
-    type WeightInfo = ();
-    type MaxLocks = ();
-}
-
-impl GovernanceCurrency for Test {
-    type Currency = Balances;
-}
-
-type TestMintId = u64;
-impl minting::Trait for Test {
-    type Currency = Balances;
-    type MintId = TestMintId;
-}
-
-type TestRecipientId = u64;
-type TestRewardRelationshipId = u64;
-impl recurringrewards::Trait for Test {
-    type PayoutStatusHandler = ();
-    type RecipientId = TestRecipientId;
-    type RewardRelationshipId = TestRewardRelationshipId;
-}
-
-type TestStakeId = u64;
-type TestSlashId = u64;
-impl stake::Trait for Test {
-    type Currency = Balances;
-    type StakePoolId = StakePoolId;
-    type StakingEventsHandler = ();
-    type StakeId = TestStakeId;
-    type SlashId = TestSlashId;
-}
-
-type TestOpeningId = u64;
-type TestApplicationId = u64;
-impl hiring::Trait for Test {
-    type OpeningId = TestOpeningId;
-    type ApplicationId = TestApplicationId;
-    type ApplicationDeactivatedHandler = ();
-    type StakeHandlerProvider = hiring::Module<Self>;
-}
-
-impl versioned_store::Trait for Test {
-    type Event = TestEvent;
-}
-
-type TestPrincipalId = u64;
-impl versioned_store_permissions::Trait for Test {
-    type Credential = TestPrincipalId;
-    type CredentialChecker = ();
-    type CreateClassPermissionsChecker = ();
-}
-
-type TestMemberId = u64;
-impl membership::Trait for Test {
-    type Event = TestEvent;
-    type MemberId = TestMemberId;
-    type PaidTermId = u64;
-    type SubscriptionId = u64;
-    type ActorId = u64;
-}
-
-impl Trait for Test {
-    type Event = TestEvent;
-}
-
-pub struct TestExternalitiesBuilder<T: Trait> {
-    system_config: Option<frame_system::GenesisConfig>,
-    membership_config: Option<membership::GenesisConfig<T>>,
-    content_wg_config: Option<GenesisConfig<T>>,
-}
-
-impl<T: Trait> Default for TestExternalitiesBuilder<T> {
-    fn default() -> Self {
-        Self {
-            system_config: None,
-            membership_config: None,
-            content_wg_config: None,
-        }
-    }
-}
-
-impl<T: Trait> TestExternalitiesBuilder<T> {
-    pub fn with_content_wg_config(mut self, conteng_wg_config: GenesisConfig<T>) -> Self {
-        self.content_wg_config = Some(conteng_wg_config);
-        self
-    }
-
-    pub fn build(self) -> sp_io::TestExternalities {
-        // Add system
-        let mut t = self
-            .system_config
-            .unwrap_or(frame_system::GenesisConfig::default())
-            .build_storage::<T>()
-            .unwrap();
-
-        // Add membership
-        self.membership_config
-            .unwrap_or(membership::GenesisConfig::default())
-            .assimilate_storage(&mut t)
-            .unwrap();
-
-        // Add content wg
-        if self.content_wg_config.is_none() {
-            genesis::GenesisConfigBuilder::<Test>::default()
-                .build()
-                .assimilate_storage(&mut t)
-                .unwrap();
-        } else {
-            self.content_wg_config
-                .unwrap()
-                .assimilate_storage(&mut t)
-                .unwrap();
-        }
-
-        t.into()
-    }
-}
-
-pub type System = frame_system::Module<Test>;
-pub type Balances = balances::Module<Test>;
-pub type ContentWorkingGroup = Module<Test>;
-pub type Minting = minting::Module<Test>;
-
-// Recommendation from Parity on testing on_finalize
-// https://substrate.dev/docs/en/next/development/module/tests
-pub fn run_to_block(n: u64) {
-    while System::block_number() < n {
-        <System as OnFinalize<u64>>::on_finalize(System::block_number());
-        <ContentWorkingGroup as OnFinalize<u64>>::on_finalize(System::block_number());
-        System::set_block_number(System::block_number() + 1);
-        <System as OnInitialize<u64>>::on_initialize(System::block_number());
-        <ContentWorkingGroup as OnInitialize<u64>>::on_initialize(System::block_number());
-    }
-}

+ 0 - 2285
runtime-modules/content-working-group/src/tests.rs

@@ -1,2285 +0,0 @@
-#![cfg(test)]
-
-use super::genesis;
-use super::mock::*;
-
-use frame_support::{assert_err, assert_ok, traits::Currency, StorageValue};
-use frame_system::RawOrigin;
-use sp_arithmetic::traits::One;
-use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
-
-use common::constraints::InputValidationLengthConstraint;
-use hiring;
-
-#[test]
-fn create_channel_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-               Events are not emitted on block 0.
-               So any dispatchable calls made during genesis block formation will have no events emitted.
-               https://substrate.dev/recipes/2-appetizers/4-events.html
-            */
-            run_to_block(1);
-
-            // Add channel creator as member
-            let channel_creator_member_root_and_controller_account = 12312;
-
-            let channel_creator_member_id = add_member(
-                channel_creator_member_root_and_controller_account,
-                to_vec(CHANNEL_CREATOR_HANDLE),
-            );
-
-            let fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            fixture.call_and_assert_success();
-        });
-}
-
-#[test]
-fn create_channel_is_not_a_member() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let mut fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            // Change to invalid member id, i.e. != channel_creator_member_id
-            fixture.channel_creator_member_id = fixture.channel_creator_member_id
-                + <<Test as membership::Trait>::MemberId as One>::one();
-
-            fixture.call_and_assert_error(MSG_MEMBER_ID_INVALID);
-        });
-}
-
-#[test]
-fn create_channel_not_enabled() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            add_member_and_set_as_lead();
-
-            set_channel_creation_enabled(false);
-
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            fixture.call_and_assert_error(MSG_CHANNEL_CREATION_DISABLED);
-        });
-}
-
-#[test]
-fn create_channel_with_bad_member_role_account() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                Some(0),
-            );
-
-            fixture.call_and_assert_error(MSG_SIGNER_NOT_CONTROLLER_ACCOUNT);
-        });
-}
-
-#[test]
-fn create_channel_handle_too_long() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let mut fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            fixture.channel_handle =
-                generate_too_long_length_buffer(&ChannelHandleConstraint::get());
-
-            fixture.call_and_assert_error(MSG_CHANNEL_HANDLE_TOO_LONG);
-        });
-}
-
-#[test]
-fn create_channel_handle_too_short() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let mut fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            fixture.channel_handle =
-                generate_too_short_length_buffer(&ChannelHandleConstraint::get());
-
-            fixture.call_and_assert_error(MSG_CHANNEL_HANDLE_TOO_SHORT);
-        });
-}
-
-#[test]
-fn create_channel_description_too_long() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let mut fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            fixture.description = Some(generate_too_long_length_buffer(
-                &ChannelDescriptionConstraint::get(),
-            ));
-
-            fixture.call_and_assert_error(MSG_CHANNEL_DESCRIPTION_TOO_LONG);
-        });
-}
-
-#[test]
-fn create_channel_description_too_short() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let channel_creator_member_id = add_channel_creator_member();
-
-            let mut fixture = CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            );
-
-            fixture.description = Some(generate_too_short_length_buffer(
-                &ChannelDescriptionConstraint::get(),
-            ));
-
-            fixture.call_and_assert_error(MSG_CHANNEL_DESCRIPTION_TOO_SHORT);
-        });
-}
-
-#[test]
-fn transfer_channel_ownership_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-               Events are not emitted on block 0.
-               So any dispatchable calls made during genesis block formation will have no events emitted.
-               https://substrate.dev/recipes/2-appetizers/4-events.html
-            */
-            run_to_block(1);
-
-            // Add channel creator as member
-            let channel_creator_member_root_and_controller_account_1 = 1111;
-            let channel_creator_member_root_and_controller_account_2 = 2222;
-
-            let channel_creator_member_id_1 = add_member(
-                channel_creator_member_root_and_controller_account_1,
-                to_vec(CHANNEL_CREATOR_HANDLE),
-            );
-
-            let channel_creator_member_id_2 = add_member(
-                channel_creator_member_root_and_controller_account_2,
-                to_vec(CHANNEL_CREATOR_HANDLE2),
-            );
-
-            let create_channel_fixture =
-                CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                    channel_creator_member_id_1,
-                    None,
-                );
-
-            let channel_id = create_channel_fixture.call_and_assert_success();
-
-            let original_channel = ChannelById::<Test>::get(channel_id);
-
-            let new_role_account = 3333;
-
-            let transfer_result = ContentWorkingGroup::transfer_channel_ownership(
-                Origin::signed(create_channel_fixture.channel_creator_role_account),
-                channel_id,
-                channel_creator_member_id_2,
-                new_role_account,
-            );
-
-            assert_ok!(transfer_result);
-
-            let updated_channel = ChannelById::<Test>::get(channel_id);
-
-            assert_eq!(
-                updated_channel,
-                Channel {
-                    owner: channel_creator_member_id_2,
-                    role_account: new_role_account,
-                    ..original_channel
-                }
-            );
-        });
-}
-
-#[test]
-fn update_channel_as_owner_success() {}
-
-struct UpdateChannelAsCurationActorFixture {
-    pub origin: Origin,
-    pub curation_actor: CurationActor<CuratorId<Test>>,
-    pub new_verified: Option<bool>,
-    pub new_description: Option<OptionalText>,
-    pub new_curation_status: Option<ChannelCurationStatus>,
-}
-
-impl UpdateChannelAsCurationActorFixture {
-    fn update_channel_as_curation_actor(
-        &self,
-        channel_id: ChannelId<Test>,
-    ) -> Result<(), &'static str> {
-        ContentWorkingGroup::update_channel_as_curation_actor(
-            self.origin.clone(),
-            self.curation_actor.clone(),
-            channel_id,
-            self.new_verified,
-            self.new_curation_status,
-        )
-        .map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_success(&self, channel_id: ChannelId<Test>) {
-        let old_channel = ChannelById::<Test>::get(channel_id);
-
-        let upd_verified = self.new_verified.unwrap_or(old_channel.verified);
-        let upd_description = self
-            .new_description
-            .clone()
-            .unwrap_or(old_channel.description);
-        let upd_curation_status = self
-            .new_curation_status
-            .unwrap_or(old_channel.curation_status);
-
-        let expected_updated_channel = Channel {
-            verified: upd_verified,
-            handle: old_channel.handle,
-            title: old_channel.title,
-            description: upd_description,
-            avatar: old_channel.avatar,
-            banner: old_channel.banner,
-            content: old_channel.content,
-            owner: old_channel.owner,
-            role_account: old_channel.role_account,
-            publication_status: old_channel.publication_status,
-            curation_status: upd_curation_status,
-            created: old_channel.created,
-            principal_id: old_channel.principal_id,
-        };
-
-        // Call and check result
-
-        let call_result = self.update_channel_as_curation_actor(channel_id);
-
-        assert_eq!(call_result, Ok(()));
-
-        // Event triggered
-        let event_channel_id = Self::get_event_deposited();
-
-        assert_eq!(event_channel_id, channel_id);
-
-        // Channel has been updated correctly
-        assert!(ChannelById::<Test>::contains_key(channel_id));
-
-        let updated_channel = ChannelById::<Test>::get(channel_id);
-
-        assert_eq!(updated_channel, expected_updated_channel);
-    }
-
-    fn get_event_deposited() -> crate::ChannelId<Test> {
-        if let mock::TestEvent::lib(ref x) = System::events().last().unwrap().event {
-            if let crate::RawEvent::ChannelUpdatedByCurationActor(ref channel_id) = x {
-                return channel_id.clone();
-            } else {
-                panic!("Event was not ChannelUpdatedByCurationActor.")
-            }
-        } else {
-            panic!("No event deposited.")
-        }
-    }
-}
-
-#[test]
-fn update_channel_as_curation_actor_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            // Add lead and hire curator
-            let curator_params = AddMemberAndApplyOnOpeningParams::new(
-                2222,
-                to_vec("yoyoyo0"), // generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-                2222 * 2,
-                generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-            );
-
-            // Hire curator
-            let setup_and_fill_opening_result =
-                setup_and_fill_opening(&vec![FillOpeningApplicantParams::new(
-                    curator_params.clone(),
-                    true,
-                )]);
-
-            let curator_id = match setup_and_fill_opening_result.application_outomes[0] {
-                FillOpeningApplicantOutcome::Hired { curator_id } => curator_id,
-                _ => panic!(),
-            };
-
-            // Make channel
-            let channel_creator_member_id = add_channel_creator_member();
-            let channel_id = channel_creator_member_id;
-
-            CreateChannelFixture::make_valid_unpulished_video_channel_for(
-                channel_creator_member_id,
-                None,
-            )
-            .call_and_assert_success();
-
-            // Update channel as curator
-            UpdateChannelAsCurationActorFixture {
-                origin: Origin::signed(curator_params.curator_applicant_role_account),
-                curation_actor: CurationActor::Curator(curator_id),
-                new_verified: Some(true),
-                new_description: None, //  don't touch!
-                new_curation_status: Some(ChannelCurationStatus::Censored),
-            }
-            .call_and_assert_success(channel_id);
-        });
-}
-
-#[test]
-fn add_curator_opening_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            add_member_and_set_as_lead();
-
-            let expected_opening_id = hiring::NextOpeningId::<Test>::get();
-
-            let expected_curator_opening_id = NextCuratorOpeningId::<Test>::get();
-
-            /*
-             * Test
-             */
-
-            // Add opening
-            let activate_at = hiring::ActivateOpeningAt::ExactBlock(34);
-
-            let human_readable_text =
-                generate_valid_length_buffer(&OpeningHumanReadableText::get());
-
-            assert_eq!(
-                ContentWorkingGroup::add_curator_opening(
-                    Origin::signed(LEAD_ROLE_ACCOUNT),
-                    activate_at.clone(),
-                    get_baseline_opening_policy(),
-                    human_readable_text.clone()
-                )
-                .unwrap(),
-                ()
-            );
-
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::CuratorOpeningAdded(expected_curator_opening_id)
-            );
-
-            // Assert that given opening id has been added,
-            // and has the right properties.
-            assert!(crate::CuratorOpeningById::<Test>::contains_key(
-                expected_curator_opening_id
-            ));
-
-            let created_curator_opening =
-                crate::CuratorOpeningById::<Test>::get(expected_curator_opening_id);
-
-            let expected_curator_opening = CuratorOpening {
-                opening_id: expected_opening_id,
-                curator_applications: BTreeSet::new(),
-                policy_commitment: get_baseline_opening_policy(),
-            };
-
-            assert_eq!(created_curator_opening, expected_curator_opening);
-
-            // Assert that next id incremented.
-            assert_eq!(
-                crate::NextCuratorOpeningId::<Test>::get(),
-                expected_opening_id + 1
-            );
-
-            /*
-             * TODO: add assertion abouot side-effect in hiring module,
-             * this is where state of application has fundamentally changed.
-             */
-        });
-}
-
-#[test]
-fn accept_curator_applications_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            add_member_and_set_as_lead();
-
-            let curator_opening_id = add_curator_opening();
-
-            /*
-             * Test
-             */
-
-            assert_eq!(
-                ContentWorkingGroup::accept_curator_applications(
-                    Origin::signed(LEAD_ROLE_ACCOUNT),
-                    curator_opening_id
-                )
-                .unwrap(),
-                ()
-            );
-
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::AcceptedCuratorApplications(curator_opening_id)
-            )
-
-            /*
-             * TODO: add assertion abouot side-effect in hiring module,
-             * this is where state of application has fundamentally changed.
-             */
-        });
-}
-
-#[test]
-fn begin_curator_applicant_review_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            let normal_opening_constructed = setup_normal_accepting_opening();
-
-            let _ = add_member_and_apply_on_opening(
-                normal_opening_constructed.curator_opening_id,
-                333,
-                to_vec("CuratorWannabe"),
-                11111,
-                generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-            );
-
-            /*
-             * Test
-             */
-
-            assert_eq!(
-                ContentWorkingGroup::begin_curator_applicant_review(
-                    Origin::signed(LEAD_ROLE_ACCOUNT),
-                    normal_opening_constructed.curator_opening_id
-                )
-                .unwrap(),
-                ()
-            );
-
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::BeganCuratorApplicationReview(
-                    normal_opening_constructed.curator_opening_id
-                )
-            );
-
-            /*
-             * TODO: add assertion abouot side-effect in hiring module,
-             * this is where state of application has fundamentally changed.
-             */
-
-            // Assert opening is in opening stage... hiring::ActiveOpeningStage::ReviewPeriod
-            let opening =
-                <hiring::OpeningById<Test>>::get(&normal_opening_constructed.curator_opening_id);
-            match opening.stage {
-                hiring::OpeningStage::Active { stage, .. } => {
-                    match stage {
-                        hiring::ActiveOpeningStage::ReviewPeriod {
-                            started_review_period_at_block,
-                            ..
-                        } => {
-                            /* OK */
-                            // assert_eq!(started_accepting_applicants_at_block, 0);
-                            assert_eq!(started_review_period_at_block, System::block_number());
-                        }
-                        _ => panic!("ActiveOpeningStage must be in ReviewPeriod"),
-                    }
-                }
-                _ => panic!("OpeningStage must be Active"),
-            };
-        });
-}
-
-#[test]
-fn fill_curator_opening_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            let applicants = vec![
-                FillOpeningApplicantParams::new(
-                    AddMemberAndApplyOnOpeningParams::new(
-                        2222,
-                        to_vec("yoyoyo0"), // generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-                        2222 * 2,
-                        generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-                    ),
-                    true,
-                ),
-                FillOpeningApplicantParams::new(
-                    AddMemberAndApplyOnOpeningParams::new(
-                        3333,
-                        to_vec("yoyoyo1"), // generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-                        3333 * 2,
-                        generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-                    ),
-                    true,
-                ),
-                FillOpeningApplicantParams::new(
-                    AddMemberAndApplyOnOpeningParams::new(
-                        5555,
-                        to_vec("yoyoyo2"), // generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-                        5555 * 2,
-                        generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-                    ),
-                    false,
-                ),
-                FillOpeningApplicantParams::new(
-                    AddMemberAndApplyOnOpeningParams::new(
-                        6666,
-                        to_vec("yoyoyo3"), // generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-                        6666 * 2,
-                        generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-                    ),
-                    true,
-                ),
-            ];
-
-            /*
-             * Exercise and assert
-             */
-
-            setup_and_fill_opening(&applicants);
-        });
-}
-
-#[test]
-fn withdraw_curator_application_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            let normal_opening_constructed = setup_normal_accepting_opening();
-
-            let curator_applicant_root_and_controller_account = 333;
-            let curator_applicant_role_account = 11111;
-            let human_readable_text =
-                generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get());
-
-            let result = add_member_and_apply_on_opening(
-                normal_opening_constructed.curator_opening_id,
-                curator_applicant_root_and_controller_account,
-                to_vec("CuratorWannabe"),
-                curator_applicant_role_account,
-                human_readable_text,
-            );
-
-            /*
-             * Test
-             */
-
-            assert_eq!(
-                ContentWorkingGroup::withdraw_curator_application(
-                    Origin::signed(curator_applicant_role_account),
-                    result.curator_application_id
-                )
-                .unwrap(),
-                ()
-            );
-
-            // Event was triggered
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::CuratorApplicationWithdrawn(result.curator_application_id)
-            );
-
-            /*
-             * TODO: add assertion abouot side-effect in hiring module,
-             * this is where state of application has fundamentally changed.
-             */
-        });
-}
-
-#[test]
-fn terminate_curator_application_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            let normal_opening_constructed = setup_normal_accepting_opening();
-
-            let result = add_member_and_apply_on_opening(
-                normal_opening_constructed.curator_opening_id,
-                333,
-                to_vec("CuratorWannabe"),
-                11111,
-                generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-            );
-
-            /*
-             * Test
-             */
-
-            assert_eq!(
-                ContentWorkingGroup::terminate_curator_application(
-                    Origin::signed(LEAD_ROLE_ACCOUNT),
-                    normal_opening_constructed.curator_opening_id
-                )
-                .unwrap(),
-                ()
-            );
-
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::CuratorApplicationTerminated(result.curator_application_id)
-            );
-
-            /*
-             * TODO: add assertion abouot side-effect in hiring module,
-             * this is where state of application has fundamentally changed.
-             */
-        });
-}
-
-#[test]
-fn apply_on_curator_opening_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            let normal_opening_constructed = setup_normal_accepting_opening();
-
-            // Add curator membership
-
-            let curator_applicant_root_and_controller_account = 72618;
-
-            let curator_applicant_member_id = add_member(
-                curator_applicant_root_and_controller_account,
-                to_vec("IwillTrytoapplyhere"),
-            );
-
-            let curator_applicant_role_account = 8881111;
-
-            let role_stake_balance = get_baseline_opening_policy()
-                .role_staking_policy
-                .unwrap()
-                .amount;
-            let application_stake_balance = get_baseline_opening_policy()
-                .application_staking_policy
-                .unwrap()
-                .amount;
-            let total_balance = role_stake_balance + application_stake_balance;
-
-            let source_account = curator_applicant_root_and_controller_account;
-
-            // Credit staking source account
-            let _ = balances::Module::<Test>::deposit_creating(&source_account, total_balance);
-
-            let human_readable_text = generate_valid_length_buffer(&ChannelHandleConstraint::get());
-
-            let expected_curator_application_id = NextCuratorApplicationId::<Test>::get();
-
-            let old_curator_opening =
-                CuratorOpeningById::<Test>::get(normal_opening_constructed.curator_opening_id);
-
-            let new_curator_application_id = NextCuratorApplicationId::<Test>::get();
-
-            /*
-             * Test
-             */
-
-            assert_eq!(
-                ContentWorkingGroup::apply_on_curator_opening(
-                    Origin::signed(curator_applicant_root_and_controller_account),
-                    curator_applicant_member_id,
-                    normal_opening_constructed.curator_opening_id,
-                    curator_applicant_role_account,
-                    Some(role_stake_balance),
-                    Some(application_stake_balance),
-                    human_readable_text
-                )
-                .unwrap(),
-                ()
-            );
-
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::AppliedOnCuratorOpening(
-                    normal_opening_constructed.curator_opening_id,
-                    new_curator_application_id
-                )
-            );
-
-            assert!(CuratorApplicationById::<Test>::contains_key(
-                new_curator_application_id
-            ));
-
-            // Assert that appropriate application has been added
-            let new_curator_application =
-                CuratorApplicationById::<Test>::get(new_curator_application_id);
-
-            let expected_curator_application = CuratorApplication {
-                role_account: curator_applicant_role_account,
-                curator_opening_id: normal_opening_constructed.curator_opening_id,
-                member_id: curator_applicant_member_id,
-                application_id: expected_curator_application_id,
-            };
-
-            assert_eq!(expected_curator_application, new_curator_application);
-
-            // Assert that the opening has had the application added to application list
-            let mut singleton = BTreeSet::new(); // Unavoidable mutable, BTreeSet can only be populated this way.
-            singleton.insert(new_curator_application_id);
-
-            let new_curator_applications = old_curator_opening
-                .curator_applications
-                .union(&singleton)
-                .cloned()
-                .collect();
-
-            let expected_curator_opening = CuratorOpening {
-                curator_applications: new_curator_applications,
-                ..old_curator_opening
-            };
-
-            let new_curator_opening =
-                CuratorOpeningById::<Test>::get(normal_opening_constructed.curator_opening_id);
-
-            assert_eq!(expected_curator_opening, new_curator_opening);
-        });
-}
-
-#[test]
-fn multiple_applications_by_same_member_to_opening_fails() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-             * Setup
-             */
-
-            let normal_opening_constructed = setup_normal_accepting_opening();
-
-            // Add curator membership
-
-            let curator_applicant_root_and_controller_account = 72618;
-
-            let curator_applicant_member_id = add_member(
-                curator_applicant_root_and_controller_account,
-                to_vec("IwillTrytoapplyhere"),
-            );
-
-            let curator_applicant_role_account = 8881111;
-
-            let role_stake_balance = get_baseline_opening_policy()
-                .role_staking_policy
-                .unwrap()
-                .amount;
-            let application_stake_balance = get_baseline_opening_policy()
-                .application_staking_policy
-                .unwrap()
-                .amount;
-            let total_balance = role_stake_balance + application_stake_balance;
-
-            let source_account = curator_applicant_root_and_controller_account;
-
-            // Credit staking source account with enough funds for two applications,
-            // because we don't want our second application to fail for lack of funds
-            let _ = balances::Module::<Test>::deposit_creating(&source_account, total_balance * 2);
-
-            let human_readable_text = generate_valid_length_buffer(&ChannelHandleConstraint::get());
-
-            /*
-             * Test
-             */
-
-            // First application should work
-            assert_ok!(ContentWorkingGroup::apply_on_curator_opening(
-                Origin::signed(curator_applicant_root_and_controller_account),
-                curator_applicant_member_id,
-                normal_opening_constructed.curator_opening_id,
-                curator_applicant_role_account,
-                Some(role_stake_balance),
-                Some(application_stake_balance),
-                human_readable_text.clone()
-            ));
-
-            // Second application should fail since
-            // first application is still active
-            assert_err!(
-                ContentWorkingGroup::apply_on_curator_opening(
-                    Origin::signed(curator_applicant_root_and_controller_account),
-                    curator_applicant_member_id,
-                    normal_opening_constructed.curator_opening_id,
-                    curator_applicant_role_account,
-                    Some(role_stake_balance),
-                    Some(application_stake_balance),
-                    human_readable_text
-                ),
-                MSG_MEMBER_HAS_ACTIVE_APPLICATION_ON_OPENING
-            );
-        });
-}
-
-struct UpdateCuratorRoleAccountFixture {
-    pub origin: Origin,
-    pub member_id: <Test as membership::Trait>::MemberId,
-    pub curator_id: CuratorId<Test>,
-    pub new_role_account: <Test as frame_system::Trait>::AccountId,
-}
-
-impl UpdateCuratorRoleAccountFixture {
-    fn call(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::update_curator_role_account(
-            self.origin.clone(),
-            self.member_id,
-            self.curator_id,
-            self.new_role_account,
-        )
-        .map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_success(&self) {
-        let original_curator = CuratorById::<Test>::get(self.curator_id);
-
-        let call_result = self.call();
-
-        assert_eq!(call_result, Ok(()));
-
-        let updated_curator = CuratorById::<Test>::get(self.curator_id);
-
-        assert_eq!(
-            crate::Curator {
-                role_account: self.new_role_account,
-                ..original_curator
-            },
-            updated_curator
-        );
-
-        let (event_curator_id, event_new_role_account) =
-            if let mock::TestEvent::lib(ref x) = System::events().last().unwrap().event {
-                if let crate::RawEvent::CuratorRoleAccountUpdated(
-                    ref curator_id,
-                    ref new_role_account,
-                ) = x
-                {
-                    (curator_id.clone(), new_role_account.clone())
-                } else {
-                    panic!("Event was not CuratorRoleAccountUpdated.")
-                }
-            } else {
-                panic!("No event deposited.")
-            };
-
-        assert_eq!(self.curator_id, event_curator_id);
-
-        assert_eq!(self.new_role_account, event_new_role_account);
-    }
-}
-
-#[test]
-fn update_curator_role_account_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let result = setup_lead_and_hire_curator();
-
-            let fixture = UpdateCuratorRoleAccountFixture {
-                origin: Origin::signed(
-                    result
-                        .curator_params()
-                        .curator_applicant_root_and_controller_account,
-                ),
-                member_id: result.curator_member_id(),
-                curator_id: result.curator_id(),
-                new_role_account: 777777,
-            };
-
-            fixture.call_and_assert_success();
-        });
-}
-
-struct UpdateCuratorRewardAccountFixture {
-    pub origin: Origin,
-    pub curator_id: CuratorId<Test>,
-    pub new_reward_account: <Test as frame_system::Trait>::AccountId,
-}
-
-impl UpdateCuratorRewardAccountFixture {
-    #[allow(dead_code)] // delete if the method is unnecessary
-    fn call(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::update_curator_reward_account(
-            self.origin.clone(),
-            self.curator_id,
-            self.new_reward_account,
-        )
-        .map_err(<&str>::from)
-    }
-
-    #[allow(dead_code)] // delete if the method is unnecessary
-    pub fn call_and_assert_success(&self) {
-        let _original_curator = CuratorById::<Test>::get(self.curator_id);
-
-        let call_result = self.call();
-
-        assert_eq!(call_result, Ok(()));
-
-        /*
-            Actually checking new reward account requires checking call to token mint module, but we cannot do that properly yet.
-        */
-
-        let (event_curator_id, event_reward_account) =
-            if let mock::TestEvent::lib(ref x) = System::events().last().unwrap().event {
-                if let crate::RawEvent::CuratorRewardAccountUpdated(
-                    ref curator_id,
-                    ref reward_account,
-                ) = x
-                {
-                    (curator_id.clone(), reward_account.clone())
-                } else {
-                    panic!("Event was not CuratorRewardAccountUpdated.")
-                }
-            } else {
-                panic!("No event deposited.")
-            };
-
-        assert_eq!(self.curator_id, event_curator_id);
-
-        assert_eq!(self.new_reward_account, event_reward_account);
-    }
-}
-
-#[test]
-fn update_curator_reward_account_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let result = setup_lead_and_hire_curator();
-
-            let _fixture = UpdateCuratorRewardAccountFixture {
-                origin: Origin::signed(result.curator_params().curator_applicant_role_account),
-                curator_id: result.curator_id(),
-                new_reward_account: 123321,
-            };
-
-            // TEMPORARILY DISABLED
-            //fixture.call_and_assert_success();
-        });
-}
-
-struct LeaveCuratorRoleFixture {
-    pub origin: Origin,
-    pub curator_id: CuratorId<Test>,
-    pub rationale_text: Vec<u8>,
-}
-
-impl LeaveCuratorRoleFixture {
-    fn call(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::leave_curator_role(
-            self.origin.clone(),
-            self.curator_id,
-            self.rationale_text.clone(),
-        )
-        .map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_success(&self) {
-        let original_curator = CuratorById::<Test>::get(self.curator_id);
-
-        let call_result = self.call();
-
-        assert_eq!(call_result, Ok(()));
-
-        let expected_curator = Curator {
-            stage: CuratorRoleStage::Unstaking(CuratorExitSummary::new(
-                &CuratorExitInitiationOrigin::Curator,
-                &1,
-                &self.rationale_text,
-            )),
-            ..(original_curator.clone())
-        };
-
-        let updated_curator = CuratorById::<Test>::get(self.curator_id);
-
-        assert_eq!(updated_curator, expected_curator);
-
-        assert_eq!(
-            get_last_event_or_panic(),
-            crate::RawEvent::CuratorUnstaking(self.curator_id)
-        );
-
-        // Tracking unstaking
-        let curator_role_stake_id = original_curator.role_stake_profile.unwrap().stake_id;
-
-        assert!(UnstakerByStakeId::<Test>::contains_key(
-            curator_role_stake_id
-        ));
-
-        let unstaker = UnstakerByStakeId::<Test>::get(curator_role_stake_id);
-
-        assert_eq!(unstaker, WorkingGroupUnstaker::Curator(self.curator_id));
-
-        /*
-         * TODO: Missing checks to calls to
-         * recurringrewards, stake
-         */
-    }
-}
-
-#[test]
-fn leave_curator_role_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let result = setup_lead_and_hire_curator();
-
-            let fixture = LeaveCuratorRoleFixture {
-                origin: Origin::signed(result.curator_params().curator_applicant_role_account),
-                curator_id: result.curator_id(),
-                rationale_text: "I am sick of this horrible thing".as_bytes().to_vec(),
-            };
-
-            fixture.call_and_assert_success();
-        });
-}
-
-struct TerminateCuratorRoleFixture {
-    pub origin: Origin,
-    pub curator_id: CuratorId<Test>,
-    pub rationale_text: Vec<u8>,
-}
-
-impl TerminateCuratorRoleFixture {
-    fn call(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::terminate_curator_role(
-            self.origin.clone(),
-            self.curator_id,
-            self.rationale_text.clone(),
-        )
-        .map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_success(&self) {
-        let original_curator = CuratorById::<Test>::get(self.curator_id);
-
-        let call_result = self.call();
-
-        assert_eq!(call_result, Ok(()));
-
-        let expected_curator = Curator {
-            stage: CuratorRoleStage::Unstaking(CuratorExitSummary::new(
-                &CuratorExitInitiationOrigin::Lead,
-                &1,
-                &self.rationale_text,
-            )),
-            ..(original_curator.clone())
-        };
-
-        let updated_curator = CuratorById::<Test>::get(self.curator_id);
-
-        assert_eq!(updated_curator, expected_curator);
-
-        assert_eq!(
-            get_last_event_or_panic(),
-            crate::RawEvent::CuratorUnstaking(self.curator_id)
-        );
-
-        // Tracking unstaking
-        let curator_role_stake_id = original_curator.role_stake_profile.unwrap().stake_id;
-
-        assert!(UnstakerByStakeId::<Test>::contains_key(
-            curator_role_stake_id
-        ));
-
-        let unstaker = UnstakerByStakeId::<Test>::get(curator_role_stake_id);
-
-        assert_eq!(unstaker, WorkingGroupUnstaker::Curator(self.curator_id));
-
-        /*
-         * TODO: Missing checks to calls to
-         * recurringrewards, stake
-         */
-    }
-}
-
-#[test]
-fn terminate_curator_role_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let result = setup_lead_and_hire_curator();
-
-            let fixture = TerminateCuratorRoleFixture {
-                origin: Origin::signed(LEAD_ROLE_ACCOUNT),
-                curator_id: result.curator_id(),
-                rationale_text: "This curator is a joke!".as_bytes().to_vec(),
-            };
-
-            fixture.call_and_assert_success();
-        });
-}
-
-struct SetLeadFixture {
-    pub origin: Origin,
-    pub member_id: <Test as membership::Trait>::MemberId,
-    pub new_role_account: <Test as frame_system::Trait>::AccountId,
-}
-
-impl SetLeadFixture {
-    fn call(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::replace_lead(
-            self.origin.clone(),
-            Some((self.member_id, self.new_role_account)),
-        )
-        .map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_success(&self) {
-        let original_next_lead_id = NextLeadId::<Test>::get();
-
-        let call_result = self.call();
-
-        assert_eq!(call_result, Ok(()));
-
-        let updated_next_lead_id = NextLeadId::<Test>::get();
-
-        assert_eq!(original_next_lead_id + 1, updated_next_lead_id);
-
-        let new_lead_id = if let Some(id) = CurrentLeadId::<Test>::get() {
-            id
-        } else {
-            panic!("Lead not set when it must be.")
-        };
-
-        let new_lead = LeadById::<Test>::get(new_lead_id);
-
-        let expected_new_lead = Lead {
-            member_id: self.member_id,
-            role_account: self.new_role_account,
-            reward_relationship: None,
-            inducted: 1, // make dynamic later
-            stage: LeadRoleState::Active,
-        };
-
-        assert_eq!(new_lead, expected_new_lead);
-
-        assert_eq!(
-            get_last_event_or_panic(),
-            crate::RawEvent::LeadSet(new_lead_id)
-        );
-    }
-}
-
-#[test]
-fn set_lead_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            /*
-               Events are not emitted on block 0.
-               So any dispatchable calls made during genesis block formation will have no events emitted.
-               https://substrate.dev/recipes/2-appetizers/4-events.html
-            */
-            run_to_block(1);
-
-            let member_id =
-                add_member(LEAD_ROOT_AND_CONTROLLER_ACCOUNT, to_vec(LEAD_MEMBER_HANDLE));
-
-            SetLeadFixture {
-                origin: RawOrigin::Root.into(),
-                member_id,
-                new_role_account: 44444,
-            }
-            .call_and_assert_success();
-        });
-}
-
-struct UnsetLeadFixture {
-    pub origin: Origin,
-}
-
-impl UnsetLeadFixture {
-    fn call(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::replace_lead(self.origin.clone(), None).map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_success(&self) {
-        let original_lead_id = CurrentLeadId::<Test>::get().unwrap();
-        let original_lead = LeadById::<Test>::get(original_lead_id);
-
-        let call_result = self.call();
-
-        assert_eq!(call_result, Ok(()));
-
-        assert!(CurrentLeadId::<Test>::get().is_none());
-
-        let updated_lead = LeadById::<Test>::get(original_lead_id);
-
-        let expected_updated_lead = Lead {
-            stage: LeadRoleState::Exited(ExitedLeadRole {
-                initiated_at_block_number: 1,
-            }),
-            ..original_lead
-        };
-
-        assert_eq!(updated_lead, expected_updated_lead);
-
-        assert_eq!(
-            get_last_event_or_panic(),
-            crate::RawEvent::LeadUnset(original_lead_id)
-        );
-    }
-}
-
-#[test]
-fn unset_lead_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let _ = add_member_and_set_as_lead();
-
-            UnsetLeadFixture {
-                origin: RawOrigin::Root.into(),
-            }
-            .call_and_assert_success();
-        });
-}
-
-struct UnstakedFixture {
-    pub stake_id: StakeId<Test>,
-}
-
-impl UnstakedFixture {
-    fn call(&self) {
-        ContentWorkingGroup::unstaked(self.stake_id);
-    }
-
-    pub fn call_and_assert_success(&self) {
-        let unstaker = UnstakerByStakeId::<Test>::get(self.stake_id);
-
-        let curator_id = if let WorkingGroupUnstaker::Curator(curator_id) = unstaker {
-            curator_id
-        } else {
-            panic!("Unstaker not curator")
-        };
-
-        let original_curator = CuratorById::<Test>::get(curator_id);
-
-        let original_exit_summary =
-            if let CuratorRoleStage::Unstaking(exit_summary) = (original_curator.clone()).stage {
-                exit_summary
-            } else {
-                panic!("Curator not unstaking")
-            };
-
-        self.call();
-
-        let expected_curator = Curator {
-            stage: CuratorRoleStage::Exited(original_exit_summary),
-            ..(original_curator.clone())
-        };
-
-        let updated_curator = CuratorById::<Test>::get(curator_id);
-
-        assert_eq!(updated_curator, expected_curator);
-
-        assert_eq!(
-            get_last_event_or_panic(),
-            crate::RawEvent::TerminatedCurator(curator_id)
-        );
-
-        // Unstaker gone
-        assert!(!UnstakerByStakeId::<Test>::contains_key(self.stake_id));
-    }
-
-    // pub fn call_and_assert_failed_result(&self, error_message: &'static str) {
-    //     let call_result = self.call();
-
-    //     assert_eq!(call_result, Err(error_message));
-    // }
-}
-
-#[test]
-fn unstaked_curator_success() {
-    TestExternalitiesBuilder::<Test>::default()
-        .build()
-        .execute_with(|| {
-            let result = setup_lead_and_hire_curator();
-
-            TerminateCuratorRoleFixture {
-                origin: Origin::signed(LEAD_ROLE_ACCOUNT),
-                curator_id: result.curator_id(),
-                rationale_text: "This curator is a joke!".as_bytes().to_vec(),
-            }
-            .call_and_assert_success();
-
-            let curator_role_stake_id = CuratorById::<Test>::get(result.curator_id())
-                .role_stake_profile
-                .unwrap()
-                .stake_id;
-
-            UnstakedFixture {
-                stake_id: curator_role_stake_id,
-            }
-            .call_and_assert_success();
-        });
-}
-
-#[test]
-fn account_can_act_as_principal_success() {}
-
-/*
- * Fixtures
- */
-
-static LEAD_ROOT_AND_CONTROLLER_ACCOUNT: <Test as frame_system::Trait>::AccountId = 1289;
-static LEAD_ROLE_ACCOUNT: <Test as frame_system::Trait>::AccountId = 1289;
-static LEAD_MEMBER_HANDLE: &str = "IamTheLead";
-static CHANNEL_CREATOR_ROOT_AND_CONTROLLER_ACCOUNT: <Test as frame_system::Trait>::AccountId = 11;
-static CHANNEL_CREATOR_HANDLE: &str = "Coolcreator1";
-static CHANNEL_CREATOR_HANDLE2: &str = "Coolcreator2";
-
-fn make_generic_add_member_params() -> AddMemberAndApplyOnOpeningParams {
-    AddMemberAndApplyOnOpeningParams::new(
-        2222,
-        to_vec("yoyoyo0"), // generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-        2222 * 2,
-        generate_valid_length_buffer(&CuratorApplicationHumanReadableText::get()),
-    )
-}
-
-/// Made into function to avoid having to clone every time we read fields
-pub fn get_baseline_opening_policy(
-) -> OpeningPolicyCommitment<<Test as frame_system::Trait>::BlockNumber, BalanceOf<Test>> {
-    OpeningPolicyCommitment {
-        application_rationing_policy: Some(hiring::ApplicationRationingPolicy {
-            max_active_applicants: 5,
-        }),
-        max_review_period_length: 100,
-        application_staking_policy: Some(hiring::StakingPolicy {
-            amount: 40000,
-            amount_mode: hiring::StakingAmountLimitMode::Exact,
-            crowded_out_unstaking_period_length: Some(3),
-            review_period_expired_unstaking_period_length: Some(22),
-        }),
-        role_staking_policy: Some(hiring::StakingPolicy {
-            amount: 900000,
-            amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-            crowded_out_unstaking_period_length: Some(30),
-            review_period_expired_unstaking_period_length: Some(2),
-        }),
-        role_slashing_terms: SlashingTerms::Unslashable,
-
-        fill_opening_successful_applicant_application_stake_unstaking_period: None,
-        fill_opening_failed_applicant_application_stake_unstaking_period: None,
-        fill_opening_failed_applicant_role_stake_unstaking_period: None,
-        terminate_curator_application_stake_unstaking_period: None,
-        terminate_curator_role_stake_unstaking_period: None,
-        exit_curator_role_application_stake_unstaking_period: None,
-        exit_curator_role_stake_unstaking_period: None,
-    }
-}
-
-pub fn to_vec(s: &str) -> Vec<u8> {
-    s.as_bytes().to_vec()
-}
-
-// MOVE THIS LATER WHEN fill_opening is factored out
-#[derive(Clone)]
-pub struct FillOpeningApplicantParams {
-    pub add_and_apply_params: AddMemberAndApplyOnOpeningParams,
-    pub hire: bool,
-}
-
-impl FillOpeningApplicantParams {
-    pub fn new(add_and_apply_params: AddMemberAndApplyOnOpeningParams, hire: bool) -> Self {
-        Self {
-            add_and_apply_params: add_and_apply_params.clone(),
-            hire: hire,
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct AddMemberAndApplyOnOpeningParams {
-    pub curator_applicant_root_and_controller_account: <Test as frame_system::Trait>::AccountId,
-    pub handle: Vec<u8>,
-    pub curator_applicant_role_account: <Test as frame_system::Trait>::AccountId,
-    pub human_readable_text: Vec<u8>,
-}
-
-impl AddMemberAndApplyOnOpeningParams {
-    pub fn new(
-        curator_applicant_root_and_controller_account: <Test as frame_system::Trait>::AccountId,
-        handle: Vec<u8>,
-        curator_applicant_role_account: <Test as frame_system::Trait>::AccountId,
-        human_readable_text: Vec<u8>,
-    ) -> Self {
-        Self {
-            curator_applicant_root_and_controller_account,
-            handle,
-            curator_applicant_role_account,
-            human_readable_text,
-        }
-    }
-}
-
-fn add_members_and_apply_on_opening(
-    curator_opening_id: CuratorOpeningId<Test>,
-    applicants: &Vec<AddMemberAndApplyOnOpeningParams>,
-) -> Vec<NewMemberAppliedResult> {
-    applicants
-        .iter()
-        .cloned()
-        .map(|params| -> NewMemberAppliedResult {
-            add_member_and_apply_on_opening(
-                curator_opening_id,
-                params.curator_applicant_root_and_controller_account,
-                params.handle,
-                params.curator_applicant_role_account,
-                params.human_readable_text,
-            )
-        })
-        .collect()
-}
-
-#[derive(Clone)]
-struct NewMemberAppliedResult {
-    pub member_id: <Test as membership::Trait>::MemberId,
-    pub curator_application_id: crate::CuratorApplicationId<Test>,
-}
-
-fn add_member_and_apply_on_opening(
-    curator_opening_id: CuratorOpeningId<Test>,
-    curator_applicant_root_and_controller_account: <Test as frame_system::Trait>::AccountId,
-    handle: Vec<u8>,
-    curator_applicant_role_account: <Test as frame_system::Trait>::AccountId,
-    human_readable_text: Vec<u8>,
-) -> NewMemberAppliedResult {
-    // Make membership
-    let curator_applicant_member_id =
-        add_member(curator_applicant_root_and_controller_account, handle);
-
-    // Guarantee sufficient stake
-    let role_stake_balance = if let Some(policy) = get_baseline_opening_policy().role_staking_policy
-    {
-        policy.amount
-    } else {
-        0
-    };
-
-    let application_stake_balance =
-        if let Some(policy) = get_baseline_opening_policy().application_staking_policy {
-            policy.amount
-        } else {
-            0
-        };
-
-    let total_balance = role_stake_balance + application_stake_balance;
-
-    let source_account = curator_applicant_root_and_controller_account;
-
-    // Credit staking source account if required
-    if total_balance > 0 {
-        let _ = balances::Module::<Test>::deposit_creating(&source_account, total_balance);
-    }
-
-    let expected_hiring_application_id = <hiring::NextApplicationId<Test>>::get();
-
-    let old_curator_opening = CuratorOpeningById::<Test>::get(curator_opening_id);
-
-    let new_curator_application_id = NextCuratorApplicationId::<Test>::get();
-
-    /*
-     * Test
-     */
-
-    assert_eq!(
-        ContentWorkingGroup::apply_on_curator_opening(
-            Origin::signed(curator_applicant_root_and_controller_account),
-            curator_applicant_member_id,
-            curator_opening_id,
-            curator_applicant_role_account,
-            Some(role_stake_balance),
-            Some(application_stake_balance),
-            human_readable_text
-        )
-        .unwrap(),
-        ()
-    );
-
-    assert_eq!(
-        get_last_event_or_panic(),
-        crate::RawEvent::AppliedOnCuratorOpening(curator_opening_id, new_curator_application_id)
-    );
-
-    assert!(CuratorApplicationById::<Test>::contains_key(
-        new_curator_application_id
-    ));
-
-    // Assert that appropriate application has been added
-    let new_curator_application = CuratorApplicationById::<Test>::get(new_curator_application_id);
-
-    let expected_curator_application = CuratorApplication {
-        role_account: curator_applicant_role_account,
-        curator_opening_id: curator_opening_id,
-        member_id: curator_applicant_member_id,
-        application_id: expected_hiring_application_id,
-    };
-
-    assert_eq!(expected_curator_application, new_curator_application);
-
-    // Assert that the opening has had the application added to application list
-    let mut singleton = BTreeSet::new(); // Unavoidable mutable, BTreeSet can only be populated this way.
-    singleton.insert(new_curator_application_id);
-
-    let new_curator_applications = old_curator_opening
-        .curator_applications
-        .union(&singleton)
-        .cloned()
-        .collect();
-
-    let expected_curator_opening = CuratorOpening {
-        curator_applications: new_curator_applications,
-        ..old_curator_opening
-    };
-
-    let new_curator_opening = CuratorOpeningById::<Test>::get(curator_opening_id);
-
-    assert_eq!(expected_curator_opening, new_curator_opening);
-
-    NewMemberAppliedResult {
-        member_id: curator_applicant_member_id,
-        curator_application_id: new_curator_application_id,
-    }
-}
-
-struct NormalOpeningConstructed {
-    pub curator_opening_id: CuratorOpeningId<Test>,
-    pub new_member_as_lead: NewMemberAsLead,
-}
-
-fn setup_normal_opening() -> NormalOpeningConstructed {
-    let new_member_as_lead = add_member_and_set_as_lead();
-
-    let expected_curator_opening_id = NextCuratorOpeningId::<Test>::get();
-
-    assert_eq!(
-        ContentWorkingGroup::add_curator_opening(
-            Origin::signed(LEAD_ROLE_ACCOUNT),
-            hiring::ActivateOpeningAt::ExactBlock(34),
-            get_baseline_opening_policy(),
-            generate_valid_length_buffer(&OpeningHumanReadableText::get())
-        )
-        .unwrap(),
-        ()
-    );
-
-    assert_eq!(
-        get_last_event_or_panic(),
-        crate::RawEvent::CuratorOpeningAdded(expected_curator_opening_id)
-    );
-
-    NormalOpeningConstructed {
-        curator_opening_id: expected_curator_opening_id,
-        new_member_as_lead,
-    }
-}
-
-fn setup_normal_accepting_opening() -> NormalOpeningConstructed {
-    let normal_opening_constructed = setup_normal_opening();
-
-    assert_eq!(
-        ContentWorkingGroup::accept_curator_applications(
-            Origin::signed(LEAD_ROLE_ACCOUNT), // <== get dynamic value from somewhere else later
-            normal_opening_constructed.curator_opening_id
-        )
-        .unwrap(),
-        ()
-    );
-
-    normal_opening_constructed
-}
-
-struct SetupOpeningInReview {
-    //pub curator_opening_id: lib::CuratorOpeningId<Test>,
-    pub normal_opening_constructed: NormalOpeningConstructed,
-    pub added_members_application_result: Vec<NewMemberAppliedResult>,
-}
-
-fn setup_opening_in_review(
-    applicants: &Vec<AddMemberAndApplyOnOpeningParams>,
-) -> SetupOpeningInReview {
-    let normal_opening_constructed = setup_normal_accepting_opening();
-
-    let added_members_application_result =
-        add_members_and_apply_on_opening(normal_opening_constructed.curator_opening_id, applicants);
-
-    assert_eq!(
-        ContentWorkingGroup::begin_curator_applicant_review(
-            Origin::signed(LEAD_ROLE_ACCOUNT),
-            normal_opening_constructed.curator_opening_id
-        )
-        .unwrap(),
-        ()
-    );
-
-    // TODO: assert event stuff !!!!
-
-    SetupOpeningInReview {
-        normal_opening_constructed,
-        added_members_application_result,
-    }
-}
-
-enum FillOpeningApplicantOutcome {
-    NotHired,
-    Hired { curator_id: CuratorId<Test> },
-}
-
-struct SetupAndFillOpeningResult {
-    setup_opening_in_review: SetupOpeningInReview,
-    application_outomes: Vec<FillOpeningApplicantOutcome>,
-}
-
-fn setup_and_fill_opening(
-    applicants: &Vec<FillOpeningApplicantParams>,
-) -> SetupAndFillOpeningResult {
-    let setup_opening_params = applicants
-        .iter()
-        .cloned()
-        .map(|param| param.add_and_apply_params)
-        .collect::<Vec<_>>();
-
-    let setup_opening_in_review = setup_opening_in_review(&setup_opening_params);
-
-    let curator_opening = CuratorOpeningById::<Test>::get(
-        setup_opening_in_review
-            .normal_opening_constructed
-            .curator_opening_id,
-    );
-
-    // Set whom to hire
-    let applicants_to_hire_iter = applicants.iter().filter(|params| params.hire);
-
-    let num_applicants_hired = applicants_to_hire_iter.cloned().count();
-
-    let hired_applicant_and_result = setup_opening_in_review
-        .added_members_application_result
-        .iter()
-        .zip(applicants.iter())
-        .filter(|(_, fill_opening_applicant_params)| fill_opening_applicant_params.hire)
-        .collect::<Vec<_>>();
-
-    let successful_curator_application_ids = hired_applicant_and_result
-        .iter()
-        .map(|(new_member_applied_result, _)| new_member_applied_result.curator_application_id)
-        .collect::<BTreeSet<_>>();
-
-    // Remember original id counters
-    let original_next_curator_id = NextCuratorId::<Test>::get();
-    let original_next_principal_id = NextPrincipalId::<Test>::get();
-
-    /*
-     * Call
-     */
-
-    assert_eq!(
-        ContentWorkingGroup::fill_curator_opening(
-            Origin::signed(LEAD_ROLE_ACCOUNT),
-            setup_opening_in_review
-                .normal_opening_constructed
-                .curator_opening_id,
-            successful_curator_application_ids.clone(),
-            None
-        ),
-        Ok(())
-    );
-
-    /*
-     * Asserts
-     */
-
-    let successful_curator_application_id_to_curator_id = successful_curator_application_ids
-        .iter()
-        .enumerate()
-        .map(
-            |(index, item)| -> (CuratorApplicationId<Test>, CuratorId<Test>) {
-                let curator_id = original_next_curator_id + (index as CuratorId<Test>);
-
-                (*item, curator_id)
-            },
-        )
-        .collect::<BTreeMap<_, _>>();
-
-    assert_eq!(
-        get_last_event_or_panic(),
-        crate::RawEvent::CuratorOpeningFilled(
-            setup_opening_in_review
-                .normal_opening_constructed
-                .curator_opening_id,
-            successful_curator_application_id_to_curator_id
-        )
-    );
-
-    // The right number of curators have been created
-    let num_curators_created = NextCuratorId::<Test>::get() - original_next_curator_id;
-
-    assert_eq!(num_curators_created, (num_applicants_hired as u64));
-
-    // The right numbe of prinipals were created
-    let num_principals_created = NextPrincipalId::<Test>::get() - original_next_principal_id;
-
-    assert_eq!(num_principals_created, (num_applicants_hired as u64));
-
-    // Inspect all expected curators and principal added
-    for (hired_index, item) in hired_applicant_and_result.iter().enumerate() {
-        let (new_member_applied_result, fill_opening_applicant_params) = item;
-
-        // Curator
-        let expected_added_curator_id: u64 = (hired_index as u64) + original_next_curator_id;
-
-        // Principal
-        let expected_added_principal_id: u64 = (hired_index as u64) + original_next_principal_id;
-
-        // Curator added
-        assert!(CuratorById::<Test>::contains_key(expected_added_curator_id));
-
-        let added_curator = CuratorById::<Test>::get(expected_added_curator_id);
-
-        // expected_curator
-        let reward_relationship = None::<<Test as recurringrewards::Trait>::RewardRelationshipId>;
-
-        let curator_application =
-            CuratorApplicationById::<Test>::get(new_member_applied_result.curator_application_id);
-        let application_id = curator_application.application_id;
-        let application = hiring::ApplicationById::<Test>::get(application_id);
-
-        let role_stake_profile = if let Some(ref stake_id) = application.active_role_staking_id {
-            // get_baseline_opening_policy().role_staking_policy {
-
-            Some(CuratorRoleStakeProfile::new(
-                stake_id,
-                &curator_opening
-                    .policy_commitment
-                    .terminate_curator_role_stake_unstaking_period,
-                &curator_opening
-                    .policy_commitment
-                    .exit_curator_role_stake_unstaking_period,
-            ))
-        } else {
-            None
-        };
-
-        let expected_curator = Curator {
-            role_account: fill_opening_applicant_params
-                .add_and_apply_params
-                .curator_applicant_role_account,
-            reward_relationship: reward_relationship,
-            role_stake_profile: role_stake_profile, //  added_curator.role_stake_profile.clone(),
-            stage: CuratorRoleStage::Active,
-            induction: CuratorInduction::new(
-                &setup_opening_in_review
-                    .normal_opening_constructed
-                    .new_member_as_lead
-                    .lead_id,
-                &new_member_applied_result.curator_application_id,
-                &1,
-            ),
-            principal_id: expected_added_principal_id,
-        };
-
-        assert_eq!(expected_curator, added_curator);
-
-        // Principal added
-        assert!(PrincipalById::<Test>::contains_key(
-            expected_added_principal_id
-        ));
-
-        let added_principal = PrincipalById::<Test>::get(expected_added_principal_id);
-
-        let expected_added_principal = Principal::Curator(expected_added_principal_id);
-
-        assert_eq!(added_principal, expected_added_principal);
-    }
-
-    /*
-     * TODO: add assertion abouot side-effect in !hiring & membership! module,
-     * this is where state of application has fundamentally changed.
-     */
-
-    let application_outomes = applicants
-        .iter()
-        .enumerate()
-        .map(|(index, params)| {
-            if params.hire {
-                FillOpeningApplicantOutcome::Hired {
-                    curator_id: (index as u64) + original_next_curator_id,
-                }
-            } else {
-                FillOpeningApplicantOutcome::NotHired
-            }
-        })
-        .collect::<Vec<_>>();
-
-    SetupAndFillOpeningResult {
-        setup_opening_in_review,
-        application_outomes,
-    }
-}
-
-struct SetupLeadAndHireCuratorResult {
-    pub curator_params: AddMemberAndApplyOnOpeningParams,
-    pub setup_and_fill_opening_result: SetupAndFillOpeningResult,
-}
-
-impl SetupLeadAndHireCuratorResult {
-    fn curator_params(&self) -> AddMemberAndApplyOnOpeningParams {
-        self.curator_params.clone()
-    }
-
-    pub fn curator_id(&self) -> CuratorId<Test> {
-        match self.setup_and_fill_opening_result.application_outomes[0] {
-            FillOpeningApplicantOutcome::Hired { curator_id } => curator_id,
-            _ => panic!(),
-        }
-    }
-
-    pub fn curator_member_id(&self) -> <Test as membership::Trait>::MemberId {
-        self.setup_and_fill_opening_result
-            .setup_opening_in_review
-            .added_members_application_result[0]
-            .member_id
-    }
-}
-
-fn setup_lead_and_hire_curator() -> SetupLeadAndHireCuratorResult {
-    let curator_params = make_generic_add_member_params();
-
-    // Hire curator
-    let setup_and_fill_opening_result =
-        setup_and_fill_opening(&vec![FillOpeningApplicantParams::new(
-            curator_params.clone(),
-            true,
-        )]);
-
-    SetupLeadAndHireCuratorResult {
-        curator_params,
-        setup_and_fill_opening_result,
-    }
-}
-
-struct CreateChannelFixture {
-    pub channel_creator_member_id: <Test as membership::Trait>::MemberId,
-    pub controller_account: <Test as frame_system::Trait>::AccountId,
-    pub channel_creator_role_account: <Test as frame_system::Trait>::AccountId,
-    pub channel_handle: Vec<u8>,
-    pub channel_title: OptionalText,
-    pub description: OptionalText,
-    pub avatar: OptionalText,
-    pub banner: OptionalText,
-    pub content: ChannelContentType,
-    pub publication_status: ChannelPublicationStatus,
-}
-
-impl CreateChannelFixture {
-    pub fn make_valid_unpulished_video_channel_for(
-        channel_creator_member_id: <Test as membership::Trait>::MemberId,
-        override_controller_account: Option<<Test as frame_system::Trait>::AccountId>,
-    ) -> Self {
-        let controller_account = if let Some(account) = override_controller_account {
-            account
-        } else {
-            membership::Module::<Test>::ensure_membership(channel_creator_member_id)
-                .unwrap()
-                .controller_account
-        };
-
-        Self {
-            channel_creator_member_id,
-            controller_account,
-            channel_creator_role_account: 527489,
-            channel_handle: generate_valid_length_buffer(&ChannelHandleConstraint::get()),
-            channel_title: Some(generate_valid_length_buffer(&ChannelTitleConstraint::get())),
-            avatar: Some(generate_valid_length_buffer(&ChannelAvatarConstraint::get())),
-            banner: Some(generate_valid_length_buffer(&ChannelBannerConstraint::get())),
-            description: Some(generate_valid_length_buffer(
-                &ChannelDescriptionConstraint::get(),
-            )),
-            content: ChannelContentType::Video,
-            publication_status: ChannelPublicationStatus::Unlisted,
-        }
-    }
-
-    fn create_channel(&self) -> Result<(), &'static str> {
-        ContentWorkingGroup::create_channel(
-            Origin::signed(self.controller_account),
-            self.channel_creator_member_id,
-            self.channel_creator_role_account,
-            self.content.clone(),
-            self.channel_handle.clone(),
-            self.channel_title.clone(),
-            self.description.clone(),
-            self.avatar.clone(),
-            self.banner.clone(),
-            self.publication_status.clone(),
-        )
-        .map_err(<&str>::from)
-    }
-
-    pub fn call_and_assert_error(&self, err_message: &'static str) {
-        let number_of_events_before_call = System::events().len();
-
-        let call_result = self.create_channel();
-
-        assert_eq!(call_result, Err(err_message));
-
-        // No new events deposited
-        assert_eq!(System::events().len(), number_of_events_before_call);
-    }
-
-    pub fn call_and_assert_success(&self) -> ChannelId<Test> {
-        let old_next_channel_id = NextChannelId::<Test>::get();
-
-        let call_result = self.create_channel();
-
-        // Call result was Ok
-        assert_eq!(call_result, Ok(()));
-
-        // Assert that event was triggered,
-        // keep channel id.
-        assert_eq!(
-            get_last_event_or_panic(),
-            crate::RawEvent::ChannelCreated(old_next_channel_id)
-        );
-
-        let channel_id = old_next_channel_id;
-
-        // Assert that given channel id has been added,
-        // and has the right properties.
-        assert!(crate::ChannelById::<Test>::contains_key(channel_id));
-
-        let created_channel = crate::ChannelById::<Test>::get(channel_id);
-
-        let expected_channel = Channel {
-            verified: false,
-            handle: self.channel_handle.clone(),
-            title: self.channel_title.clone(),
-            avatar: self.avatar.clone(),
-            banner: self.banner.clone(),
-            description: self.description.clone(),
-            content: self.content.clone(),
-            owner: self.channel_creator_member_id,
-            role_account: self.channel_creator_role_account,
-            publication_status: self.publication_status.clone(),
-            curation_status: ChannelCurationStatus::Normal,
-            created: 1, // <== replace with now()
-
-            // We have no expectation here, so we just copy what was added
-            principal_id: created_channel.principal_id,
-        };
-
-        assert_eq!(created_channel, expected_channel);
-
-        // Assert that next id incremented.
-        assert_eq!(crate::NextChannelId::<Test>::get(), channel_id + 1);
-
-        // Assert that there is a mapping established for handle
-        assert_eq!(
-            crate::ChannelIdByHandle::<Test>::get(self.channel_handle.clone()),
-            channel_id
-        );
-
-        // Check that principal actually has been added
-        assert!(crate::PrincipalById::<Test>::contains_key(
-            created_channel.principal_id
-        ));
-
-        let created_principal = crate::PrincipalById::<Test>::get(created_channel.principal_id);
-
-        assert!(match created_principal {
-            Principal::Lead => false,
-            Principal::Curator(_) => false,
-            Principal::ChannelOwner(created_principal_channel_id) =>
-                created_principal_channel_id == channel_id,
-        });
-
-        channel_id
-    }
-}
-
-struct NewMemberAsLead {
-    pub member_id: <Test as membership::Trait>::MemberId,
-    pub lead_id: LeadId<Test>,
-}
-
-fn add_member_and_set_as_lead() -> NewMemberAsLead {
-    let member_id = add_member(LEAD_ROOT_AND_CONTROLLER_ACCOUNT, to_vec(LEAD_MEMBER_HANDLE));
-
-    let lead_id = set_lead(member_id, LEAD_ROLE_ACCOUNT);
-
-    NewMemberAsLead { member_id, lead_id }
-}
-
-pub fn set_channel_creation_enabled(enabled: bool) {
-    crate::Module::<Test>::set_channel_creation_enabled(Origin::signed(LEAD_ROLE_ACCOUNT), enabled)
-        .unwrap()
-}
-
-pub fn add_channel_creator_member() -> <Test as membership::Trait>::MemberId {
-    let channel_creator_member_id = add_member(
-        CHANNEL_CREATOR_ROOT_AND_CONTROLLER_ACCOUNT,
-        to_vec(CHANNEL_CREATOR_HANDLE),
-    );
-
-    channel_creator_member_id
-}
-
-pub fn add_member(
-    root_and_controller_account: <Test as frame_system::Trait>::AccountId,
-    handle: Vec<u8>,
-) -> <Test as membership::Trait>::MemberId {
-    let next_member_id = membership::NextMemberId::<Test>::get();
-
-    assert_eq!(
-        membership::Module::<Test>::buy_membership(
-            Origin::signed(root_and_controller_account),
-            0,
-            Some(handle),
-            None,
-            None
-        )
-        .unwrap(),
-        ()
-    );
-
-    next_member_id
-}
-
-pub fn set_lead(
-    member_id: <Test as membership::Trait>::MemberId,
-    new_role_account: <Test as frame_system::Trait>::AccountId,
-) -> LeadId<Test> {
-    /*
-       Events are not emitted on block 0.
-       So any dispatchable calls made during genesis block formation will have no events emitted.
-       https://substrate.dev/recipes/2-appetizers/4-events.html
-    */
-    run_to_block(1);
-
-    let expected_lead_id = NextLeadId::<Test>::get();
-    // Set lead
-    assert_eq!(
-        ContentWorkingGroup::replace_lead(
-            RawOrigin::Root.into(),
-            Some((member_id, new_role_account))
-        )
-        .unwrap(),
-        ()
-    );
-
-    assert_eq!(
-        get_last_event_or_panic(),
-        crate::RawEvent::LeadSet(expected_lead_id)
-    );
-
-    expected_lead_id
-}
-
-pub fn add_curator_opening() -> CuratorOpeningId<Test> {
-    let activate_at = hiring::ActivateOpeningAt::ExactBlock(34);
-
-    let human_readable_text = generate_valid_length_buffer(&OpeningHumanReadableText::get());
-
-    let expected_curator_opening_id = NextCuratorOpeningId::<Test>::get();
-
-    assert_eq!(
-        ContentWorkingGroup::add_curator_opening(
-            Origin::signed(LEAD_ROLE_ACCOUNT),
-            activate_at.clone(),
-            get_baseline_opening_policy(),
-            human_readable_text.clone()
-        )
-        .unwrap(),
-        ()
-    );
-
-    assert_eq!(
-        get_last_event_or_panic(),
-        crate::RawEvent::CuratorOpeningAdded(expected_curator_opening_id)
-    );
-
-    expected_curator_opening_id
-}
-
-/*
- * Buffer generators
- */
-
-pub fn generate_text(len: usize) -> Vec<u8> {
-    vec![b'x'; len]
-}
-
-pub fn generate_valid_length_buffer(constraint: &InputValidationLengthConstraint) -> Vec<u8> {
-    generate_text(constraint.min as usize)
-}
-
-pub fn generate_too_short_length_buffer(constraint: &InputValidationLengthConstraint) -> Vec<u8> {
-    generate_text((constraint.min - 1) as usize)
-}
-
-pub fn generate_too_long_length_buffer(constraint: &InputValidationLengthConstraint) -> Vec<u8> {
-    generate_text((constraint.max() + 1) as usize)
-}
-
-#[test]
-fn increasing_mint_capacity() {
-    const MINT_CAPACITY: u64 = 50000;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .with_content_wg_config(
-            genesis::GenesisConfigBuilder::<Test>::default()
-                .with_mint_capacity(MINT_CAPACITY)
-                .build(),
-        )
-        .build()
-        .execute_with(|| {
-            /*
-               Events are not emitted on block 0.
-               So any dispatchable calls made during genesis block formation will have no events emitted.
-               https://substrate.dev/recipes/2-appetizers/4-events.html
-            */
-            run_to_block(1);
-
-            let mint_id = ContentWorkingGroup::mint();
-            let mint = Minting::mints(mint_id);
-            assert_eq!(mint.capacity(), MINT_CAPACITY);
-
-            let increase = 25000;
-            // Increasing mint capacity
-            let expected_new_capacity = MINT_CAPACITY + increase;
-            assert_ok!(ContentWorkingGroup::increase_mint_capacity(
-                RawOrigin::Root.into(),
-                increase
-            ));
-            // Excpected event after increasing
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::MintCapacityIncreased(mint_id, increase, expected_new_capacity)
-            );
-            // Excpected value of capacity after increasing
-            let mint = Minting::mints(mint_id);
-            assert_eq!(mint.capacity(), expected_new_capacity);
-        });
-}
-
-#[test]
-fn setting_mint_capacity() {
-    const MINT_CAPACITY: u64 = 50000;
-
-    TestExternalitiesBuilder::<Test>::default()
-        .with_content_wg_config(
-            genesis::GenesisConfigBuilder::<Test>::default()
-                .with_mint_capacity(MINT_CAPACITY)
-                .build(),
-        )
-        .build()
-        .execute_with(|| {
-            /*
-               Events are not emitted on block 0.
-               So any dispatchable calls made during genesis block formation will have no events emitted.
-               https://substrate.dev/recipes/2-appetizers/4-events.html
-            */
-            run_to_block(1);
-
-            let mint_id = ContentWorkingGroup::mint();
-            let mint = Minting::mints(mint_id);
-            assert_eq!(mint.capacity(), MINT_CAPACITY);
-
-            // Decreasing mint capacity
-            let new_lower_capacity = 10000;
-            let decrease = MINT_CAPACITY - new_lower_capacity;
-            assert_ok!(ContentWorkingGroup::set_mint_capacity(
-                RawOrigin::Root.into(),
-                new_lower_capacity
-            ));
-            // Correct event after decreasing
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::MintCapacityDecreased(mint_id, decrease, new_lower_capacity)
-            );
-            // Correct value of capacity after decreasing
-            let mint = Minting::mints(mint_id);
-            assert_eq!(mint.capacity(), new_lower_capacity);
-
-            // Increasing mint capacity
-            let new_higher_capacity = 25000;
-            let increase = new_higher_capacity - mint.capacity();
-            assert_ok!(ContentWorkingGroup::set_mint_capacity(
-                RawOrigin::Root.into(),
-                new_higher_capacity
-            ));
-            // Excpected event after increasing
-            assert_eq!(
-                get_last_event_or_panic(),
-                crate::RawEvent::MintCapacityIncreased(mint_id, increase, new_higher_capacity)
-            );
-            // Excpected value of capacity after increasing
-            let mint = Minting::mints(mint_id);
-            assert_eq!(mint.capacity(), new_higher_capacity);
-        });
-}

+ 3 - 2
runtime-modules/forum/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-forum'
-version = '3.1.0'
+version = '4.0.0'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -14,9 +14,9 @@ sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://
 sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
+sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 
 [dev-dependencies]
-sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 
 [features]
@@ -29,6 +29,7 @@ std = [
 	'sp-std/std',
 	'sp-arithmetic/std',
 	'sp-runtime/std',
+	'sp-io/std',
 	'pallet-timestamp/std',
 	'common/std',
 ]

Diff do ficheiro suprimidas por serem muito extensas
+ 955 - 619
runtime-modules/forum/src/lib.rs


Diff do ficheiro suprimidas por serem muito extensas
+ 579 - 355
runtime-modules/forum/src/mock.rs


+ 1653 - 747
runtime-modules/forum/src/tests.rs

@@ -2,980 +2,1886 @@
 
 use super::*;
 use crate::mock::*;
+use frame_support::assert_err;
 
-use frame_support::{assert_err, assert_ok};
+/// test cases are arranged as two layers.
+/// first layer is each method in defined in module.
+/// second layer is each parameter of the specific method.
 
 /*
-* NB!: No test checks for event emission!!!!
-*/
-
-/*
- * set_forum_sudo
- * ==============================================================================
- *
- * Missing cases
- *
- * set_forum_bad_origin
- *
+ * update_category_membership_of_moderator_origin
  */
-
 #[test]
-fn set_forum_sudo_unset() {
-    let config = default_genesis_config();
-
-    build_test_externalities(config).execute_with(|| {
-        // Ensure that forum sudo is default
-        assert_eq!(TestForumModule::forum_sudo(), Some(33));
-
-        // Unset forum sudo
-        assert_ok!(TestForumModule::set_forum_sudo(
-            mock_origin(OriginType::Root),
-            None
-        ));
-
-        // Sudo no longer set
-        assert!(TestForumModule::forum_sudo().is_none());
-
-        // event emitted?!
+// test case for check if origin is forum lead
+fn update_category_membership_of_moderator_origin() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let moderator_id = forum_lead;
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        update_category_membership_of_moderator_mock(
+            origin,
+            moderator_id,
+            category_id,
+            true,
+            Ok(()),
+        );
+        update_category_membership_of_moderator_mock(
+            NOT_FORUM_LEAD_ORIGIN,
+            moderator_id,
+            category_id,
+            true,
+            Err(Error::<Runtime>::OriginNotForumLead.into()),
+        );
     });
 }
 
 #[test]
-fn set_forum_sudo_update() {
-    let config = default_genesis_config();
-
-    build_test_externalities(config).execute_with(|| {
-        // Ensure that forum sudo is default
-        assert_eq!(
-            TestForumModule::forum_sudo(),
-            Some(default_genesis_config().forum_sudo)
+// test case for check whether category is existed.
+fn update_category_membership_of_moderator_category() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let moderator_id = forum_lead;
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
         );
-
-        let new_forum_sudo_account_id = 780;
-
-        // Unset forum sudo
-        assert_ok!(TestForumModule::set_forum_sudo(
-            mock_origin(OriginType::Root),
-            Some(new_forum_sudo_account_id)
-        ));
-
-        // Sudo no longer set
-        assert_eq!(
-            TestForumModule::forum_sudo(),
-            Some(new_forum_sudo_account_id)
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderator_id,
+            category_id,
+            true,
+            Ok(()),
+        );
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderator_id,
+            INVLAID_CATEGORY_ID,
+            true,
+            Err(Error::<Runtime>::CategoryDoesNotExist.into()),
         );
     });
 }
 
-/*
- * create_category
- * ==============================================================================
- *
- * Missing cases
- *
- * create_category_bad_origin
- * create_category_forum_sudo_not_set
- */
-
 #[test]
-fn create_root_category_successfully() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        assert_create_category(origin, None, Ok(()));
-    });
+// test case for check if origin is forum lead
+fn create_category_origin() {
+    let origins = vec![FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_ORIGIN];
+    let results = vec![Ok(()), Err(Error::<Runtime>::OriginNotForumLead.into())];
+    for index in 0..origins.len() {
+        with_test_externalities(|| {
+            create_category_mock(
+                origins[index].clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                results[index].clone(),
+            );
+        });
+    }
 }
 
 #[test]
-fn create_subcategory_successfully() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+// test case for check if parent category is archived or not existing.
+fn create_category_parent() {
+    let parents = vec![Some(1), Some(2), Some(3)];
+    let results = vec![
+        Ok(()),
+        Err(Error::<Runtime>::AncestorCategoryImmutable.into()),
+        Err(Error::<Runtime>::CategoryDoesNotExist.into()),
+    ];
 
-    build_test_externalities(config).execute_with(|| {
-        let root_category_id = create_root_category(origin.clone());
-        assert_create_category(origin, Some(root_category_id), Ok(()));
-    });
+    for index in 0..parents.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+            create_category_mock(
+                origin.clone(),
+                Some(1),
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+            update_category_archival_status_mock(
+                origin.clone(),
+                PrivilegedActor::Lead,
+                2,
+                true,
+                Ok(()),
+            );
+
+            create_category_mock(
+                origin.clone(),
+                parents[index],
+                good_category_title(),
+                good_category_description(),
+                results[index],
+            );
+        });
+    }
 }
 
 #[test]
-fn create_category_title_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.category_title_constraint.min as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        CreateCategoryFixture {
-            origin,
-            parent: None,
-            title: generate_text(min_len - 1),
-            description: good_category_description(),
-            result: Err(ERROR_CATEGORY_TITLE_TOO_SHORT),
+// test case set category depth
+fn create_category_depth() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let max_depth = <Runtime as Trait>::MaxCategoryDepth::get();
+        for i in 0..(max_depth + 1) {
+            let parent_category_id = match i {
+                0 => None,
+                _ => Some(i),
+            };
+            let expected_result = match i {
+                _ if i >= max_depth => Err(Error::<Runtime>::MaxValidCategoryDepthExceeded.into()),
+                _ => Ok(()),
+            };
+
+            create_category_mock(
+                origin.clone(),
+                parent_category_id,
+                good_category_title(),
+                good_category_description(),
+                expected_result,
+            );
         }
-        .call_and_assert();
     });
 }
 
+/*
+ ** update_category
+ */
 #[test]
-fn create_category_title_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.category_title_constraint.max() as usize;
+// test if category updator is forum lead
+fn update_category_archival_status_origin() {
+    let origins = [FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_ORIGIN];
+    let results = vec![Ok(()), Err(Error::<Runtime>::OriginNotForumLead.into())];
+
+    for index in 0..origins.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            let category_id = create_category_mock(
+                origin,
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+            update_category_archival_status_mock(
+                origins[index].clone(),
+                PrivilegedActor::Lead,
+                category_id,
+                true,
+                results[index],
+            );
+        });
+    }
+}
 
-    build_test_externalities(config).execute_with(|| {
-        CreateCategoryFixture {
+#[test]
+// test case for new setting actually not update category status
+fn update_category_archival_status_no_change() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        update_category_archival_status_mock(
             origin,
-            parent: None,
-            title: generate_text(max_len + 1),
-            description: good_category_description(),
-            result: Err(ERROR_CATEGORY_TITLE_TOO_LONG),
-        }
-        .call_and_assert();
+            PrivilegedActor::Lead,
+            category_id,
+            false,
+            Err(Error::<Runtime>::CategoryNotBeingUpdated.into()),
+        );
     });
 }
 
 #[test]
-fn create_category_description_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.category_description_constraint.min as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        CreateCategoryFixture {
-            origin,
-            parent: None,
-            title: good_category_title(),
-            description: generate_text(min_len - 1),
-            result: Err(ERROR_CATEGORY_DESCRIPTION_TOO_SHORT),
-        }
-        .call_and_assert();
+// test case for editing nonexistent category
+fn update_category_archival_status_category_exists() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        update_category_archival_status_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
+            1,
+            true,
+            Ok(()),
+        );
+        update_category_archival_status_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
+            2,
+            true,
+            Err(Error::<Runtime>::CategoryDoesNotExist.into()),
+        );
     });
 }
 
 #[test]
-fn create_category_description_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.category_description_constraint.max() as usize;
+// test if moderator can archive category
+fn update_category_archival_status_moderator() {
+    let moderators = [FORUM_MODERATOR_ORIGIN_ID];
+    let origins = [FORUM_MODERATOR_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        CreateCategoryFixture {
-            origin,
-            parent: None,
-            title: good_category_title(),
-            description: generate_text(max_len + 1),
-            result: Err(ERROR_CATEGORY_DESCRIPTION_TOO_LONG),
-        }
-        .call_and_assert();
+        // unprivileged moderator will fail to update category
+        update_category_archival_status_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id,
+            true,
+            Err(Error::<Runtime>::ModeratorCantUpdateCategory.into()),
+        );
+
+        // give permision to moderate category itself
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id,
+            true,
+            Ok(()),
+        );
+
+        // moderator associated with category will succeed
+        update_category_archival_status_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id,
+            true,
+            Ok(()),
+        );
     });
 }
 
-/*
- * update_category
- * ==============================================================================
- *
- * Missing cases
- *
- * create_category_bad_origin
- * create_category_forum_sudo_not_set
- * create_category_origin_not_forum_sudo
- * create_category_immutable_ancestor_category
- */
-
 #[test]
-fn update_category_undelete_and_unarchive() {
-    /*
-     * Create an initial state with two levels of categories, where
-     * leaf category is deleted, and then try to undelete.
-     */
+// test if moderator can archive category
+fn update_category_archival_status_lock_works() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    let forum_sudo = 32;
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-    let created_at = RuntimeBlockchainTimestamp { block: 0, time: 0 };
+        let post_id = create_post_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            good_post_text(),
+            Ok(()),
+        );
 
-    let category_by_id = vec![
-        // A root category
-        (
+        update_category_archival_status_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
             1,
-            Category {
-                id: 1,
-                title: "New root".as_bytes().to_vec(),
-                description: "This is a new root category".as_bytes().to_vec(),
-                created_at: created_at.clone(),
-                deleted: false,
-                archived: false,
-                num_direct_subcategories: 1,
-                num_direct_unmoderated_threads: 0,
-                num_direct_moderated_threads: 0,
-                position_in_parent_category: None,
-                moderator_id: forum_sudo,
-            },
-        ),
-        // A subcategory of the one above
-        (
-            2,
-            Category {
-                id: 2,
-                title: "New subcategory".as_bytes().to_vec(),
-                description: "This is a new subcategory to root category"
-                    .as_bytes()
-                    .to_vec(),
-                created_at: created_at.clone(),
-                deleted: true,
-                archived: false,
-                num_direct_subcategories: 0,
-                num_direct_unmoderated_threads: 0,
-                num_direct_moderated_threads: 0,
-                position_in_parent_category: Some(ChildPositionInParentCategory {
-                    parent_id: 1,
-                    child_nr_in_parent_category: 1,
-                }),
-                moderator_id: forum_sudo,
-            },
-        ),
-    ];
-
-    // Set constraints to be sloppy, we don't care about enforcing them.
-    let sloppy_constraint = InputValidationLengthConstraint {
-        min: 0,
-        max_min_diff: 1000,
-    };
-
-    let config = genesis_config(
-        &category_by_id,             // category_by_id
-        category_by_id.len() as u64, // next_category_id
-        &vec![],                     // thread_by_id
-        1,                           // next_thread_id
-        &vec![],                     // post_by_id
-        1,                           // next_post_id
-        forum_sudo,
-        &sloppy_constraint,
-        &sloppy_constraint,
-        &sloppy_constraint,
-        &sloppy_constraint,
-        &sloppy_constraint,
-        &sloppy_constraint,
-    );
-
-    build_test_externalities(config).execute_with(|| {
-        UpdateCategoryFixture {
-            origin: OriginType::Signed(forum_sudo),
-            category_id: 2,
-            new_archival_status: None,        // same as before
-            new_deletion_status: Some(false), // undelete
-            result: Ok(()),
-        }
-        .call_and_assert();
-    });
-}
+            true,
+            Ok(()),
+        );
 
-/*
- * create_thread
- * ==============================================================================
- *
- * Missing cases
- *
- * create_thread_bad_origin
- * create_thread_forum_sudo_not_set
- * ...
- */
+        // can't add more threads
+        create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Err(Error::<Runtime>::AncestorCategoryImmutable.into()),
+        );
 
-#[test]
-fn create_thread_successfully() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+        // can't add more posts to thread inside category
+        create_post_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            good_post_text(),
+            Err(Error::<Runtime>::AncestorCategoryImmutable.into()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(origin);
-        let member_origin = create_forum_member();
+        // can't update post
+        edit_post_text_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            post_id,
+            good_post_new_text(),
+            Err(Error::<Runtime>::AncestorCategoryImmutable.into()),
+        );
 
-        CreateThreadFixture {
-            origin: member_origin,
+        // can't update thread
+        edit_thread_title_mock(
+            origin.clone(),
+            forum_lead,
             category_id,
-            title: good_thread_title(),
-            text: good_thread_text(),
-            result: Ok(()),
-        }
-        .call_and_assert();
+            thread_id,
+            good_thread_new_title(),
+            Err(Error::<Runtime>::AncestorCategoryImmutable.into()),
+        );
     });
 }
 
 #[test]
-fn create_thread_title_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.thread_title_constraint.min as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(origin);
-        let member_origin = create_forum_member();
-
-        CreateThreadFixture {
-            origin: member_origin,
-            category_id,
-            title: generate_text(min_len - 1),
-            text: good_thread_text(),
-            result: Err(ERROR_THREAD_TITLE_TOO_SHORT),
-        }
-        .call_and_assert();
+// test category can be deleted
+fn delete_category() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        assert!(<CategoryById<Runtime>>::contains_key(category_id));
+        delete_category_mock(origin.clone(), PrivilegedActor::Lead, category_id, Ok(()));
+        assert!(!<CategoryById<Runtime>>::contains_key(category_id));
     });
 }
 
 #[test]
-fn create_thread_title_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.thread_title_constraint.max() as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(origin);
-        let member_origin = create_forum_member();
-
-        CreateThreadFixture {
-            origin: member_origin,
+// test category can't be deleted when it has subcategories
+fn delete_category_non_empty_subcategories() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        create_category_mock(
+            origin.clone(),
+            Some(category_id),
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        delete_category_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
             category_id,
-            title: generate_text(max_len + 1),
-            text: good_thread_text(),
-            result: Err(ERROR_THREAD_TITLE_TOO_LONG),
-        }
-        .call_and_assert();
+            Err(Error::<Runtime>::CategoryNotEmptyCategories.into()),
+        );
     });
 }
 
 #[test]
-fn create_thread_text_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.post_text_constraint.min as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(origin);
-        let member_origin = create_forum_member();
+// test category can't be deleted when it contains threads
+fn delete_category_non_empty_threads() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-        CreateThreadFixture {
-            origin: member_origin,
+        delete_category_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
             category_id,
-            title: good_thread_title(),
-            text: generate_text(min_len - 1),
-            result: Err(ERROR_POST_TEXT_TOO_SHORT),
-        }
-        .call_and_assert();
+            Err(Error::<Runtime>::CategoryNotEmptyThreads.into()),
+        );
     });
 }
 
 #[test]
-fn create_thread_text_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.post_text_constraint.max() as usize;
+// test category can't be deleted by moderator only if he is moderating one of parent categories
+fn delete_category_need_ancestor_moderation() {
+    let moderators = [FORUM_MODERATOR_ORIGIN_ID];
+    let origins = [FORUM_MODERATOR_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id_1 = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let category_id_2 = create_category_mock(
+            origin.clone(),
+            Some(category_id_1),
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(origin);
-        let member_origin = create_forum_member();
+        // without any permissions, moderator can't delete category
+        delete_category_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id_2,
+            Err(Error::<Runtime>::ModeratorCantDeleteCategory.into()),
+        );
 
-        CreateThreadFixture {
-            origin: member_origin,
-            category_id,
-            title: good_thread_title(),
-            text: generate_text(max_len + 1),
-            result: Err(ERROR_POST_TEXT_TOO_LONG),
-        }
-        .call_and_assert();
+        // give permision to moderate category itself
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id_2,
+            true,
+            Ok(()),
+        );
+
+        // without permissions to moderate only category itself, moderator can't delete category
+        delete_category_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id_2,
+            Err(Error::<Runtime>::ModeratorCantDeleteCategory.into()),
+        );
+
+        // give permision to moderate parent category
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id_1,
+            true,
+            Ok(()),
+        );
+
+        // check number of subcategories is correct
+        assert_eq!(
+            <CategoryById<Runtime>>::get(category_id_1).num_direct_subcategories,
+            1,
+        );
+
+        // with permissions to moderate parent category, delete will work
+        delete_category_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id_2,
+            Ok(()),
+        );
+
+        // check that subcategory count was decreased
+        assert_eq!(
+            <CategoryById<Runtime>>::get(category_id_1).num_direct_subcategories,
+            0,
+        );
     });
 }
 
 #[test]
-fn create_post_successfully() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+// test if lead can delete root category
+fn delete_category_root_by_lead() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, _) = create_root_category_and_thread_and_post(origin);
+        delete_category_mock(origin.clone(), PrivilegedActor::Lead, category_id, Ok(()));
     });
 }
 
+/*
+ ** create_thread
+ */
 #[test]
-fn create_post_text_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.post_text_constraint.min as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        let (member_origin, _, thread_id) = create_root_category_and_thread(origin);
+// test if thread creator is valid forum user
+fn create_thread_origin() {
+    let origins = [NOT_FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_2_ORIGIN];
+    let forum_user_id = NOT_FORUM_LEAD_ORIGIN_ID;
+    let results = vec![
+        Ok(()),
+        Err(Error::<Runtime>::ForumUserIdNotMatchAccount.into()),
+    ];
+    for index in 0..origins.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            let category_id = create_category_mock(
+                origin,
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+            create_thread_mock(
+                origins[index].clone(),
+                forum_user_id,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+                results[index],
+            );
+        });
+    }
+}
 
-        CreatePostFixture {
-            origin: member_origin,
-            thread_id,
-            text: generate_text(min_len - 1),
-            result: Err(ERROR_POST_TEXT_TOO_SHORT),
-        }
-        .call_and_assert();
-    });
+#[test]
+// test if timestamp of poll start time and end time are valid
+fn create_thread_poll_timestamp() {
+    let expiration_diff = 10;
+    let results = vec![Ok(()), Err(Error::<Runtime>::PollTimeSetting.into())];
+
+    for index in 0..results.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+
+        with_test_externalities(|| {
+            change_current_time(1);
+            let poll = generate_poll_timestamp_cases(index, expiration_diff);
+            change_current_time(index as u64 * expiration_diff + 1);
+
+            let category_id = create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+
+            create_thread_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                Some(poll),
+                results[index],
+            );
+        });
+    }
 }
 
 #[test]
-fn create_post_text_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.post_text_constraint.max() as usize;
+// test if author can edit thread's title
+fn edit_thread_title() {
+    let forum_users = [NOT_FORUM_LEAD_ORIGIN_ID, NOT_FORUM_LEAD_2_ORIGIN_ID];
+    let origins = [NOT_FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_2_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        // create thread by author
+        let thread_id = create_thread_mock(
+            origins[0].clone(),
+            forum_users[0],
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (member_origin, _, thread_id) = create_root_category_and_thread(origin);
+        // check author can edit text
+        edit_thread_title_mock(
+            origins[0].clone(),
+            forum_users[0],
+            category_id,
+            thread_id,
+            good_thread_new_title(),
+            Ok(()),
+        );
 
-        CreatePostFixture {
-            origin: member_origin,
+        // check non-author is forbidden from editing text
+        edit_thread_title_mock(
+            origins[1].clone(),
+            forum_users[1],
+            category_id,
             thread_id,
-            text: generate_text(max_len + 1),
-            result: Err(ERROR_POST_TEXT_TOO_LONG),
-        }
-        .call_and_assert();
+            good_thread_new_title(),
+            Err(Error::<Runtime>::AccountDoesNotMatchThreadAuthor.into()),
+        );
     });
 }
 
-// Test moderation:
-// -----------------------------------------------------------------------------
-
+/*
+ ** update_category
+ */
 #[test]
-fn moderate_thread_successfully() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(origin.clone());
-        assert_eq!(moderate_thread(origin, thread_id, good_rationale()), Ok(()));
-    });
+// test if category updator is forum lead
+fn update_thread_archival_status_origin() {
+    let origins = [FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_ORIGIN];
+    let results = vec![Ok(()), Err(Error::<Runtime>::OriginNotForumLead.into())];
+
+    for index in 0..origins.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            let category_id = create_category_mock(
+                origin,
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+
+            let thread_id = create_thread_mock(
+                origins[0].clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+                Ok(()),
+            );
+            update_thread_archival_status_mock(
+                origins[index].clone(),
+                PrivilegedActor::Lead,
+                category_id,
+                thread_id,
+                true,
+                results[index],
+            );
+        });
+    }
 }
 
 #[test]
-fn cannot_moderate_already_moderated_thread() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(origin.clone());
-        assert_eq!(
-            moderate_thread(origin.clone(), thread_id.clone(), good_rationale()),
-            Ok(())
+// test case for new setting actually not update thread status
+fn update_thread_archival_status_no_change() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
         );
-        assert_eq!(
-            moderate_thread(origin, thread_id, good_rationale()),
-            Err(ERROR_THREAD_ALREADY_MODERATED)
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        update_thread_archival_status_mock(
+            origin,
+            PrivilegedActor::Lead,
+            category_id,
+            thread_id,
+            false,
+            Err(Error::<Runtime>::ThreadNotBeingUpdated.into()),
         );
     });
 }
 
 #[test]
-fn moderate_thread_rationale_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.thread_moderation_rationale_constraint.min as usize;
-
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(origin.clone());
-        let bad_rationale = generate_text(min_len - 1);
-        assert_eq!(
-            moderate_thread(origin, thread_id, bad_rationale),
-            Err(ERROR_THREAD_MODERATION_RATIONALE_TOO_SHORT)
+// test case for editing nonexistent thread
+fn update_thread_archival_status_thread_exists() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        update_thread_archival_status_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
+            category_id,
+            thread_id,
+            true,
+            Ok(()),
+        );
+        update_thread_archival_status_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
+            category_id,
+            thread_id + 1,
+            true,
+            Err(Error::<Runtime>::ThreadDoesNotExist.into()),
         );
     });
 }
 
 #[test]
-fn moderate_thread_rationale_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.thread_moderation_rationale_constraint.max() as usize;
+// test if moderator can archive thread
+fn update_thread_archival_status_moderator() {
+    let moderators = [FORUM_MODERATOR_ORIGIN_ID];
+    let origins = [FORUM_MODERATOR_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(origin.clone());
-        let bad_rationale = generate_text(max_len + 1);
-        assert_eq!(
-            moderate_thread(origin, thread_id, bad_rationale),
-            Err(ERROR_THREAD_MODERATION_RATIONALE_TOO_LONG)
+        // unprivileged moderator will fail to update category
+        update_thread_archival_status_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id,
+            thread_id,
+            true,
+            Err(Error::<Runtime>::ModeratorCantUpdateCategory.into()),
         );
-    });
-}
 
-#[test]
-fn moderate_post_successfully() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+        // give permision to moderate category itself
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id,
+            true,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, post_id) = create_root_category_and_thread_and_post(origin.clone());
-        assert_eq!(moderate_post(origin, post_id, good_rationale()), Ok(()));
+        // moderator associated with category will succeed
+        update_thread_archival_status_mock(
+            origins[0].clone(),
+            PrivilegedActor::Moderator(moderators[0]),
+            category_id,
+            thread_id,
+            true,
+            Ok(()),
+        );
     });
 }
 
 #[test]
-fn moderate_post_rationale_too_short() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let min_len = config.post_moderation_rationale_constraint.min as usize;
+// test if moderator can archive thread
+fn update_thread_archival_status_lock_works() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, post_id) = create_root_category_and_thread_and_post(origin.clone());
-        let bad_rationale = generate_text(min_len - 1);
-        assert_eq!(
-            moderate_post(origin, post_id, bad_rationale),
-            Err(ERROR_POST_MODERATION_RATIONALE_TOO_SHORT)
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
         );
-    });
-}
 
-#[test]
-fn moderate_post_rationale_too_long() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-    let max_len = config.post_moderation_rationale_constraint.max() as usize;
+        let post_id = create_post_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            good_post_text(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, post_id) = create_root_category_and_thread_and_post(origin.clone());
-        let bad_rationale = generate_text(max_len + 1);
-        assert_eq!(
-            moderate_post(origin, post_id, bad_rationale),
-            Err(ERROR_POST_MODERATION_RATIONALE_TOO_LONG)
+        update_thread_archival_status_mock(
+            origin.clone(),
+            PrivilegedActor::Lead,
+            category_id,
+            thread_id,
+            true,
+            Ok(()),
+        );
+
+        // can't add more posts to thread inside category
+        create_post_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            good_post_text(),
+            Err(Error::<Runtime>::ThreadImmutable.into()),
+        );
+
+        // can't update post
+        edit_post_text_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            post_id,
+            good_post_new_text(),
+            Err(Error::<Runtime>::ThreadImmutable.into()),
+        );
+
+        // can't update thread
+        edit_thread_title_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            good_thread_new_title(),
+            Err(Error::<Runtime>::ThreadImmutable.into()),
         );
     });
 }
 
 #[test]
-fn cannot_moderate_already_moderated_post() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+// test if moderator can delete thread
+fn delete_thread() {
+    let moderators = [
+        FORUM_MODERATOR_ORIGIN_ID,
+        FORUM_MODERATOR_2_ORIGIN_ID,
+        NOT_FORUM_LEAD_ORIGIN_ID,
+    ];
+    let origins = [
+        FORUM_MODERATOR_ORIGIN,
+        FORUM_MODERATOR_2_ORIGIN,
+        NOT_FORUM_LEAD_ORIGIN,
+    ];
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, post_id) = create_root_category_and_thread_and_post(origin.clone());
-        assert_eq!(
-            moderate_post(origin.clone(), post_id.clone(), good_rationale()),
-            Ok(())
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+
+        let post_id = create_post_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            good_post_text(),
+            Ok(()),
+        );
+
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id,
+            true,
+            Ok(()),
         );
+
+        // check number of category's threads match before delete
         assert_eq!(
-            moderate_post(origin, post_id, good_rationale()),
-            Err(ERROR_POST_MODERATED)
+            <CategoryById<Runtime>>::get(category_id).num_direct_threads,
+            1
         );
-    });
-}
 
-// Not a forum sudo:
-// -----------------------------------------------------------------------------
+        // regular user will fail to delete the thread
+        delete_thread_mock(
+            origins[2].clone(),
+            moderators[2],
+            category_id,
+            thread_id,
+            Err(Error::<Runtime>::ModeratorIdNotMatchAccount.into()),
+        );
 
-#[test]
-fn not_forum_sudo_cannot_create_root_category() {
-    let config = default_genesis_config();
+        // moderator not associated with thread will fail to delete it
+        delete_thread_mock(
+            origins[1].clone(),
+            moderators[1],
+            category_id,
+            thread_id,
+            Err(Error::<Runtime>::ModeratorCantUpdateCategory.into()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        assert_create_category(
-            NOT_FORUM_SUDO_ORIGIN,
-            None,
-            Err(ERROR_ORIGIN_NOT_FORUM_SUDO),
+        // moderator will delete thread
+        delete_thread_mock(
+            origins[0].clone(),
+            moderators[0],
+            category_id,
+            thread_id,
+            Ok(()),
         );
-    });
-}
 
-#[test]
-fn not_forum_sudo_cannot_create_subcategory() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+        // check thread's post was deleted
+        assert!(!<PostById<Runtime>>::contains_key(thread_id, post_id));
 
-    build_test_externalities(config).execute_with(|| {
-        let root_category_id = create_root_category(origin);
-        assert_create_category(
-            NOT_FORUM_SUDO_ORIGIN,
-            Some(root_category_id),
-            Err(ERROR_ORIGIN_NOT_FORUM_SUDO),
+        // check category's thread count was decreased
+        assert_eq!(
+            <CategoryById<Runtime>>::get(category_id).num_direct_threads,
+            0
         );
     });
 }
 
 #[test]
-fn not_forum_sudo_cannot_archive_category() {
-    assert_not_forum_sudo_cannot_update_category(archive_category);
-}
+// test if moderator can move thread between two categories he moderates
+fn move_thread_moderator_permissions() {
+    let moderators = [FORUM_MODERATOR_ORIGIN_ID, FORUM_MODERATOR_2_ORIGIN_ID];
+    let origins = [FORUM_MODERATOR_ORIGIN, FORUM_MODERATOR_2_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id_1 = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let category_id_2 = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-#[test]
-fn not_forum_sudo_cannot_unarchive_category() {
-    assert_not_forum_sudo_cannot_update_category(unarchive_category);
-}
+        // sanity check
+        assert_ne!(category_id_1, category_id_2);
 
-#[test]
-fn not_forum_sudo_cannot_delete_category() {
-    assert_not_forum_sudo_cannot_update_category(delete_category);
-}
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id_1,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-#[test]
-fn not_forum_sudo_cannot_undelete_category() {
-    assert_not_forum_sudo_cannot_update_category(undelete_category);
-}
+        // moderator not associated with any category will fail to move thread
+        move_thread_mock(
+            origins[0].clone(),
+            moderators[0],
+            category_id_1,
+            thread_id,
+            category_id_2,
+            Err(Error::<Runtime>::ModeratorModerateOriginCategory.into()),
+        );
 
-#[test]
-fn not_forum_sudo_cannot_moderate_thread() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+        // set incomplete permissions for first user (only category 1)
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id_1,
+            true,
+            Ok(()),
+        );
+        // set incomplete permissions for second user (only category 2)
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[1],
+            category_id_2,
+            true,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(origin.clone());
+        // moderator associated only with the first category will fail to move thread
+        move_thread_mock(
+            origins[1].clone(),
+            moderators[1],
+            category_id_1,
+            thread_id,
+            category_id_2,
+            Err(Error::<Runtime>::ModeratorModerateOriginCategory.into()),
+        );
+
+        // moderator associated only with the second category will fail to move thread
+        move_thread_mock(
+            origins[0].clone(),
+            moderators[0],
+            category_id_1,
+            thread_id,
+            category_id_2,
+            Err(Error::<Runtime>::ModeratorModerateDestinationCategory.into()),
+        );
+
+        // give the rest of necessary permissions to the first moderator
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id_2,
+            true,
+            Ok(()),
+        );
+
+        // check counters of threads in category
+        assert_eq!(
+            <CategoryById<Runtime>>::get(category_id_1).num_direct_threads,
+            1,
+        );
         assert_eq!(
-            moderate_thread(NOT_FORUM_SUDO_ORIGIN, thread_id, good_rationale()),
-            Err(ERROR_ORIGIN_NOT_FORUM_SUDO)
+            <CategoryById<Runtime>>::get(category_id_2).num_direct_threads,
+            0,
         );
-    });
-}
 
-#[test]
-fn not_forum_sudo_cannot_moderate_post() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+        // moderator associated with both categories will succeed to move thread
+        move_thread_mock(
+            origins[0].clone(),
+            moderators[0],
+            category_id_1,
+            thread_id,
+            category_id_2,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, post_id) = create_root_category_and_thread_and_post(origin.clone());
+        // check counters of threads in category
         assert_eq!(
-            moderate_post(NOT_FORUM_SUDO_ORIGIN, post_id, good_rationale()),
-            Err(ERROR_ORIGIN_NOT_FORUM_SUDO)
+            <CategoryById<Runtime>>::get(category_id_1).num_direct_threads,
+            0,
+        );
+        assert_eq!(
+            <CategoryById<Runtime>>::get(category_id_2).num_direct_threads,
+            1,
         );
     });
 }
 
-// Not a member:
-// -----------------------------------------------------------------------------
-
 #[test]
-fn not_member_cannot_create_thread() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+// test if error is thrown when origin and destination category is the same
+fn move_thread_invalid_move() {
+    let moderators = [FORUM_MODERATOR_ORIGIN_ID];
+    let origins = [FORUM_MODERATOR_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        CreateThreadFixture {
-            origin: NOT_MEMBER_ORIGIN,
-            category_id: create_root_category(origin),
-            title: good_thread_title(),
-            text: good_thread_text(),
-            result: Err(ERROR_NOT_FORUM_USER),
-        }
-        .call_and_assert();
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+
+        // set permissions
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderators[0],
+            category_id,
+            true,
+            Ok(()),
+        );
+
+        move_thread_mock(
+            origins[0].clone(),
+            moderators[0],
+            category_id,
+            thread_id,
+            category_id,
+            Err(Error::<Runtime>::ThreadMoveInvalid.into()),
+        );
     });
 }
 
+/*
+ ** vote_on_poll
+ */
 #[test]
-fn not_member_cannot_create_post() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+// test if poll submitter is a forum user
+fn vote_on_poll_origin() {
+    let origins = vec![FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_ORIGIN];
+    let results = vec![
+        Ok(()),
+        Err(Error::<Runtime>::ForumUserIdNotMatchAccount.into()),
+    ];
+    let expiration_diff = 10;
+
+    for index in 0..origins.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            let category_id = create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+            let thread_id = create_thread_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                Some(generate_poll(expiration_diff)),
+                Ok(()),
+            );
+
+            vote_on_poll_mock(
+                origins[index].clone(),
+                forum_lead,
+                thread_id,
+                category_id,
+                1,
+                results[index],
+            );
+        });
+    }
+}
 
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(origin);
-        CreatePostFixture {
-            origin: NOT_MEMBER_ORIGIN,
+#[test]
+// test if poll metadata created
+fn vote_on_poll_exists() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        vote_on_poll_mock(
+            origin.clone(),
+            forum_lead,
             thread_id,
-            text: good_post_text(),
-            result: Err(ERROR_NOT_FORUM_USER),
-        }
-        .call_and_assert();
+            category_id,
+            1,
+            Err(Error::<Runtime>::PollNotExist.into()),
+        );
     });
 }
 
 #[test]
-fn not_member_cannot_edit_post() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, _, post_id) = create_root_category_and_thread_and_post(origin);
-        assert_err!(
-            TestForumModule::edit_post_text(
-                mock_origin(NOT_MEMBER_ORIGIN),
-                post_id,
-                good_rationale()
-            ),
-            ERROR_NOT_FORUM_USER
+// test if forum reject poll submit after expiration
+fn vote_on_poll_expired() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    let expiration_diff = 10;
+
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            Some(generate_poll(expiration_diff)),
+            Ok(()),
+        );
+        change_current_time(expiration_diff + 1);
+        vote_on_poll_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            thread_id,
+            1,
+            Err(Error::<Runtime>::PollCommitExpired.into()),
         );
     });
 }
 
-// Invalid id passed:
-// -----------------------------------------------------------------------------
+/*
+ ** moderate_thread
+ */
 
 #[test]
-fn cannot_create_subcategory_with_invalid_parent_category_id() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        assert_create_category(
+// test if thread moderator registered as valid moderator
+fn moderate_thread_origin_ok() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let moderator_id = forum_lead;
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderator_id,
+            category_id,
+            true,
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        moderate_thread_mock(
             origin,
-            Some(INVLAID_CATEGORY_ID),
-            Err(ERROR_CATEGORY_DOES_NOT_EXIST),
+            moderator_id,
+            category_id,
+            thread_id,
+            good_moderation_rationale(),
+            Ok(()),
         );
     });
 }
 
-#[test]
-fn cannot_create_thread_with_invalid_category_id() {
-    let config = default_genesis_config();
-
-    build_test_externalities(config).execute_with(|| {
-        CreateThreadFixture {
-            origin: create_forum_member(),
-            category_id: INVLAID_CATEGORY_ID,
-            title: good_thread_title(),
-            text: good_thread_text(),
-            result: Err(ERROR_CATEGORY_DOES_NOT_EXIST),
-        }
-        .call_and_assert();
-    });
-}
+/*
+ ** add_post
+ */
 
 #[test]
-fn cannot_create_post_with_invalid_thread_id() {
-    let config = default_genesis_config();
-
-    build_test_externalities(config).execute_with(|| {
-        CreatePostFixture {
-            origin: create_forum_member(),
-            thread_id: INVLAID_THREAD_ID,
-            text: good_post_text(),
-            result: Err(ERROR_THREAD_DOES_NOT_EXIST),
-        }
-        .call_and_assert();
-    });
+// test if post origin registered as forum user
+fn add_post_origin() {
+    let origins = vec![FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_ORIGIN];
+    let results = vec![
+        Ok(()),
+        Err(Error::<Runtime>::ForumUserIdNotMatchAccount.into()),
+    ];
+    for index in 0..origins.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            let category_id = create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+
+            let thread_id = create_thread_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+                Ok(()),
+            );
+            create_post_mock(
+                origins[index].clone(),
+                forum_lead,
+                category_id,
+                thread_id,
+                good_post_text(),
+                results[index],
+            );
+        });
+    }
 }
 
 #[test]
-fn cannot_moderate_thread_with_invalid_id() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+// test if post text can be edited by author
+fn edit_post_text() {
+    let forum_users = [NOT_FORUM_LEAD_ORIGIN_ID, NOT_FORUM_LEAD_2_ORIGIN_ID];
+    let origins = [NOT_FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_2_ORIGIN];
+
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+
+    with_test_externalities(|| {
+        // prepare category and thread
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        assert_err!(
-            moderate_thread(origin, INVLAID_THREAD_ID, good_rationale()),
-            ERROR_THREAD_DOES_NOT_EXIST
+        // create post by author
+        let post_id = create_post_mock(
+            origins[0].clone(),
+            forum_users[0],
+            category_id,
+            thread_id,
+            good_post_text(),
+            Ok(()),
         );
-    });
-}
 
-#[test]
-fn cannot_moderate_post_with_invalid_id() {
-    let config = default_genesis_config();
-    let origin = OriginType::Signed(config.forum_sudo);
+        // check author can edit text
+        edit_post_text_mock(
+            origins[0].clone(),
+            forum_users[0],
+            category_id,
+            thread_id,
+            post_id,
+            good_post_new_text(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        assert_err!(
-            moderate_post(origin, INVLAID_POST_ID, good_rationale()),
-            ERROR_POST_DOES_NOT_EXIST
+        // check non-author is forbidden from editing text
+        edit_post_text_mock(
+            origins[1].clone(),
+            forum_users[1],
+            category_id,
+            thread_id,
+            post_id,
+            good_post_new_text(),
+            Err(Error::<Runtime>::AccountDoesNotMatchPostAuthor.into()),
         );
     });
 }
 
-// Successfull extrinsics
-// -----------------------------------------------------------------------------
-
+/*
+ ** react_post
+ */
 #[test]
-fn archive_then_unarchive_category_successfully() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
+// test if post react take effect
+fn react_post() {
+    // three reations to post, test them one by one.
+    let reactions = vec![0, 1, 2];
+    for index in 0..reactions.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = FORUM_LEAD_ORIGIN;
+
+        with_test_externalities(|| {
+            let category_id = create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+
+            let thread_id = create_thread_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+                Ok(()),
+            );
+            let post_id = create_post_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                thread_id,
+                good_post_text(),
+                Ok(()),
+            );
+            react_post_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                thread_id,
+                post_id,
+                reactions[index],
+                Ok(()),
+            );
+        });
+    }
+}
 
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        assert_ok!(archive_category(forum_sudo.clone(), category_id.clone(),));
-        // TODO get category by id and assert archived == true.
+/*
+ ** moderate_post
+ */
 
-        assert_ok!(unarchive_category(forum_sudo, category_id,));
-        // TODO get category by id and assert archived == false.
-    });
+#[test]
+// test if post moderator registered
+fn moderate_post_origin() {
+    let origins = vec![FORUM_LEAD_ORIGIN, NOT_FORUM_LEAD_ORIGIN];
+    let results = vec![
+        Ok(()),
+        Err(Error::<Runtime>::ModeratorIdNotMatchAccount.into()),
+    ];
+    for index in 0..origins.len() {
+        let forum_lead = FORUM_LEAD_ORIGIN_ID;
+        let origin = OriginType::Signed(forum_lead);
+        with_test_externalities(|| {
+            let moderator_id = forum_lead;
+
+            let category_id = create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                Ok(()),
+            );
+            update_category_membership_of_moderator_mock(
+                origin.clone(),
+                moderator_id,
+                category_id,
+                true,
+                Ok(()),
+            );
+
+            let thread_id = create_thread_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+                Ok(()),
+            );
+            let post_id = create_post_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                thread_id,
+                good_post_text(),
+                Ok(()),
+            );
+            moderate_post_mock(
+                origins[index].clone(),
+                moderator_id,
+                category_id,
+                thread_id,
+                post_id,
+                good_moderation_rationale(),
+                results[index],
+            );
+        });
+    }
 }
 
 #[test]
-fn delete_then_undelete_category_successfully() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        assert_ok!(delete_category(forum_sudo.clone(), category_id.clone(),));
-        // TODO get category by id and assert deleted == true.
-
-        assert_ok!(undelete_category(forum_sudo.clone(), category_id.clone(),));
-        // TODO get category by id and assert deleted == false.
-    });
-}
-
-// TODO Consider to fix the logic of the forum module
-// #[test]
-// fn cannot_unarchive_not_archived_category() {
-//     let config = default_genesis_config();
-//     let forum_sudo = OriginType::Signed(config.forum_sudo);
-
-//     build_test_externalities(config).execute_with(|| {
-//         let category_id = create_root_category(forum_sudo.clone());
-
-//         // TODO bug in a logic! it should not be possible. !!!
-
-//         assert_err!(
-//             archive_category(
-//                 forum_sudo.clone(),
-//                 category_id.clone(),
-//             ),
-//             "... TODO expect error ..."
-//         );
-//     });
-// }
-
-// TODO Consider to fix the logic of the forum module
-// #[test]
-// fn cannot_undelete_not_deleted_category() {
-//     let config = default_genesis_config();
-//     let forum_sudo = OriginType::Signed(config.forum_sudo);
-
-//     build_test_externalities(config).execute_with(|| {
-//         let category_id = create_root_category(forum_sudo.clone());
-//         assert_err!(
-//             delete_category(
-//                 forum_sudo.clone(),
-//                 category_id.clone(),
-//             ),
-//             "... TODO expect error ..."
-//         );
-//     });
-// }
-
-// With archived / deleted category, moderated thread
-// -----------------------------------------------------------------------------
-
-#[test]
-fn cannot_create_subcategory_in_archived_category() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        assert_ok!(archive_category(forum_sudo.clone(), category_id.clone(),));
-        assert_create_category(
-            forum_sudo,
-            Some(category_id),
-            Err(ERROR_ANCESTOR_CATEGORY_IMMUTABLE),
+fn set_stickied_threads_ok() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let moderator_id = forum_lead;
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderator_id,
+            category_id,
+            true,
+            Ok(()),
         );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        set_stickied_threads_mock(origin, moderator_id, category_id, vec![thread_id], Ok(()));
     });
 }
 
 #[test]
-fn cannot_create_subcategory_in_deleted_category() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
+fn set_stickied_threads_wrong_moderator() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let moderator_id = forum_lead;
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        assert_ok!(delete_category(forum_sudo.clone(), category_id.clone(),));
-        assert_create_category(
-            forum_sudo,
-            Some(category_id),
-            Err(ERROR_ANCESTOR_CATEGORY_IMMUTABLE),
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        set_stickied_threads_mock(
+            origin,
+            moderator_id,
+            category_id,
+            vec![thread_id],
+            Err(Error::<Runtime>::ModeratorCantUpdateCategory.into()),
         );
     });
 }
 
 #[test]
-fn cannot_create_thread_in_archived_category() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        assert_ok!(archive_category(forum_sudo.clone(), category_id.clone(),));
-        assert_create_thread(
-            create_forum_member(),
+fn set_stickied_threads_thread_not_exists() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+    with_test_externalities(|| {
+        let moderator_id = forum_lead;
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        update_category_membership_of_moderator_mock(
+            origin.clone(),
+            moderator_id,
+            category_id,
+            true,
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
+        let wrong_thread_id = thread_id + 1;
+        set_stickied_threads_mock(
+            origin,
+            moderator_id,
             category_id,
-            Err(ERROR_ANCESTOR_CATEGORY_IMMUTABLE),
+            vec![wrong_thread_id],
+            Err(Error::<Runtime>::ThreadDoesNotExist.into()),
         );
     });
 }
 
 #[test]
-fn cannot_create_thread_in_deleted_category() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
-
+fn test_migration_not_done() {
+    let config = migration_not_done_config();
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
     build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        assert_ok!(delete_category(forum_sudo.clone(), category_id.clone(),));
-        assert_create_thread(
-            create_forum_member(),
-            category_id,
-            Err(ERROR_ANCESTOR_CATEGORY_IMMUTABLE),
+        let forum_user_id = 1;
+        let moderator_id = 1;
+        let category_id = 1;
+        let thread_id = 1;
+        let post_id = 1;
+
+        assert_err!(
+            TestForumModule::create_category(
+                mock_origin(origin.clone()),
+                None,
+                good_category_title(),
+                good_category_description()
+            ),
+            Error::<Runtime>::DataMigrationNotDone,
         );
-    });
-}
 
-#[test]
-fn cannot_create_post_in_thread_of_archived_category() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
+        assert_err!(
+            TestForumModule::create_thread(
+                mock_origin(origin.clone()),
+                forum_user_id,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+            ),
+            Error::<Runtime>::DataMigrationNotDone,
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        let thread_id = TestForumModule::next_thread_id();
-        assert_create_thread(create_forum_member(), category_id, Ok(()));
-        assert_ok!(archive_category(forum_sudo.clone(), category_id.clone(),));
-        assert_create_post(
-            create_forum_member(),
-            thread_id,
-            Err(ERROR_ANCESTOR_CATEGORY_IMMUTABLE),
+        assert_err!(
+            TestForumModule::add_post(
+                mock_origin(origin.clone()),
+                forum_user_id,
+                category_id,
+                thread_id,
+                good_post_text(),
+            ),
+            Error::<Runtime>::DataMigrationNotDone,
         );
-    });
-}
 
-#[test]
-fn cannot_create_post_in_thread_of_deleted_category() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
+        assert_err!(
+            TestForumModule::moderate_thread(
+                mock_origin(origin.clone()),
+                PrivilegedActor::Moderator(moderator_id),
+                category_id,
+                thread_id,
+                good_moderation_rationale(),
+            ),
+            Error::<Runtime>::DataMigrationNotDone,
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let category_id = create_root_category(forum_sudo.clone());
-        let thread_id = TestForumModule::next_thread_id();
-        assert_create_thread(create_forum_member(), category_id, Ok(()));
-        assert_ok!(delete_category(forum_sudo.clone(), category_id.clone(),));
-        assert_create_post(
-            create_forum_member(),
-            thread_id,
-            Err(ERROR_ANCESTOR_CATEGORY_IMMUTABLE),
+        assert_err!(
+            TestForumModule::moderate_post(
+                mock_origin(origin.clone()),
+                PrivilegedActor::Moderator(moderator_id),
+                category_id,
+                thread_id,
+                post_id,
+                good_moderation_rationale(),
+            ),
+            Error::<Runtime>::DataMigrationNotDone,
         );
     });
 }
 
 #[test]
-fn cannot_create_post_in_moderated_thread() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
-
-    build_test_externalities(config).execute_with(|| {
-        let (_, _, thread_id) = create_root_category_and_thread(forum_sudo.clone());
-        assert_ok!(moderate_thread(
-            forum_sudo,
-            thread_id.clone(),
-            good_rationale()
-        ));
-        assert_create_post(
-            create_forum_member(),
-            thread_id,
-            Err(ERROR_THREAD_MODERATED),
+// test storage limits are enforced
+fn storage_limit_checks() {
+    let forum_lead = FORUM_LEAD_ORIGIN_ID;
+    let origin = OriginType::Signed(forum_lead);
+
+    // test MaxSubcategories and MaxThreadsInCategory
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
         );
+
+        // test max subcategories limit
+        let max = <<<Runtime as Trait>::MapLimits as StorageLimits>::MaxSubcategories>::get();
+        for i in 0..max {
+            create_category_mock(
+                origin.clone(),
+                Some(category_id),
+                good_category_title(),
+                good_category_description(),
+                match i {
+                    _ if i == max => Err(Error::<Runtime>::MapSizeLimit.into()),
+                    _ => Ok(()),
+                },
+            );
+        }
+
+        // test max threads in category
+        let max = <<<Runtime as Trait>::MapLimits as StorageLimits>::MaxThreadsInCategory>::get();
+        for i in 0..max {
+            create_thread_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                good_thread_title(),
+                good_thread_text(),
+                None,
+                match i {
+                    _ if i == max => Err(Error::<Runtime>::MapSizeLimit.into()),
+                    _ => Ok(()),
+                },
+            );
+        }
     });
-}
 
-#[test]
-fn cannot_edit_post_in_moderated_thread() {
-    let config = default_genesis_config();
-    let forum_sudo = OriginType::Signed(config.forum_sudo);
+    // test MaxPostsInThread
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
+        );
+        let thread_id = create_thread_mock(
+            origin.clone(),
+            forum_lead,
+            category_id,
+            good_thread_title(),
+            good_thread_text(),
+            None,
+            Ok(()),
+        );
 
-    build_test_externalities(config).execute_with(|| {
-        let (member_origin, _, thread_id, post_id) =
-            create_root_category_and_thread_and_post(forum_sudo.clone());
-        assert_ok!(moderate_thread(forum_sudo, thread_id, good_rationale()));
-        assert_err!(
-            TestForumModule::edit_post_text(mock_origin(member_origin), post_id, good_rationale()),
-            ERROR_THREAD_MODERATED
+        // test max posts in thread
+        let max = <<<Runtime as Trait>::MapLimits as StorageLimits>::MaxPostsInThread>::get();
+        // starting from 1 because create_thread_mock creates one post by itself
+        for i in 1..max {
+            create_post_mock(
+                origin.clone(),
+                forum_lead,
+                category_id,
+                thread_id,
+                good_post_text(),
+                match i {
+                    _ if i == max => Err(Error::<Runtime>::MapSizeLimit.into()),
+                    _ => Ok(()),
+                },
+            );
+        }
+    });
+
+    // test MaxModeratorsForCategory
+    with_test_externalities(|| {
+        let category_id = create_category_mock(
+            origin.clone(),
+            None,
+            good_category_title(),
+            good_category_description(),
+            Ok(()),
         );
+
+        let max: usize =
+            <<<Runtime as Trait>::MapLimits as StorageLimits>::MaxModeratorsForCategory>::get()
+                as usize;
+        for i in 0..max {
+            let moderator_id = EXTRA_MODERATORS[i];
+            update_category_membership_of_moderator_mock(
+                origin.clone(),
+                moderator_id,
+                category_id,
+                true,
+                match i {
+                    _ if i == max => Err(Error::<Runtime>::MapSizeLimit.into()),
+                    _ => Ok(()),
+                },
+            );
+        }
     });
-}
 
-// TODO impl
-// #[test]
-// fn cannot_edit_moderated_post() {}
+    // test MaxCategories
+    with_test_externalities(|| {
+        let max: usize =
+            <<<Runtime as Trait>::MapLimits as StorageLimits>::MaxPostsInThread>::get() as usize;
+        for i in 0..max {
+            create_category_mock(
+                origin.clone(),
+                None,
+                good_category_title(),
+                good_category_description(),
+                match i {
+                    _ if i == max => Err(Error::<Runtime>::MapSizeLimit.into()),
+                    _ => Ok(()),
+                },
+            );
+        }
+    });
+}

+ 0 - 1
runtime-modules/proposals/codex/src/lib.rs

@@ -40,7 +40,6 @@
 //! - [proposals discussion](../substrate_proposals_discussion_module/index.html)
 //! - [membership](../substrate_membership_module/index.html)
 //! - [governance](../substrate_governance_module/index.html)
-//! - [content_working_group](../substrate_content_working_group_module/index.html)
 //!
 //! ### Notes
 //! The module uses [ProposalEncoder](./trait.ProposalEncoder.html) to encode the proposal using

+ 55 - 1
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -1,7 +1,7 @@
 #![cfg(test)]
 
 use frame_support::traits::LockIdentifier;
-use frame_support::{impl_outer_dispatch, impl_outer_origin, parameter_types};
+use frame_support::{impl_outer_dispatch, impl_outer_origin, parameter_types, weights::Weight};
 pub use frame_system;
 use sp_core::H256;
 use sp_runtime::curve::PiecewiseLinear;
@@ -84,6 +84,8 @@ parameter_types! {
     pub const LockId: LockIdentifier = [2; 8];
 }
 
+pub struct MockProposalsEngineWeight;
+
 impl proposals_engine::Trait for Test {
     type Event = ();
     type ProposerOriginValidator = ();
@@ -98,6 +100,41 @@ impl proposals_engine::Trait for Test {
     type MaxActiveProposalLimit = MaxActiveProposalLimit;
     type DispatchableCallCode = crate::Call<Test>;
     type ProposalObserver = crate::Module<Test>;
+    type WeightInfo = MockProposalsEngineWeight;
+}
+
+impl proposals_engine::WeightInfo for MockProposalsEngineWeight {
+    fn vote(_: u32) -> Weight {
+        0
+    }
+
+    fn cancel_proposal(_: u32) -> Weight {
+        0
+    }
+
+    fn veto_proposal() -> Weight {
+        0
+    }
+
+    fn on_initialize_immediate_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_pending_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_approved_pending_constitutionality(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_rejected(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_slashed(_: u32) -> Weight {
+        0
+    }
 }
 
 impl Default for crate::Call<Test> {
@@ -130,6 +167,8 @@ parameter_types! {
     pub const MaxWhiteListSize: u32 = 20;
 }
 
+pub struct MockProposalsDiscussionWeight;
+
 impl proposals_discussion::Trait for Test {
     type Event = ();
     type AuthorOriginValidator = ();
@@ -137,6 +176,21 @@ impl proposals_discussion::Trait for Test {
     type ThreadId = u64;
     type PostId = u64;
     type MaxWhiteListSize = MaxWhiteListSize;
+    type WeightInfo = MockProposalsDiscussionWeight;
+}
+
+impl proposals_discussion::WeightInfo for MockProposalsDiscussionWeight {
+    fn add_post(_: u32) -> Weight {
+        0
+    }
+
+    fn update_post() -> Weight {
+        0
+    }
+
+    fn change_thread_mode(_: u32) -> Weight {
+        0
+    }
 }
 
 pub struct MockVotersParameters;

+ 2 - 2
runtime-modules/proposals/discussion/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-proposals-discussion'
-version = '4.0.0'
+version = '4.0.1'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -32,4 +32,4 @@ std = [
 	'frame-system/std',
     'membership/std',
     'common/std',
-]
+]

+ 4 - 7
runtime-modules/proposals/discussion/src/benchmarking.rs

@@ -1,15 +1,14 @@
 #![cfg(feature = "runtime-benchmarks")]
 use super::*;
 use crate::Module as ProposalsDiscussion;
-use core::convert::TryInto;
 use frame_benchmarking::{account, benchmarks};
+use frame_system::EventRecord;
+use frame_system::Module as System;
+use frame_system::RawOrigin;
 use membership::Module as Membership;
 use sp_std::cmp::min;
+use sp_std::convert::TryInto;
 use sp_std::prelude::*;
-use system as frame_system;
-use system::EventRecord;
-use system::Module as System;
-use system::RawOrigin;
 
 const SEED: u32 = 0;
 
@@ -115,8 +114,6 @@ benchmarks! {
     }
 
     update_post {
-        // TODO: this parameter doesn't affect the running time
-        // maybe we should bound it here with the UI limit?
         let j in 0 .. MAX_BYTES;
 
         // We do this to ignore the id 0 because the `Test` runtime

+ 54 - 4
runtime-modules/proposals/discussion/src/lib.rs

@@ -54,7 +54,9 @@ mod types;
 use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_support::sp_runtime::SaturatedConversion;
 use frame_support::traits::Get;
-use frame_support::{decl_error, decl_event, decl_module, decl_storage, ensure, Parameter};
+use frame_support::{
+    decl_error, decl_event, decl_module, decl_storage, ensure, weights::Weight, Parameter,
+};
 use sp_std::clone::Clone;
 use sp_std::vec::Vec;
 
@@ -65,6 +67,16 @@ pub use types::ThreadMode;
 
 type MemberId<T> = <T as membership::Trait>::MemberId;
 
+/// Proposals discussion WeightInfo.
+/// Note: This was auto generated through the benchmark CLI using the `--weight-trait` flag
+pub trait WeightInfo {
+    fn add_post(i: u32) -> Weight; // Note: since parameter doesn't affect weight it's discarded
+    fn update_post() -> Weight; // Note: since parameter doesn't affect weight it's discarded
+    fn change_thread_mode(i: u32) -> Weight;
+}
+
+type WeightInfoDiscussion<T> = <T as Trait>::WeightInfo;
+
 decl_event!(
     /// Proposals engine events
     pub enum Event<T>
@@ -112,6 +124,9 @@ pub trait Trait: frame_system::Trait + membership::Trait {
 
     /// Defines author list size limit for the Closed discussion.
     type MaxWhiteListSize: Get<u32>;
+
+    /// Weight information for extrinsics in this pallet.
+    type WeightInfo: WeightInfo;
 }
 
 decl_error! {
@@ -170,7 +185,18 @@ decl_module! {
         fn deposit_event() = default;
 
         /// Adds a post with author origin check.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (W)` where:
+        /// - `W` is the number of whitelisted members for `thread_id`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoDiscussion::<T>::add_post(
+            T::MaxWhiteListSize::get(),
+        )]
         pub fn add_post(
             origin,
             post_author_id: MemberId<T>,
@@ -202,7 +228,15 @@ decl_module! {
        }
 
         /// Updates a post with author origin check. Update attempts number is limited.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (1)` doesn't depend on the state or parameters
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoDiscussion::<T>::update_post()]
         pub fn update_post(
             origin,
             post_author_id: MemberId<T>,
@@ -228,7 +262,22 @@ decl_module! {
        }
 
         /// Changes thread permission mode.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (W)` if ThreadMode is close or O(1) otherwise where:
+        /// - `W` is the number of whitelisted members in `mode`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoDiscussion::<T>::change_thread_mode(
+            if let ThreadMode::Closed(ref list) = mode {
+                list.len().saturated_into()
+            } else {
+                0
+            }
+        )]
         pub fn change_thread_mode(
             origin,
             member_id: MemberId<T>,
@@ -260,6 +309,7 @@ decl_module! {
             <ThreadById<T>>::mutate(thread_id, |thread| {
                 thread.mode = mode.clone();
             });
+
             Self::deposit_event(RawEvent::ThreadModeChanged(thread_id, mode));
        }
     }

+ 17 - 1
runtime-modules/proposals/discussion/src/tests/mock.rs

@@ -3,7 +3,7 @@
 pub use frame_system;
 
 use frame_support::traits::{OnFinalize, OnInitialize};
-use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
 use sp_core::H256;
 use sp_runtime::{
     testing::Header,
@@ -12,6 +12,7 @@ use sp_runtime::{
 };
 
 use crate::ActorOriginValidator;
+use crate::WeightInfo;
 
 impl_outer_origin! {
     pub enum Origin for Test {}
@@ -87,6 +88,21 @@ impl crate::Trait for Test {
     type ThreadId = u64;
     type PostId = u64;
     type MaxWhiteListSize = MaxWhiteListSize;
+    type WeightInfo = ();
+}
+
+impl WeightInfo for () {
+    fn add_post(_: u32) -> Weight {
+        0
+    }
+
+    fn update_post() -> Weight {
+        0
+    }
+
+    fn change_thread_mode(_: u32) -> Weight {
+        0
+    }
 }
 
 impl ActorOriginValidator<Origin, u64, u64> for () {

+ 16 - 1
runtime-modules/proposals/engine/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-proposals-engine'
-version = '4.0.0'
+version = '4.0.1'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -18,12 +18,27 @@ membership = { package = 'pallet-membership', default-features = false, path = '
 common = { package = 'pallet-common', default-features = false, path = '../../common'}
 staking-handler = { package = 'staking-handler', default-features = false, path = '../../staking-handler'}
 
+# Benchmark dependencies.
+frame-benchmarking = { package = 'frame-benchmarking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
+governance = { package = 'pallet-governance', default-features = false, path = '../../governance', optional = true}
+recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../../recurring-reward', optional = true}
+minting = { package = 'pallet-token-mint', default-features = false, path = '../../token-minting', optional = true}
+
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+governance = { package = 'pallet-governance', default-features = false, path = '../../governance'}
+recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../../recurring-reward'}
+minting = { package = 'pallet-token-mint', default-features = false, path = '../../token-minting'}
 
 [features]
 default = ['std']
+runtime-benchmarks = [
+    'frame-benchmarking',
+    'governance',
+    'recurringrewards',
+    'minting',
+]
 std = [
 	'serde',
 	'codec/std',

+ 631 - 0
runtime-modules/proposals/engine/src/benchmarking.rs

@@ -0,0 +1,631 @@
+#![cfg(feature = "runtime-benchmarks")]
+use super::*;
+use crate::Module as ProposalsEngine;
+use balances::Module as Balances;
+use core::convert::TryInto;
+use frame_benchmarking::{account, benchmarks};
+use frame_support::traits::{Currency, OnFinalize, OnInitialize};
+use frame_system::EventRecord;
+use frame_system::Module as System;
+use frame_system::RawOrigin;
+use governance::council::Module as Council;
+use membership::Module as Membership;
+use sp_runtime::traits::{Bounded, One};
+use sp_std::cmp::{max, min};
+use sp_std::prelude::*;
+
+const SEED: u32 = 0;
+
+fn get_byte(num: u32, byte_number: u8) -> u8 {
+    ((num & (0xff << (8 * byte_number))) >> 8 * byte_number) as u8
+}
+
+// Method to generate a distintic valid handle
+// for a membership. For each index.
+fn handle_from_id<T: membership::Trait>(id: u32) -> Vec<u8> {
+    let min_handle_length = Membership::<T>::min_handle_length();
+
+    let mut handle = vec![];
+
+    for i in 0..min(Membership::<T>::max_handle_length().try_into().unwrap(), 4) {
+        handle.push(get_byte(id, i));
+    }
+
+    while handle.len() < (min_handle_length as usize) {
+        handle.push(0u8);
+    }
+
+    handle
+}
+
+fn assert_last_event<T: Trait>(generic_event: <T as Trait>::Event) {
+    let events = System::<T>::events();
+    let system_event: <T as frame_system::Trait>::Event = generic_event.into();
+    assert!(
+        events.len() > 0,
+        "If you are checking for last event there must be at least 1 event"
+    );
+    let EventRecord { event, .. } = &events[events.len() - 1];
+    assert_eq!(event, &system_event);
+}
+
+fn assert_in_events<T: Trait>(generic_event: <T as Trait>::Event) {
+    let events = System::<T>::events();
+    let system_event: <T as frame_system::Trait>::Event = generic_event.into();
+
+    assert!(
+        events.len() > 0,
+        "If you are checking for last event there must be at least 1 event"
+    );
+
+    assert!(events.iter().any(|event| {
+        let EventRecord { event, .. } = event;
+        event == &system_event
+    }));
+}
+
+fn member_funded_account<T: Trait>(name: &'static str, id: u32) -> (T::AccountId, T::MemberId) {
+    let account_id = account::<T::AccountId>(name, id, SEED);
+    let handle = handle_from_id::<T>(id);
+
+    let authority_account = account::<T::AccountId>(name, 0, SEED);
+
+    Membership::<T>::set_screening_authority(RawOrigin::Root.into(), authority_account.clone())
+        .unwrap();
+
+    Membership::<T>::add_screened_member(
+        RawOrigin::Signed(authority_account.clone()).into(),
+        account_id.clone(),
+        Some(handle),
+        None,
+        None,
+    )
+    .unwrap();
+
+    let _ = Balances::<T>::make_free_balance_be(&account_id, T::Balance::max_value());
+
+    (account_id, T::MemberId::from(id.try_into().unwrap()))
+}
+
+fn create_proposal<T: Trait>(
+    id: u32,
+    proposal_number: u32,
+    constitutionality: u32,
+    grace_period: u32,
+) -> (T::AccountId, T::MemberId, T::ProposalId) {
+    let (account_id, member_id) = member_funded_account::<T>("member", id);
+
+    let proposal_parameters = ProposalParameters {
+        voting_period: T::BlockNumber::from(1),
+        grace_period: T::BlockNumber::from(grace_period),
+        approval_quorum_percentage: 1,
+        approval_threshold_percentage: 1,
+        slashing_quorum_percentage: 0,
+        slashing_threshold_percentage: 1,
+        required_stake: Some(T::Balance::max_value()),
+        constitutionality,
+    };
+
+    let call_code = vec![];
+
+    let proposal_creation_parameters = ProposalCreationParameters {
+        account_id: account_id.clone(),
+        proposer_id: member_id.clone(),
+        proposal_parameters,
+        title: vec![0u8],
+        description: vec![0u8],
+        staking_account_id: Some(account_id.clone()),
+        encoded_dispatchable_call_code: call_code.clone(),
+        exact_execution_block: None,
+    };
+
+    let proposal_id = ProposalsEngine::<T>::create_proposal(proposal_creation_parameters).unwrap();
+
+    assert!(
+        Proposals::<T>::contains_key(proposal_id),
+        "Proposal not created"
+    );
+    assert!(
+        DispatchableCallCode::<T>::contains_key(proposal_id),
+        "Dispatchable code not added"
+    );
+
+    assert_eq!(
+        ProposalsEngine::<T>::proposal_codes(proposal_id),
+        call_code,
+        "Dispatchable code does not match"
+    );
+
+    assert_eq!(
+        ProposalsEngine::<T>::proposal_count(),
+        proposal_number,
+        "Not correct number of proposals stored"
+    );
+
+    // We assume here that active proposals == number of proposals
+    assert_eq!(
+        ProposalsEngine::<T>::active_proposal_count(),
+        proposal_number,
+        "Created proposal not active"
+    );
+
+    assert_eq!(
+        T::StakingHandler::current_stake(&account_id),
+        T::Balance::max_value()
+    );
+
+    (account_id, member_id, proposal_id)
+}
+
+fn create_multiple_finalized_proposals<T: Trait + governance::council::Trait>(
+    number_of_proposals: u32,
+    constitutionality: u32,
+    vote_kind: VoteKind,
+    total_voters: u32,
+    grace_period: u32,
+) -> (Vec<T::AccountId>, Vec<T::ProposalId>) {
+    let mut voters = Vec::new();
+    for i in 0..total_voters {
+        voters.push(member_funded_account::<T>("voter", i));
+    }
+
+    Council::<T>::set_council(
+        RawOrigin::Root.into(),
+        voters
+            .iter()
+            .map(|(account_id, _)| account_id.clone())
+            .collect(),
+    )
+    .unwrap();
+
+    let mut proposers = Vec::new();
+    let mut proposals = Vec::new();
+    for id in total_voters..number_of_proposals + total_voters {
+        let (proposer_account_id, _, proposal_id) =
+            create_proposal::<T>(id, id - total_voters + 1, constitutionality, grace_period);
+        proposers.push(proposer_account_id);
+        proposals.push(proposal_id);
+
+        for (voter_id, member_id) in voters.clone() {
+            ProposalsEngine::<T>::vote(
+                RawOrigin::Signed(voter_id.clone()).into(),
+                member_id,
+                proposal_id,
+                vote_kind.clone(),
+                vec![0u8],
+            )
+            .unwrap()
+        }
+    }
+
+    (proposers, proposals)
+}
+
+const MAX_BYTES: u32 = 16384;
+
+benchmarks! {
+    // Note: this is the syntax for this macro can't use "+"
+    where_clause {
+        where T: governance::council::Trait
+    }
+
+    _ { }
+
+    vote {
+        let i in 0 .. MAX_BYTES;
+
+        let (_, _, proposal_id) = create_proposal::<T>(0, 1, 0, 0);
+
+        let (account_voter_id, member_voter_id) = member_funded_account::<T>("voter", 1);
+
+        Council::<T>::set_council(RawOrigin::Root.into(), vec![account_voter_id.clone()]).unwrap();
+    }: _ (
+            RawOrigin::Signed(account_voter_id),
+            member_voter_id,
+            proposal_id,
+            VoteKind::Approve,
+            vec![0u8; i.try_into().unwrap()]
+        )
+    verify {
+        assert!(Proposals::<T>::contains_key(proposal_id), "Proposal should still exist");
+
+        let voting_results = ProposalsEngine::<T>::proposals(proposal_id).voting_results;
+
+        assert_eq!(
+          voting_results,
+          VotingResults{ approvals: 1, abstentions: 0, rejections: 0, slashes: 0 },
+          "There should only be 1 approval"
+        );
+
+        assert!(
+          VoteExistsByProposalByVoter::<T>::contains_key(proposal_id, member_voter_id),
+          "Voter not added to existing voters"
+        );
+
+        assert_eq!(
+          ProposalsEngine::<T>::vote_by_proposal_by_voter(proposal_id, member_voter_id),
+          VoteKind::Approve,
+          "Stored vote doesn't match"
+        );
+
+        assert_last_event::<T>(
+            RawEvent::Voted(member_voter_id, proposal_id, VoteKind::Approve).into()
+        );
+    }
+
+    cancel_proposal {
+        let i in 1 .. T::MaxLocks::get();
+
+        let (account_id, member_id, proposal_id) = create_proposal::<T>(0, 1, 0, 0);
+
+        for lock_number in 1 .. i {
+            let (locked_account_id, _) = member_funded_account::<T>("locked_member", lock_number);
+            T::StakingHandler::set_stake(&locked_account_id, One::one()).unwrap();
+        }
+
+    }: _ (RawOrigin::Signed(account_id.clone()), member_id, proposal_id)
+    verify {
+        assert!(!Proposals::<T>::contains_key(proposal_id), "Proposal still in storage");
+
+        assert!(
+            !DispatchableCallCode::<T>::contains_key(proposal_id),
+            "Proposal code still in storage"
+        );
+
+        assert_eq!(ProposalsEngine::<T>::active_proposal_count(), 0, "Proposal still active");
+
+        assert_eq!(
+            Balances::<T>::usable_balance(account_id),
+            T::Balance::max_value() - T::CancellationFee::get(),
+            "Balance not slashed"
+        );
+
+        assert_last_event::<T>(
+            RawEvent::ProposalDecisionMade(proposal_id, ProposalDecision::Canceled).into()
+        );
+    }
+
+    veto_proposal {
+        let (account_id, _, proposal_id) = create_proposal::<T>(0, 1, 0, 0);
+    }: _ (RawOrigin::Root, proposal_id)
+    verify {
+        assert!(!Proposals::<T>::contains_key(proposal_id), "Proposal still in storage");
+
+        assert!(
+            !DispatchableCallCode::<T>::contains_key(proposal_id),
+            "Proposal code still in storage"
+        );
+
+        assert_eq!(ProposalsEngine::<T>::active_proposal_count(), 0, "Proposal still active");
+
+        assert_eq!(
+            Balances::<T>::usable_balance(account_id),
+            T::Balance::max_value(),
+            "Vetoed proposals shouldn't be slashed"
+        );
+
+        assert_last_event::<T>(
+            RawEvent::ProposalDecisionMade(proposal_id, ProposalDecision::Vetoed).into()
+        );
+    }
+
+    // We use that branches for decode failing, failing and passing are very similar
+    // without any different DB access in each. To use the failing/passing branch
+    // we need to include the EncodeProposal trait from codex which depends on engine
+    // therefore we should move it to a common crate
+    on_initialize_immediate_execution_decode_fails {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Approve,
+            1,
+            0,
+        );
+
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Should've unlocked all stake"
+            );
+        }
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            0,
+            "Proposals should no longer be active"
+        );
+
+        for proposal_id in proposals.iter() {
+            assert!(
+                !Proposals::<T>::contains_key(proposal_id),
+                "Proposals should've been removed"
+            );
+
+            assert!(
+                !DispatchableCallCode::<T>::contains_key(proposal_id),
+                "Dispatchable code should've been removed"
+            );
+        }
+
+        if cfg!(test) {
+            for proposal_id in proposals.iter() {
+                assert_in_events::<T>(
+                    RawEvent::ProposalExecuted(
+                        proposal_id.clone(),
+                        ExecutionStatus::failed_execution("Not enough data to fill buffer")).into()
+                );
+            }
+        }
+    }
+
+    on_initialize_pending_execution_decode_fails {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Approve,
+            1,
+            1,
+        );
+
+        let mut current_block_number = System::<T>::block_number();
+
+        System::<T>::on_finalize(current_block_number);
+        System::<T>::on_finalize(current_block_number);
+
+        current_block_number += One::one();
+
+        System::<T>::on_initialize(current_block_number);
+        ProposalsEngine::<T>::on_initialize(current_block_number);
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            i,
+            "Proposals should still be active"
+        );
+
+        for proposal_id in proposals.iter() {
+            assert!(
+                Proposals::<T>::contains_key(proposal_id),
+                "All proposals should still be stored"
+            );
+
+            assert!(
+                DispatchableCallCode::<T>::contains_key(proposal_id),
+                "All dispatchable call code should still be stored"
+            );
+        }
+
+    }: { ProposalsEngine::<T>::on_initialize(current_block_number) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Should've unlocked all stake"
+            );
+        }
+
+        assert_eq!(ProposalsEngine::<T>::active_proposal_count(), 0, "Proposals should no longer be active");
+        for proposal_id in proposals.iter() {
+            assert!(!Proposals::<T>::contains_key(proposal_id), "Proposals should've been removed");
+            assert!(!DispatchableCallCode::<T>::contains_key(proposal_id), "Dispatchable code should've been removed");
+        }
+
+        if cfg!(test) {
+            for proposal_id in proposals.iter() {
+                assert_in_events::<T>(
+                    RawEvent::ProposalExecuted(
+                        proposal_id.clone(),
+                        ExecutionStatus::failed_execution("Not enough data to fill buffer")).into()
+                );
+            }
+        }
+    }
+
+    on_initialize_approved_pending_constitutionality {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            2,
+            VoteKind::Approve,
+            1,
+            0,
+        );
+
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_ne!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Should've still stake locked"
+            );
+        }
+
+        for proposal_id in proposals.iter() {
+            assert!(
+                Proposals::<T>::contains_key(proposal_id),
+                "Proposal should still be in the store"
+            );
+            let proposal = ProposalsEngine::<T>::proposals(proposal_id);
+            let status = ProposalStatus::approved(
+                ApprovedProposalDecision::PendingConstitutionality,
+                System::<T>::block_number()
+            );
+
+            assert_eq!(proposal.status, status);
+            assert_eq!(proposal.current_constitutionality_level, 1);
+            assert_in_events::<T>(
+                RawEvent::ProposalStatusUpdated(proposal_id.clone(), status).into()
+            );
+        }
+    }
+
+    on_initialize_rejected {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Reject,
+            max(T::TotalVotersCounter::total_voters_count(), 1),
+            0,
+        );
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Shouldn't have any stake locked"
+            );
+        }
+
+        for proposal_id in proposals.iter() {
+
+            assert!(
+                !Proposals::<T>::contains_key(proposal_id),
+                "Proposal should not be in store"
+            );
+
+            assert!(
+                !DispatchableCallCode::<T>::contains_key(proposal_id),
+                "Dispatchable should not be in store"
+            );
+
+            assert_in_events::<T>(
+                RawEvent::ProposalDecisionMade(proposal_id.clone(), ProposalDecision::Rejected)
+                    .into()
+            );
+        }
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            0,
+            "There should not be any proposal left active"
+        );
+    }
+
+    on_initialize_slashed {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Slash,
+            max(T::TotalVotersCounter::total_voters_count(), 1),
+            0,
+        );
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Shouldn't have any stake locked"
+            );
+
+            assert_eq!(
+                Balances::<T>::free_balance(&proposer_account_id),
+                Zero::zero(),
+                "Should've all balance slashed"
+            );
+        }
+
+        for proposal_id in proposals.iter() {
+
+            assert!(
+                !Proposals::<T>::contains_key(proposal_id),
+                "Proposal should not be in store"
+            );
+            assert!(
+                !DispatchableCallCode::<T>::contains_key(proposal_id),
+                "Dispatchable should not be in store"
+            );
+
+            assert_in_events::<T>(
+                RawEvent::ProposalDecisionMade(
+                    proposal_id.clone(),
+                    ProposalDecision::Slashed
+                ).into()
+            );
+        }
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            0,
+            "There should not be any proposal left active"
+        );
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::mock::{initial_test_ext, Test};
+    use frame_support::assert_ok;
+
+    #[test]
+    fn test_vote() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_vote::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_cancel_proposal() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_cancel_proposal::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_veto_proposal() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_veto_proposal::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_immediate_execution_decode_fails() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_immediate_execution_decode_fails::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_approved_pending_constitutionality() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_approved_pending_constitutionality::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_pending_execution_decode_fails() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_pending_execution_decode_fails::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_rejected() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_rejected::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_slashed() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_slashed::<Test>());
+        });
+    }
+}

+ 151 - 16
runtime-modules/proposals/engine/src/lib.rs

@@ -119,6 +119,8 @@ pub use types::{
 
 pub(crate) mod types;
 
+mod benchmarking;
+
 #[cfg(test)]
 mod tests;
 
@@ -126,16 +128,32 @@ use codec::Decode;
 use frame_support::dispatch::{DispatchError, DispatchResult, UnfilteredDispatchable};
 use frame_support::storage::IterableStorageMap;
 use frame_support::traits::Get;
+use frame_support::weights::{GetDispatchInfo, Weight};
 use frame_support::{
     decl_error, decl_event, decl_module, decl_storage, ensure, Parameter, StorageDoubleMap,
 };
 use frame_system::{ensure_root, RawOrigin};
-use sp_arithmetic::traits::Zero;
+use sp_arithmetic::traits::{SaturatedConversion, Saturating, Zero};
 use sp_std::vec::Vec;
 
 use common::origin::ActorOriginValidator;
 use staking_handler::StakingHandler;
 
+/// Proposals engine WeightInfo.
+/// Note: This was auto generated through the benchmark CLI using the `--weight-trait` flag
+pub trait WeightInfo {
+    fn vote(i: u32) -> Weight;
+    fn cancel_proposal(i: u32) -> Weight;
+    fn veto_proposal() -> Weight;
+    fn on_initialize_immediate_execution_decode_fails(i: u32) -> Weight;
+    fn on_initialize_pending_execution_decode_fails(i: u32) -> Weight;
+    fn on_initialize_approved_pending_constitutionality(i: u32) -> Weight;
+    fn on_initialize_rejected(i: u32) -> Weight;
+    fn on_initialize_slashed(i: u32) -> Weight;
+}
+
+type WeightInfoEngine<T> = <T as Trait>::WeightInfo;
+
 /// Proposals engine trait.
 pub trait Trait:
     frame_system::Trait + pallet_timestamp::Trait + membership::Trait + balances::Trait
@@ -178,10 +196,16 @@ pub trait Trait:
     type MaxActiveProposalLimit: Get<u32>;
 
     /// Proposals executable code. Can be instantiated by external module Call enum members.
-    type DispatchableCallCode: Parameter + UnfilteredDispatchable<Origin = Self::Origin> + Default;
+    type DispatchableCallCode: Parameter
+        + UnfilteredDispatchable<Origin = Self::Origin>
+        + GetDispatchInfo
+        + Default;
 
     /// Proposal state change observer.
     type ProposalObserver: ProposalObserver<Self>;
+
+    /// Weight information for extrinsics in this pallet.
+    type WeightInfo: WeightInfo;
 }
 
 /// Proposal state change observer.
@@ -348,8 +372,47 @@ decl_module! {
         /// Exports const -  max simultaneous active proposals number.
         const MaxActiveProposalLimit: u32 = T::MaxActiveProposalLimit::get();
 
+        /// Block Initialization. Perform voting period check, vote result tally, approved proposals
+        /// grace period checks, and proposal execution.
+        /// # <weight>
+        ///
+        /// ## Weight
+        /// `O (P + I)` where:
+        /// - `P` is the weight of all executed proposals
+        /// - `I` is the weight of the worst branch for anything else in `on_initialize`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state
+        /// # </weight>
+        fn on_initialize() -> Weight {
+            // `process_proposal` returns the weight of the executed proposals. The weight of the
+            // executed proposals doesn't include any access to the store or calculation that
+            // `on_initialize` does. Therefore, to get the total weight of `on_initialize` we need
+            // to add the weight of the execution of `on_intialize` to the weight returned by
+            // `process_proposal`.
+            // To be safe, we use the worst possible case for `on_initialize`, meaning that there
+            // are as many proposals active as possible and they all take the worst possible branch.
+
+            // Maximum Weight of all possible worst case scenarios
+            let maximum_branch_weight = Self::weight_of_worst_on_initialize_branch();
+
+            // Weight of the executed proposals
+            let executed_proposals_weight = Self::process_proposals();
+
+            // total_weight = executed_proposals_weight + maximum_branch_weight
+            executed_proposals_weight.saturating_add(maximum_branch_weight)
+        }
+
         /// Vote extrinsic. Conditions:  origin must allow votes.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (R)` where:
+        /// - `R` is the length of `rationale`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or paraemters
+        /// # </weight>
+        #[weight = WeightInfoEngine::<T>::vote(_rationale.len().saturated_into())]
         pub fn vote(
             origin,
             voter_id: MemberId<T>,
@@ -381,7 +444,16 @@ decl_module! {
         }
 
         /// Cancel a proposal by its original proposer.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (L)` where:
+        /// - `L` is the total number of locks in `Balances`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoEngine::<T>::cancel_proposal(T::MaxLocks::get())]
         pub fn cancel_proposal(origin, proposer_id: MemberId<T>, proposal_id: T::ProposalId) {
             T::ProposerOriginValidator::ensure_actor_origin(origin, proposer_id)?;
 
@@ -398,7 +470,15 @@ decl_module! {
         }
 
         /// Veto a proposal. Must be root.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (1)` doesn't depend on the state or parameters
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoEngine::<T>::veto_proposal()]
         pub fn veto_proposal(origin, proposal_id: T::ProposalId) {
             ensure_root(origin)?;
 
@@ -415,11 +495,6 @@ decl_module! {
             Self::finalize_proposal(proposal_id, proposal, ProposalDecision::Vetoed);
         }
 
-        /// Block finalization. Perform voting period check, vote result tally, approved proposals
-        /// grace period checks, and proposal execution.
-        fn on_finalize(_n: T::BlockNumber) {
-            Self::process_proposals();
-        }
     }
 }
 
@@ -609,19 +684,62 @@ impl<T: Trait> Module<T> {
 }
 
 impl<T: Trait> Module<T> {
+    // Helper to calculate the weight of the worst `on_initialize` branch
+    fn weight_of_worst_on_initialize_branch() -> Weight {
+        let max_active_proposals = T::MaxActiveProposalLimit::get();
+
+        // Weight when all the proposals are immediatly approved and executed
+        let immediate_execution_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_immediate_execution_decode_fails(
+                max_active_proposals,
+            );
+
+        let pending_execution_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_pending_execution_decode_fails(
+                max_active_proposals,
+            );
+
+        // Weight when all the proposals are approved and pending constitutionality
+        let approved_pending_constitutionality_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_approved_pending_constitutionality(
+                max_active_proposals,
+            );
+
+        // Weight when all proposals are rejected
+        let rejected_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_rejected(max_active_proposals);
+
+        // Weight when all proposals are slashed
+        let slashed_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_slashed(max_active_proposals);
+
+        // Maximum Weight of all possible worst case scenarios
+        immediate_execution_branch_weight
+            .max(pending_execution_branch_weight)
+            .max(approved_pending_constitutionality_branch_weight)
+            .max(rejected_branch_weight)
+            .max(slashed_branch_weight)
+    }
+
     // Wrapper-function over System::block_number()
     fn current_block() -> T::BlockNumber {
         <frame_system::Module<T>>::block_number()
     }
 
     // Executes proposal code.
-    fn execute_proposal(proposal_id: T::ProposalId) {
+    // Returns the weight of the proposal(wether execution failed or not) or 0 if the proposal
+    // couldn't be decoded.
+    fn execute_proposal(proposal_id: T::ProposalId) -> Weight {
         let proposal_code = Self::proposal_codes(proposal_id);
 
         let proposal_code_result = T::DispatchableCallCode::decode(&mut &proposal_code[..]);
 
+        let mut execution_code_weight = 0;
+
         let execution_status = match proposal_code_result {
             Ok(proposal_code) => {
+                execution_code_weight = proposal_code.get_dispatch_info().weight;
+
                 if let Err(dispatch_error) =
                     proposal_code.dispatch_bypass_filter(T::Origin::from(RawOrigin::Root))
                 {
@@ -638,6 +756,8 @@ impl<T: Trait> Module<T> {
         Self::deposit_event(RawEvent::ProposalExecuted(proposal_id, execution_status));
 
         Self::remove_proposal_data(&proposal_id);
+
+        execution_code_weight
     }
 
     // Computes a finalized proposal:
@@ -648,17 +768,20 @@ impl<T: Trait> Module<T> {
     // - fire an event,
     // - update or delete proposal state.
     // Executes the proposal if it ready.
+    // If proposal was executed returns its weight otherwise it returns 0.
     fn finalize_proposal(
         proposal_id: T::ProposalId,
         proposal: ProposalOf<T>,
         proposal_decision: ProposalDecision,
-    ) {
+    ) -> Weight {
         // fire the proposal decision event
         Self::deposit_event(RawEvent::ProposalDecisionMade(
             proposal_id,
             proposal_decision.clone(),
         ));
 
+        let mut executed_weight = 0;
+
         // deal with stakes if necessary
         if proposal_decision
             != ProposalDecision::Approved(ApprovedProposalDecision::PendingConstitutionality)
@@ -685,13 +808,15 @@ impl<T: Trait> Module<T> {
 
             // immediately execute proposal if it ready for execution or save it for the future otherwise.
             if finalized_proposal.is_ready_for_execution(now) {
-                Self::execute_proposal(proposal_id);
+                executed_weight = Self::execute_proposal(proposal_id);
             } else {
                 <Proposals<T>>::insert(proposal_id, finalized_proposal);
             }
         } else {
             Self::remove_proposal_data(&proposal_id);
         }
+
+        executed_weight
     }
 
     // Slashes the stake and perform unstake only in case of existing stake.
@@ -766,11 +891,14 @@ impl<T: Trait> Module<T> {
 
     /// Perform voting period check, vote result tally, approved proposals
     /// grace period checks, and proposal execution.
-    fn process_proposals() {
+    /// Returns the total weight of all the executed proposals or 0 if none was executed.
+    fn process_proposals() -> Weight {
         // Collect all proposals.
         let proposals = <Proposals<T>>::iter().collect::<Vec<_>>();
         let now = Self::current_block();
 
+        let mut executed_weight = 0;
+
         for (proposal_id, proposal) in proposals {
             match proposal.status {
                 // Try to determine a decision for an active proposal.
@@ -780,18 +908,25 @@ impl<T: Trait> Module<T> {
 
                     // If decision is calculated for a proposal - finalize it.
                     if let Some(decision_status) = decision_status {
-                        Self::finalize_proposal(proposal_id, proposal, decision_status);
+                        executed_weight.saturating_add(Self::finalize_proposal(
+                            proposal_id,
+                            proposal,
+                            decision_status,
+                        ));
                     }
                 }
                 // Execute the proposal code if the proposal is ready for execution.
                 ProposalStatus::PendingExecution(_) => {
                     if proposal.is_ready_for_execution(now) {
-                        Self::execute_proposal(proposal_id);
+                        executed_weight =
+                            executed_weight.saturating_add(Self::execute_proposal(proposal_id));
                     }
                 }
                 // Skip the proposal until it gets reactivated.
                 ProposalStatus::PendingConstitutionality => {}
             }
         }
+
+        executed_weight
     }
 }

+ 57 - 1
runtime-modules/proposals/engine/src/tests/mock/mod.rs

@@ -7,7 +7,7 @@
 #![cfg(test)]
 
 use frame_support::traits::LockIdentifier;
-use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
 pub use frame_system;
 use sp_core::H256;
 use sp_runtime::{
@@ -37,12 +37,17 @@ mod membership_mod {
     pub use membership::Event;
 }
 
+mod council {
+    pub use governance::council::Event;
+}
+
 impl_outer_event! {
     pub enum TestEvent for Test {
         balances<T>,
         engine<T>,
         membership_mod<T>,
         frame_system<T>,
+        council<T>,
     }
 }
 
@@ -97,6 +102,41 @@ impl crate::Trait for Test {
     type MaxActiveProposalLimit = MaxActiveProposalLimit;
     type DispatchableCallCode = proposals::Call<Test>;
     type ProposalObserver = ();
+    type WeightInfo = ();
+}
+
+impl crate::WeightInfo for () {
+    fn vote(_: u32) -> Weight {
+        0
+    }
+
+    fn cancel_proposal(_: u32) -> Weight {
+        0
+    }
+
+    fn veto_proposal() -> Weight {
+        0
+    }
+
+    fn on_initialize_immediate_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_pending_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_approved_pending_constitutionality(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_rejected(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_slashed(_: u32) -> Weight {
+        0
+    }
 }
 
 impl ProposalObserver<Test> for () {
@@ -169,6 +209,22 @@ impl pallet_timestamp::Trait for Test {
     type WeightInfo = ();
 }
 
+impl governance::council::Trait for Test {
+    type Event = TestEvent;
+    type CouncilTermEnded = ();
+}
+
+impl recurringrewards::Trait for Test {
+    type PayoutStatusHandler = ();
+    type RecipientId = u64;
+    type RewardRelationshipId = u64;
+}
+
+impl minting::Trait for Test {
+    type Currency = Balances;
+    type MintId = u64;
+}
+
 pub fn initial_test_ext() -> sp_io::TestExternalities {
     let t = frame_system::GenesisConfig::default()
         .build_storage::<Test>()

+ 29 - 31
runtime-modules/proposals/engine/src/tests/mod.rs

@@ -375,7 +375,7 @@ fn vote_fails_with_insufficient_rights() {
 fn proposal_execution_succeeds() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default();
         let dummy_proposal =
@@ -391,7 +391,7 @@ fn proposal_execution_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(2);
+        run_to_block(2);
 
         EventFixture::assert_events(vec![
             RawEvent::ProposalCreated(1, proposal_id),
@@ -405,7 +405,7 @@ fn proposal_execution_succeeds() {
             ),
             RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(starting_block),
+                ProposalStatus::PendingExecution(starting_block + 1),
             ),
             RawEvent::ProposalExecuted(proposal_id, ExecutionStatus::Executed),
         ]);
@@ -420,7 +420,7 @@ fn proposal_execution_succeeds() {
 fn proposal_execution_failed() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default();
 
@@ -441,7 +441,7 @@ fn proposal_execution_failed() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(2);
+        run_to_block(2);
 
         assert!(!<crate::Proposals<Test>>::contains_key(proposal_id));
 
@@ -457,7 +457,7 @@ fn proposal_execution_failed() {
             ),
             RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(starting_block),
+                ProposalStatus::PendingExecution(starting_block + 1),
             ),
             RawEvent::ProposalExecuted(
                 proposal_id,
@@ -472,7 +472,7 @@ fn voting_results_calculation_succeeds() {
     initial_test_ext().execute_with(|| {
         // to enable events
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters = ProposalParameters {
             voting_period: 3,
@@ -494,7 +494,7 @@ fn voting_results_calculation_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Abstain);
 
         let block_number = 3;
-        run_to_block_and_finalize(block_number);
+        run_to_block(block_number);
 
         EventFixture::assert_events(vec![
             RawEvent::ProposalCreated(1, proposal_id),
@@ -508,7 +508,7 @@ fn voting_results_calculation_succeeds() {
             ),
             RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(starting_block),
+                ProposalStatus::PendingExecution(starting_block + 1),
             ),
             RawEvent::ProposalExecuted(proposal_id, ExecutionStatus::Executed),
         ]);
@@ -877,8 +877,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(1);
-        run_to_block_and_finalize(2);
+        run_to_block(3);
 
         let proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -888,7 +887,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
                 parameters: parameters_fixture.params(),
                 proposer_id: 1,
                 activated_at: 0,
-                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 0),
+                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 1),
                 voting_results: VotingResults {
                     abstentions: 0,
                     approvals: 4,
@@ -907,7 +906,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
 fn proposal_execution_vetoed_successfully_during_the_grace_period() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default().with_grace_period(3);
         let dummy_proposal =
@@ -921,8 +920,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(1);
-        run_to_block_and_finalize(2);
+        run_to_block(3);
 
         let pre_veto_proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -934,7 +932,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
                 activated_at: starting_block,
                 status: ProposalStatus::approved(
                     ApprovedProposalDecision::PendingExecution,
-                    starting_block
+                    starting_block + 1
                 ),
                 voting_results: VotingResults {
                     abstentions: 0,
@@ -962,7 +960,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
 fn proposal_execution_succeeds_after_the_grace_period() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default().with_grace_period(2);
         let dummy_proposal =
@@ -975,7 +973,7 @@ fn proposal_execution_succeeds_after_the_grace_period() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(1);
+        run_to_block(2);
 
         let proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -985,7 +983,7 @@ fn proposal_execution_succeeds_after_the_grace_period() {
             activated_at: starting_block,
             status: ProposalStatus::approved(
                 ApprovedProposalDecision::PendingExecution,
-                starting_block,
+                starting_block + 1,
             ),
             voting_results: VotingResults {
                 abstentions: 0,
@@ -1000,8 +998,8 @@ fn proposal_execution_succeeds_after_the_grace_period() {
 
         assert_eq!(proposal, expected_proposal);
 
-        let finalization_block = 3;
-        run_to_block_and_finalize(finalization_block);
+        let finalization_block = 4;
+        run_to_block(finalization_block);
 
         EventFixture::assert_last_crate_event(RawEvent::ProposalExecuted(
             proposal_id,
@@ -1535,7 +1533,7 @@ fn proposal_execution_with_exact_execution_works() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
         // Proposal exists after the grace period
-        run_to_block_and_finalize(5);
+        run_to_block(5);
 
         let proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -1545,7 +1543,7 @@ fn proposal_execution_with_exact_execution_works() {
                 parameters: parameters_fixture.params(),
                 proposer_id: 1,
                 activated_at: 0,
-                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 0),
+                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 1),
                 voting_results: VotingResults {
                     abstentions: 0,
                     approvals: 4,
@@ -1559,7 +1557,7 @@ fn proposal_execution_with_exact_execution_works() {
         );
 
         // Exact execution block time.
-        run_to_block_and_finalize(exact_block);
+        run_to_block(exact_block);
 
         EventFixture::assert_last_crate_event(RawEvent::ProposalExecuted(
             proposal_id,
@@ -1719,7 +1717,7 @@ fn proposal_with_pending_constitutionality_reactivation_succeeds() {
 fn proposal_with_pending_constitutionality_execution_succeeds() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(1);
+        run_to_block(starting_block);
 
         let account_id = 1;
         let total_balance = 1000;
@@ -1749,7 +1747,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(2);
+        run_to_block(2);
 
         // first chain of event from the creation to the approval
         EventFixture::assert_global_events(vec![
@@ -1780,7 +1778,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
                 activated_at: starting_block,
                 status: ProposalStatus::approved(
                     ApprovedProposalDecision::PendingConstitutionality,
-                    starting_block
+                    starting_block + 1
                 ),
                 voting_results: VotingResults {
                     abstentions: 0,
@@ -1800,7 +1798,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
         );
 
         let reactivation_block = 5;
-        run_to_block_and_finalize(reactivation_block);
+        run_to_block(reactivation_block);
 
         ProposalsEngine::reactivate_pending_constitutionality_proposals();
 
@@ -1822,8 +1820,8 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        let next_block_after_approval = 6;
-        run_to_block_and_finalize(next_block_after_approval);
+        let next_block_after_approval = 7;
+        run_to_block(next_block_after_approval);
 
         // internal active proposal counter check
         assert_eq!(<ActiveProposalCount>::get(), 0);
@@ -1863,7 +1861,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
             )),
             TestEvent::engine(RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(reactivation_block),
+                ProposalStatus::PendingExecution(reactivation_block + 1),
             )),
             // execution
             TestEvent::engine(RawEvent::ProposalExecuted(

+ 0 - 34
runtime-modules/versioned-store-permissions/Cargo.toml

@@ -1,34 +0,0 @@
-[package]
-name = 'pallet-versioned-store-permissions'
-version = '3.1.0'
-authors = ['Joystream contributors']
-edition = '2018'
-
-[dependencies]
-serde = { version = "1.0.101", optional = true, features = ["derive"] }
-codec = { package = 'parity-scale-codec', version = '1.3.4', default-features = false, features = ['derive'] }
-sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-versioned-store = { package = 'pallet-versioned-store', default-features = false, path = '../versioned-store'}
-
-[dev-dependencies]
-sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-common = { package = 'pallet-common', default-features = false, path = '../common'}
-
-[features]
-default = ['std']
-std = [
-	'serde',
-	'codec/std',
-	'sp-std/std',
-	'frame-support/std',
-	'frame-system/std',
-	'sp-arithmetic/std',
-	'sp-runtime/std',
-    'versioned-store/std',
-]

+ 0 - 32
runtime-modules/versioned-store-permissions/src/constraint.rs

@@ -1,32 +0,0 @@
-use codec::{Decode, Encode};
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-use sp_std::collections::btree_set::BTreeSet;
-
-/// Reference to a specific property of a specific class.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
-pub struct PropertyOfClass<ClassId, PropertyIndex> {
-    pub class_id: ClassId,
-    pub property_index: PropertyIndex,
-}
-
-/// The type of constraint imposed on referencing a class via class property of type "Internal".
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub enum ReferenceConstraint<ClassId: Ord, PropertyIndex: Ord> {
-    /// No property can reference the class.
-    NoReferencingAllowed,
-
-    /// Any property of any class may reference the class.
-    NoConstraint,
-
-    /// Only a set of properties of specific classes can reference the class.
-    Restricted(BTreeSet<PropertyOfClass<ClassId, PropertyIndex>>),
-}
-
-impl<ClassId: Ord, PropertyIndex: Ord> Default for ReferenceConstraint<ClassId, PropertyIndex> {
-    fn default() -> Self {
-        ReferenceConstraint::NoReferencingAllowed
-    }
-}

+ 0 - 61
runtime-modules/versioned-store-permissions/src/credentials.rs

@@ -1,61 +0,0 @@
-use codec::{Decode, Encode};
-use sp_std::collections::btree_set::BTreeSet;
-use sp_std::vec::Vec;
-
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct CredentialSet<Credential: Ord>(BTreeSet<Credential>);
-
-impl<Credential> From<Vec<Credential>> for CredentialSet<Credential>
-where
-    Credential: Ord,
-{
-    fn from(v: Vec<Credential>) -> CredentialSet<Credential> {
-        let mut set = CredentialSet(BTreeSet::new());
-        for credential in v.into_iter() {
-            set.insert(credential);
-        }
-        set
-    }
-}
-
-/// Default CredentialSet set is just an empty set.
-impl<Credential: Ord> Default for CredentialSet<Credential> {
-    fn default() -> Self {
-        CredentialSet(BTreeSet::new())
-    }
-}
-
-impl<Credential: Ord> CredentialSet<Credential> {
-    pub fn new() -> Self {
-        Self(BTreeSet::new())
-    }
-
-    pub fn insert(&mut self, value: Credential) -> bool {
-        self.0.insert(value)
-    }
-
-    pub fn contains(&self, value: &Credential) -> bool {
-        self.0.contains(value)
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.0.is_empty()
-    }
-}
-
-/// Type, derived from dispatchable call, identifies the caller
-#[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
-pub enum AccessLevel<Credential> {
-    /// ROOT origin
-    System,
-    /// Caller identified as the entity maintainer
-    EntityMaintainer, // Maybe enclose EntityId?
-    /// Verified Credential
-    Credential(Credential),
-    /// In cases where a signed extrinsic doesn't provide a Credential
-    Unspecified,
-}

+ 0 - 664
runtime-modules/versioned-store-permissions/src/lib.rs

@@ -1,664 +0,0 @@
-// Ensure we're `no_std` when compiling for Wasm.
-#![cfg_attr(not(feature = "std"), no_std)]
-
-use codec::Codec;
-use frame_support::{decl_module, decl_storage, ensure, Parameter};
-use sp_arithmetic::traits::BaseArithmetic;
-use sp_runtime::traits::{MaybeSerialize, Member};
-use sp_std::collections::btree_map::BTreeMap;
-use sp_std::vec::Vec;
-
-//TODO: Convert errors to the Substrate decl_error! macro.
-/// Result with string error message. This exists for backward compatibility purpose.
-pub type DispatchResult = Result<(), &'static str>;
-
-// EntityId, ClassId -> should be configured on versioned_store::Trait
-pub use versioned_store::{ClassId, ClassPropertyValue, EntityId, Property, PropertyValue};
-
-mod constraint;
-mod credentials;
-mod mock;
-mod operations;
-mod permissions;
-mod tests;
-
-pub use constraint::*;
-pub use credentials::*;
-pub use operations::*;
-pub use permissions::*;
-
-/// Trait for checking if an account has specified Credential
-pub trait CredentialChecker<T: Trait> {
-    fn account_has_credential(account: &T::AccountId, credential: T::Credential) -> bool;
-}
-
-/// An implementation where no account has any credential. Effectively
-/// only the system will be able to perform any action on the versioned store.
-impl<T: Trait> CredentialChecker<T> for () {
-    fn account_has_credential(_account: &T::AccountId, _credential: T::Credential) -> bool {
-        false
-    }
-}
-
-/// An implementation that calls into multiple checkers. This allows for multiple modules
-/// to maintain AccountId to Credential mappings.
-impl<T: Trait, X: CredentialChecker<T>, Y: CredentialChecker<T>> CredentialChecker<T> for (X, Y) {
-    fn account_has_credential(account: &T::AccountId, group: T::Credential) -> bool {
-        X::account_has_credential(account, group) || Y::account_has_credential(account, group)
-    }
-}
-
-/// Trait for externally checking if an account can create new classes in the versioned store.
-pub trait CreateClassPermissionsChecker<T: Trait> {
-    fn account_can_create_class_permissions(account: &T::AccountId) -> bool;
-}
-
-/// An implementation that does not permit any account to create classes. Effectively
-/// only the system can create classes.
-impl<T: Trait> CreateClassPermissionsChecker<T> for () {
-    fn account_can_create_class_permissions(_account: &T::AccountId) -> bool {
-        false
-    }
-}
-
-pub type ClassPermissionsType<T> = ClassPermissions<
-    ClassId,
-    <T as Trait>::Credential,
-    u16,
-    <T as frame_system::Trait>::BlockNumber,
->;
-
-pub trait Trait: frame_system::Trait + versioned_store::Trait {
-    /// Type that represents an actor or group of actors in the system.
-    type Credential: Parameter
-        + Member
-        + BaseArithmetic
-        + Codec
-        + Default
-        + Copy
-        + Clone
-        + MaybeSerialize
-        + Eq
-        + PartialEq
-        + Ord;
-
-    /// External type for checking if an account has specified credential.
-    type CredentialChecker: CredentialChecker<Self>;
-
-    /// External type used to check if an account has permission to create new Classes.
-    type CreateClassPermissionsChecker: CreateClassPermissionsChecker<Self>;
-}
-
-decl_storage! {
-    trait Store for Module<T: Trait> as VersionedStorePermissions {
-      /// ClassPermissions of corresponding Classes in the versioned store
-      pub ClassPermissionsByClassId get(fn class_permissions_by_class_id) config(): map hasher(blake2_128_concat)
-        ClassId => ClassPermissionsType<T>;
-
-      /// Owner of an entity in the versioned store. If it is None then it is owned by the system.
-      pub EntityMaintainerByEntityId get(fn entity_maintainer_by_entity_id) config(): map hasher(blake2_128_concat)
-        EntityId => Option<T::Credential>;
-    }
-}
-
-decl_module! {
-    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
-
-        /// Sets the admins for a class
-        #[weight = 10_000_000] // TODO: adjust weight
-        fn set_class_admins(
-            origin,
-            class_id: ClassId,
-            admins: CredentialSet<T::Credential>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::mutate_class_permissions(
-                &raw_origin,
-                None,
-                Self::is_system, // root origin
-                class_id,
-                |class_permissions| {
-                    class_permissions.admins = admins;
-                    Ok(())
-                }
-            )
-        }
-
-        // Methods for updating concrete permissions
-        #[weight = 10_000_000] // TODO: adjust weight
-        fn set_class_entity_permissions(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId,
-            entity_permissions: EntityPermissions<T::Credential>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::mutate_class_permissions(
-                &raw_origin,
-                with_credential,
-                ClassPermissions::is_admin,
-                class_id,
-                |class_permissions| {
-                    class_permissions.entity_permissions = entity_permissions;
-                    Ok(())
-                }
-            )
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        fn set_class_entities_can_be_created(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId,
-            can_be_created: bool
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::mutate_class_permissions(
-                &raw_origin,
-                with_credential,
-                ClassPermissions::is_admin,
-                class_id,
-                |class_permissions| {
-                    class_permissions.entities_can_be_created = can_be_created;
-                    Ok(())
-                }
-            )
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        fn set_class_add_schemas_set(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId,
-            credential_set: CredentialSet<T::Credential>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::mutate_class_permissions(
-                &raw_origin,
-                with_credential,
-                ClassPermissions::is_admin,
-                class_id,
-                |class_permissions| {
-                    class_permissions.add_schemas = credential_set;
-                    Ok(())
-                }
-            )
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        fn set_class_create_entities_set(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId,
-            credential_set: CredentialSet<T::Credential>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::mutate_class_permissions(
-                &raw_origin,
-                with_credential,
-                ClassPermissions::is_admin,
-                class_id,
-                |class_permissions| {
-                    class_permissions.create_entities = credential_set;
-                    Ok(())
-                }
-            )
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        fn set_class_reference_constraint(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId,
-            constraint: ReferenceConstraint<ClassId, u16>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::mutate_class_permissions(
-                &raw_origin,
-                with_credential,
-                ClassPermissions::is_admin,
-                class_id,
-                |class_permissions| {
-                    class_permissions.reference_constraint = constraint;
-                    Ok(())
-                }
-            )
-        }
-
-        // Setting a new maintainer for an entity may require having additional constraints.
-        // So for now it is disabled.
-        // pub fn set_entity_maintainer(
-        //     origin,
-        //     entity_id: EntityId,
-        //     new_maintainer: Option<T::Credential>
-        // ) -> DispatchResult {
-        //     ensure_root(origin)?;
-
-        //     // ensure entity exists in the versioned store
-        //     let _ = Self::get_class_id_by_entity_id(entity_id)?;
-
-        //     <EntityMaintainerByEntityId<T>>::mutate(entity_id, |maintainer| {
-        //         *maintainer = new_maintainer;
-        //     });
-
-        //     Ok(())
-        // }
-
-        // Permissioned proxy calls to versioned store
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_class(
-            origin,
-            name: Vec<u8>,
-            description: Vec<u8>,
-            class_permissions: ClassPermissionsType<T>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            let can_create_class = match raw_origin {
-                frame_system::RawOrigin::Root => true,
-                frame_system::RawOrigin::Signed(sender) => {
-                    T::CreateClassPermissionsChecker::account_can_create_class_permissions(&sender)
-                },
-                _ => false
-            };
-
-            if can_create_class {
-                let class_id = <versioned_store::Module<T>>::create_class(name, description)?;
-
-                // is there a need to assert class_id is unique?
-
-                <ClassPermissionsByClassId<T>>::insert(&class_id, class_permissions);
-
-                Ok(())
-            } else {
-                Err("NotPermittedToCreateClass")
-            }
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_class_with_default_permissions(
-            origin,
-            name: Vec<u8>,
-            description: Vec<u8>
-        ) -> DispatchResult {
-            Self::create_class(origin, name, description, ClassPermissions::default())
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn add_class_schema(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId,
-            existing_properties: Vec<u16>,
-            new_properties: Vec<Property>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            Self::if_class_permissions_satisfied(
-                &raw_origin,
-                with_credential,
-                None,
-                ClassPermissions::can_add_class_schema,
-                class_id,
-                |_class_permissions, _access_level| {
-                    // If a new property points at another class,
-                    // at this point we don't enforce anything about reference constraints
-                    // because of the chicken and egg problem. Instead enforcement is done
-                    // at the time of creating an entity.
-                    let _schema_index = <versioned_store::Module<T>>::add_class_schema(class_id, existing_properties, new_properties)?;
-                    Ok(())
-                }
-            )
-        }
-
-        /// Creates a new entity of type class_id. The maintainer is set to be either None if the origin is root, or the provided credential
-        /// associated with signer.
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn create_entity(
-            origin,
-            with_credential: Option<T::Credential>,
-            class_id: ClassId
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-            let _entity_id = Self::do_create_entity(&raw_origin, with_credential, class_id)?;
-            Ok(())
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn add_schema_support_to_entity(
-            origin,
-            with_credential: Option<T::Credential>,
-            as_entity_maintainer: bool,
-            entity_id: EntityId,
-            schema_id: u16, // Do not type alias u16!! - u16,
-            property_values: Vec<ClassPropertyValue>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-            Self::do_add_schema_support_to_entity(&raw_origin, with_credential, as_entity_maintainer, entity_id, schema_id, property_values)
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn update_entity_property_values(
-            origin,
-            with_credential: Option<T::Credential>,
-            as_entity_maintainer: bool,
-            entity_id: EntityId,
-            property_values: Vec<ClassPropertyValue>
-        ) -> DispatchResult {
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-            Self::do_update_entity_property_values(&raw_origin, with_credential, as_entity_maintainer, entity_id, property_values)
-        }
-
-        #[weight = 10_000_000] // TODO: adjust weight
-        pub fn transaction(origin, operations: Vec<Operation<T::Credential>>) -> DispatchResult {
-            // This map holds the EntityId of the entity created as a result of executing a CreateEntity Operation
-            // keyed by the indexed of the operation, in the operations vector.
-            let mut entity_created_in_operation: BTreeMap<usize, EntityId> = BTreeMap::new();
-
-            let raw_origin = Self::ensure_root_or_signed(origin)?;
-
-            for (op_index, operation) in operations.into_iter().enumerate() {
-                match operation.operation_type {
-                    OperationType::CreateEntity(create_entity_operation) => {
-                        let entity_id = Self::do_create_entity(&raw_origin, operation.with_credential, create_entity_operation.class_id)?;
-                        entity_created_in_operation.insert(op_index, 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::do_update_entity_property_values(&raw_origin, operation.with_credential, operation.as_entity_maintainer, 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::do_add_schema_support_to_entity(&raw_origin, operation.with_credential, operation.as_entity_maintainer, entity_id, schema_id, property_values)?;
-                    }
-                }
-            }
-
-            Ok(())
-        }
-    }
-}
-
-impl<T: Trait> Module<T> {
-    fn ensure_root_or_signed(
-        origin: T::Origin,
-    ) -> Result<frame_system::RawOrigin<T::AccountId>, &'static str> {
-        match origin.into() {
-            Ok(frame_system::RawOrigin::Root) => Ok(frame_system::RawOrigin::Root),
-            Ok(frame_system::RawOrigin::Signed(account_id)) => {
-                Ok(frame_system::RawOrigin::Signed(account_id))
-            }
-            _ => Err("BadOrigin:ExpectedRootOrSigned"),
-        }
-    }
-
-    fn do_create_entity(
-        raw_origin: &frame_system::RawOrigin<T::AccountId>,
-        with_credential: Option<T::Credential>,
-        class_id: ClassId,
-    ) -> Result<EntityId, &'static str> {
-        Self::if_class_permissions_satisfied(
-            raw_origin,
-            with_credential,
-            None,
-            ClassPermissions::can_create_entity,
-            class_id,
-            |_class_permissions, access_level| {
-                let entity_id = <versioned_store::Module<T>>::create_entity(class_id)?;
-
-                // Note: mutating value to None is equivalient to removing the value from storage map
-                <EntityMaintainerByEntityId<T>>::mutate(
-                    entity_id,
-                    |maintainer| match access_level {
-                        AccessLevel::System => *maintainer = None,
-                        AccessLevel::Credential(credential) => *maintainer = Some(*credential),
-                        _ => *maintainer = None,
-                    },
-                );
-
-                Ok(entity_id)
-            },
-        )
-    }
-
-    fn do_update_entity_property_values(
-        raw_origin: &frame_system::RawOrigin<T::AccountId>,
-        with_credential: Option<T::Credential>,
-        as_entity_maintainer: bool,
-        entity_id: EntityId,
-        property_values: Vec<ClassPropertyValue>,
-    ) -> DispatchResult {
-        let class_id = Self::get_class_id_by_entity_id(entity_id)?;
-
-        Self::ensure_internal_property_values_permitted(class_id, &property_values)?;
-
-        let as_entity_maintainer = if as_entity_maintainer {
-            Some(entity_id)
-        } else {
-            None
-        };
-
-        Self::if_class_permissions_satisfied(
-            raw_origin,
-            with_credential,
-            as_entity_maintainer,
-            ClassPermissions::can_update_entity,
-            class_id,
-            |_class_permissions, _access_level| {
-                <versioned_store::Module<T>>::update_entity_property_values(
-                    entity_id,
-                    property_values,
-                )
-            },
-        )
-    }
-
-    fn do_add_schema_support_to_entity(
-        raw_origin: &frame_system::RawOrigin<T::AccountId>,
-        with_credential: Option<T::Credential>,
-        as_entity_maintainer: bool,
-        entity_id: EntityId,
-        schema_id: u16,
-        property_values: Vec<ClassPropertyValue>,
-    ) -> DispatchResult {
-        // class id of the entity being updated
-        let class_id = Self::get_class_id_by_entity_id(entity_id)?;
-
-        Self::ensure_internal_property_values_permitted(class_id, &property_values)?;
-
-        let as_entity_maintainer = if as_entity_maintainer {
-            Some(entity_id)
-        } else {
-            None
-        };
-
-        Self::if_class_permissions_satisfied(
-            raw_origin,
-            with_credential,
-            as_entity_maintainer,
-            ClassPermissions::can_update_entity,
-            class_id,
-            |_class_permissions, _access_level| {
-                <versioned_store::Module<T>>::add_schema_support_to_entity(
-                    entity_id,
-                    schema_id,
-                    property_values,
-                )
-            },
-        )
-    }
-
-    /// Derives the AccessLevel the caller is attempting to act with.
-    /// It expects only signed or root origin.
-    fn derive_access_level(
-        raw_origin: &frame_system::RawOrigin<T::AccountId>,
-        with_credential: Option<T::Credential>,
-        as_entity_maintainer: Option<EntityId>,
-    ) -> Result<AccessLevel<T::Credential>, &'static str> {
-        match raw_origin {
-            frame_system::RawOrigin::Root => Ok(AccessLevel::System),
-            frame_system::RawOrigin::Signed(account_id) => {
-                if let Some(credential) = with_credential {
-                    if T::CredentialChecker::account_has_credential(&account_id, credential) {
-                        if let Some(entity_id) = as_entity_maintainer {
-                            // is entity maintained by system
-                            ensure!(
-                                <EntityMaintainerByEntityId<T>>::contains_key(entity_id),
-                                "NotEnityMaintainer"
-                            );
-                            // ensure entity maintainer matches
-                            match Self::entity_maintainer_by_entity_id(entity_id) {
-                                Some(maintainer_credential)
-                                    if credential == maintainer_credential =>
-                                {
-                                    Ok(AccessLevel::EntityMaintainer)
-                                }
-                                _ => Err("NotEnityMaintainer"),
-                            }
-                        } else {
-                            Ok(AccessLevel::Credential(credential))
-                        }
-                    } else {
-                        Err("OriginCannotActWithRequestedCredential")
-                    }
-                } else {
-                    Ok(AccessLevel::Unspecified)
-                }
-            }
-            _ => Err("BadOrigin:ExpectedRootOrSigned"),
-        }
-    }
-
-    /// Returns the stored class permissions if exist, error otherwise.
-    fn ensure_class_permissions(
-        class_id: ClassId,
-    ) -> Result<ClassPermissionsType<T>, &'static str> {
-        ensure!(
-            <ClassPermissionsByClassId<T>>::contains_key(class_id),
-            "ClassPermissionsNotFoundByClassId"
-        );
-        Ok(Self::class_permissions_by_class_id(class_id))
-    }
-
-    /// Derives the access level of the caller.
-    /// If the predicate passes, the mutate method is invoked.
-    fn mutate_class_permissions<Predicate, Mutate>(
-        raw_origin: &frame_system::RawOrigin<T::AccountId>,
-        with_credential: Option<T::Credential>,
-        // predicate to test
-        predicate: Predicate,
-        // class permissions to perform mutation on if it exists
-        class_id: ClassId,
-        // actual mutation to apply.
-        mutate: Mutate,
-    ) -> DispatchResult
-    where
-        Predicate: FnOnce(&ClassPermissionsType<T>, &AccessLevel<T::Credential>) -> DispatchResult,
-        Mutate: FnOnce(&mut ClassPermissionsType<T>) -> DispatchResult,
-    {
-        let access_level = Self::derive_access_level(raw_origin, with_credential, None)?;
-        let mut class_permissions = Self::ensure_class_permissions(class_id)?;
-
-        predicate(&class_permissions, &access_level)?;
-        mutate(&mut class_permissions)?;
-        class_permissions.last_permissions_update = <frame_system::Module<T>>::block_number();
-        <ClassPermissionsByClassId<T>>::insert(class_id, class_permissions);
-        Ok(())
-    }
-
-    fn is_system(
-        _: &ClassPermissionsType<T>,
-        access_level: &AccessLevel<T::Credential>,
-    ) -> DispatchResult {
-        if *access_level == AccessLevel::System {
-            Ok(())
-        } else {
-            Err("NotRootOrigin")
-        }
-    }
-
-    /// Derives the access level of the caller.
-    /// If the peridcate passes the callback is invoked. Returns result of the callback
-    /// or error from failed predicate.
-    fn if_class_permissions_satisfied<Predicate, Callback, R>(
-        raw_origin: &frame_system::RawOrigin<T::AccountId>,
-        with_credential: Option<T::Credential>,
-        as_entity_maintainer: Option<EntityId>,
-        // predicate to test
-        predicate: Predicate,
-        // class permissions to test
-        class_id: ClassId,
-        // callback to invoke if predicate passes
-        callback: Callback,
-    ) -> Result<R, &'static str>
-    where
-        Predicate: FnOnce(&ClassPermissionsType<T>, &AccessLevel<T::Credential>) -> DispatchResult,
-        Callback: FnOnce(
-            &ClassPermissionsType<T>,
-            &AccessLevel<T::Credential>,
-        ) -> Result<R, &'static str>,
-    {
-        let access_level =
-            Self::derive_access_level(raw_origin, with_credential, as_entity_maintainer)?;
-        let class_permissions = Self::ensure_class_permissions(class_id)?;
-
-        predicate(&class_permissions, &access_level)?;
-        callback(&class_permissions, &access_level)
-    }
-
-    fn get_class_id_by_entity_id(entity_id: EntityId) -> Result<ClassId, &'static str> {
-        // use a utility method on versioned_store module
-        ensure!(
-            versioned_store::EntityById::contains_key(entity_id),
-            "EntityNotFound"
-        );
-        let entity = <versioned_store::Module<T>>::entity_by_id(entity_id);
-        Ok(entity.class_id)
-    }
-
-    // Ensures property_values of type Internal that point to a class,
-    // the target entity and class exists and constraint allows it.
-    fn ensure_internal_property_values_permitted(
-        source_class_id: ClassId,
-        property_values: &[ClassPropertyValue],
-    ) -> DispatchResult {
-        for property_value in property_values.iter() {
-            if let PropertyValue::Internal(ref target_entity_id) = property_value.value {
-                // get the class permissions for target class
-                let target_class_id = Self::get_class_id_by_entity_id(*target_entity_id)?;
-                // assert class permissions exists for target class
-                let class_permissions = Self::class_permissions_by_class_id(target_class_id);
-
-                // ensure internal reference is permitted
-                match class_permissions.reference_constraint {
-                    ReferenceConstraint::NoConstraint => Ok(()),
-                    ReferenceConstraint::NoReferencingAllowed => {
-                        Err("EntityCannotReferenceTargetEntity")
-                    }
-                    ReferenceConstraint::Restricted(permitted_properties) => {
-                        if permitted_properties.contains(&PropertyOfClass {
-                            class_id: source_class_id,
-                            property_index: property_value.in_class_index,
-                        }) {
-                            Ok(())
-                        } else {
-                            Err("EntityCannotReferenceTargetEntity")
-                        }
-                    }
-                }?;
-            }
-        }
-
-        // if we reach here all Internal properties have passed the constraint check
-        Ok(())
-    }
-}

+ 0 - 173
runtime-modules/versioned-store-permissions/src/mock.rs

@@ -1,173 +0,0 @@
-#![cfg(test)]
-
-use crate::*;
-use crate::{Module, Trait};
-
-use common::constraints::InputValidationLengthConstraint;
-use frame_support::{impl_outer_origin, parameter_types};
-use sp_core::H256;
-use sp_runtime::{
-    testing::Header,
-    traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
-};
-
-impl_outer_origin! {
-    pub enum Origin for Runtime {}
-}
-
-// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
-#[derive(Clone, 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 frame_system::Trait for Runtime {
-    type BaseCallFilter = ();
-    type Origin = Origin;
-    type Call = ();
-    type Index = u64;
-    type BlockNumber = u64;
-    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 DbWeight = ();
-    type BlockExecutionWeight = ();
-    type ExtrinsicBaseWeight = ();
-    type MaximumExtrinsicWeight = ();
-    type MaximumBlockLength = MaximumBlockLength;
-    type AvailableBlockRatio = AvailableBlockRatio;
-    type Version = ();
-    type PalletInfo = ();
-    type AccountData = ();
-    type OnNewAccount = ();
-    type OnKilledAccount = ();
-    type SystemWeightInfo = ();
-}
-
-impl pallet_timestamp::Trait for Runtime {
-    type Moment = u64;
-    type OnTimestampSet = ();
-    type MinimumPeriod = MinimumPeriod;
-    type WeightInfo = ();
-}
-
-impl versioned_store::Trait for Runtime {
-    type Event = ();
-}
-
-impl Trait for Runtime {
-    type Credential = u64;
-    type CredentialChecker = MockCredentialChecker;
-    type CreateClassPermissionsChecker = MockCreateClassPermissionsChecker;
-}
-
-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 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 struct MockCredentialChecker {}
-
-impl CredentialChecker<Runtime> for MockCredentialChecker {
-    fn account_has_credential(
-        account_id: &<Runtime as frame_system::Trait>::AccountId,
-        credential_id: <Runtime as Trait>::Credential,
-    ) -> bool {
-        if (credential_id as usize) < PRINCIPAL_GROUP_MEMBERS.len() {
-            PRINCIPAL_GROUP_MEMBERS[credential_id as usize]
-                .iter()
-                .any(|id| *id == *account_id)
-        } else {
-            false
-        }
-    }
-}
-
-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];
-
-pub struct MockCreateClassPermissionsChecker {}
-
-impl CreateClassPermissionsChecker<Runtime> for MockCreateClassPermissionsChecker {
-    fn account_can_create_class_permissions(
-        account_id: &<Runtime as frame_system::Trait>::AccountId,
-    ) -> bool {
-        CLASS_PERMISSIONS_CREATORS
-            .iter()
-            .any(|id| *id == *account_id)
-    }
-}
-
-// This function basically just builds a genesis storage key/value store according to
-// our desired mockup.
-
-fn default_versioned_store_genesis_config() -> versioned_store::GenesisConfig {
-    versioned_store::GenesisConfig {
-        class_by_id: vec![],
-        entity_by_id: vec![],
-        next_class_id: 1,
-        next_entity_id: 1,
-        property_name_constraint: InputValidationLengthConstraint {
-            min: 1,
-            max_min_diff: 49,
-        },
-        property_description_constraint: InputValidationLengthConstraint {
-            min: 0,
-            max_min_diff: 500,
-        },
-        class_name_constraint: InputValidationLengthConstraint {
-            min: 1,
-            max_min_diff: 49,
-        },
-        class_description_constraint: InputValidationLengthConstraint {
-            min: 0,
-            max_min_diff: 500,
-        },
-    }
-}
-
-fn build_test_externalities(config: versioned_store::GenesisConfig) -> sp_io::TestExternalities {
-    let mut t = frame_system::GenesisConfig::default()
-        .build_storage::<Runtime>()
-        .unwrap();
-
-    config.assimilate_storage(&mut t).unwrap();
-
-    t.into()
-}
-
-pub fn with_test_externalities<R, F: FnOnce() -> R>(f: F) -> R {
-    let versioned_store_config = default_versioned_store_genesis_config();
-    build_test_externalities(versioned_store_config).execute_with(f)
-}
-
-// pub type System = frame_system::Module;
-
-/// Export module on a test runtime
-pub type Permissions = Module<Runtime>;

+ 0 - 136
runtime-modules/versioned-store-permissions/src/operations.rs

@@ -1,136 +0,0 @@
-use codec::{Decode, Encode};
-use sp_std::collections::btree_map::BTreeMap;
-use sp_std::vec;
-use sp_std::vec::Vec;
-use versioned_store::{ClassId, ClassPropertyValue, EntityId, PropertyValue};
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub enum ParametrizedPropertyValue {
-    /// Same fields as normal PropertyValue
-    PropertyValue(PropertyValue),
-
-    /// 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>),
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub enum ParameterizedEntity {
-    InternalEntityJustAdded(u32),
-    ExistingEntity(EntityId),
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct ParametrizedClassPropertyValue {
-    /// Index is into properties vector of class.
-    pub in_class_index: u16,
-
-    /// Value of property with index `in_class_index` in a given class.
-    pub value: ParametrizedPropertyValue,
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct CreateEntityOperation {
-    pub class_id: ClassId,
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct UpdatePropertyValuesOperation {
-    pub entity_id: ParameterizedEntity,
-    pub new_parametrized_property_values: Vec<ParametrizedClassPropertyValue>,
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct AddSchemaSupportToEntityOperation {
-    pub entity_id: ParameterizedEntity,
-    pub schema_id: u16,
-    pub parametrized_property_values: Vec<ParametrizedClassPropertyValue>,
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub enum OperationType {
-    CreateEntity(CreateEntityOperation),
-    UpdatePropertyValues(UpdatePropertyValuesOperation),
-    AddSchemaSupportToEntity(AddSchemaSupportToEntityOperation),
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct Operation<Credential> {
-    pub with_credential: Option<Credential>,
-    pub as_entity_maintainer: bool,
-    pub operation_type: OperationType,
-}
-
-pub fn parametrized_entity_to_entity_id(
-    created_entities: &BTreeMap<usize, EntityId>,
-    entity: ParameterizedEntity,
-) -> Result<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;
-            if created_entities.contains_key(&op_index) {
-                let entity_id = created_entities.get(&op_index).unwrap();
-                Ok(*entity_id)
-            } else {
-                Err("EntityNotCreatedByOperation")
-            }
-        }
-    }
-}
-
-pub fn parametrized_property_values_to_property_values(
-    created_entities: &BTreeMap<usize, EntityId>,
-    parametrized_property_values: Vec<ParametrizedClassPropertyValue>,
-) -> Result<Vec<ClassPropertyValue>, &'static str> {
-    let mut class_property_values: Vec<ClassPropertyValue> = vec![];
-
-    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;
-                if created_entities.contains_key(&op_index) {
-                    let entity_id = created_entities.get(&op_index).unwrap();
-                    PropertyValue::Internal(*entity_id)
-                } else {
-                    return Err("EntityNotCreatedByOperation");
-                }
-            }
-            ParametrizedPropertyValue::InternalEntityVec(parametrized_entities) => {
-                let mut entities: Vec<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;
-                            if created_entities.contains_key(&op_index) {
-                                let entity_id = created_entities.get(&op_index).unwrap();
-                                entities.push(*entity_id);
-                            } else {
-                                return Err("EntityNotCreatedByOperation");
-                            }
-                        }
-                    }
-                }
-
-                PropertyValue::InternalVec(entities)
-            }
-        };
-
-        class_property_values.push(ClassPropertyValue {
-            in_class_index: parametrized_class_property_value.in_class_index,
-            value: property_value,
-        });
-    }
-
-    Ok(class_property_values)
-}

+ 0 - 159
runtime-modules/versioned-store-permissions/src/permissions.rs

@@ -1,159 +0,0 @@
-use codec::{Decode, Encode};
-
-use crate::constraint::*;
-use crate::credentials::*;
-use crate::DispatchResult;
-
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-
-/// Permissions for an instance of a Class in the versioned store.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Eq, PartialEq, Clone, Debug)]
-pub struct ClassPermissions<ClassId, Credential, PropertyIndex, BlockNumber>
-where
-    ClassId: Ord,
-    Credential: Ord + Clone,
-    PropertyIndex: Ord,
-{
-    // concrete permissions
-    /// Permissions that are applied to entities of this class, define who in addition to
-    /// root origin can update entities of this class.
-    pub entity_permissions: EntityPermissions<Credential>,
-
-    /// Wether new entities of this class be created or not. Is not enforced for root origin.
-    pub entities_can_be_created: bool,
-
-    /// Who can add new schemas in the versioned store for this class
-    pub add_schemas: CredentialSet<Credential>,
-
-    /// Who can create new entities in the versioned store of this class
-    pub create_entities: CredentialSet<Credential>,
-
-    /// The type of constraint on referencing the class from other entities.
-    pub reference_constraint: ReferenceConstraint<ClassId, PropertyIndex>,
-
-    /// Who (in addition to root origin) can update all concrete permissions.
-    /// The admins can only be set by the root origin, "System".
-    pub admins: CredentialSet<Credential>,
-
-    // Block where permissions were changed
-    pub last_permissions_update: BlockNumber,
-}
-
-impl<ClassId, Credential, PropertyIndex, BlockNumber>
-    ClassPermissions<ClassId, Credential, PropertyIndex, BlockNumber>
-where
-    ClassId: Ord,
-    Credential: Ord + Clone,
-    PropertyIndex: Ord,
-{
-    /// Returns Ok if access_level is root origin or credential is in admins set, Err otherwise
-    pub fn is_admin(
-        class_permissions: &Self,
-        access_level: &AccessLevel<Credential>,
-    ) -> DispatchResult {
-        match access_level {
-            AccessLevel::System => Ok(()),
-            AccessLevel::Credential(credential) => {
-                if class_permissions.admins.contains(credential) {
-                    Ok(())
-                } else {
-                    Err("NotInAdminsSet")
-                }
-            }
-            AccessLevel::Unspecified => Err("UnspecifiedActor"),
-            AccessLevel::EntityMaintainer => Err("AccessLevel::EntityMaintainer-UsedOutOfPlace"),
-        }
-    }
-
-    pub fn can_add_class_schema(
-        class_permissions: &Self,
-        access_level: &AccessLevel<Credential>,
-    ) -> DispatchResult {
-        match access_level {
-            AccessLevel::System => Ok(()),
-            AccessLevel::Credential(credential) => {
-                if class_permissions.add_schemas.contains(credential) {
-                    Ok(())
-                } else {
-                    Err("NotInAddSchemasSet")
-                }
-            }
-            AccessLevel::Unspecified => Err("UnspecifiedActor"),
-            AccessLevel::EntityMaintainer => Err("AccessLevel::EntityMaintainer-UsedOutOfPlace"),
-        }
-    }
-
-    pub fn can_create_entity(
-        class_permissions: &Self,
-        access_level: &AccessLevel<Credential>,
-    ) -> DispatchResult {
-        match access_level {
-            AccessLevel::System => Ok(()),
-            AccessLevel::Credential(credential) => {
-                if !class_permissions.entities_can_be_created {
-                    Err("EntitiesCannotBeCreated")
-                } else if class_permissions.create_entities.contains(credential) {
-                    Ok(())
-                } else {
-                    Err("NotInCreateEntitiesSet")
-                }
-            }
-            AccessLevel::Unspecified => Err("UnspecifiedActor"),
-            AccessLevel::EntityMaintainer => Err("AccessLevel::EntityMaintainer-UsedOutOfPlace"),
-        }
-    }
-
-    pub fn can_update_entity(
-        class_permissions: &Self,
-        access_level: &AccessLevel<Credential>,
-    ) -> DispatchResult {
-        match access_level {
-            AccessLevel::System => Ok(()),
-            AccessLevel::Credential(credential) => {
-                if class_permissions
-                    .entity_permissions
-                    .update
-                    .contains(credential)
-                {
-                    Ok(())
-                } else {
-                    Err("CredentialNotInEntityPermissionsUpdateSet")
-                }
-            }
-            AccessLevel::EntityMaintainer => {
-                if class_permissions
-                    .entity_permissions
-                    .maintainer_has_all_permissions
-                {
-                    Ok(())
-                } else {
-                    Err("MaintainerNotGivenAllPermissions")
-                }
-            }
-            _ => Err("UnknownActor"),
-        }
-    }
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
-pub struct EntityPermissions<Credential>
-where
-    Credential: Ord,
-{
-    // Principals permitted to update any entity of the class which this permission is associated with.
-    pub update: CredentialSet<Credential>,
-    /// Wether the designated maintainer (if set) of an entity has permission to update it.
-    pub maintainer_has_all_permissions: bool,
-}
-
-impl<Credential: Ord> Default for EntityPermissions<Credential> {
-    fn default() -> Self {
-        EntityPermissions {
-            maintainer_has_all_permissions: true,
-            update: CredentialSet::new(),
-        }
-    }
-}

+ 0 - 673
runtime-modules/versioned-store-permissions/src/tests.rs

@@ -1,673 +0,0 @@
-#![cfg(test)]
-
-use super::*;
-use crate::mock::*;
-use sp_std::collections::btree_set::BTreeSet;
-use versioned_store::PropertyType;
-
-use frame_support::{assert_err, assert_ok};
-
-fn simple_test_schema() -> Vec<Property> {
-    vec![Property {
-        prop_type: PropertyType::Int64,
-        required: false,
-        name: b"field1".to_vec(),
-        description: b"Description field1".to_vec(),
-    }]
-}
-
-fn simple_test_entity_property_values() -> Vec<ClassPropertyValue> {
-    vec![ClassPropertyValue {
-        in_class_index: 0,
-        value: PropertyValue::Int64(1337),
-    }]
-}
-
-fn create_simple_class(permissions: ClassPermissionsType<Runtime>) -> ClassId {
-    let class_id = <versioned_store::Module<Runtime>>::next_class_id();
-    assert_ok!(Permissions::create_class(
-        Origin::signed(CLASS_PERMISSIONS_CREATOR1),
-        b"class_name_1".to_vec(),
-        b"class_description_1".to_vec(),
-        permissions
-    ));
-    class_id
-}
-
-fn create_simple_class_with_default_permissions() -> ClassId {
-    create_simple_class(Default::default())
-}
-
-fn class_permissions_minimal() -> ClassPermissionsType<Runtime> {
-    ClassPermissions {
-        // remove special permissions for entity maintainers
-        entity_permissions: EntityPermissions {
-            maintainer_has_all_permissions: false,
-            ..Default::default()
-        },
-        ..Default::default()
-    }
-}
-
-fn class_permissions_minimal_with_admins(
-    admins: Vec<<Runtime as Trait>::Credential>,
-) -> ClassPermissionsType<Runtime> {
-    ClassPermissions {
-        admins: admins.into(),
-        ..class_permissions_minimal()
-    }
-}
-
-fn next_entity_id() -> EntityId {
-    <versioned_store::Module<Runtime>>::next_entity_id()
-}
-
-#[test]
-fn create_class_then_entity_with_default_class_permissions() {
-    with_test_externalities(|| {
-        // Only authorized accounts can create classes
-        assert_err!(
-            Permissions::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!(<ClassPermissionsByClassId<Runtime>>::contains_key(class_id));
-
-        // default class permissions have empty add_schema acl
-        assert_err!(
-            Permissions::add_class_schema(
-                Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ZERO),
-                Some(0),
-                class_id,
-                vec![],
-                simple_test_schema()
-            ),
-            "NotInAddSchemasSet"
-        );
-
-        // give members of GROUP_ZERO permission to add schemas
-        let add_schema_set = CredentialSet::from(vec![0]);
-        assert_ok!(Permissions::set_class_add_schemas_set(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            add_schema_set
-        ));
-
-        // successfully add a new schema
-        assert_ok!(Permissions::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!(Permissions::create_entity(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-        ));
-        // entities created by system are "un-owned"
-        assert!(!<EntityMaintainerByEntityId<Runtime>>::contains_key(
-            entity_id_1
-        ));
-        assert_eq!(
-            Permissions::entity_maintainer_by_entity_id(entity_id_1),
-            None
-        );
-
-        // default permissions have empty create_entities set and by default no entities can be created
-        assert_err!(
-            Permissions::create_entity(
-                Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-                Some(1),
-                class_id,
-            ),
-            "EntitiesCannotBeCreated"
-        );
-
-        assert_ok!(Permissions::set_class_entities_can_be_created(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            true
-        ));
-
-        assert_err!(
-            Permissions::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!(Permissions::set_class_create_entities_set(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            create_entities_set
-        ));
-
-        let entity_id_2 = next_entity_id();
-        assert_ok!(Permissions::create_entity(
-            Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-            Some(1),
-            class_id,
-        ));
-        assert!(<EntityMaintainerByEntityId<Runtime>>::contains_key(
-            entity_id_2
-        ));
-        assert_eq!(
-            Permissions::entity_maintainer_by_entity_id(entity_id_2),
-            Some(1)
-        );
-
-        // Updating entity must be authorized
-        assert_err!(
-            Permissions::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()
-            ),
-            "CredentialNotInEntityPermissionsUpdateSet"
-        );
-
-        // default permissions give entity maintainer permission to update and delete
-        assert_ok!(Permissions::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!(Permissions::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 class_permissions_set_admins() {
-    with_test_externalities(|| {
-        // create a class where all permission sets are empty
-        let class_id = create_simple_class(class_permissions_minimal());
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-
-        assert!(class_permissions.admins.is_empty());
-
-        let credential_set = CredentialSet::from(vec![1]);
-
-        // only root should be able to set admins
-        assert_err!(
-            Permissions::set_class_admins(Origin::signed(1), class_id, credential_set.clone()),
-            "NotRootOrigin"
-        );
-        assert_err!(
-            Permissions::set_class_admins(
-                frame_system::RawOrigin::None.into(), //unsigned inherent?
-                class_id,
-                credential_set.clone()
-            ),
-            "BadOrigin:ExpectedRootOrSigned"
-        );
-
-        // root origin can set admins
-        assert_ok!(Permissions::set_class_admins(
-            frame_system::RawOrigin::Root.into(),
-            class_id,
-            credential_set.clone()
-        ));
-
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.admins, credential_set);
-    })
-}
-
-#[test]
-fn class_permissions_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_permissions_minimal_with_admins(vec![0]));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-
-        assert!(class_permissions.add_schemas.is_empty());
-
-        let credential_set1 = CredentialSet::from(vec![1, 2]);
-        let credential_set2 = CredentialSet::from(vec![3, 4]);
-
-        // root
-        assert_ok!(Permissions::set_class_add_schemas_set(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            credential_set1.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.add_schemas, credential_set1);
-
-        // admins
-        assert_ok!(Permissions::set_class_add_schemas_set(
-            Origin::signed(ADMIN_ACCOUNT),
-            Some(0),
-            class_id,
-            credential_set2.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.add_schemas, credential_set2);
-
-        // non-admins
-        assert_err!(
-            Permissions::set_class_add_schemas_set(
-                Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-                Some(1),
-                class_id,
-                credential_set2.clone()
-            ),
-            "NotInAdminsSet"
-        );
-    })
-}
-
-#[test]
-fn class_permissions_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_permissions_minimal_with_admins(vec![0]));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-
-        assert!(class_permissions.create_entities.is_empty());
-
-        let credential_set1 = CredentialSet::from(vec![1, 2]);
-        let credential_set2 = CredentialSet::from(vec![3, 4]);
-
-        // root
-        assert_ok!(Permissions::set_class_create_entities_set(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            credential_set1.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.create_entities, credential_set1);
-
-        // admins
-        assert_ok!(Permissions::set_class_create_entities_set(
-            Origin::signed(ADMIN_ACCOUNT),
-            Some(0),
-            class_id,
-            credential_set2.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.create_entities, credential_set2);
-
-        // non-admins
-        assert_err!(
-            Permissions::set_class_create_entities_set(
-                Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-                Some(1),
-                class_id,
-                credential_set2.clone()
-            ),
-            "NotInAdminsSet"
-        );
-    })
-}
-
-#[test]
-fn class_permissions_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_permissions_minimal_with_admins(vec![0]));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-
-        assert_eq!(class_permissions.entities_can_be_created, false);
-
-        // root
-        assert_ok!(Permissions::set_class_entities_can_be_created(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            true
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.entities_can_be_created, true);
-
-        // admins
-        assert_ok!(Permissions::set_class_entities_can_be_created(
-            Origin::signed(ADMIN_ACCOUNT),
-            Some(0),
-            class_id,
-            false
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.entities_can_be_created, false);
-
-        // non-admins
-        assert_err!(
-            Permissions::set_class_entities_can_be_created(
-                Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-                Some(1),
-                class_id,
-                true
-            ),
-            "NotInAdminsSet"
-        );
-    })
-}
-
-#[test]
-fn class_permissions_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_permissions_minimal_with_admins(vec![0]));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-
-        assert!(class_permissions.entity_permissions.update.is_empty());
-
-        let entity_permissions1 = EntityPermissions {
-            update: CredentialSet::from(vec![1]),
-            maintainer_has_all_permissions: true,
-        };
-
-        //root
-        assert_ok!(Permissions::set_class_entity_permissions(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            entity_permissions1.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.entity_permissions, entity_permissions1);
-
-        let entity_permissions2 = EntityPermissions {
-            update: CredentialSet::from(vec![4]),
-            maintainer_has_all_permissions: true,
-        };
-        //admins
-        assert_ok!(Permissions::set_class_entity_permissions(
-            Origin::signed(ADMIN_ACCOUNT),
-            Some(0),
-            class_id,
-            entity_permissions2.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(class_permissions.entity_permissions, entity_permissions2);
-
-        // non admins
-        assert_err!(
-            Permissions::set_class_entity_permissions(
-                Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-                Some(1),
-                class_id,
-                entity_permissions2.clone()
-            ),
-            "NotInAdminsSet"
-        );
-    })
-}
-
-#[test]
-fn class_permissions_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_permissions_minimal_with_admins(vec![0]));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-
-        assert_eq!(class_permissions.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!(Permissions::set_class_reference_constraint(
-            frame_system::RawOrigin::Root.into(),
-            None,
-            class_id,
-            reference_constraint1.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(
-            class_permissions.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!(Permissions::set_class_reference_constraint(
-            Origin::signed(ADMIN_ACCOUNT),
-            Some(0),
-            class_id,
-            reference_constraint2.clone()
-        ));
-        let class_permissions = Permissions::class_permissions_by_class_id(class_id);
-        assert_eq!(
-            class_permissions.reference_constraint,
-            reference_constraint2
-        );
-
-        // non admins
-        assert_err!(
-            Permissions::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 {
-            entities_can_be_created: true,
-            create_entities: vec![CREDENTIAL_ONE].into(),
-            reference_constraint: ReferenceConstraint::NoConstraint,
-            ..Default::default()
-        });
-
-        let new_properties = vec![Property {
-            prop_type: PropertyType::Internal(new_class_id),
-            required: true,
-            name: b"entity".to_vec(),
-            description: b"another entity of same class".to_vec(),
-        }];
-
-        assert_ok!(Permissions::add_class_schema(
-            frame_system::RawOrigin::Root.into(),
-            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!(Permissions::transaction(
-            Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-            operations
-        ));
-
-        // two entities created
-        assert!(versioned_store::EntityById::contains_key(entity_id));
-        assert!(versioned_store::EntityById::contains_key(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 {
-            entities_can_be_created: true,
-            create_entities: vec![CREDENTIAL_ONE].into(),
-            reference_constraint: ReferenceConstraint::NoConstraint,
-            ..Default::default()
-        });
-
-        let new_properties = vec![Property {
-            prop_type: PropertyType::InternalVec(10, new_class_id),
-            required: true,
-            name: b"entities".to_vec(),
-            description: b"vector of entities of same class".to_vec(),
-        }];
-
-        assert_ok!(Permissions::add_class_schema(
-            frame_system::RawOrigin::Root.into(),
-            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!(Permissions::transaction(
-            Origin::signed(MEMBER_ONE_WITH_CREDENTIAL_ONE),
-            operations
-        ));
-
-        // three entities created
-        assert!(versioned_store::EntityById::contains_key(entity_id));
-        assert!(versioned_store::EntityById::contains_key(entity_id + 1));
-        assert!(versioned_store::EntityById::contains_key(entity_id + 2));
-
-        assert_eq!(
-            versioned_store::EntityById::get(entity_id),
-            versioned_store::Entity {
-                class_id: new_class_id,
-                id: entity_id,
-                in_class_schema_indexes: vec![0],
-                values: vec![ClassPropertyValue {
-                    in_class_index: 0,
-                    value: PropertyValue::InternalVec(vec![entity_id + 1, entity_id + 2,])
-                }]
-            }
-        );
-    })
-}

+ 0 - 30
runtime-modules/versioned-store/Cargo.toml

@@ -1,30 +0,0 @@
-[package]
-name = 'pallet-versioned-store'
-version = '3.1.0'
-authors = ['Joystream contributors']
-edition = '2018'
-
-[dependencies]
-serde = { version = "1.0.101", optional = true, features = ["derive"] }
-codec = { package = 'parity-scale-codec', version = '1.3.4', default-features = false, features = ['derive'] }
-sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-common = { package = 'pallet-common', default-features = false, path = '../common'}
-
-[dev-dependencies]
-sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-
-[features]
-default = ['std']
-std = [
-	'serde',
-	'codec/std',
-	'sp-std/std',
-	'frame-support/std',
-	'frame-system/std',
-	'common/std'
-]

+ 0 - 528
runtime-modules/versioned-store/src/example.rs

@@ -1,528 +0,0 @@
-#![cfg(test)]
-
-use super::*;
-use crate::mock::*;
-
-use frame_support::assert_ok;
-
-/// This example uses Class, Properties, Schema and Entity structures
-/// to describe the Staked podcast channel and its second episode.
-/// See https://staked.libsyn.com/rss
-
-#[test]
-fn create_podcast_class_schema() {
-    with_test_externalities(|| {
-        fn common_text_prop() -> PropertyType {
-            PropertyType::Text(200)
-        }
-
-        fn long_text_prop() -> PropertyType {
-            PropertyType::Text(4000)
-        }
-
-        // Channel props:
-        // ------------------------------------------
-
-        let channel_props = vec![
-            // 0
-            Property {
-                prop_type: common_text_prop(),
-                required: true,
-                name: b"atom:link".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 1
-            Property {
-                prop_type: common_text_prop(),
-                required: true,
-                name: b"title".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 2
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"pubDate".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 3
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"lastBuildDate".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 4
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"generator".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 5
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"link".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 6
-            // Example: en-us
-            Property {
-                prop_type: PropertyType::Text(5),
-                required: false,
-                name: b"language".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 7
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"copyright".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 8
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"docs".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 9
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"managingEditor".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 10
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"image/url".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 11
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"image/title".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 12
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"image/link".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 13
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:summary".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 14
-            // TODO this could be Internal prop.
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:author".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 15
-            // TODO make this as a text vec?
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:keywords".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 16
-            Property {
-                prop_type: PropertyType::TextVec(10, 100),
-                required: false,
-                name: b"itunes:category".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 17
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:image".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 18
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:explicit".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 19
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:owner/itunes:name".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 20
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:owner/itunes:email".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 21
-            Property {
-                prop_type: PropertyType::Text(4000),
-                required: false,
-                name: b"description".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 22
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:subtitle".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 23
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:type".to_vec(),
-                description: b"".to_vec(),
-            },
-        ];
-
-        // Episode props
-        // ------------------------------------------
-
-        let episode_props = vec![
-            // 0
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"title".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 1
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:title".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 2
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"pubDate".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 3
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"guid".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 4
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"link".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 5
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:image".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 6
-            Property {
-                prop_type: long_text_prop(),
-                required: false,
-                name: b"description".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 7
-            Property {
-                prop_type: long_text_prop(),
-                required: false,
-                name: b"content:encoded".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 8
-            Property {
-                prop_type: PropertyType::Text(50),
-                required: false,
-                name: b"enclosure/length".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 9
-            Property {
-                prop_type: PropertyType::Text(50),
-                required: false,
-                name: b"enclosure/type".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 10
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"enclosure/url".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 11
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:duration".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 12
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:explicit".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 13
-            // TODO make this as a text vec?
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:keywords".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 14
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:subtitle".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 15
-            Property {
-                prop_type: long_text_prop(),
-                required: false,
-                name: b"itunes:summary".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 16
-            Property {
-                prop_type: PropertyType::Uint16,
-                required: false,
-                name: b"itunes:season".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 17
-            Property {
-                prop_type: PropertyType::Uint16,
-                required: false,
-                name: b"itunes:episode".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 18
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:episodeType".to_vec(),
-                description: b"".to_vec(),
-            },
-            // 19
-            // TODO this could be Internal prop.
-            Property {
-                prop_type: common_text_prop(),
-                required: false,
-                name: b"itunes:author".to_vec(),
-                description: b"".to_vec(),
-            },
-        ];
-
-        // Channel
-
-        let channel_class_id = TestModule::next_class_id();
-        assert_ok!(
-            TestModule::create_class(b"PodcastChannel".to_vec(), b"A podcast channel".to_vec(),),
-            channel_class_id
-        );
-
-        let channel_schema_id: u16 = 0;
-
-        assert_ok!(
-            TestModule::add_class_schema(channel_class_id, vec![], channel_props,),
-            channel_schema_id
-        );
-
-        // Episodes:
-
-        let episode_class_id = TestModule::next_class_id();
-        assert_ok!(
-            TestModule::create_class(b"PodcastEpisode".to_vec(), b"A podcast episode".to_vec(),),
-            episode_class_id
-        );
-
-        let episode_schema_id: u16 = 0;
-
-        assert_ok!(
-            TestModule::add_class_schema(episode_class_id, vec![], episode_props,),
-            episode_schema_id
-        );
-
-        let mut p = PropHelper::new();
-        let channel_entity_id = TestModule::next_entity_id();
-
-        assert_ok!(
-            TestModule::create_entity(channel_class_id,),
-            channel_entity_id
-        );
-
-        assert_ok!(
-            TestModule::add_schema_support_to_entity(
-                channel_entity_id,
-                channel_schema_id,
-                vec![
-                    // 0
-                    p.next_text_value(b"https://staked.libsyn.com/rss".to_vec()),
-                    // 1
-                    p.next_text_value(b"Staked".to_vec()),
-                    // 2
-                    p.next_text_value(b"Wed, 15 May 2019 20:36:20 +0000".to_vec()),
-                    // 3
-                    p.next_text_value(b"Fri, 23 Aug 2019 11:26:24 +0000".to_vec()),
-                    // 4
-                    p.next_text_value(b"Libsyn WebEngine 2.0".to_vec()),
-                    // 5
-                    p.next_text_value(b"https://twitter.com/staked_podcast".to_vec()),
-                    // 6
-                    p.next_text_value(b"en".to_vec()),
-                    // 7
-                    p.next_value(PropertyValue::None),
-                    // 8
-                    p.next_text_value(b"https://twitter.com/staked_podcast".to_vec()),
-                    // 9
-                    p.next_text_value(b"staked@jsgenesis.com (staked@jsgenesis.com)".to_vec()),
-                    // 10
-                    p.next_text_value(b"https://ssl-static.libsyn.com/p/assets/2/d/2/5/2d25eb5fa72739f7/iTunes_Cover.png".to_vec()),
-                    // 11
-                    p.next_text_value(b"Staked".to_vec()),
-                    // 12
-                    p.next_text_value(b"https://twitter.com/staked_podcast".to_vec()),
-                    // 13
-                    p.next_text_value(b"Exploring crypto and blockchain governance.".to_vec()),
-                    // 14
-                    p.next_text_value(b"Staked".to_vec()),
-                    // 15
-                    p.next_text_value(b"crypto,blockchain,governance,staking,bitcoin,ethereum".to_vec()),
-                    // 16
-                    p.next_value(PropertyValue::TextVec(vec![
-                        b"Technology".to_vec(), 
-                        b"Software How-To".to_vec()
-                    ])),
-                    // 17
-                    p.next_text_value(b"https://ssl-static.libsyn.com/p/assets/2/d/2/5/2d25eb5fa72739f7/iTunes_Cover.png".to_vec()),
-                    // 18
-                    p.next_text_value(b"yes".to_vec()),
-                    // 19
-                    p.next_text_value(b"Martin Wessel-Berg".to_vec()),
-                    // 20
-                    p.next_text_value(b"staked@jsgenesis.com".to_vec()),
-                    // 21
-                    p.next_text_value(b"Exploring crypto and blockchain governance.".to_vec()),
-                    // 22
-                    p.next_text_value(b"Exploring crypto and blockchain governance.".to_vec()),
-                    // 23
-                    p.next_text_value(b"episodic".to_vec()),
-                ]
-            )
-        );
-
-        let episode_2_summary = b"<p>In July 2017, the SEC published a report following their <a href=\"https://www.sec.gov/litigation/investreport/34-81207.pdf\">investigation of the DAO</a>. This was significant as it was the first actionable statement from the SEC, giving some insight as to how they interpret this new asset class in light of existing securities laws.</p> <p>Staked is brought to you by Joystream - A user governed media platform.</p>".to_vec();
-
-        p = PropHelper::new();
-        let episode_2_entity_id = TestModule::next_entity_id();
-
-        assert_ok!(
-            TestModule::create_entity(episode_class_id,),
-            episode_2_entity_id
-        );
-
-        assert_ok!(
-            TestModule::add_schema_support_to_entity(
-                episode_2_entity_id,
-                episode_schema_id,
-                vec![
-                    // 0
-                    p.next_text_value(b"Implications of the DAO Report for Crypto Governance".to_vec()),
-                    // 1
-                    p.next_text_value(b"Implications of the DAO Report for Crypto Governance".to_vec()),
-                    // 2
-                    p.next_text_value(b"Wed, 13 Mar 2019 11:20:33 +0000".to_vec()),
-                    // 3
-                    p.next_text_value(b"1bf862ba81ab4ee797526d98e09ad301".to_vec()),
-                    // 4
-                    p.next_text_value(b"http://staked.libsyn.com/implications-of-the-dao-report-for-crypto-governance".to_vec()),
-                    // 5
-                    p.next_text_value(b"https://ssl-static.libsyn.com/p/assets/2/d/2/5/2d25eb5fa72739f7/iTunes_Cover.png".to_vec()),
-                    // 6
-                    p.next_text_value(episode_2_summary.clone()),
-                    // 7
-                    p.next_text_value(episode_2_summary.clone()),
-                    // 8
-                    p.next_text_value(b"87444374".to_vec()),
-                    // 9
-                    p.next_text_value(b"audio/mpeg".to_vec()),
-                    // 10
-                    p.next_text_value(b"https://traffic.libsyn.com/secure/staked/Staked_-_Ep._2_final_cut.mp3?dest-id=1097396".to_vec()),
-                    // 11
-                    p.next_text_value(b"36:27".to_vec()),
-                    // 12
-                    p.next_text_value(b"yes".to_vec()),
-                    // 13
-                    p.next_text_value(b"governance,crypto,sec,securities,dao,bitcoin,blockchain,ethereum".to_vec()),
-                    // 14
-                    p.next_text_value(b"Part I in a series exploring decentralized governance and securities law".to_vec()),
-                    // 15
-                    p.next_text_value(episode_2_summary),
-                    // 16
-                    p.next_value(PropertyValue::Uint16(1)),
-                    // 17
-                    p.next_value(PropertyValue::Uint16(2)),
-                    // 18
-                    p.next_text_value(b"full".to_vec()),
-                    // 19
-                    p.next_text_value(b"Staked".to_vec()),
-                ]
-            )
-        );
-    })
-}
-
-struct PropHelper {
-    prop_idx: u16,
-}
-
-impl PropHelper {
-    fn new() -> PropHelper {
-        PropHelper { prop_idx: 0 }
-    }
-
-    fn next_value(&mut self, value: PropertyValue) -> ClassPropertyValue {
-        let value = ClassPropertyValue {
-            in_class_index: self.prop_idx,
-            value,
-        };
-        self.prop_idx += 1;
-        value
-    }
-
-    fn next_text_value(&mut self, text: Vec<u8>) -> ClassPropertyValue {
-        self.next_value(PropertyValue::Text(text))
-    }
-}

+ 0 - 775
runtime-modules/versioned-store/src/lib.rs

@@ -1,775 +0,0 @@
-// Copyright 2019 Jsgenesis.
-//
-// This is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
-
-// Ensure we're `no_std` when compiling for Wasm.
-#![cfg_attr(not(feature = "std"), no_std)]
-
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-
-use codec::{Decode, Encode};
-use common::constraints::InputValidationLengthConstraint;
-use frame_support::{decl_event, decl_module, decl_storage, ensure};
-use sp_std::collections::btree_set::BTreeSet;
-use sp_std::vec;
-use sp_std::vec::Vec;
-
-mod example;
-mod mock;
-mod tests;
-
-//TODO: Convert errors to the Substrate decl_error! macro.
-/// Result with string error message. This exists for backward compatibility purpose.
-pub type DispatchResult = Result<(), &'static str>;
-
-// Validation errors
-// --------------------------------------
-
-const ERROR_PROPERTY_NAME_TOO_SHORT: &str = "Property name is too short";
-const ERROR_PROPERTY_NAME_TOO_LONG: &str = "Property name is too long";
-const ERROR_PROPERTY_DESCRIPTION_TOO_SHORT: &str = "Property description is too long";
-const ERROR_PROPERTY_DESCRIPTION_TOO_LONG: &str = "Property description is too long";
-
-const ERROR_CLASS_NAME_TOO_SHORT: &str = "Class name is too short";
-const ERROR_CLASS_NAME_TOO_LONG: &str = "Class name is too long";
-const ERROR_CLASS_DESCRIPTION_TOO_SHORT: &str = "Class description is too long";
-const ERROR_CLASS_DESCRIPTION_TOO_LONG: &str = "Class description is too long";
-
-// Main logic errors
-// --------------------------------------
-
-const ERROR_CLASS_NOT_FOUND: &str = "Class was not found by id";
-const ERROR_UNKNOWN_CLASS_SCHEMA_ID: &str = "Unknown class schema id";
-const ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_PROP_INDEX: &str =
-    "New class schema refers to an unknown property index";
-const ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_INTERNAL_ID: &str =
-    "New class schema refers to an unknown internal class id";
-const ERROR_NO_PROPS_IN_CLASS_SCHEMA: &str =
-    "Cannot add a class schema with an empty list of properties";
-const ERROR_ENTITY_NOT_FOUND: &str = "Entity was not found by id";
-// const ERROR_ENTITY_ALREADY_DELETED: &str = "Entity is already deleted";
-const ERROR_SCHEMA_ALREADY_ADDED_TO_ENTITY: &str =
-    "Cannot add a schema that is already added to this entity";
-const ERROR_PROP_VALUE_DONT_MATCH_TYPE: &str =
-    "Some of the provided property values don't match the expected property type";
-const ERROR_PROP_NAME_NOT_UNIQUE_IN_CLASS: &str = "Property name is not unique within its class";
-const ERROR_MISSING_REQUIRED_PROP: &str =
-    "Some required property was not found when adding schema support to entity";
-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";
-const ERROR_TEXT_PROP_IS_TOO_LONG: &str = "Text propery is too long";
-const ERROR_VEC_PROP_IS_TOO_LONG: &str = "Vector propery is too long";
-const ERROR_INTERNAL_RPOP_DOES_NOT_MATCH_ITS_CLASS: &str =
-    "Internal property does not match its class";
-
-pub type ClassId = u64;
-pub type EntityId = u64;
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct Class {
-    pub id: ClassId,
-
-    /// 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>,
-
-    /// All scehmas that are available for this class, think v0.0 Person, v.1.0 Person, etc.
-    pub schemas: Vec<ClassSchema>,
-
-    pub name: Vec<u8>,
-    pub description: Vec<u8>,
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct Entity {
-    pub id: EntityId,
-
-    /// The class id of this entity.
-    pub class_id: 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 in_class_schema_indexes: Vec<u16>, // 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: Vec<ClassPropertyValue>,
-}
-
-/// A schema defines what properties describe an entity
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ClassSchema {
-    /// Indices into properties vector for the corresponding class.
-    pub properties: Vec<u16>,
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct Property {
-    pub prop_type: PropertyType,
-    pub required: bool,
-    pub name: Vec<u8>,
-    pub description: Vec<u8>,
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub enum PropertyType {
-    None,
-
-    // Single value:
-    Bool,
-    Uint16,
-    Uint32,
-    Uint64,
-    Int16,
-    Int32,
-    Int64,
-    Text(u16),
-    Internal(ClassId),
-
-    // Vector of values.
-    // The first u16 value is the max length of this vector.
-    BoolVec(u16),
-    Uint16Vec(u16),
-    Uint32Vec(u16),
-    Uint64Vec(u16),
-    Int16Vec(u16),
-    Int32Vec(u16),
-    Int64Vec(u16),
-
-    /// The first u16 value is the max length of this vector.
-    /// The second u16 value is the max length of every text item in this vector.
-    TextVec(u16, u16),
-
-    /// The first u16 value is the max length of this vector.
-    /// The second ClassId value tells that an every element of this vector
-    /// should be of a specific ClassId.
-    InternalVec(u16, ClassId),
-    // External(ExternalProperty),
-    // ExternalVec(u16, ExternalProperty),
-}
-
-impl Default for PropertyType {
-    fn default() -> Self {
-        PropertyType::None
-    }
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub enum PropertyValue {
-    None,
-
-    // Single value:
-    Bool(bool),
-    Uint16(u16),
-    Uint32(u32),
-    Uint64(u64),
-    Int16(i16),
-    Int32(i32),
-    Int64(i64),
-    Text(Vec<u8>),
-    Internal(EntityId),
-
-    // Vector of values:
-    BoolVec(Vec<bool>),
-    Uint16Vec(Vec<u16>),
-    Uint32Vec(Vec<u32>),
-    Uint64Vec(Vec<u64>),
-    Int16Vec(Vec<i16>),
-    Int32Vec(Vec<i32>),
-    Int64Vec(Vec<i64>),
-    TextVec(Vec<Vec<u8>>),
-    InternalVec(Vec<EntityId>),
-    // External(ExternalPropertyType),
-    // ExternalVec(Vec<ExternalPropertyType>),
-}
-
-impl Default for PropertyValue {
-    fn default() -> Self {
-        PropertyValue::None
-    }
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
-pub struct ClassPropertyValue {
-    /// Index is into properties vector of class.
-    pub in_class_index: u16,
-
-    /// Value of property with index `in_class_index` in a given class.
-    pub value: PropertyValue,
-}
-
-pub trait Trait: frame_system::Trait + Sized {
-    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
-}
-
-decl_storage! {
-
-    trait Store for Module<T: Trait> as VersionedStore {
-
-        pub ClassById get(fn class_by_id) config(): map hasher(blake2_128_concat) ClassId => Class;
-
-        pub EntityById get(fn entity_by_id) config(): map hasher(blake2_128_concat) EntityId => Entity;
-
-        pub NextClassId get(fn next_class_id) config(): ClassId;
-
-        pub NextEntityId get(fn next_entity_id) config(): EntityId;
-
-        pub PropertyNameConstraint get(fn property_name_constraint)
-            config(): InputValidationLengthConstraint;
-
-        pub PropertyDescriptionConstraint get(fn property_description_constraint)
-            config(): InputValidationLengthConstraint;
-
-        pub ClassNameConstraint get(fn class_name_constraint)
-            config(): InputValidationLengthConstraint;
-
-        pub ClassDescriptionConstraint get(fn class_description_constraint)
-            config(): InputValidationLengthConstraint;
-    }
-}
-
-decl_event!(
-    pub enum Event<T>
-    where
-        <T as frame_system::Trait>::AccountId,
-    {
-        ClassCreated(ClassId),
-        ClassSchemaAdded(ClassId, u16),
-
-        EntityCreated(EntityId),
-        // EntityDeleted(EntityId),
-        EntityPropertiesUpdated(EntityId),
-        EntitySchemaAdded(EntityId, u16),
-
-        /// This is a fake event that uses AccountId type just to make Rust compiler happy to compile this module.
-        FixCompilation(AccountId),
-    }
-);
-
-decl_module! {
-    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
-        fn deposit_event() = default;
-    }
-}
-
-// Shortcuts for faster readability of match expression:
-use PropertyType as PT;
-use PropertyValue as PV;
-
-impl<T: Trait> Module<T> {
-    /// Returns an id of a newly added class.
-    pub fn create_class(name: Vec<u8>, description: Vec<u8>) -> Result<ClassId, &'static str> {
-        Self::ensure_class_name_is_valid(&name)?;
-
-        Self::ensure_class_description_is_valid(&description)?;
-
-        let class_id = NextClassId::get();
-
-        let new_class = Class {
-            id: class_id,
-            properties: vec![],
-            schemas: vec![],
-            name,
-            description,
-        };
-
-        // Save newly created class:
-        ClassById::insert(class_id, new_class);
-
-        // Increment the next class id:
-        NextClassId::mutate(|n| *n += 1);
-
-        Self::deposit_event(RawEvent::ClassCreated(class_id));
-        Ok(class_id)
-    }
-
-    /// Returns an index of a newly added class schema on success.
-    pub fn add_class_schema(
-        class_id: ClassId,
-        existing_properties: Vec<u16>,
-        new_properties: Vec<Property>,
-    ) -> Result<u16, &'static str> {
-        Self::ensure_known_class_id(class_id)?;
-
-        let non_empty_schema = !existing_properties.is_empty() || !new_properties.is_empty();
-
-        ensure!(non_empty_schema, ERROR_NO_PROPS_IN_CLASS_SCHEMA);
-
-        let class = ClassById::get(class_id);
-
-        // TODO Use BTreeSet for prop unique names when switched to Substrate 2.
-        // There is no support for BTreeSet in Substrate 1 runtime.
-        // use sp_std::collections::btree_set::BTreeSet;
-        let mut unique_prop_names = BTreeSet::new();
-        for prop in class.properties.iter() {
-            unique_prop_names.insert(prop.name.clone());
-        }
-
-        for prop in new_properties.iter() {
-            Self::ensure_property_name_is_valid(&prop.name)?;
-            Self::ensure_property_description_is_valid(&prop.description)?;
-
-            // Check that the name of a new property is unique within its class.
-            ensure!(
-                !unique_prop_names.contains(&prop.name),
-                ERROR_PROP_NAME_NOT_UNIQUE_IN_CLASS
-            );
-            unique_prop_names.insert(prop.name.clone());
-        }
-
-        // Check that existing props are valid indices of class properties vector:
-        let has_unknown_props = existing_properties
-            .iter()
-            .any(|&prop_id| prop_id >= class.properties.len() as u16);
-        ensure!(
-            !has_unknown_props,
-            ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_PROP_INDEX
-        );
-
-        // Check validity of Internal(ClassId) for new_properties.
-        let has_unknown_internal_id = new_properties.iter().any(|prop| match prop.prop_type {
-            PropertyType::Internal(other_class_id) => !ClassById::contains_key(other_class_id),
-            _ => false,
-        });
-        ensure!(
-            !has_unknown_internal_id,
-            ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_INTERNAL_ID
-        );
-
-        // Use the current length of schemas in this class as an index
-        // for the next schema that will be sent in a result of this function.
-        let schema_idx = class.schemas.len() as u16;
-
-        let mut schema = ClassSchema {
-            properties: existing_properties,
-        };
-
-        let mut updated_class_props = class.properties;
-        new_properties.into_iter().for_each(|prop| {
-            let prop_id = updated_class_props.len() as u16;
-            updated_class_props.push(prop);
-            schema.properties.push(prop_id);
-        });
-
-        ClassById::mutate(class_id, |class| {
-            class.properties = updated_class_props;
-            class.schemas.push(schema);
-        });
-
-        Self::deposit_event(RawEvent::ClassSchemaAdded(class_id, schema_idx));
-        Ok(schema_idx)
-    }
-
-    pub fn create_entity(class_id: ClassId) -> Result<EntityId, &'static str> {
-        Self::ensure_known_class_id(class_id)?;
-
-        let entity_id = NextEntityId::get();
-
-        let new_entity = Entity {
-            id: entity_id,
-            class_id,
-            in_class_schema_indexes: vec![],
-            values: vec![],
-            // deleted: false,
-        };
-
-        // Save newly created entity:
-        EntityById::insert(entity_id, new_entity);
-
-        // Increment the next entity id:
-        NextEntityId::mutate(|n| *n += 1);
-
-        Self::deposit_event(RawEvent::EntityCreated(entity_id));
-        Ok(entity_id)
-    }
-
-    pub fn add_schema_support_to_entity(
-        entity_id: EntityId,
-        schema_id: u16,
-        property_values: Vec<ClassPropertyValue>,
-    ) -> DispatchResult {
-        Self::ensure_known_entity_id(entity_id)?;
-
-        let (entity, class) = Self::get_entity_and_class(entity_id);
-
-        // Check that schema_id is a valid index of class schemas vector:
-        let known_schema_id = schema_id < class.schemas.len() as u16;
-        ensure!(known_schema_id, ERROR_UNKNOWN_CLASS_SCHEMA_ID);
-
-        // Check that schema id is not yet added to this entity:
-        let schema_not_added = entity
-            .in_class_schema_indexes
-            .iter()
-            .position(|x| *x == schema_id)
-            .is_none();
-        ensure!(schema_not_added, ERROR_SCHEMA_ALREADY_ADDED_TO_ENTITY);
-
-        let class_schema_opt = class.schemas.get(schema_id as usize);
-        let schema_prop_ids = class_schema_opt.unwrap().properties.clone();
-
-        let current_entity_values = entity.values.clone();
-        let mut appended_entity_values = entity.values;
-
-        for &prop_id in schema_prop_ids.iter() {
-            let prop_already_added = current_entity_values
-                .iter()
-                .any(|prop| prop.in_class_index == prop_id);
-
-            if prop_already_added {
-                // A property is already added to the entity and cannot be updated
-                // while adding a schema support to this entity.
-                continue;
-            }
-
-            let class_prop = class.properties.get(prop_id as usize).unwrap();
-
-            // If a value was not povided for the property of this schema:
-            match property_values
-                .iter()
-                .find(|prop| prop.in_class_index == prop_id)
-            {
-                Some(new_prop) => {
-                    let ClassPropertyValue {
-                        in_class_index: new_id,
-                        value: new_value,
-                    } = new_prop;
-
-                    Self::ensure_property_value_is_valid(new_value.clone(), class_prop.clone())?;
-
-                    appended_entity_values.push(ClassPropertyValue {
-                        in_class_index: *new_id,
-                        value: new_value.clone(),
-                    });
-                }
-                None => {
-                    // All required prop values should be are provided
-                    if class_prop.required {
-                        return Err(ERROR_MISSING_REQUIRED_PROP);
-                    }
-                    // Add all missing non required schema prop values as PropertyValue::None
-                    else {
-                        appended_entity_values.push(ClassPropertyValue {
-                            in_class_index: prop_id,
-                            value: PropertyValue::None,
-                        });
-                    }
-                }
-            }
-        }
-
-        EntityById::mutate(entity_id, |entity| {
-            // Add a new schema to the list of schemas supported by this entity.
-            entity.in_class_schema_indexes.push(schema_id);
-
-            // Update entity values only if new properties have been added.
-            if appended_entity_values.len() > entity.values.len() {
-                entity.values = appended_entity_values;
-            }
-        });
-
-        Self::deposit_event(RawEvent::EntitySchemaAdded(entity_id, schema_id));
-        Ok(())
-    }
-
-    pub fn update_entity_property_values(
-        entity_id: EntityId,
-        new_property_values: Vec<ClassPropertyValue>,
-    ) -> DispatchResult {
-        Self::ensure_known_entity_id(entity_id)?;
-
-        let (entity, class) = Self::get_entity_and_class(entity_id);
-
-        // Get current property values of an entity as a mutable vector,
-        // so we can update them if new values provided present in new_property_values.
-        let mut updated_values = entity.values;
-        let mut updates_count = 0;
-
-        // Iterate over a vector of new values and update corresponding properties
-        // of this entity if new values are valid.
-        for new_prop_value in new_property_values.iter() {
-            let ClassPropertyValue {
-                in_class_index: id,
-                value: new_value,
-            } = new_prop_value;
-
-            // Try to find a current property value in the entity
-            // by matching its id to the id of a property with an updated value.
-            if let Some(current_prop_value) = updated_values
-                .iter_mut()
-                .find(|prop| *id == prop.in_class_index)
-            {
-                let ClassPropertyValue {
-                    in_class_index: valid_id,
-                    value: current_value,
-                } = current_prop_value;
-
-                // Get class-level information about this property
-                let class_prop = class.properties.get(*valid_id as usize).unwrap();
-
-                // Validate a new property value against the type of this property
-                // and check any additional constraints like the length of a vector
-                // if it's a vector property or the length of a text if it's a text property.
-                Self::ensure_property_value_is_valid(new_value.clone(), class_prop.clone())?;
-
-                // Update a current prop value in a mutable vector, if a new value is valid.
-                *current_value = new_value.clone();
-                updates_count += 1;
-            } else {
-                // Throw an error if a property was not found on entity
-                // by an in-class index of a property update.
-                return Err(ERROR_UNKNOWN_ENTITY_PROP_ID);
-            }
-        }
-
-        // If at least one of the entity property values should be update:
-        if updates_count > 0 {
-            EntityById::mutate(entity_id, |entity| {
-                entity.values = updated_values;
-            });
-            Self::deposit_event(RawEvent::EntityPropertiesUpdated(entity_id));
-        }
-
-        Ok(())
-    }
-
-    // Commented out for now <- requested by Bedeho.
-    // pub fn delete_entity(entity_id: EntityId) -> DispatchResult {
-    //     Self::ensure_known_entity_id(entity_id)?;
-
-    //     let is_deleted = EntityById::get(entity_id).deleted;
-    //     ensure!(!is_deleted, ERROR_ENTITY_ALREADY_DELETED);
-
-    //     EntityById::mutate(entity_id, |x| {
-    //         x.deleted = true;
-    //     });
-
-    //     Self::deposit_event(RawEvent::EntityDeleted(entity_id));
-    //     Ok(())
-    // }
-
-    // Helper functions:
-    // ----------------------------------------------------------------
-
-    pub fn ensure_known_class_id(class_id: ClassId) -> DispatchResult {
-        ensure!(ClassById::contains_key(class_id), ERROR_CLASS_NOT_FOUND);
-        Ok(())
-    }
-
-    pub fn ensure_known_entity_id(entity_id: EntityId) -> DispatchResult {
-        ensure!(EntityById::contains_key(entity_id), ERROR_ENTITY_NOT_FOUND);
-        Ok(())
-    }
-
-    pub fn ensure_valid_internal_prop(value: PropertyValue, prop: Property) -> DispatchResult {
-        match (value, prop.prop_type) {
-            (PV::Internal(entity_id), PT::Internal(class_id)) => {
-                Self::ensure_known_class_id(class_id)?;
-                Self::ensure_known_entity_id(entity_id)?;
-                let entity = Self::entity_by_id(entity_id);
-                ensure!(
-                    entity.class_id == class_id,
-                    ERROR_INTERNAL_RPOP_DOES_NOT_MATCH_ITS_CLASS
-                );
-                Ok(())
-            }
-            _ => Ok(()),
-        }
-    }
-
-    pub fn is_unknown_internal_entity_id(id: PropertyValue) -> bool {
-        if let PropertyValue::Internal(entity_id) = id {
-            !EntityById::contains_key(entity_id)
-        } else {
-            false
-        }
-    }
-
-    pub fn get_entity_and_class(entity_id: EntityId) -> (Entity, Class) {
-        let entity = EntityById::get(entity_id);
-        let class = ClassById::get(entity.class_id);
-        (entity, class)
-    }
-
-    pub fn ensure_property_value_is_valid(value: PropertyValue, prop: Property) -> DispatchResult {
-        Self::ensure_prop_value_matches_its_type(value.clone(), prop.clone())?;
-        Self::ensure_valid_internal_prop(value.clone(), prop.clone())?;
-        Self::validate_max_len_if_text_prop(value.clone(), prop.clone())?;
-        Self::validate_max_len_if_vec_prop(value, prop)?;
-        Ok(())
-    }
-
-    pub fn validate_max_len_if_text_prop(value: PropertyValue, prop: Property) -> DispatchResult {
-        match (value, prop.prop_type) {
-            (PV::Text(text), PT::Text(max_len)) => Self::validate_max_len_of_text(text, max_len),
-            _ => Ok(()),
-        }
-    }
-
-    pub fn validate_max_len_of_text(text: Vec<u8>, max_len: u16) -> DispatchResult {
-        if text.len() <= max_len as usize {
-            Ok(())
-        } else {
-            Err(ERROR_TEXT_PROP_IS_TOO_LONG)
-        }
-    }
-
-    #[rustfmt::skip]
-    pub fn validate_max_len_if_vec_prop(
-        value: PropertyValue,
-        prop: Property,
-    ) -> DispatchResult {
-
-        fn validate_vec_len<T>(vec: Vec<T>, max_len: u16) -> bool {
-            vec.len() <= max_len as usize
-        }
-
-        fn validate_vec_len_ref<T>(vec: &[T], max_len: u16) -> bool {
-            vec.len() <= max_len as usize
-        }
-
-        let is_valid_len = match (value, prop.prop_type) {
-            (PV::BoolVec(vec),     PT::BoolVec(max_len))   => validate_vec_len(vec, max_len),
-            (PV::Uint16Vec(vec),   PT::Uint16Vec(max_len)) => validate_vec_len(vec, max_len),
-            (PV::Uint32Vec(vec),   PT::Uint32Vec(max_len)) => validate_vec_len(vec, max_len),
-            (PV::Uint64Vec(vec),   PT::Uint64Vec(max_len)) => validate_vec_len(vec, max_len),
-            (PV::Int16Vec(vec),    PT::Int16Vec(max_len))  => validate_vec_len(vec, max_len),
-            (PV::Int32Vec(vec),    PT::Int32Vec(max_len))  => validate_vec_len(vec, max_len),
-            (PV::Int64Vec(vec),    PT::Int64Vec(max_len))  => validate_vec_len(vec, max_len),
-
-            (PV::TextVec(vec),     PT::TextVec(vec_max_len, text_max_len)) => {
-                if validate_vec_len_ref(&vec, vec_max_len) {
-                    for text_item in vec.iter() {
-                        Self::validate_max_len_of_text(text_item.clone(), text_max_len)?;
-                    }
-                    true
-                } else {
-                    false
-                }
-            },
-
-            (PV::InternalVec(vec), PT::InternalVec(vec_max_len, class_id)) => {
-                Self::ensure_known_class_id(class_id)?;
-                if validate_vec_len_ref(&vec, vec_max_len) {
-                    for entity_id in vec.iter() {
-                        Self::ensure_known_entity_id(*entity_id)?;
-                        let entity = Self::entity_by_id(entity_id);
-                        ensure!(entity.class_id == class_id, ERROR_INTERNAL_RPOP_DOES_NOT_MATCH_ITS_CLASS);
-                    }
-                    true
-                } else {
-                    false
-                }
-            },
-
-            _ => true
-        };
-
-        if is_valid_len {
-            Ok(())
-        } else {
-            Err(ERROR_VEC_PROP_IS_TOO_LONG)
-        }
-    }
-
-    pub fn ensure_prop_value_matches_its_type(
-        value: PropertyValue,
-        prop: Property,
-    ) -> DispatchResult {
-        if Self::does_prop_value_match_type(value, prop) {
-            Ok(())
-        } else {
-            Err(ERROR_PROP_VALUE_DONT_MATCH_TYPE)
-        }
-    }
-
-    // Preserve special formatting.
-    #[rustfmt::skip]
-    #[allow(clippy::match_like_matches_macro)]
-    pub fn does_prop_value_match_type(
-        value: PropertyValue,
-        prop: Property,
-    ) -> bool {
-
-        // A non required property can be updated to None:
-        if !prop.required && value == PV::None {
-            return true
-        }
-
-        match (value, prop.prop_type) {
-            (PV::None,        PT::None) |
-
-            // Single values
-            (PV::Bool(_),     PT::Bool) |
-            (PV::Uint16(_),   PT::Uint16) |
-            (PV::Uint32(_),   PT::Uint32) |
-            (PV::Uint64(_),   PT::Uint64) |
-            (PV::Int16(_),    PT::Int16) |
-            (PV::Int32(_),    PT::Int32) |
-            (PV::Int64(_),    PT::Int64) |
-            (PV::Text(_),     PT::Text(_)) |
-            (PV::Internal(_), PT::Internal(_)) |
-
-            // Vectors:
-            (PV::BoolVec(_),     PT::BoolVec(_)) |
-            (PV::Uint16Vec(_),   PT::Uint16Vec(_)) |
-            (PV::Uint32Vec(_),   PT::Uint32Vec(_)) |
-            (PV::Uint64Vec(_),   PT::Uint64Vec(_)) |
-            (PV::Int16Vec(_),    PT::Int16Vec(_)) |
-            (PV::Int32Vec(_),    PT::Int32Vec(_)) |
-            (PV::Int64Vec(_),    PT::Int64Vec(_)) |
-            (PV::TextVec(_),     PT::TextVec(_, _)) |
-            (PV::InternalVec(_), PT::InternalVec(_, _)) => true,
-
-            // (PV::External(_), PT::External(_)) => true,
-            // (PV::ExternalVec(_), PT::ExternalVec(_, _)) => true,
-            _ => false,
-        }
-    }
-
-    pub fn ensure_property_name_is_valid(text: &[u8]) -> DispatchResult {
-        PropertyNameConstraint::get().ensure_valid(
-            text.len(),
-            ERROR_PROPERTY_NAME_TOO_SHORT,
-            ERROR_PROPERTY_NAME_TOO_LONG,
-        )
-    }
-
-    pub fn ensure_property_description_is_valid(text: &[u8]) -> DispatchResult {
-        PropertyDescriptionConstraint::get().ensure_valid(
-            text.len(),
-            ERROR_PROPERTY_DESCRIPTION_TOO_SHORT,
-            ERROR_PROPERTY_DESCRIPTION_TOO_LONG,
-        )
-    }
-
-    pub fn ensure_class_name_is_valid(text: &[u8]) -> DispatchResult {
-        ClassNameConstraint::get().ensure_valid(
-            text.len(),
-            ERROR_CLASS_NAME_TOO_SHORT,
-            ERROR_CLASS_NAME_TOO_LONG,
-        )
-    }
-
-    pub fn ensure_class_description_is_valid(text: &[u8]) -> DispatchResult {
-        ClassDescriptionConstraint::get().ensure_valid(
-            text.len(),
-            ERROR_CLASS_DESCRIPTION_TOO_SHORT,
-            ERROR_CLASS_DESCRIPTION_TOO_LONG,
-        )
-    }
-}

+ 0 - 262
runtime-modules/versioned-store/src/mock.rs

@@ -1,262 +0,0 @@
-#![cfg(test)]
-
-use crate::*;
-use crate::{GenesisConfig, Module, Trait};
-
-use frame_support::{assert_err, assert_ok, impl_outer_origin, parameter_types};
-use sp_core::H256;
-use sp_runtime::{
-    testing::Header,
-    traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
-};
-
-impl_outer_origin! {
-    pub enum Origin for Runtime {}
-}
-
-// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
-#[derive(Clone, 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 frame_system::Trait for Runtime {
-    type BaseCallFilter = ();
-    type Origin = Origin;
-    type Call = ();
-    type Index = u64;
-    type BlockNumber = u64;
-    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 DbWeight = ();
-    type BlockExecutionWeight = ();
-    type ExtrinsicBaseWeight = ();
-    type MaximumExtrinsicWeight = ();
-    type MaximumBlockLength = MaximumBlockLength;
-    type AvailableBlockRatio = AvailableBlockRatio;
-    type Version = ();
-    type PalletInfo = ();
-    type AccountData = ();
-    type OnNewAccount = ();
-    type OnKilledAccount = ();
-    type SystemWeightInfo = ();
-}
-
-impl pallet_timestamp::Trait for Runtime {
-    type Moment = u64;
-    type OnTimestampSet = ();
-    type MinimumPeriod = MinimumPeriod;
-    type WeightInfo = ();
-}
-
-impl Trait for Runtime {
-    type Event = ();
-}
-
-pub const UNKNOWN_CLASS_ID: ClassId = 111;
-
-pub const UNKNOWN_ENTITY_ID: EntityId = 222;
-
-pub const UNKNOWN_PROP_ID: u16 = 333;
-
-pub const SCHEMA_ID_0: u16 = 0;
-pub const SCHEMA_ID_1: u16 = 1;
-
-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()
-}
-
-impl Property {
-    fn required(&self) -> Property {
-        let mut new_self = self.clone();
-        new_self.required = true;
-        new_self
-    }
-}
-
-pub fn good_prop_bool() -> Property {
-    Property {
-        prop_type: PropertyType::Bool,
-        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 {
-    Property {
-        prop_type: PropertyType::Uint32,
-        required: false,
-        name: b"Name of a u32 property".to_vec(),
-        description: b"Description of a u32 property".to_vec(),
-    }
-}
-
-pub fn good_prop_text() -> Property {
-    Property {
-        prop_type: PropertyType::Text(20),
-        required: false,
-        name: b"Name of a text property".to_vec(),
-        description: b"Description of a text property".to_vec(),
-    }
-}
-
-pub fn new_internal_class_prop(class_id: ClassId) -> Property {
-    Property {
-        prop_type: PropertyType::Internal(class_id),
-        required: false,
-        name: b"Name of a internal property".to_vec(),
-        description: b"Description of a internal property".to_vec(),
-    }
-}
-
-pub fn good_props() -> Vec<Property> {
-    vec![good_prop_bool(), good_prop_u32()]
-}
-
-pub fn good_prop_ids() -> Vec<u16> {
-    vec![0, 1]
-}
-
-pub fn create_class() -> ClassId {
-    let class_id = TestModule::next_class_id();
-    assert_ok!(
-        TestModule::create_class(good_class_name(), good_class_description(),),
-        class_id
-    );
-    class_id
-}
-
-pub fn bool_prop_value() -> ClassPropertyValue {
-    ClassPropertyValue {
-        in_class_index: 0,
-        value: PropertyValue::Bool(true),
-    }
-}
-
-pub fn prop_value(index: u16, value: PropertyValue) -> ClassPropertyValue {
-    ClassPropertyValue {
-        in_class_index: index,
-        value,
-    }
-}
-
-pub fn create_class_with_schema_and_entity() -> (ClassId, u16, EntityId) {
-    let class_id = create_class();
-    if let Ok(schema_id) = TestModule::add_class_schema(
-        class_id,
-        vec![],
-        vec![
-            good_prop_bool().required(),
-            good_prop_u32(),
-            new_internal_class_prop(class_id),
-        ],
-    ) {
-        let entity_id = create_entity_of_class(class_id);
-        (class_id, schema_id, entity_id)
-    } else {
-        panic!("This should not happen")
-    }
-}
-
-pub const PROP_ID_BOOL: u16 = 0;
-pub const PROP_ID_U32: u16 = 1;
-pub const PROP_ID_INTERNAL: u16 = 2;
-
-pub fn create_entity_with_schema_support() -> EntityId {
-    let (_, schema_id, entity_id) = create_class_with_schema_and_entity();
-    assert_ok!(TestModule::add_schema_support_to_entity(
-        entity_id,
-        schema_id,
-        vec![prop_value(PROP_ID_BOOL, PropertyValue::Bool(true))]
-    ));
-    entity_id
-}
-
-pub fn create_entity_of_class(class_id: ClassId) -> EntityId {
-    let entity_id = TestModule::next_entity_id();
-    assert_ok!(TestModule::create_entity(class_id,), entity_id);
-    entity_id
-}
-
-pub fn assert_class_props(class_id: ClassId, expected_props: Vec<Property>) {
-    let class = TestModule::class_by_id(class_id);
-    assert_eq!(class.properties, expected_props);
-}
-
-pub fn assert_class_schemas(class_id: ClassId, expected_schema_prop_ids: Vec<Vec<u16>>) {
-    let class = TestModule::class_by_id(class_id);
-    let schemas: Vec<_> = expected_schema_prop_ids
-        .iter()
-        .map(|prop_ids| ClassSchema {
-            properties: prop_ids.clone(),
-        })
-        .collect();
-    assert_eq!(class.schemas, schemas);
-}
-
-pub fn assert_entity_not_found(result: crate::DispatchResult) {
-    assert_err!(result, ERROR_ENTITY_NOT_FOUND);
-}
-
-// This function basically just builds a genesis storage key/value store according to
-// our desired mockup.
-
-pub fn default_genesis_config() -> GenesisConfig {
-    GenesisConfig {
-        class_by_id: vec![],
-        entity_by_id: vec![],
-        next_class_id: 1,
-        next_entity_id: 1,
-        property_name_constraint: InputValidationLengthConstraint {
-            min: 1,
-            max_min_diff: 49,
-        },
-        property_description_constraint: InputValidationLengthConstraint {
-            min: 0,
-            max_min_diff: 500,
-        },
-        class_name_constraint: InputValidationLengthConstraint {
-            min: 1,
-            max_min_diff: 49,
-        },
-        class_description_constraint: InputValidationLengthConstraint {
-            min: 0,
-            max_min_diff: 500,
-        },
-    }
-}
-
-fn build_test_externalities(config: GenesisConfig) -> sp_io::TestExternalities {
-    let mut t = frame_system::GenesisConfig::default()
-        .build_storage::<Runtime>()
-        .unwrap();
-
-    config.assimilate_storage(&mut t).unwrap();
-
-    t.into()
-}
-
-pub fn with_test_externalities<R, F: FnOnce() -> R>(f: F) -> R {
-    let config = default_genesis_config();
-    build_test_externalities(config).execute_with(f)
-}
-
-/// Export module on a test runtime
-pub type TestModule = Module<Runtime>;

+ 0 - 503
runtime-modules/versioned-store/src/tests.rs

@@ -1,503 +0,0 @@
-#![cfg(test)]
-
-use super::*;
-use crate::mock::*;
-
-use frame_support::{assert_err, assert_ok};
-
-// Create class
-// --------------------------------------
-
-#[test]
-fn create_class_successfully() {
-    with_test_externalities(|| {
-        let class_id = TestModule::next_class_id();
-        assert_ok!(
-            TestModule::create_class(good_class_name(), good_class_description(),),
-            class_id
-        );
-        assert_eq!(TestModule::next_class_id(), class_id + 1);
-    })
-}
-
-#[test]
-fn cannot_create_class_with_empty_name() {
-    with_test_externalities(|| {
-        let empty_name = vec![];
-        assert_err!(
-            TestModule::create_class(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_eq!(
-            TestModule::create_class(good_class_name(), empty_description,),
-            Ok(1)
-        );
-    })
-}
-
-// Add class schema
-// --------------------------------------
-
-#[test]
-fn cannot_add_schema_to_unknown_class() {
-    with_test_externalities(|| {
-        assert_err!(
-            TestModule::add_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_class();
-        assert_err!(
-            TestModule::add_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_class();
-        assert_err!(
-            TestModule::add_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_class();
-
-        assert_eq!(
-            TestModule::add_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::add_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_class();
-        let bad_internal_prop = new_internal_class_prop(UNKNOWN_CLASS_ID);
-
-        assert_err!(
-            TestModule::add_class_schema(
-                class_id,
-                vec![],
-                vec![good_prop_bool(), bad_internal_prop]
-            ),
-            ERROR_CLASS_SCHEMA_REFERS_UNKNOWN_INTERNAL_ID
-        );
-    })
-}
-
-#[test]
-fn should_add_class_schema_with_internal_class_prop() {
-    with_test_externalities(|| {
-        let class_id = create_class();
-        let internal_class_prop = new_internal_class_prop(class_id);
-
-        // Add first schema with new props.
-        // No other props on the class at this time.
-        assert_eq!(
-            TestModule::add_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_class();
-
-        // Add first schema with new props.
-        // No other props on the class at this time.
-        assert_eq!(
-            TestModule::add_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_class();
-
-        // Add first schema with new props.
-        // No other props on the class at this time.
-        assert_eq!(
-            TestModule::add_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::add_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_class();
-
-        // Add first schema with new props.
-        // No other props on the class at this time.
-        assert_eq!(
-            TestModule::add_class_schema(class_id, vec![], good_props()),
-            Ok(SCHEMA_ID_0)
-        );
-
-        // Add a new schema with not unique property names:
-        assert_err!(
-            TestModule::add_class_schema(class_id, vec![], good_props()),
-            ERROR_PROP_NAME_NOT_UNIQUE_IN_CLASS
-        );
-    })
-}
-
-#[test]
-fn should_add_class_schema_when_both_prop_ids_and_new_props_passed() {
-    with_test_externalities(|| {
-        let class_id = create_class();
-
-        // Add first schema with new props.
-        // No other props on the class at this time.
-        assert_eq!(
-            TestModule::add_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::add_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]]);
-    })
-}
-
-// Create entity
-// --------------------------------------
-
-#[test]
-fn create_entity_successfully() {
-    with_test_externalities(|| {
-        let class_id = create_class();
-        let entity_id_1 = TestModule::next_entity_id();
-        assert_ok!(TestModule::create_entity(class_id,), entity_id_1);
-        // TODO assert entity from storage
-        assert_eq!(TestModule::next_entity_id(), entity_id_1 + 1);
-    })
-}
-
-#[test]
-fn cannot_create_entity_with_unknown_class_id() {
-    with_test_externalities(|| {
-        assert_err!(
-            TestModule::create_entity(UNKNOWN_CLASS_ID,),
-            ERROR_CLASS_NOT_FOUND
-        );
-    })
-}
-
-// 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_schema_support_to_entity(
-            UNKNOWN_ENTITY_ID,
-            1,
-            vec![],
-        ));
-    })
-}
-
-#[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_schema_support_to_entity(
-            entity_id,
-            schema_id,
-            vec![bool_prop_value()]
-        ));
-
-        // Secondly we try to add support for the same schema.
-        assert_err!(
-            TestModule::add_schema_support_to_entity(entity_id, schema_id, vec![]),
-            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_schema_support_to_entity(
-                entity_id,
-                unknown_schema_id,
-                vec![prop_value(0, PropertyValue::None)]
-            ),
-            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();
-        assert_err!(
-            TestModule::add_schema_support_to_entity(
-                entity_id,
-                schema_id,
-                vec![
-                    bool_prop_value(),
-                    prop_value(PROP_ID_U32, PropertyValue::Bool(true))
-                ]
-            ),
-            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();
-        assert_err!(
-            TestModule::add_schema_support_to_entity(
-                entity_id,
-                schema_id,
-                vec![
-                    bool_prop_value(),
-                    prop_value(PROP_ID_INTERNAL, PropertyValue::Internal(UNKNOWN_ENTITY_ID))
-                ]
-            ),
-            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_schema_support_to_entity(
-                entity_id,
-                schema_id,
-                vec![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();
-        assert_ok!(TestModule::add_schema_support_to_entity(
-            entity_id,
-            schema_id,
-            vec![
-                bool_prop_value(),
-                prop_value(PROP_ID_U32, PropertyValue::Uint32(123)),
-                // Note that an optional internal prop is not provided here.
-            ]
-        ));
-
-        let entity = TestModule::entity_by_id(entity_id);
-        assert_eq!(entity.in_class_schema_indexes, [SCHEMA_ID_0]);
-        assert_eq!(
-            entity.values,
-            vec![
-                bool_prop_value(),
-                prop_value(PROP_ID_U32, PropertyValue::Uint32(123)),
-                prop_value(PROP_ID_INTERNAL, PropertyValue::None),
-            ]
-        );
-    })
-}
-
-// Update entity properties
-// --------------------------------------
-
-#[test]
-fn cannot_update_entity_props_when_entity_not_found() {
-    with_test_externalities(|| {
-        assert_entity_not_found(TestModule::update_entity_property_values(
-            UNKNOWN_ENTITY_ID,
-            vec![],
-        ));
-    })
-}
-
-#[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::update_entity_property_values(
-                entity_id,
-                vec![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::update_entity_property_values(
-                entity_id,
-                vec![prop_value(
-                    PROP_ID_INTERNAL,
-                    PropertyValue::Internal(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::update_entity_property_values(
-                entity_id,
-                vec![prop_value(UNKNOWN_PROP_ID, PropertyValue::Bool(true))]
-            ),
-            ERROR_UNKNOWN_ENTITY_PROP_ID
-        );
-    })
-}
-
-#[test]
-fn update_entity_props_successfully() {
-    with_test_externalities(|| {
-        let entity_id = create_entity_with_schema_support();
-        assert_eq!(
-            TestModule::entity_by_id(entity_id).values,
-            vec![
-                prop_value(PROP_ID_BOOL, PropertyValue::Bool(true)),
-                prop_value(PROP_ID_U32, PropertyValue::None),
-                prop_value(PROP_ID_INTERNAL, PropertyValue::None),
-            ]
-        );
-        assert_ok!(TestModule::update_entity_property_values(
-            entity_id,
-            vec![
-                prop_value(PROP_ID_BOOL, PropertyValue::Bool(false)),
-                prop_value(PROP_ID_U32, PropertyValue::Uint32(123)),
-                prop_value(PROP_ID_INTERNAL, PropertyValue::Internal(entity_id)),
-            ]
-        ));
-        assert_eq!(
-            TestModule::entity_by_id(entity_id).values,
-            vec![
-                prop_value(PROP_ID_BOOL, PropertyValue::Bool(false)),
-                prop_value(PROP_ID_U32, PropertyValue::Uint32(123)),
-                prop_value(PROP_ID_INTERNAL, PropertyValue::Internal(entity_id)),
-            ]
-        );
-    })
-}
-
-// 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
-//         );
-//     })
-// }

+ 1 - 6
runtime/Cargo.toml

@@ -72,9 +72,6 @@ hiring = { package = 'pallet-hiring', default-features = false, path = '../runti
 minting = { package = 'pallet-token-mint', default-features = false, path = '../runtime-modules/token-minting'}
 recurring-rewards = { package = 'pallet-recurring-reward', default-features = false, path = '../runtime-modules/recurring-reward'}
 working-group = { package = 'pallet-working-group', default-features = false, path = '../runtime-modules/working-group'}
-content-working-group = { package = 'pallet-content-working-group', default-features = false, path = '../runtime-modules/content-working-group'}
-versioned-store = { package = 'pallet-versioned-store', default-features = false, path = '../runtime-modules/versioned-store'}
-versioned-store-permissions = { package = 'pallet-versioned-store-permissions', default-features = false, path = '../runtime-modules/versioned-store-permissions'}
 storage = { package = 'pallet-storage', default-features = false, path = '../runtime-modules/storage'}
 service-discovery = { package = 'pallet-service-discovery', default-features = false, path = '../runtime-modules/service-discovery'}
 proposals-engine = { package = 'pallet-proposals-engine', default-features = false, path = '../runtime-modules/proposals/engine'}
@@ -151,9 +148,6 @@ std = [
     'minting/std',
     'recurring-rewards/std',
     'working-group/std',
-    'content-working-group/std',
-    'versioned-store/std',
-    'versioned-store-permissions/std',
     'storage/std',
     'service-discovery/std',
     'proposals-engine/std',
@@ -177,6 +171,7 @@ runtime-benchmarks = [
 	"pallet-session-benchmarking",
     "pallet-utility/runtime-benchmarks",
     "proposals-discussion/runtime-benchmarks",
+    "proposals-engine/runtime-benchmarks",
     "hex-literal",
 ]
 

+ 0 - 142
runtime/src/integration/content_working_group.rs

@@ -1,142 +0,0 @@
-use crate::{AccountId, Credential, Runtime};
-
-use frame_support::traits::{Currency, Imbalance};
-use frame_support::{parameter_types, IterableStorageMap, StorageMap};
-
-parameter_types! {
-    pub const CurrentLeadCredential: Credential = 0;
-    pub const AnyActiveCuratorCredential: Credential = 1;
-    pub const AnyActiveChannelOwnerCredential: Credential = 2;
-    pub const PrincipalIdMappingStartsAtCredential: Credential = 1000;
-}
-
-pub struct ContentWorkingGroupCredentials {}
-impl versioned_store_permissions::CredentialChecker<Runtime> for ContentWorkingGroupCredentials {
-    fn account_has_credential(
-        account: &AccountId,
-        credential: <Runtime as versioned_store_permissions::Trait>::Credential,
-    ) -> bool {
-        match credential {
-            // Credentials from 0..999 represents groups or more complex requirements
-            // Current Lead if set
-            credential if credential == CurrentLeadCredential::get() => {
-                match <content_working_group::Module<Runtime>>::ensure_lead_is_set() {
-                    Ok((_, lead)) => lead.role_account == *account,
-                    _ => false,
-                }
-            }
-            // Any Active Curator
-            credential if credential == AnyActiveCuratorCredential::get() => {
-                // Look for a Curator with a matching role account
-                for (_principal_id, principal) in
-                    <content_working_group::PrincipalById<Runtime>>::iter()
-                {
-                    if let content_working_group::Principal::Curator(curator_id) = principal {
-                        let curator =
-                            <content_working_group::CuratorById<Runtime>>::get(curator_id);
-                        if curator.role_account == *account
-                            && curator.stage == content_working_group::CuratorRoleStage::Active
-                        {
-                            return true;
-                        }
-                    }
-                }
-
-                false
-            }
-            // Any Active Channel Owner
-            credential if credential == AnyActiveChannelOwnerCredential::get() => {
-                // Look for a ChannelOwner with a matching role account
-                for (_principal_id, principal) in
-                    <content_working_group::PrincipalById<Runtime>>::iter()
-                {
-                    if let content_working_group::Principal::ChannelOwner(channel_id) = principal {
-                        let channel =
-                            <content_working_group::ChannelById<Runtime>>::get(channel_id);
-                        if channel.role_account == *account {
-                            return true; // should we also take publishing_status/curation_status into account ?
-                        }
-                    }
-                }
-
-                false
-            }
-            // mapping to working group principal id
-            n if n >= PrincipalIdMappingStartsAtCredential::get() => {
-                <content_working_group::Module<Runtime>>::account_has_credential(
-                    account,
-                    n - PrincipalIdMappingStartsAtCredential::get(),
-                )
-            }
-            _ => false,
-        }
-    }
-}
-
-#[allow(dead_code)]
-pub struct ContentWorkingGroupStakingEventHandler {}
-impl stake::StakingEventsHandler<Runtime> for ContentWorkingGroupStakingEventHandler {
-    fn unstaked(
-        stake_id: &<Runtime as stake::Trait>::StakeId,
-        _unstaked_amount: stake::BalanceOf<Runtime>,
-        remaining_imbalance: stake::NegativeImbalance<Runtime>,
-    ) -> stake::NegativeImbalance<Runtime> {
-        if !hiring::ApplicationIdByStakingId::<Runtime>::contains_key(stake_id) {
-            // Stake not related to a staked role managed by the hiring module
-            return remaining_imbalance;
-        }
-
-        let application_id = hiring::ApplicationIdByStakingId::<Runtime>::get(stake_id);
-
-        if !content_working_group::CuratorApplicationById::<Runtime>::contains_key(application_id) {
-            // Stake not for a Curator
-            return remaining_imbalance;
-        }
-
-        // Notify the Hiring module - is there a potential re-entrancy bug if
-        // instant unstaking is occuring?
-        hiring::Module::<Runtime>::unstaked(*stake_id);
-
-        // Only notify working group module if non instantaneous unstaking occured
-        if content_working_group::UnstakerByStakeId::<Runtime>::contains_key(stake_id) {
-            content_working_group::Module::<Runtime>::unstaked(*stake_id);
-        }
-
-        // Determine member id of the curator
-        let curator_application =
-            content_working_group::CuratorApplicationById::<Runtime>::get(application_id);
-        let member_id = curator_application.member_id;
-
-        // get member's profile
-        let member_profile = membership::MembershipById::<Runtime>::get(member_id);
-
-        // deposit funds to member's root_account
-        // The application doesn't recorded the original source_account from which staked funds were
-        // provided, so we don't really have another option at the moment.
-        <Runtime as stake::Trait>::Currency::resolve_creating(
-            &member_profile.root_account,
-            remaining_imbalance,
-        );
-
-        stake::NegativeImbalance::<Runtime>::zero()
-    }
-
-    // Handler for slashing event
-    fn slashed(
-        _id: &<Runtime as stake::Trait>::StakeId,
-        _slash_id: Option<<Runtime as stake::Trait>::SlashId>,
-        _slashed_amount: stake::BalanceOf<Runtime>,
-        _remaining_stake: stake::BalanceOf<Runtime>,
-        remaining_imbalance: stake::NegativeImbalance<Runtime>,
-    ) -> stake::NegativeImbalance<Runtime> {
-        // Check if the stake is associated with a hired curator or applicant
-        // if their stake goes below minimum required for the role,
-        // they should get deactivated.
-        // Since we don't currently implement any slash initiation in working group,
-        // there is nothing to do for now.
-
-        // Not interested in transfering the slashed amount anywhere for now,
-        // so return it to next handler.
-        remaining_imbalance
-    }
-}

+ 0 - 31
runtime/src/integration/forum.rs

@@ -1,31 +0,0 @@
-/*
- * Forum module integration
- *
- * ForumUserRegistry could have been implemented directly on
- * the membership module, and likewise ForumUser on Profile,
- * however this approach is more loosely coupled.
- *
- * Further exploration required to decide what the long
- * run convention should be.
- */
-
-use crate::{AccountId, Runtime};
-
-/// Shim registry which will proxy ForumUserRegistry behaviour to the members module
-pub struct ShimMembershipRegistry {}
-
-impl forum::ForumUserRegistry<AccountId> for ShimMembershipRegistry {
-    fn get_forum_user(id: &AccountId) -> Option<forum::ForumUser<AccountId>> {
-        if membership::Module::<Runtime>::is_member_account(id) {
-            // For now we don't retrieve the members profile since it is not used for anything,
-            // but in the future we may need it to read out more
-            // information possibly required to construct a
-            // ForumUser.
-
-            // Now convert member profile to a forum user
-            Some(forum::ForumUser { id: id.clone() })
-        } else {
-            None
-        }
-    }
-}

+ 0 - 3
runtime/src/integration/mod.rs

@@ -1,7 +1,4 @@
 pub mod content_directory;
-pub mod content_working_group;
-pub mod forum;
 pub mod proposals;
 pub mod storage;
 pub mod transactions;
-pub mod versioned_store_permissions;

+ 1 - 0
runtime/src/integration/proposals/proposal_encoder.rs

@@ -21,6 +21,7 @@ macro_rules! wrap_working_group_call {
                 Call::ContentDirectoryWorkingGroup($working_group_instance_call)
             }
             WorkingGroup::Storage => Call::StorageWorkingGroup($working_group_instance_call),
+            WorkingGroup::Forum => Call::ForumWorkingGroup($working_group_instance_call),
         }
     }};
 }

+ 0 - 50
runtime/src/integration/versioned_store_permissions.rs

@@ -1,50 +0,0 @@
-use crate::{AccountId, Runtime};
-use frame_support::{StorageMap, StorageValue};
-
-// Credential Checker that gives the sudo key holder all credentials
-pub struct SudoKeyHasAllCredentials {}
-impl versioned_store_permissions::CredentialChecker<Runtime> for SudoKeyHasAllCredentials {
-    fn account_has_credential(
-        account: &AccountId,
-        _credential: <Runtime as versioned_store_permissions::Trait>::Credential,
-    ) -> bool {
-        <pallet_sudo::Module<Runtime>>::key() == *account
-    }
-}
-
-// Allow sudo key holder permission to create classes
-pub struct SudoKeyCanCreateClasses {}
-impl versioned_store_permissions::CreateClassPermissionsChecker<Runtime>
-    for SudoKeyCanCreateClasses
-{
-    fn account_can_create_class_permissions(account: &AccountId) -> bool {
-        <pallet_sudo::Module<Runtime>>::key() == *account
-    }
-}
-
-pub struct ContentLeadOrSudoKeyCanCreateClasses {}
-impl versioned_store_permissions::CreateClassPermissionsChecker<Runtime>
-    for ContentLeadOrSudoKeyCanCreateClasses
-{
-    fn account_can_create_class_permissions(account: &AccountId) -> bool {
-        ContentLeadCanCreateClasses::account_can_create_class_permissions(account)
-            || SudoKeyCanCreateClasses::account_can_create_class_permissions(account)
-    }
-}
-
-// Allow content working group lead to create classes in content directory
-pub struct ContentLeadCanCreateClasses {}
-impl versioned_store_permissions::CreateClassPermissionsChecker<Runtime>
-    for ContentLeadCanCreateClasses
-{
-    fn account_can_create_class_permissions(account: &AccountId) -> bool {
-        // get current lead id
-        let maybe_current_lead_id = content_working_group::CurrentLeadId::<Runtime>::get();
-        if let Some(lead_id) = maybe_current_lead_id {
-            let lead = content_working_group::LeadById::<Runtime>::get(lead_id);
-            lead.role_account == *account
-        } else {
-            false
-        }
-    }
-}

+ 96 - 27
runtime/src/lib.rs

@@ -63,7 +63,6 @@ pub use content_directory;
 pub use content_directory::{
     HashedTextMaxLength, InputValidationLengthConstraint, MaxNumber, TextMaxLength, VecMaxLength,
 };
-pub use content_working_group as content_wg;
 pub use forum;
 pub use governance::election_params::ElectionParameters;
 pub use membership;
@@ -72,8 +71,6 @@ pub use pallet_balances::Call as BalancesCall;
 pub use pallet_staking::StakerStatus;
 pub use proposals_engine::ProposalParameters;
 pub use storage::{data_directory, data_object_type_registry};
-pub use versioned_store;
-pub use versioned_store_permissions;
 pub use working_group;
 
 #[cfg(feature = "std")]
@@ -409,20 +406,6 @@ impl pallet_finality_tracker::Trait for Runtime {
     type ReportLatency = ReportLatency;
 }
 
-impl versioned_store::Trait for Runtime {
-    type Event = Event;
-}
-
-impl versioned_store_permissions::Trait for Runtime {
-    type Credential = Credential;
-    type CredentialChecker = (
-        integration::content_working_group::ContentWorkingGroupCredentials,
-        integration::versioned_store_permissions::SudoKeyHasAllCredentials,
-    );
-    type CreateClassPermissionsChecker =
-        integration::versioned_store_permissions::ContentLeadOrSudoKeyCanCreateClasses;
-}
-
 type EntityId = <Runtime as content_directory::Trait>::EntityId;
 
 parameter_types! {
@@ -495,10 +478,6 @@ impl stake::Trait for Runtime {
     type SlashId = u64;
 }
 
-impl content_wg::Trait for Runtime {
-    type Event = Event;
-}
-
 impl common::currency::GovernanceCurrency for Runtime {
     type Currency = pallet_balances::Module<Self>;
 }
@@ -549,13 +528,88 @@ impl membership::Trait for Runtime {
     type ActorId = ActorId;
 }
 
+parameter_types! {
+    pub const MaxCategoryDepth: u64 = 5;
+
+    pub const MaxSubcategories: u64 = 20;
+    pub const MaxThreadsInCategory: u64 = 20;
+    pub const MaxPostsInThread: u64 = 20;
+    pub const MaxModeratorsForCategory: u64 = 20;
+    pub const MaxCategories: u64 = 20;
+    pub const MaxPollAlternativesNumber: u64 = 20;
+}
+
+pub struct MapLimits;
+
+impl forum::StorageLimits for MapLimits {
+    type MaxSubcategories = MaxSubcategories;
+    type MaxThreadsInCategory = MaxThreadsInCategory;
+    type MaxPostsInThread = MaxPostsInThread;
+    type MaxModeratorsForCategory = MaxModeratorsForCategory;
+    type MaxCategories = MaxCategories;
+    type MaxPollAlternativesNumber = MaxPollAlternativesNumber;
+}
+
+// Alias for forum working group
+type ForumGroup<T> = working_group::Module<T, ForumWorkingGroupInstance>;
+
 impl forum::Trait for Runtime {
     type Event = Event;
-    type MembershipRegistry = integration::forum::ShimMembershipRegistry;
+    //type MembershipRegistry = ShimMembershipRegistry;
     type ThreadId = ThreadId;
     type PostId = PostId;
+    type ForumUserId = ForumUserId;
+    type ModeratorId = ModeratorId;
+    type CategoryId = u64;
+    type PostReactionId = u64;
+    type MaxCategoryDepth = MaxCategoryDepth;
+
+    type MapLimits = MapLimits;
+
+    fn is_lead(_account_id: &AccountId) -> bool {
+        // get current lead id
+        let maybe_current_lead_id = ForumGroup::<Runtime>::current_lead();
+        if let Some(ref current_lead_id) = maybe_current_lead_id {
+            if let Ok(worker) = working_group::ensure_worker_exists::<
+                Runtime,
+                ForumWorkingGroupInstance,
+            >(current_lead_id)
+            {
+                *_account_id == worker.role_account_id
+            } else {
+                false
+            }
+        } else {
+            false
+        }
+    }
+
+    fn is_forum_member(_account_id: &Self::AccountId, _forum_user_id: &Self::ForumUserId) -> bool {
+        membership::Module::<Runtime>::ensure_is_controller_account_for_member(
+            _forum_user_id,
+            _account_id,
+        )
+        .is_ok()
+    }
+
+    fn is_moderator(_account_id: &Self::AccountId, _moderator_id: &Self::ModeratorId) -> bool {
+        if let Ok(worker) =
+            working_group::ensure_worker_exists::<Runtime, ForumWorkingGroupInstance>(_moderator_id)
+        {
+            *_account_id == worker.role_account_id
+        } else {
+            false
+        }
+    }
+
+    fn calculate_hash(text: &[u8]) -> Self::Hash {
+        Self::Hash::from_slice(text)
+    }
 }
 
+// The forum working group instance alias.
+pub type ForumWorkingGroupInstance = working_group::Instance1;
+
 // The storage working group instance alias.
 pub type StorageWorkingGroupInstance = working_group::Instance2;
 
@@ -565,10 +619,21 @@ pub type ContentDirectoryWorkingGroupInstance = working_group::Instance3;
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 100;
     pub const MinUnstakingPeriodLimit: u32 = 43200;
+    pub const ForumWorkingGroupRewardPeriod: u32 = 14400 + 10;
     pub const StorageWorkingGroupRewardPeriod: u32 = 14400 + 20;
     pub const ContentWorkingGroupRewardPeriod: u32 = 14400 + 30;
     pub const StorageWorkingGroupLockId: LockIdentifier = [6; 8];
     pub const ContentWorkingGroupLockId: LockIdentifier = [7; 8];
+    pub const ForumGroupLockId: LockIdentifier = [8; 8];
+}
+
+impl working_group::Trait<ForumWorkingGroupInstance> for Runtime {
+    type Event = Event;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type StakingHandler = staking_handler::StakingManager<Self, ForumGroupLockId>;
+    type MemberOriginValidator = MembershipOriginValidator<Self>;
+    type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
+    type RewardPeriod = ForumWorkingGroupRewardPeriod;
 }
 
 impl working_group::Trait<StorageWorkingGroupInstance> for Runtime {
@@ -616,6 +681,7 @@ impl proposals_engine::Trait for Runtime {
     type MaxActiveProposalLimit = ProposalMaxActiveProposalLimit;
     type DispatchableCallCode = Call;
     type ProposalObserver = ProposalsCodex;
+    type WeightInfo = weights::proposals_engine::WeightInfo;
 }
 
 impl Default for Call {
@@ -635,6 +701,7 @@ impl proposals_discussion::Trait for Runtime {
     type ThreadId = ThreadId;
     type PostId = PostId;
     type MaxWhiteListSize = MaxWhiteListSize;
+    type WeightInfo = weights::proposals_discussion::WeightInfo;
 }
 
 parameter_types! {
@@ -679,6 +746,11 @@ parameter_types! {
     pub const SurchargeReward: Balance = 0; // no reward
 }
 
+/// Forum identifiers for user, moderator and category
+pub type ForumUserId = u64;
+pub type ModeratorId = u64;
+pub type CategoryId = u64;
+
 /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
 /// the specifics of the runtime. They can then be made to be agnostic over specific formats
 /// of data like extrinsics, allowing for them to continue syncing the network through upgrades
@@ -726,18 +798,15 @@ construct_runtime!(
         Memo: memo::{Module, Call, Storage, Event<T>},
         Members: membership::{Module, Call, Storage, Event<T>, Config<T>},
         Forum: forum::{Module, Call, Storage, Event<T>, Config<T>},
-        VersionedStore: versioned_store::{Module, Call, Storage, Event<T>, Config},
-        VersionedStorePermissions: versioned_store_permissions::{Module, Call, Storage, Config<T>},
         Stake: stake::{Module, Call, Storage},
         Minting: minting::{Module, Call, Storage},
         RecurringRewards: recurring_rewards::{Module, Call, Storage},
         Hiring: hiring::{Module, Call, Storage},
-        ContentWorkingGroup: content_wg::{Module, Call, Storage, Event<T>, Config<T>},
         ContentDirectory: content_directory::{Module, Call, Storage, Event<T>, Config<T>},
         Constitution: constitution::{Module, Call, Storage, Event},
         // --- Storage
         DataObjectTypeRegistry: data_object_type_registry::{Module, Call, Storage, Event<T>, Config<T>},
-        DataDirectory: data_directory::{Module, Call, Storage, Event<T>, Config<T>},
+        DataDirectory: data_directory::{Module, Call, Storage, Event<T>},
         DataObjectStorageRegistry: data_object_storage_registry::{Module, Call, Storage, Event<T>, Config<T>},
         Discovery: service_discovery::{Module, Call, Storage, Event<T>},
         // --- Proposals
@@ -745,7 +814,7 @@ construct_runtime!(
         ProposalsDiscussion: proposals_discussion::{Module, Call, Storage, Event<T>},
         ProposalsCodex: proposals_codex::{Module, Call, Storage},
         // --- Working groups
-        // reserved for the future use: ForumWorkingGroup: working_group::<Instance1>::{Module, Call, Storage, Event<T>},
+        ForumWorkingGroup: working_group::<Instance1>::{Module, Call, Storage, Event<T>},
         StorageWorkingGroup: working_group::<Instance2>::{Module, Call, Storage, Event<T>},
         ContentDirectoryWorkingGroup: working_group::<Instance3>::{Module, Call, Storage, Event<T>},
     }

+ 16 - 22
runtime/src/runtime_api.rs

@@ -249,48 +249,42 @@ impl_runtime_apis! {
         }
     }
 
-     #[cfg(feature = "runtime-benchmarks")]
+    #[cfg(feature = "runtime-benchmarks")]
     impl frame_benchmarking::Benchmark<Block> for Runtime {
         fn dispatch_benchmark(
-            pallet: Vec<u8>,
-            benchmark: Vec<u8>,
-            lowest_range_values: Vec<u32>,
-            highest_range_values: Vec<u32>,
-            steps: Vec<u32>,
-            repeat: u32,
+            config: frame_benchmarking::BenchmarkConfig
         ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {
-            /*
-             * TODO: remember to benchhmark every pallet
-             */
             use sp_std::vec;
-            use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark};
+            use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};
             use frame_system_benchmarking::Module as SystemBench;
             impl frame_system_benchmarking::Trait for Runtime {}
 
             use crate::ProposalsDiscussion;
+            use crate::ProposalsEngine;
 
-            let whitelist: Vec<Vec<u8>> = vec![
+            let whitelist: Vec<TrackedStorageKey> = vec![
                 // Block Number
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
                 // Total Issuance
-                hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec(),
+                hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),
                 // Execution Phase
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),
                 // Event Count
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),
                 // System Events
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),
                 // Caller 0 Account
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec().into(),
                 // Treasury Account
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000").to_vec().into(),
             ];
 
             let mut batches = Vec::<BenchmarkBatch>::new();
-            let params = (&pallet, &benchmark, &lowest_range_values, &highest_range_values, &steps, repeat, &whitelist);
+            let params = (&config, &whitelist);
 
-            add_benchmark!(params, batches, b"system", SystemBench::<Runtime>);
-            add_benchmark!(params, batches, b"proposals-discussion", ProposalsDiscussion);
+            add_benchmark!(params, batches, system, SystemBench::<Runtime>);
+            add_benchmark!(params, batches, proposals_discussion, ProposalsDiscussion);
+            add_benchmark!(params, batches, proposals_engine, ProposalsEngine);
 
             if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
             Ok(batches)

+ 54 - 1
runtime/src/tests/proposals_integration/working_group_proposals.rs

@@ -12,7 +12,7 @@ use working_group::Penalty;
 use crate::primitives::{ActorId, MemberId};
 use crate::{
     Balance, BlockNumber, ContentDirectoryWorkingGroup, ContentDirectoryWorkingGroupInstance,
-    StorageWorkingGroup, StorageWorkingGroupInstance,
+    ForumWorkingGroup, ForumWorkingGroupInstance, StorageWorkingGroup, StorageWorkingGroupInstance,
 };
 use frame_support::traits;
 use strum::IntoEnumIterator;
@@ -48,6 +48,14 @@ fn add_opening(
             >>::contains_key(opening_id));
             opening_id
         }
+        WorkingGroup::Forum => {
+            let opening_id = ForumWorkingGroup::next_opening_id();
+            assert!(!<working_group::OpeningById<
+                Runtime,
+                ForumWorkingGroupInstance,
+            >>::contains_key(opening_id));
+            opening_id
+        }
     };
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
@@ -296,6 +304,12 @@ fn create_add_working_group_leader_opening_proposal_execution_succeeds() {
                     StorageWorkingGroupInstance,
                 >(group);
             }
+            WorkingGroup::Forum => {
+                run_create_add_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    ForumWorkingGroupInstance,
+                >(group);
+            }
         }
     }
 }
@@ -346,6 +360,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     StorageWorkingGroupInstance,
                 >(group);
             }
+            WorkingGroup::Forum => {
+                run_create_fill_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    ForumWorkingGroupInstance,
+                >(group);
+            }
         }
     }
 
@@ -417,6 +437,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                         StorageWorkingGroupInstance,
                     >(group);
                 }
+                WorkingGroup::Forum => {
+                    run_create_decrease_group_leader_stake_proposal_execution_succeeds::<
+                        Runtime,
+                        ForumWorkingGroupInstance,
+                    >(group);
+                }
             }
         }
     }
@@ -520,6 +546,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                         StorageWorkingGroupInstance,
                     >(group)
                 }
+                WorkingGroup::Forum => {
+                    run_create_slash_group_leader_stake_proposal_execution_succeeds::<
+                        Runtime,
+                        ForumWorkingGroupInstance,
+                    >(group)
+                }
             }
         }
     }
@@ -624,6 +656,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                         StorageWorkingGroupInstance,
                     >(group);
                 }
+                WorkingGroup::Forum => {
+                    run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                        Runtime,
+                        ForumWorkingGroupInstance,
+                    >(group);
+                }
             }
         }
 
@@ -676,6 +714,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                             StorageWorkingGroupInstance,
                         >(group);
                     }
+                    WorkingGroup::Forum => {
+                        run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                            Runtime,
+                            ForumWorkingGroupInstance,
+                        >(group);
+                    }
                 }
             }
         }
@@ -768,6 +812,12 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                             StorageWorkingGroupInstance,
                         >(group);
                     }
+                    WorkingGroup::Forum => {
+                        run_create_terminate_group_leader_role_proposal_execution_succeeds::<
+                            Runtime,
+                            ForumWorkingGroupInstance,
+                        >(group);
+                    }
                 }
             }
         }
@@ -863,6 +913,9 @@ fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
                     WorkingGroup::Storage => {
                         run_create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds::<Runtime, StorageWorkingGroupInstance>(group);
                     }
+                    WorkingGroup::Forum => {
+                        run_create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds::<Runtime, ForumWorkingGroupInstance>(group);
+                    }
                 }
             }
         }

+ 3 - 0
runtime/src/weights/mod.rs

@@ -22,3 +22,6 @@ pub mod pallet_session;
 pub mod pallet_staking;
 pub mod pallet_timestamp;
 pub mod pallet_utility;
+
+pub mod proposals_discussion;
+pub mod proposals_engine;

+ 27 - 0
runtime/src/weights/proposals_discussion.rs

@@ -0,0 +1,27 @@
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
+
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
+
+pub struct WeightInfo;
+impl proposals_discussion::WeightInfo for WeightInfo {
+    // WARNING! Some components were not used: ["j"]
+    fn add_post(i: u32) -> Weight {
+        (361_189_000 as Weight)
+            .saturating_add((508_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(4 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }
+    // WARNING! Some components were not used: ["j"]
+    fn update_post() -> Weight {
+        (231_487_000 as Weight).saturating_add(DbWeight::get().reads(3 as Weight))
+    }
+    fn change_thread_mode(i: u32) -> Weight {
+        (379_400_000 as Weight)
+            .saturating_add((1_244_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
+}

+ 66 - 0
runtime/src/weights/proposals_engine.rs

@@ -0,0 +1,66 @@
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
+
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
+
+pub struct WeightInfo;
+impl proposals_engine::WeightInfo for WeightInfo {
+    fn vote(i: u32) -> Weight {
+        (375_240_000 as Weight)
+            .saturating_add((35_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(4 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }
+    fn cancel_proposal(i: u32) -> Weight {
+        (874_300_000 as Weight)
+            .saturating_add((1_713_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(5 as Weight))
+            .saturating_add(DbWeight::get().writes(8 as Weight))
+    }
+    fn veto_proposal() -> Weight {
+        (404_254_000 as Weight)
+            .saturating_add(DbWeight::get().reads(4 as Weight))
+            .saturating_add(DbWeight::get().writes(8 as Weight))
+    }
+    fn on_initialize_immediate_execution_decode_fails(i: u32) -> Weight {
+        (22_531_000 as Weight)
+            .saturating_add((578_486_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().reads((4 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_pending_execution_decode_fails(i: u32) -> Weight {
+        (31_944_000 as Weight)
+            .saturating_add((274_852_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().reads((2 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((5 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_approved_pending_constitutionality(i: u32) -> Weight {
+        (50_422_000 as Weight)
+            .saturating_add((250_210_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_rejected(i: u32) -> Weight {
+        (0 as Weight)
+            .saturating_add((884_947_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_slashed(i: u32) -> Weight {
+        (24_867_000 as Weight)
+            .saturating_add((628_899_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(i as Weight)))
+    }
+}

+ 14 - 0
scripts/generate-weights.sh

@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+# Executes and replaces all benchmarks with the new weights
+
+echo "Benchmarking proposals_discussion..."
+./target/release/joystream-node benchmark --pallet=proposals_discussion --extrinsic=* --chain=dev --steps=50 --repeat=20 --execution=wasm --output=. > /dev/null
+mv proposals_discussion.rs runtime/src/weights/
+echo "proposals_discussion benchmarked"
+
+
+echo "Benchmarking proposals_engine..."
+./target/release/joystream-node benchmark --pallet=proposals_engine --extrinsic=* --chain=dev --steps=50 --repeat=20 --execution=wasm --output=. > /dev/null
+mv proposals_engine.rs runtime/src/weights/
+echo "proposals_engine benchmarked"

+ 1 - 54
utils/chain-spec-builder/src/main.rs

@@ -24,8 +24,7 @@ use rand::{distributions::Alphanumeric, rngs::OsRng, Rng};
 use structopt::StructOpt;
 
 use joystream_node::chain_spec::{
-    self, chain_spec_properties, content_config, forum_config, initial_balances, initial_members,
-    AccountId,
+    self, chain_spec_properties, forum_config, initial_balances, initial_members, AccountId,
 };
 
 use sc_chain_spec::ChainType;
@@ -85,9 +84,6 @@ enum ChainSpecBuilder {
         /// The path to an initial forum data
         #[structopt(long, short)]
         initial_forum_path: Option<PathBuf>,
-        /// The path to an initial content directory data file
-        #[structopt(long, short)]
-        initial_content_path: Option<PathBuf>,
         /// The path to an initial balances file
         #[structopt(long, short)]
         initial_balances_path: Option<PathBuf>,
@@ -120,9 +116,6 @@ enum ChainSpecBuilder {
         /// The path to an initial forum data
         #[structopt(long, short)]
         initial_forum_path: Option<PathBuf>,
-        /// The path to an initial content directory data file
-        #[structopt(long, short)]
-        initial_content_path: Option<PathBuf>,
         /// The path to an initial balances file
         #[structopt(long, short)]
         initial_balances_path: Option<PathBuf>,
@@ -171,20 +164,6 @@ impl ChainSpecBuilder {
         }
     }
 
-    /// Returns the path to load initial platform content from
-    fn initial_content_path(&self) -> &Option<PathBuf> {
-        match self {
-            ChainSpecBuilder::New {
-                initial_content_path,
-                ..
-            } => initial_content_path,
-            ChainSpecBuilder::Generate {
-                initial_content_path,
-                ..
-            } => initial_content_path,
-        }
-    }
-
     /// Returns the path to load initial platform content from
     fn initial_balances_path(&self) -> &Option<PathBuf> {
         match self {
@@ -222,7 +201,6 @@ fn genesis_constructor(
     sudo_account: &AccountId,
     initial_members_path: &Option<PathBuf>,
     initial_forum_path: &Option<PathBuf>,
-    initial_content_path: &Option<PathBuf>,
     initial_balances_path: &Option<PathBuf>,
 ) -> chain_spec::GenesisConfig {
     let authorities = authority_seeds
@@ -241,29 +219,6 @@ fn genesis_constructor(
         .map(|path| forum_config::from_json(sudo_account.clone(), path.as_path()))
         .unwrap_or_else(|| forum_config::empty(sudo_account.clone()));
 
-    let (
-        versioned_store_cfg,
-        versioned_store_permissions_cfg,
-        data_directory_config,
-        content_working_group_config,
-    ) = if let Some(path) = initial_content_path {
-        let path = path.as_path();
-
-        (
-            content_config::versioned_store_config_from_json(path),
-            content_config::versioned_store_permissions_config_from_json(path),
-            content_config::data_directory_config_from_json(path),
-            content_config::content_working_group_config_from_json(path),
-        )
-    } else {
-        (
-            content_config::empty_versioned_store_config(),
-            content_config::empty_versioned_store_permissions_config(),
-            content_config::empty_data_directory_config(),
-            content_config::empty_content_working_group_config(),
-        )
-    };
-
     let initial_account_balances = initial_balances_path
         .as_ref()
         .map(|path| initial_balances::from_json(path.as_path()))
@@ -275,10 +230,6 @@ fn genesis_constructor(
         endowed_accounts.to_vec(),
         members,
         forum_cfg,
-        versioned_store_cfg,
-        versioned_store_permissions_cfg,
-        data_directory_config,
-        content_working_group_config,
         initial_account_balances,
     )
 }
@@ -293,7 +244,6 @@ fn generate_chain_spec(
     sudo_account: String,
     initial_members_path: Option<PathBuf>,
     initial_forum_path: Option<PathBuf>,
-    initial_content_path: Option<PathBuf>,
     initial_balances_path: Option<PathBuf>,
 ) -> Result<String, String> {
     let parse_account = |address: &String| {
@@ -327,7 +277,6 @@ fn generate_chain_spec(
                 &sudo_account,
                 &initial_members_path,
                 &initial_forum_path,
-                &initial_content_path,
                 &initial_balances_path,
             )
         },
@@ -408,7 +357,6 @@ fn main() -> Result<(), String> {
     let chain_spec_path = builder.chain_spec_path().to_path_buf();
     let initial_members_path = builder.initial_members_path().clone();
     let initial_forum_path = builder.initial_forum_path().clone();
-    let initial_content_path = builder.initial_content_path().clone();
     let initial_balances_path = builder.initial_balances_path().clone();
     let deployment = builder.chain_deployment();
 
@@ -460,7 +408,6 @@ fn main() -> Result<(), String> {
         sudo_account,
         initial_members_path,
         initial_forum_path,
-        initial_content_path,
         initial_balances_path,
     )?;
 

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff